class Error(Exception):
class Warning(Warning):
class FormatError(Error):
class InvalidMetadataJSONError(FormatError):
class UnsupportedAlgorithmError(Error):
class BadHashError(Error):
class BadPasswordError(Error):
class CryptoError(Error):
class BadSignatureError(CryptoError):
class UnknownMethodError(CryptoError):
class UnsupportedLibraryError(Error):
class InvalidNameError(Error):
class NotFoundError(Error):
class URLMatchesNoPatternError(Error):
class InvalidConfigurationError(Error):
class StorageError(Error):
  def __init__(self, exception):
  def __str__(self):
  def __init__(self, expected_hash, observed_hash):
  def __str__(self):
  def __init__(self, metadata_role_name):
  def __str__(self): ANY_STRING_SCHEMA = SCHEMA.AnyString()
LIST_OF_ANY_STRING_SCHEMA = SCHEMA.ListOf(ANY_STRING_SCHEMA)
ISO8601_DATETIME_SCHEMA = SCHEMA.RegularExpression(r'\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z')
UNIX_TIMESTAMP_SCHEMA = SCHEMA.Integer(lo=0, hi=2147483647)
HEX_SCHEMA = SCHEMA.RegularExpression(r'[a-fA-F0-9]+')
HASH_SCHEMA = HEX_SCHEMA
HASHDICT_SCHEMA = SCHEMA.DictOf(
  key_schema = SCHEMA.AnyString(),
  value_schema = HASH_SCHEMA)
URL_SCHEMA = SCHEMA.AnyString()
KEYID_SCHEMA = HASH_SCHEMA
KEYIDS_SCHEMA = SCHEMA.ListOf(KEYID_SCHEMA)
SCHEME_SCHEMA = SCHEMA.AnyString()
PATH_SCHEMA = SCHEMA.AnyNonemptyString()
PATHS_SCHEMA = SCHEMA.ListOf(PATH_SCHEMA)
LOGLEVEL_SCHEMA = SCHEMA.Integer(lo=0, hi=50)
NAME_SCHEMA = SCHEMA.AnyString()
NAMES_SCHEMA = SCHEMA.ListOf(NAME_SCHEMA)
DATA_SCHEMA = SCHEMA.AnyBytes()
TEXT_SCHEMA = SCHEMA.AnyString()
HASHALGORITHMS_SCHEMA = SCHEMA.ListOf(SCHEMA.OneOf(
  [SCHEMA.String('md5'), SCHEMA.String('sha1'),
   SCHEMA.String('sha224'), SCHEMA.String('sha256'),
   SCHEMA.String('sha384'), SCHEMA.String('sha512'),
   SCHEMA.String('blake2s'), SCHEMA.String('blake2b'),
   SCHEMA.String('blake2b-256')]))
ENCRYPTEDKEY_SCHEMA = SCHEMA.AnyString()
BOOLEAN_SCHEMA = SCHEMA.Boolean()
RSAKEYBITS_SCHEMA = SCHEMA.Integer(lo=2048)
ECDSA_SCHEME_SCHEMA = SCHEMA.RegularExpression(r'ecdsa-sha2-nistp(256|384)')
PYCACRYPTOSIGNATURE_SCHEMA = SCHEMA.AnyBytes()
PEMRSA_SCHEMA = SCHEMA.AnyString()
PEMECDSA_SCHEMA = SCHEMA.AnyString()
PASSWORD_SCHEMA = SCHEMA.AnyString()
PASSWORDS_SCHEMA = SCHEMA.ListOf(PASSWORD_SCHEMA)
KEYVAL_SCHEMA = SCHEMA.Object(
  object_name = 'KEYVAL_SCHEMA',
  public = SCHEMA.AnyString(),
  private = SCHEMA.Optional(SCHEMA.AnyString()))
PUBLIC_KEYVAL_SCHEMA = SCHEMA.Object(
  object_name = 'KEYVAL_SCHEMA',
  public = SCHEMA.AnyString(),
  private = SCHEMA.Optional(SCHEMA.String("")))
