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): CRYPTO = True
NO_CRYPTO_MSG = 'gpg.utils requires the cryptography library'
  CRYPTO = False
def get_mpi_length(data):
def hash_object(headers, algorithm, content):
def parse_packet_header(data, expected_type=None):
def compute_keyid(pubkey_packet_data):
def parse_subpacket_header(data):
def parse_subpackets(data):
def get_version():
def is_version_fully_supported():
def get_hashing_class(hash_algorithm_id): class PacketVersionNotSupportedError(Exception):
  pass class SignatureAlgorithmNotSupportedError(Exception):
  pass class KeyNotFoundError(Exception):
  pass class PacketParsingError(Exception):
  pass PACKET_TYPE_PRIMARY_KEY = 0x06 PACKET_TYPE_USER_ID = 0x0D PACKET_TYPE_USER_ATTR = 0x11 PACKET_TYPE_SUB_KEY = 0x0E PACKET_TYPE_SIGNATURE = 0x02 SUPPORTED_PUBKEY_PACKET_VERSIONS = {0x04} SIGNATURE_TYPE_BINARY = 0x00 SIGNATURE_TYPE_CERTIFICATES = {0x10, 0x11, 0x12, 0x13} SIGNATURE_TYPE_SUB_KEY_BINDING = 0x18 SUPPORTED_SIGNATURE_PACKET_VERSIONS = {0x04} FULL_KEYID_SUBPACKET = 0x21 PARTIAL_KEYID_SUBPACKET = 0x10 SHA1 = 0x02 SHA256 = 0x08 SHA512 = 0x0A KEY_EXPIRATION_SUBPACKET = 0x09 PRIMARY_USERID_SUBPACKET = 0x19 SIG_CREATION_SUBPACKET = 0x02 SIGNATURE_HANDLERS = {
    "rsa": rsa,
    "dsa": dsa,
    "eddsa": eddsa
} SUPPORTED_SIGNATURE_ALGORITHMS = {
    0x01: {
      "type":"rsa",
      "method": "pgp+rsa-pkcsv1.5",
      "handler": rsa
    },
    0x11: {
      "type": "dsa",
      "method": "pgp+dsa-fips-180-2",
      "handler": dsa
    },
    0x16: {
      "type": "eddsa",
      "method": "pgp+eddsa-ed25519",
      "handler": eddsa
    }
}
import struct
import binascii
import logging
import collections
from securesystemslib import formats
from securesystemslib.gpg import util as gpg_util
from securesystemslib.gpg.exceptions import (PacketVersionNotSupportedError,
    SignatureAlgorithmNotSupportedError, KeyNotFoundError, PacketParsingError)
from securesystemslib.gpg.constants import (
    PACKET_TYPE_PRIMARY_KEY, PACKET_TYPE_USER_ID, PACKET_TYPE_USER_ATTR,
    PACKET_TYPE_SUB_KEY, PACKET_TYPE_SIGNATURE,
    SUPPORTED_PUBKEY_PACKET_VERSIONS, SIGNATURE_TYPE_BINARY,
    SIGNATURE_TYPE_CERTIFICATES, SIGNATURE_TYPE_SUB_KEY_BINDING,
    SUPPORTED_SIGNATURE_PACKET_VERSIONS,
    FULL_KEYID_SUBPACKET, PARTIAL_KEYID_SUBPACKET,
    SHA1,SHA256, SHA512, KEY_EXPIRATION_SUBPACKET, PRIMARY_USERID_SUBPACKET,
    SIG_CREATION_SUBPACKET)
from securesystemslib.gpg.handlers import (
    SIGNATURE_HANDLERS, SUPPORTED_SIGNATURE_ALGORITHMS)
  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: