1""" 
    2<Module Name> 
    3  constants.py 
    4 
    5<Author> 
    6  Santiago Torres-Arias <santiago@nyu.edu> 
    7 
    8<Started> 
    9  Nov 15, 2017 
    10 
    11<Copyright> 
    12  See LICENSE for licensing information. 
    13 
    14<Purpose> 
    15  aggregates all the constant definitions and lookup structures for signature 
    16  handling 
    17""" 
    18 
    19from __future__ import annotations 
    20 
    21import functools 
    22import logging 
    23import os 
    24import shlex 
    25import subprocess 
    26 
    27log = logging.getLogger(__name__) 
    28 
    29GPG_TIMEOUT = 10 
    30 
    31 
    32@functools.lru_cache(maxsize=3) 
    33def is_available_gnupg(gnupg: str, timeout: int | None = None) -> bool: 
    34    """Returns whether gnupg points to a gpg binary.""" 
    35    if timeout is None: 
    36        timeout = GPG_TIMEOUT 
    37 
    38    gpg_version_cmd = shlex.split(f"{gnupg} --version") 
    39    try: 
    40        subprocess.run(  # noqa: S603 
    41            gpg_version_cmd, 
    42            capture_output=True, 
    43            timeout=timeout, 
    44            check=True, 
    45        ) 
    46        return True 
    47    except (OSError, subprocess.TimeoutExpired): 
    48        return False 
    49 
    50 
    51GPG_ENV_COMMAND = os.environ.get("GNUPG") 
    52GPG2_COMMAND = "gpg2" 
    53GPG1_COMMAND = "gpg" 
    54 
    55 
    56def gpg_command() -> str: 
    57    """Returns command to run GPG, or ``""``` if not found).""" 
    58    # By default, we allow providing GPG client through the environment 
    59    # assuming gpg2 as default value and test if exists. Otherwise, we assume gpg 
    60    # exists. 
    61    if GPG_ENV_COMMAND: 
    62        if is_available_gnupg(GPG_ENV_COMMAND): 
    63            return GPG_ENV_COMMAND 
    64    elif is_available_gnupg(GPG2_COMMAND): 
    65        return GPG2_COMMAND 
    66    elif is_available_gnupg(GPG1_COMMAND): 
    67        return GPG1_COMMAND 
    68    return "" 
    69 
    70 
    71def have_gpg() -> bool: 
    72    """Returns True if a gpg_command is available.""" 
    73    return bool(gpg_command()) 
    74 
    75 
    76def gpg_version_command() -> list[str]: 
    77    """Returns the command to get the current GPG version.""" 
    78    return shlex.split(f"{gpg_command()} --version") 
    79 
    80 
    81FULLY_SUPPORTED_MIN_VERSION = "2.1.0" 
    82NO_GPG_MSG = ( 
    83    f"GPG support requires a GPG client. 'gpg2' or 'gpg' with version " 
    84    f"{FULLY_SUPPORTED_MIN_VERSION} or newer is fully supported." 
    85) 
    86 
    87 
    88def gpg_sign_command(keyarg: str, homearg: str) -> list[str]: 
    89    """Returns the command to use GPG to sign STDIN.""" 
    90    return shlex.split( 
    91        f"{gpg_command()} --detach-sign --digest-algo SHA256 {keyarg} {homearg}" 
    92    ) 
    93 
    94 
    95def gpg_export_pubkey_command(homearg: str, keyid: str) -> list[str]: 
    96    """Returns the GPG command to export a public key.""" 
    97    return shlex.split(f"{gpg_command()} {homearg} --export {keyid}") 
    98 
    99 
    100# See RFC4880 section 4.3. Packet Tags for a list of all packet types The 
    101# relevant packets defined below are described in sections 5.2 (signature), 
    102# 5.5.1.1 (primary pubkey) and 5.5.1.2 (pub subkey), 5.12 (user id) and 5.13 
    103# (user attribute) 
    104PACKET_TYPE_SIGNATURE = 0x02 
    105PACKET_TYPE_PRIMARY_KEY = 0x06 
    106PACKET_TYPE_USER_ID = 0x0D 
    107PACKET_TYPE_USER_ATTR = 0x11 
    108PACKET_TYPE_SUB_KEY = 0x0E 
    109 
    110 
    111# See sections 5.2.3 (signature) and 5.5.2 (public key) of RFC4880 
    112SUPPORTED_SIGNATURE_PACKET_VERSIONS = {0x04} 
    113SUPPORTED_PUBKEY_PACKET_VERSIONS = {0x04} 
    114 
    115# The constants for hash algorithms are taken from section 9.4 of RFC4880. 
    116SHA1 = 0x02 
    117SHA256 = 0x08 
    118SHA512 = 0x0A 
    119 
    120# See section 5.2.1 of RFC4880 
    121SIGNATURE_TYPE_BINARY = 0x00 
    122SIGNATURE_TYPE_SUB_KEY_BINDING = 0x18 
    123SIGNATURE_TYPE_CERTIFICATES = {0x10, 0x11, 0x12, 0x13} 
    124 
    125# See section 5.2.3.4 (Signature Creation Time) of RFC4880 
    126SIG_CREATION_SUBPACKET = 0x02 
    127# See section 5.2.3.5. (Issuer) of RFC4880 
    128PARTIAL_KEYID_SUBPACKET = 0x10 
    129# See section 5.2.3.6 (Key Expiration Time) of RFC4880 
    130KEY_EXPIRATION_SUBPACKET = 0x09 
    131# See section 5.2.3.19 (Primary User ID) of RFC4880 
    132PRIMARY_USERID_SUBPACKET = 0x19 
    133# See section 5.2.3.28. (Issuer Fingerprint) of rfc4880bis-06 
    134FULL_KEYID_SUBPACKET = 0x21 
    135 
    136GPG_HASH_ALGORITHM_STRING = "pgp+SHA2"