KEYTYPE_SCHEMA = SCHEMA.OneOf(
  [SCHEMA.String('rsa'), SCHEMA.String('ed25519'), SCHEMA.String('ecdsa'),
   SCHEMA.RegularExpression(r'ecdsa-sha2-nistp(256|384)')])
KEY_SCHEMA = SCHEMA.Object(
  object_name = 'KEY_SCHEMA',
  keytype = SCHEMA.AnyString(),
  scheme = SCHEME_SCHEMA,
  keyval = KEYVAL_SCHEMA,
  expires = SCHEMA.Optional(ISO8601_DATETIME_SCHEMA))
PUBLIC_KEY_SCHEMA = SCHEMA.Object(
  object_name = 'PUBLIC_KEY_SCHEMA',
  keytype = SCHEMA.AnyString(),
  keyid_hash_algorithms = SCHEMA.Optional(HASHALGORITHMS_SCHEMA),
  keyval = PUBLIC_KEYVAL_SCHEMA,
  expires = SCHEMA.Optional(ISO8601_DATETIME_SCHEMA))
ANYKEY_SCHEMA = SCHEMA.Object(
  object_name = 'ANYKEY_SCHEMA',
  keytype = KEYTYPE_SCHEMA,
  scheme = SCHEME_SCHEMA,
  keyid = KEYID_SCHEMA,
  keyid_hash_algorithms = SCHEMA.Optional(HASHALGORITHMS_SCHEMA),
  keyval = KEYVAL_SCHEMA,
  expires = SCHEMA.Optional(ISO8601_DATETIME_SCHEMA))
ANYKEYLIST_SCHEMA = SCHEMA.ListOf(ANYKEY_SCHEMA)
RSA_SCHEME_SCHEMA = SCHEMA.OneOf([
  SCHEMA.RegularExpression(r'rsassa-pss-(md5|sha1|sha224|sha256|sha384|sha512)'),
  SCHEMA.RegularExpression(r'rsa-pkcs1v15-(md5|sha1|sha224|sha256|sha384|sha512)')])
RSAKEY_SCHEMA = SCHEMA.Object(
  object_name = 'RSAKEY_SCHEMA',
  keytype = SCHEMA.String('rsa'),
  scheme = RSA_SCHEME_SCHEMA,
  keyid = KEYID_SCHEMA,
  keyid_hash_algorithms = SCHEMA.Optional(HASHALGORITHMS_SCHEMA),
  keyval = KEYVAL_SCHEMA)
ECDSAKEY_SCHEMA = SCHEMA.Object(
  object_name = 'ECDSAKEY_SCHEMA',
  keytype = SCHEMA.OneOf([SCHEMA.String('ecdsa'),
                          SCHEMA.RegularExpression(r'ecdsa-sha2-nistp(256|384)')]),
  scheme = ECDSA_SCHEME_SCHEMA,
  keyid = KEYID_SCHEMA,
  keyid_hash_algorithms = SCHEMA.Optional(HASHALGORITHMS_SCHEMA),
  keyval = KEYVAL_SCHEMA)
ED25519PUBLIC_SCHEMA = SCHEMA.LengthBytes(32)
ED25519SEED_SCHEMA = SCHEMA.LengthBytes(32)
ED25519SIGNATURE_SCHEMA = SCHEMA.LengthBytes(64)
ECDSASIGNATURE_SCHEMA = SCHEMA.AnyBytes()
ED25519_SIG_SCHEMA = SCHEMA.OneOf([SCHEMA.String('ed25519')])
ED25519KEY_SCHEMA = SCHEMA.Object(
  object_name = 'ED25519KEY_SCHEMA',
  keytype = SCHEMA.String('ed25519'),
  scheme = ED25519_SIG_SCHEMA,
  keyid = KEYID_SCHEMA,
  keyid_hash_algorithms = SCHEMA.Optional(HASHALGORITHMS_SCHEMA),
  keyval = KEYVAL_SCHEMA)
GPG_HASH_ALGORITHM_STRING = "pgp+SHA2"
GPG_RSA_PUBKEY_METHOD_STRING = "pgp+rsa-pkcsv1.5"
GPG_DSA_PUBKEY_METHOD_STRING = "pgp+dsa-fips-180-2"
GPG_ED25519_PUBKEY_METHOD_STRING = "pgp+eddsa-ed25519"
GPG_RSA_PUBKEYVAL_SCHEMA = SCHEMA.Object(
  object_name = "GPG_RSA_PUBKEYVAL_SCHEMA",
  e = SCHEMA.AnyString(),
  n = HEX_SCHEMA
)
_GPG_RSA_PUBKEY_SCHEMA = SCHEMA.Object(
  object_name = "GPG_RSA_PUBKEY_SCHEMA",
  type = SCHEMA.String("rsa"),
  method = SCHEMA.String(GPG_RSA_PUBKEY_METHOD_STRING),
  hashes = SCHEMA.ListOf(SCHEMA.String(GPG_HASH_ALGORITHM_STRING)),
  creation_time = SCHEMA.Optional(UNIX_TIMESTAMP_SCHEMA),
  validity_period = SCHEMA.Optional(SCHEMA.Integer(lo=0)),
  keyid = KEYID_SCHEMA,
  keyval = SCHEMA.Object(
      public = GPG_RSA_PUBKEYVAL_SCHEMA,
      private = SCHEMA.String("")
    )
)
GPG_RSA_PUBKEY_SCHEMA = _create_gpg_pubkey_with_subkey_schema(
    _GPG_RSA_PUBKEY_SCHEMA)
GPG_DSA_PUBKEYVAL_SCHEMA = SCHEMA.Object(
  object_name = "GPG_DSA_PUBKEYVAL_SCHEMA",
  y = HEX_SCHEMA,
  p = HEX_SCHEMA,
  q = HEX_SCHEMA,
  g = HEX_SCHEMA
)
_GPG_DSA_PUBKEY_SCHEMA = SCHEMA.Object(
  object_name = "GPG_DSA_PUBKEY_SCHEMA",
  type = SCHEMA.String("dsa"),
  method = SCHEMA.String(GPG_DSA_PUBKEY_METHOD_STRING),
  hashes = SCHEMA.ListOf(SCHEMA.String(GPG_HASH_ALGORITHM_STRING)),
  creation_time = SCHEMA.Optional(UNIX_TIMESTAMP_SCHEMA),
  validity_period = SCHEMA.Optional(SCHEMA.Integer(lo=0)),
  keyid = KEYID_SCHEMA,
  keyval = SCHEMA.Object(
      public = GPG_DSA_PUBKEYVAL_SCHEMA,
      private = SCHEMA.String("")
    )
)
GPG_DSA_PUBKEY_SCHEMA = _create_gpg_pubkey_with_subkey_schema(
    _GPG_DSA_PUBKEY_SCHEMA)
GPG_ED25519_PUBKEYVAL_SCHEMA = SCHEMA.Object(
  object_name = "GPG_ED25519_PUBKEYVAL_SCHEMA",
  q = HEX_SCHEMA,
)
_GPG_ED25519_PUBKEY_SCHEMA = SCHEMA.Object(
  object_name = "GPG_ED25519_PUBKEY_SCHEMA",
  type = SCHEMA.String("eddsa"),
  method = SCHEMA.String(GPG_ED25519_PUBKEY_METHOD_STRING),
  hashes = SCHEMA.ListOf(SCHEMA.String(GPG_HASH_ALGORITHM_STRING)),
  creation_time = SCHEMA.Optional(UNIX_TIMESTAMP_SCHEMA),
  validity_period = SCHEMA.Optional(SCHEMA.Integer(lo=0)),
  keyid = KEYID_SCHEMA,
  keyval = SCHEMA.Object(
      public = GPG_ED25519_PUBKEYVAL_SCHEMA,
      private = SCHEMA.String("")
    )
)
GPG_ED25519_PUBKEY_SCHEMA = _create_gpg_pubkey_with_subkey_schema(
    _GPG_ED25519_PUBKEY_SCHEMA)
GPG_PUBKEY_SCHEMA = SCHEMA.OneOf([GPG_RSA_PUBKEY_SCHEMA,
    GPG_DSA_PUBKEY_SCHEMA, GPG_ED25519_PUBKEY_SCHEMA])
GPG_SIGNATURE_SCHEMA = SCHEMA.Object(
    object_name = "SIGNATURE_SCHEMA",
    keyid = KEYID_SCHEMA,
    short_keyid = SCHEMA.Optional(KEYID_SCHEMA),
    other_headers = HEX_SCHEMA,
    signature = HEX_SCHEMA,
    info = SCHEMA.Optional(SCHEMA.Any()),
  )
SIGNATURE_SCHEMA = SCHEMA.Object(
  object_name = 'SIGNATURE_SCHEMA',
  keyid = KEYID_SCHEMA,
  sig = HEX_SCHEMA)
KEYDICT_SCHEMA = SCHEMA.DictOf(
  key_schema = KEYID_SCHEMA,
  value_schema = KEY_SCHEMA)
ANY_SIGNATURE_SCHEMA = SCHEMA.OneOf([SIGNATURE_SCHEMA,
    GPG_SIGNATURE_SCHEMA])
SIGNATURES_SCHEMA = SCHEMA.ListOf(ANY_SIGNATURE_SCHEMA)
SIGNABLE_SCHEMA = SCHEMA.Object(
  object_name = 'SIGNABLE_SCHEMA',
  signed = SCHEMA.Any(),
  signatures = SIGNATURES_SCHEMA)
ANY_VERIFICATION_KEY_SCHEMA = SCHEMA.OneOf([ANYKEY_SCHEMA,
    GPG_PUBKEY_SCHEMA])
VERIFICATION_KEY_DICT_SCHEMA = SCHEMA.DictOf(
  key_schema = KEYID_SCHEMA,
  value_schema = ANY_VERIFICATION_KEY_SCHEMA)
ANY_KEYDICT_SCHEMA = SCHEMA.OneOf([KEYDICT_SCHEMA,
    VERIFICATION_KEY_DICT_SCHEMA])
ANY_PUBKEY_SCHEMA = SCHEMA.OneOf([PUBLIC_KEY_SCHEMA, GPG_PUBKEY_SCHEMA])
ANY_PUBKEY_DICT_SCHEMA = SCHEMA.DictOf(
  key_schema = KEYID_SCHEMA,
  value_schema = ANY_PUBKEY_SCHEMA)
def _create_gpg_pubkey_with_subkey_schema(pubkey_schema):
def datetime_to_unix_timestamp(datetime_object):
def unix_timestamp_to_datetime(unix_timestamp):
def format_base64(data):
def parse_base64(base64_string):
def _canonical_string_encoder(string):
def _encode_canonical(object, output_function):
def encode_canonical(object, output_function=None): def get_pubkey_bundle(data, keyid):
  """
  <Purpose>
    Call function to extract and verify master key and subkeys from the passed
    gpg key data, where either the master key or one of the subkeys matches the
    passed keyid.

    NOTE:
    - If the keyid matches one of the subkeys, a warning is issued to notify
      the user about potential privilege escalation
    - Subkeys with invalid key binding signatures are discarded

  <Arguments>
    data:
          Public key data as written to stdout by
          securesystemslib.gpg.constants.GPG_EXPORT_PUBKEY_COMMAND.

    keyid:
          The keyid of the master key or one of its subkeys expected to be
          contained in the passed gpg data.

  <Exceptions>
    securesystemslib.gpg.exceptions.PacketParsingError
          If the key data could not be parsed

    securesystemslib.gpg.exceptions.KeyNotFoundError
          If the passed data is empty.
          If no master key or subkeys could be found that matches the passed
          keyid.

    securesystemslib.exceptions.FormatError
          If the passed keyid does not match
          securesystemslib.formats.KEYID_SCHEMA

  <Side Effects>
    None.

  <Returns>
    A public key in the format securesystemslib.formats.GPG_PUBKEY_SCHEMA with
    optional subkeys.

  """
  formats.KEYID_SCHEMA.check_match(keyid)
  if not data:
    raise KeyNotFoundError("Could not find gpg key '{}' in empty exported key "
        "data.".format(keyid))

  # Parse out master key and subkeys (enriched and verified via certificates
  # and binding signatures)
  raw_key_bundle = parse_pubkey_bundle(data)
  master_public_key = _assign_certified_key_info(raw_key_bundle)
  sub_public_keys = _get_verified_subkeys(raw_key_bundle)

  # Since GPG returns all pubkeys associated with a keyid (master key and
  # subkeys) we check which key matches the passed keyid.
  # If the matching key is a subkey, we warn the user because we return
  # the whole bundle (master plus all subkeys) and not only the subkey.
  # If no matching key is found we raise a KeyNotFoundError.
  for idx, public_key in enumerate(
      [master_public_key] + list(sub_public_keys.values())):
    if public_key and public_key["keyid"].endswith(keyid.lower()):
      if idx > 1:
        log.warning("Exporting master key '{}' including subkeys '{}' for"
            " passed keyid '{}'.".format(master_public_key["keyid"],
            ", ".join(list(sub_public_keys.keys())), keyid))
      break

  else:
    raise KeyNotFoundError("Could not find gpg key '{}' in exported key data."
        .format(keyid))

  # Add subkeys dictionary to master pubkey "subkeys" field if subkeys exist
  if sub_public_keys:
    master_public_key["subkeys"] = sub_public_keys

  return master_public_key def parse_signature_packet(data, supported_signature_types=None,
    supported_hash_algorithms=None, include_info=False):
  """
  <Purpose>
    Parse the signature information on an RFC4880-encoded binary signature data
    buffer.

    NOTE: Older gpg versions (< FULLY_SUPPORTED_MIN_VERSION) might only
    reveal the partial key id. It is the callers responsibility to determine
    the full keyid based on the partial keyid, e.g. by exporting the related
    public and replacing the partial keyid with the full keyid.

  <Arguments>
    data:
           the RFC4880-encoded binary signature data buffer as described in
           section 5.2 (and 5.2.3.1).
    supported_signature_types: (optional)
          a set of supported signature_types, the signature packet may be
          (see securesystemslib.gpg.constants for available types). If None is
          specified the signature packet must be of type SIGNATURE_TYPE_BINARY.
    supported_hash_algorithms: (optional)
          a set of supported hash algorithm ids, the signature packet
          may use. Available ids are SHA1, SHA256, SHA512 (see
          securesystemslib.gpg.constants). If None is specified, the signature
          packet must use SHA256.
    include_info: (optional)
          a boolean that indicates whether an opaque dictionary should be
          added to the returned signature under the key "info". Default is
          False.

  <Exceptions>
    ValueError: if the signature packet is not supported or the data is
      malformed
    IndexError: if the signature packet is incomplete

  <Side Effects>
    None.

  <Returns>
    A signature dictionary matching
    securesystemslib.formats.GPG_SIGNATURE_SCHEMA with the following special
    characteristics:
     - The "keyid" field is an empty string if it cannot be determined
     - The "short_keyid" is not added if it cannot be determined
     - At least one of non-empty "keyid" or "short_keyid" are part of the
       signature

  """
  if not supported_signature_types:
    supported_signature_types = {SIGNATURE_TYPE_BINARY}

  if not supported_hash_algorithms:
    supported_hash_algorithms = {SHA256}

  _, header_len, _, packet_len = gpg_util.parse_packet_header(
      data, PACKET_TYPE_SIGNATURE)

  data = bytearray(data[header_len:packet_len])

  ptr = 0

  # we get the version number, which we also expect to be v4, or we bail
  # FIXME: support v3 type signatures (which I haven't seen in the wild)
  version_number = data[ptr]
  ptr += 1
  if version_number not in SUPPORTED_SIGNATURE_PACKET_VERSIONS:
    raise ValueError("Signature version '{}' not supported, must be one of "
        "{}.".format(version_number, SUPPORTED_SIGNATURE_PACKET_VERSIONS))

  # Per default we only parse "signatures of a binary document". Other types
  # may be allowed by passing type constants via `supported_signature_types`.
  # Types include revocation signatures, key binding signatures, persona
  # certifications, etc. (see RFC 4880 section 5.2.1.).
  signature_type = data[ptr]
  ptr += 1

  if signature_type not in supported_signature_types:
    raise ValueError("Signature type '{}' not supported, must be one of {} "
        "(see RFC4880 5.2.1. Signature Types).".format(signature_type,
        supported_signature_types))

  signature_algorithm = data[ptr]
  ptr += 1

  if signature_algorithm not in SUPPORTED_SIGNATURE_ALGORITHMS:
    raise ValueError("Signature algorithm '{}' not "
        "supported, please verify that your gpg configuration is creating "
        "either DSA, RSA, or EdDSA signatures (see RFC4880 9.1. Public-Key "
        "Algorithms).".format(signature_algorithm))

  key_type = SUPPORTED_SIGNATURE_ALGORITHMS[signature_algorithm]['type']
  handler = SIGNATURE_HANDLERS[key_type]

  hash_algorithm = data[ptr]
  ptr += 1

  if hash_algorithm not in supported_hash_algorithms:
    raise ValueError("Hash algorithm '{}' not supported, must be one of {}"
        " (see RFC4880 9.4. Hash Algorithms).".format(hash_algorithm,
        supported_hash_algorithms))

  # Obtain the hashed octets
  hashed_octet_count = struct.unpack(">H", data[ptr:ptr+2])[0]
  ptr += 2
  hashed_subpackets = data[ptr:ptr+hashed_octet_count]
  hashed_subpacket_info = gpg_util.parse_subpackets(hashed_subpackets)

  # Check whether we were actually able to read this much hashed octets
  if len(hashed_subpackets) != hashed_octet_count: # pragma: no cover
    raise ValueError("This signature packet seems to be corrupted."
        "It is missing hashed octets!")

  ptr += hashed_octet_count
  other_headers_ptr = ptr

  unhashed_octet_count = struct.unpack(">H", data[ptr: ptr + 2])[0]
  ptr += 2

  unhashed_subpackets = data[ptr:ptr+unhashed_octet_count]
  unhashed_subpacket_info = gpg_util.parse_subpackets(unhashed_subpackets)

  ptr += unhashed_octet_count

  # Use the info dict to return further signature information that may be
  # needed for intermediate processing, but does not have to be on the eventual
  # signature datastructure
  info = {
    "signature_type": signature_type,
    "hash_algorithm": hash_algorithm,
    "creation_time": None,
    "subpackets": {},
  }

  keyid = ""
  short_keyid = ""


  # Parse "Issuer" (short keyid) and "Issuer Fingerprint" (full keyid) type
  # subpackets
  # Strategy: Loop over all unhashed and hashed subpackets (in that order!) and
  # store only the last of a type. Due to the order in the loop, hashed
  # subpackets are prioritized over unhashed subpackets (see NOTEs below).

  # NOTE: A subpacket may be found either in the hashed or unhashed subpacket
  # sections of a signature. If a subpacket is not hashed, then the information
  # in it cannot be considered definitive because it is not part of the
  # signature proper. (see RFC4880 5.2.3.2.)
  # NOTE: Signatures may contain conflicting information in subpackets. In most
  # cases, an implementation SHOULD use the last subpacket, but MAY use any
  # conflict resolution scheme that makes more sense. (see RFC4880 5.2.4.1.)
  for idx, subpacket_tuple in \
      enumerate(unhashed_subpacket_info + hashed_subpacket_info):

    # The idx indicates if the info is from the unhashed (first) or
    # hashed (second) of the above concatenated lists
    is_hashed = (idx >= len(unhashed_subpacket_info))
    subpacket_type, subpacket_data = subpacket_tuple

    # Warn if expiration subpacket is not hashed
    if subpacket_type == KEY_EXPIRATION_SUBPACKET:
      if not is_hashed:
        log.warning("Expiration subpacket not hashed, gpg client possibly "
            "exporting a weakly configured key.")

    # Full keyids are only available in newer signatures
    # (see RFC4880 and rfc4880bis-06 5.2.3.1.)
    if subpacket_type == FULL_KEYID_SUBPACKET: # pragma: no cover
      # Exclude from coverage for consistent results across test envs
      # NOTE: The first byte of the subpacket payload is a version number
      # (see rfc4880bis-06 5.2.3.28.)
      keyid = binascii.hexlify(subpacket_data[1:]).decode("ascii")

    # We also return the short keyid, because the full might not be available
    if subpacket_type == PARTIAL_KEYID_SUBPACKET:
      short_keyid = binascii.hexlify(subpacket_data).decode("ascii")

    if subpacket_type == SIG_CREATION_SUBPACKET:
      info["creation_time"] = struct.unpack(">I", subpacket_data)[0]

    info["subpackets"][subpacket_type] = subpacket_data


  # Fail if there is no keyid at all (this should not happen)
  if not (keyid or short_keyid): # pragma: no cover
    raise ValueError("This signature packet seems to be corrupted. It does "
        "not have an 'Issuer' or 'Issuer Fingerprint' subpacket (see RFC4880 "
        "and rfc4880bis-06 5.2.3.1. Signature Subpacket Specification).")

  # Fail if keyid and short keyid are specified but don't match
  if keyid and not keyid.endswith(short_keyid): # pragma: no cover
    raise ValueError("This signature packet seems to be corrupted. The key ID "
        "'{}' of the 'Issuer' subpacket must match the lower 64 bits of the "
        "fingerprint '{}' of the 'Issuer Fingerprint' subpacket (see RFC4880 "
        "and rfc4880bis-06 5.2.3.28. Issuer Fingerprint).".format(
        short_keyid, keyid))

  if not info["creation_time"]: # pragma: no cover
    raise ValueError("This signature packet seems to be corrupted. It does "
        "not have a 'Signature Creation Time' subpacket (see RFC4880 5.2.3.4 "
        "Signature Creation Time).")

  # Uncomment this variable to obtain the left-hash-bits information (used for
  # early rejection)
  #left_hash_bits = struct.unpack(">H", data[ptr:ptr+2])[0]
  ptr += 2

  # Finally, fetch the actual signature (as opposed to signature metadata).
  signature = handler.get_signature_params(data[ptr:])

  signature_data = {
    'keyid': "{}".format(keyid),
    'other_headers': binascii.hexlify(data[:other_headers_ptr]).decode('ascii'),
    'signature': binascii.hexlify(signature).decode('ascii')
  }

  if short_keyid: # pragma: no branch
    signature_data["short_keyid"] = short_keyid

  if include_info:
    signature_data["info"] = info

  return signature_data class CommandError(Exception):
  pass class KeyExpirationError(Exception):
  def __init__(self, key):
    super(KeyExpirationError, self).__init__()
    self.key = key

  def __str__(self):
    creation_time = datetime.datetime.utcfromtimestamp(
        self.key["creation_time"])
    expiration_time = datetime.datetime.utcfromtimestamp(
        self.key["creation_time"] + self.key["validity_period"])
    validity_period = expiration_time - creation_time

    return ("GPG key '{}' created on '{:%Y-%m-%d %H:%M} UTC' with validity "
        "period '{}' expired on '{:%Y-%m-%d %H:%M} UTC'.".format(
        self.key["keyid"], creation_time, validity_period, expiration_time)) FULLY_SUPPORTED_MIN_VERSION = "2.1.0" GPG_EXPORT_PUBKEY_COMMAND = GPG_COMMAND + " {homearg} --export {keyid}" GPG_SIGN_COMMAND = GPG_COMMAND + \
                   " --detach-sign --digest-algo SHA256 {keyarg} {homearg}" HAVE_GPG = False NO_GPG_MSG = "GPG support requires a GPG client. 'gpg2' or 'gpg' with version {} or newer is" \
  " fully supported.".format(FULLY_SUPPORTED_MIN_VERSION) SHA256 = 0x08 SIGNATURE_HANDLERS = {
    "rsa": rsa,
    "dsa": dsa,
    "eddsa": eddsa
} DEVNULL = subprocess.DEVNULL
PIPE = subprocess.PIPE
def _default_timeout():
def run(cmd, check=True, timeout=_default_timeout(), **kwargs):
def run_duplicate_streams(cmd, timeout=_default_timeout()):
      def _duplicate_streams(): CRYPTO = True
import logging
import time
from securesystemslib import exceptions
from securesystemslib import formats
from securesystemslib.gpg.common import (
    get_pubkey_bundle, parse_signature_packet)
from securesystemslib.gpg.exceptions import (
    CommandError, KeyExpirationError)
from securesystemslib.gpg.constants import (
    FULLY_SUPPORTED_MIN_VERSION,
    GPG_EXPORT_PUBKEY_COMMAND,
    GPG_SIGN_COMMAND,
    HAVE_GPG,
    NO_GPG_MSG,
    SHA256)
from securesystemslib.gpg.handlers import (
    SIGNATURE_HANDLERS)
from securesystemslib import process
from securesystemslib.gpg.rsa import CRYPTO
"""
<Module Name>
  functions.py

<Author>
  Santiago Torres-Arias <santiago@nyu.edu>

<Started>
  Nov 15, 2017

<Copyright>
  See LICENSE for licensing information.

<Purpose>
  publicly-usable functions for exporting public-keys, signing data and
  verifying signatures.
"""



log = logging.getLogger(__name__)

NO_CRYPTO_MSG = "GPG support requires the cryptography library"



def create_signature(content, keyid=None, homedir=None):
  """
  <Purpose>
    Calls the gpg command line utility to sign the passed content with the key
    identified by the passed keyid from the gpg keyring at the passed homedir.

    The executed base command is defined in
    securesystemslib.gpgp.constants.GPG_SIGN_COMMAND.

    NOTE: On not fully supported versions of GPG, i.e. versions below
    securesystemslib.gpg.constants.FULLY_SUPPORTED_MIN_VERSION the returned
    signature does not contain the full keyid. As a work around, we export the
    public key bundle identified by the short keyid to compute the full keyid
    and add it to the returned signature.

  <Arguments>
    content:
            The content to be signed. (bytes)

    keyid: (optional)
            The keyid of the gpg signing keyid. If not passed the default
            key in the keyring is used.

    homedir: (optional)
            Path to the gpg keyring. If not passed the default keyring is used.

  <Exceptions>
    securesystemslib.exceptions.FormatError:
            If the keyid was passed and does not match
            securesystemslib.formats.KEYID_SCHEMA

    ValueError:
            If the gpg command failed to create a valid signature.

    OSError:
            If the gpg command is not present or non-executable.

    securesystemslib.exceptions.UnsupportedLibraryError:
            If the gpg command is not available, or
            the cryptography library is not installed.

    securesystemslib.gpg.exceptions.CommandError:
            If the gpg command returned a non-zero exit code

    securesystemslib.gpg.exceptions.KeyNotFoundError:
            If the used gpg version is not fully supported
            and no public key can be found for short keyid.

  <Side Effects>
    None.

  <Returns>
    The created signature in the format:
    securesystemslib.formats.GPG_SIGNATURE_SCHEMA.

  """
  if not HAVE_GPG: # pragma: no cover
    raise exceptions.UnsupportedLibraryError(NO_GPG_MSG)

  if not CRYPTO: # pragma: no cover
    raise exceptions.UnsupportedLibraryError(NO_CRYPTO_MSG)

  keyarg = ""
  if keyid:
    formats.KEYID_SCHEMA.check_match(keyid)
    keyarg = "--local-user {}".format(keyid)

  homearg = ""
  if homedir:
    homearg = "--homedir {}".format(homedir).replace("\\", "/")

  command = GPG_SIGN_COMMAND.format(keyarg=keyarg, homearg=homearg)

  gpg_process = process.run(command, input=content, check=False,

      stdout=process.PIPE, stderr=process.PIPE)