Ejemplo n.º 1
0
 def from_subpacket_content(cls, type_, critical, sub_data):
     assert len(sub_data) == 22
     tag = sub_data
     assert tag & 0x80
     sensitive = bool(tag & 0x40)
     public_key_algorithm = int(sub_data[1])
     fingerprint = utils.bytearray_to_hex(sub_data, 2, 20)
     return cls(critical, fingerprint, public_key_algorithm, sensitive=sensitive)
Ejemplo n.º 2
0
 def from_subpacket_content(cls, type_, critical, sub_data):
     assert len(sub_data) > 2
     public_key_algorithm = int(sub_data[0])
     hash_algorithm = int(sub_data[1])
     hash_length = utils.hash_lengths.get(hash_algorithm, None)
     if hash_length is None:
         hash_length = len(sub_data) - 2
     else:
         assert hash_length == len(sub_data) - 2
     hash_ = utils.bytearray_to_hex(sub_data, 2, len(sub_data) - 2)
     return cls(critical, public_key_algorithm, hash_algorithm, hash_, hash_length=hash_length)
Ejemplo n.º 3
0
    def __init__(self, critical, fingerprint, public_key_algorithm, sensitive=False):

        if isinstance(fingerprint, (bytes, bytearray)):
            assert len(fingerprint) == 20
            fingerprint = utils.bytearray_to_hex(fingerprint, 0, 20)
        else:
            assert len(fingerprint) == 40
        SignatureSubpacket.__init__(self, constants.REVOCATION_KEY_SUBPACKET_TYPE, critical)
        self.fingerprint = fingerprint
        self.public_key_algorithm = public_key_algorithm
        self.sensitive = sensitive
Ejemplo n.º 4
0
 def from_subpacket_content(cls, type_, critical, sub_data):
     assert len(sub_data) == 22
     tag = sub_data
     assert tag & 0x80
     sensitive = bool(tag & 0x40)
     public_key_algorithm = int(sub_data[1])
     fingerprint = utils.bytearray_to_hex(sub_data, 2, 20)
     return cls(critical,
                fingerprint,
                public_key_algorithm,
                sensitive=sensitive)
Ejemplo n.º 5
0
 def from_subpacket_content(cls, type_, critical, sub_data):
     assert len(sub_data) > 2
     public_key_algorithm = int(sub_data[0])
     hash_algorithm = int(sub_data[1])
     hash_length = utils.hash_lengths.get(hash_algorithm, None)
     if hash_length is None:
         hash_length = len(sub_data) - 2
     else:
         assert hash_length == len(sub_data) - 2
     hash_ = utils.bytearray_to_hex(sub_data, 2, len(sub_data) - 2)
     return cls(critical,
                public_key_algorithm,
                hash_algorithm,
                hash_,
                hash_length=hash_length)
Ejemplo n.º 6
0
    def __init__(self,
                 critical,
                 fingerprint,
                 public_key_algorithm,
                 sensitive=False):

        if isinstance(fingerprint, (bytes, bytearray)):
            assert len(fingerprint) == 20
            fingerprint = utils.bytearray_to_hex(fingerprint, 0, 20)
        else:
            assert len(fingerprint) == 40
        SignatureSubpacket.__init__(self,
                                    constants.REVOCATION_KEY_SUBPACKET_TYPE,
                                    critical)
        self.fingerprint = fingerprint
        self.public_key_algorithm = public_key_algorithm
        self.sensitive = sensitive
Ejemplo n.º 7
0
    def from_bytes(cls, symmetric_algorithm, data, offset=0):
        # GnuPG string-to-key
        # According to g10/parse-packet.c near line 1832, the 101 packet
        # type is a special GnuPG extension.  This S2K extension is
        # 6 bytes in total:
        #
        #   Octet 0:   101
        #   Octet 1:   hash algorithm
        #   Octet 2-4: "GNU"
        #   Octet 5:   mode integer
        #   Octet 6-n: serial number
        serial_number = None
        serial_len = None
        hash_algorithm = data[offset]
        offset += 1
        gnu = data[offset:offset + 3]
        offset += 3
        if gnu != bytearray(b"GNU"):
            raise ValueError(
                    "S2K parsing error: expected 'GNU', got %s" % gnu)

        mode = data[offset]
        mode += 1000
        offset += 1
        if mode == 1001:
            # GnuPG dummy
            pass
        elif mode == 1002:
            # OpenPGP card
            serial_len = data[offset]
            offset += 1
            if serial_len < 0:
                raise ValueError(
                        "Unexpected serial number length: %d" %
                        serial_len)

            serial_number = utils.bytearray_to_hex(data, offset, serial_len)
            offset += serial_len
        else:
            raise ValueError(
                    "Unsupported GnuPG S2K extension, encountered mode %d" %
                    mode)

        return cls(hash_algorithm, mode, symmetric_algorithm,
                   serial_number, serial_len), offset
Ejemplo n.º 8
0
def parse_signature_packet(p, parent_type, parent_key_ids=None,
                           sig_hashed=False,
                           # For testing
                           parse_signature_subpacket=parse_signature_subpacket
                           ):
    """Parse a single pgpdump signature packet into a Python dictionary
    of values. sig_hashed should be set to True for signature packets
    which are embedded as hashed signature subpackets.
    """

    signature = {}
    signature['validated'] = None
    signature['hash_algorithm_type'] = p.raw_hash_algorithm
    signature['pub_algorithm_type'] = p.raw_pub_algorithm
    signature['sig_type'] = p.raw_sig_type
    signature['sig_version'] = p.sig_version
    signature['hash2'] = utils.bytearray_to_hex(p.hash2)

    # "If a subpacket is not hashed, then the information in it cannot be
    #  considered definitive because it is not part of the signature proper."
    #
    # For convenience, include the fields that are always hashed.
    signature['hashed'] = [
        'hash_algorithm_type',
        'pub_algorithm_type',
        'sig_type',
        'sig_version',
        ]

    if p.sig_version in (2, 3):
        # Only trust these explicitly if the version is < 4. If the version is
        # 4 or greater, these values may have come from unhashed subpackets
        # and could have been manipulated.

        signature['key_ids'] = [{
                'key_id': p.key_id.upper(),
                'hashed': True,
            }]
        signature['selfsig'] = p.key_id.upper() in parent_key_ids
        signature['creation_time'] = p.raw_creation_time
        signature['hashed'].extend([
                'creation_time',
            ])

    elif p.sig_version >= 4:
        hashed = {}

        # Parse Key IDs first so we know if it's a selfsignature or not
        for sub in p.subpackets:
            if sub.subtype == 16:
                parse_signature_subpacket(sub, signature, parent_type)

        sig_key_ids = set(map(
                lambda k: k['key_id'],
                signature.get('key_ids', [])
            ))
        signature['selfsig'] = bool(sig_key_ids & set(parent_key_ids))

        for sub in p.subpackets:
            if sub.subtype == 16:
                continue
            parse_signature_subpacket(sub, signature, parent_type)
            if sub.subtype not in (6, 16, 20, 32):
                hashed[sub.subtype] = sig_hashed or sub.hashed

        for k, v in hashed.items():
            if not k or not v:
                continue
            keys = subpacket_type_to_keys(k)
            signature['hashed'].extend(keys)
    else:
        raise UnsupportedSignatureVersion(p.sig_version)

    return signature
Ejemplo n.º 9
0
def parse_signature_subpacket(sub, signature, signature_owner_type,
                  signature_hashed=False,
                  # For testing
                  validate_subpacket_regex=validate_subpacket_regex,
                  parse_notation=parse_notation,
                  parse_embedded_signature=parse_embedded_signature):

    if sub.subtype == 2:
        if not sub.hashed:
            # "MUST be present in the hashed area."
            # Specification does not say what behavior should be if it
            # is present in the unhashed area. GnuPG & SKS ignore the
            # signature subpacket.
            return
        signature['creation_time'] = utils.long_to_int(sub.data, 0)
    elif sub.subtype == 3:
        # Raw expiration time is actually the time from the creation time
        # that expiration occurs in seconds, rather than a unix timestamp
        # as its name might imply
        exp_time = utils.long_to_int(sub.data, 0)
        if exp_time != 0:
            signature['expiration_seconds'] = exp_time
    elif sub.subtype == 4:
        exportable = bool(sub.data[0])
        if not exportable:
            # "Non-exportable, or "local", certifications are signatures
            #  made by a user to mark a key as valid within that user's
            #  implementation only."
            #
            # "[...]"
            #
            # "The receiver of a transported key "imports" it, and likewise
            #  trims any local certifications.  In normal operation, there
            #  won't be any, assuming the import is performed on an
            #  exported key.  However, there are instances where this can
            #  reasonably happen.  For example, if an implementation allows
            #  keys to be imported from a key database in addition to an
            #  exported key, then this situation can arise."
            #
            # "Some implementations do not represent the interest of a
            #  single user (for example, a key server).  Such
            #  implementations always trim local certifications from any
            #  key they handle."
            raise LocalCertificationSignature

        signature['exportable'] = exportable
    elif sub.subtype == 5:
        signature['trust_level'] = int(sub.data[0])
        signature['trust_amount'] = int(sub.data[1])
    elif sub.subtype == 6:
        # Null-terminated
        regex = sub.data[:-1].decode('ascii', 'replace')
        try:
            validate_subpacket_regex(regex)
        except RegexValueError:
            return
        signature.setdefault('regexes', [])
        # The specifcation doesn't cover it, but it is reasonable that a
        # signature might specify multiple regular expressions.
        #
        # "In most cases, an implementation SHOULD use the last subpacket
        #  in the signature, but MAY use any conflict resolution scheme
        #  that makes more sense."
        signature['regexes'].append({
                'regex': regex,
                'hashed': signature_hashed or sub.hashed
            })
    elif sub.subtype == 7:
        signature['revocable'] = bool(sub.data[0])
    elif sub.subtype == 9:
        # "This is found only on a self-signature."
        if not signature['selfsig']:
            return

        # Raw expiration time is actually the time from the creation time
        # that expiration occurs in seconds, rather than a unix timestamp
        # as its name might imply
        exp_time = utils.long_to_int(sub.data, 0)
        if exp_time == 0:
            return
        signature['key_expiration_seconds'] = exp_time
    elif sub.subtype == 11:
        # "This is found only on a self-signature."
        if not signature['selfsig']:
            return
        signature['preferred_sym_algorithms'] = list(map(int, sub.data))
    elif sub.subtype == 12:
        if not bool(sub.data[0] & 0x80):
            # 0x80 MUST be set
            return
        # If this sensitive key is included when we receive it, it must
        # be for a reason. It would not be exported to be given to us
        # otherwise.
        #
        # "If this flag is set, implementations SHOULD NOT export this
        #  signature to other users except in cases where the data needs
        #  to be available: when the signature is being sent to the
        #  designated revoker, or when it is accompanied by a revocation
        #  signature from that revoker."
        signature['revocation_key_sensitive'] = bool(sub.data[0] & 0x40)
        signature['revocation_key_pub_algorithm_type'] = sub.data[1]
        signature['revocation_key'] = \
            utils.bytearray_to_hex(sub.data[2:])
    elif sub.subtype == 16:
        # "Some apparent conflicts may actually make sense -- for example,
        #  suppose a keyholder has a V3 key and a V4 key that share the
        #  same RSA key material.  Either of these keys can verify a
        #  signature created by the other, and it may be reasonable for a
        #  signature to contain an issuer subpacket for each key, as a way
        #  of explicitly tying those keys to the signature."
        signature.setdefault('key_ids', [])
        signature['key_ids'].append({
                'key_id': utils.bytearray_to_hex(sub.data, 0, 8).upper(),
                'hashed': signature_hashed or sub.hashed
                })
    elif sub.subtype == 20:
        notation = parse_notation(sub.data, signature_hashed or sub.hashed)
        if notation:
            if sub.critical:
                # "If there is a critical notation, the criticality
                #  applies to that specific notation and not to notations
                #  in general."
                raise CannotParseCriticalNotation(notation['name'])
            signature.setdefault('notations', [])
            signature['notations'].append(notation)

    elif sub.subtype == 21:
        # "This is found only on a self-signature."
        if not signature['selfsig']:
            return
        signature['preferred_hash_algorithms'] = list(map(int, sub.data))
    elif sub.subtype == 22:
        # "This is found only on a self-signature."
        if not signature['selfsig']:
            return
        signature['preferred_compression_algorithms'] = \
            list(map(int, sub.data))
    elif sub.subtype == 23:
        if not signature['selfsig']:
            # "This is found only on a self-signature."
            return
        # "All undefined flags MUST be zero."
        if sub.data[0] & 0x7f or any(sub.data[1:]):
            return

        signature['key_server_no_modify'] = bool(sub.data[0] & 0x80)
    elif sub.subtype == 24:
        signature['preferred_key_server'] = \
            sub.data.decode('utf8', 'replace')
    elif sub.subtype == 25:
        # "This is a flag in a User ID's self-signature"
        # ...
        # "there are two different and independent "primaries" -- one for
        #  User IDs, and one for User Attributes."
        if not signature['selfsig']:
            return
        if signature_owner_type not in (13, 17):
            return
        # Store the actual integer value. If we have multiple "primaries"
        # we can use this to resolve priority.
        #
        # "If more than one User ID in a key is marked as primary, the
        #  implementation may resolve the ambiguity in any way it sees
        #  fit, but it is RECOMMENDED that priority be given to the User
        #  ID with the most recent self-signature."
        signature['primary'] = int(sub.data[0])
    elif sub.subtype == 26:
        signature['policy_uri'] = sub.data.decode('utf8', 'replace')
    elif sub.subtype == 27:
        flags = sub.data[0]
        signature['may_certify_others'] = bool(flags & 0x01)
        signature['may_sign_data'] = bool(flags & 0x02)
        signature['may_encrypt_comms'] = bool(flags & 0x04)
        signature['may_encrypt_storage'] = bool(flags & 0x08)
        signature['may_be_used_for_auth'] = bool(flags & 0x20)
        # "The "split key" (0x10) and "group key" (0x80) flags are placed
        #  on a self-signature only; they are meaningless on a
        #  certification signature.  They SHOULD be placed only on a
        #  direct-key signature (type 0x1F) or a subkey signature
        #  (type 0x18), one that refers to the key the flag applies to."
        if signature['selfsig'] and signature['sig_type'] in (0x1f, 0x18):
            signature['may_have_been_split'] = bool(flags & 0x10)
            signature['may_have_multiple_owners'] = bool(flags & 0x80)
    elif sub.subtype == 28:
        signature['user_id'] = sub.data.decode('utf8', 'replace')
    elif sub.subtype == 29:
        # "This subpacket is used only in key revocation and certification
        #  revocation signatures."
        if signature['sig_type'] not in (0x20, 0x28):
            return
        revocation_code = int(sub.data[0])
        if (revocation_code in (0, 1, 2, 3, 32) or
            (revocation_code > 99 and revocation_code < 111)):
            signature['revocation_code'] = revocation_code
            signature['revocation_reason'] = \
                sub.data[1:].decode('utf8', 'replace')
    elif sub.subtype == 30:
        # "This subpacket is similar to a preferences subpacket, and only
        #  appears in a self-signature."
        if not signature['selfsig']:
            return
        if sub.data[0] & 0xfe:
            return
        if any(sub.data[1:]):
            return
        supports_modification = sub.data[0] & 0x01
        signature['supports_modification_detection'] = supports_modification
    elif sub.subtype == 31:
        signature['target_pub_key_algorithm'] = sub.data[0]
        signature['target_hash_algorithm'] = sub.data[1]
        # Store the target hash as hex
        signature['target_hash'] = utils.bytearray_to_hex(sub.data[2:])
    elif sub.subtype == 32:
        subsignature = parse_embedded_signature(
                            sub.data, signature_hashed or sub.hashed)
        signature.setdefault('embedded_signatures', [])
        signature['embedded_signatures'].append(subsignature)

    # Unparseable signature subpackets
    # "If a subpacket is encountered that is marked critical but is
    #  unknown to the evaluating software, the evaluator SHOULD consider
    #  the signature to be in error."

    elif sub.subtype > 99 and sub.subtype < 111:
        # private / experimental
        if sub.critical:
            raise CannotParseCritical(sub.subtype)
    elif sub.subtype in (0, 1, 8, 13, 14, 15, 17, 18, 19):
        # "An implementation SHOULD ignore any subpacket of a type that it
        #  does not recognize."
        # raise ReservedSignatureSubpacket(sub.subtype)
        if sub.critical:
            raise CannotParseCritical(sub.subtype)
    else:
        # "An implementation SHOULD ignore any subpacket of a type that it
        #  does not recognize."
        # raise InvalidSignatureSubpacket(sub.subtype)
        if sub.critical:
            raise CannotParseCritical(sub.subtype)
Ejemplo n.º 10
0
 def from_subpacket_content(cls, type_, critical, sub_data):
     assert len(sub_data) == 8
     key_id = utils.bytearray_to_hex(sub_data, 0, 16)
     return cls(critical, key_id)
Ejemplo n.º 11
0
 def content(self):
     tag = 0x80 + (0x40 if self.sensitive else 0x00)
     data = bytearray([tag, int(self.public_key_algorithm)] +
                      utils.bytearray_to_hex(self.fingerprint, 20))
     return data
Ejemplo n.º 12
0
 def from_subpacket_content(cls, type_, critical, sub_data):
     strong_request = bool(sub_data[0] & 0x80)
     public_key_algorithm = sub_data[1]
     fingerprint = utils.bytearray_to_hex(sub_data, 2, 20)
     return cls(critical, strong_request, public_key_algorithm, fingerprint)
Ejemplo n.º 13
0
 def from_subpacket_content(cls, type_, critical, sub_data):
     assert len(sub_data) == 8
     key_id = utils.bytearray_to_hex(sub_data, 0, 16)
     return cls(critical, key_id)
Ejemplo n.º 14
0
 def content(self):
     tag = 0x80 + (0x40 if self.sensitive else 0x00)
     data = bytearray([tag, int(self.public_key_algorithm)] + utils.bytearray_to_hex(self.fingerprint, 20))
     return data
Ejemplo n.º 15
0
def parse_signature_subpacket(
        sub,
        signature,
        signature_owner_type,
        signature_hashed=False,
        # For testing
        validate_subpacket_regex=validate_subpacket_regex,
        parse_notation=parse_notation,
        parse_embedded_signature=parse_embedded_signature):

    if sub.subtype == 2:
        if not sub.hashed:
            # "MUST be present in the hashed area."
            # Specification does not say what behavior should be if it
            # is present in the unhashed area. GnuPG & SKS ignore the
            # signature subpacket.
            return
        signature['creation_time'] = utils.long_to_int(sub.data, 0)
    elif sub.subtype == 3:
        # Raw expiration time is actually the time from the creation time
        # that expiration occurs in seconds, rather than a unix timestamp
        # as its name might imply
        exp_time = utils.long_to_int(sub.data, 0)
        if exp_time != 0:
            signature['expiration_seconds'] = exp_time
    elif sub.subtype == 4:
        exportable = bool(sub.data[0])
        if not exportable:
            # "Non-exportable, or "local", certifications are signatures
            #  made by a user to mark a key as valid within that user's
            #  implementation only."
            #
            # "[...]"
            #
            # "The receiver of a transported key "imports" it, and likewise
            #  trims any local certifications.  In normal operation, there
            #  won't be any, assuming the import is performed on an
            #  exported key.  However, there are instances where this can
            #  reasonably happen.  For example, if an implementation allows
            #  keys to be imported from a key database in addition to an
            #  exported key, then this situation can arise."
            #
            # "Some implementations do not represent the interest of a
            #  single user (for example, a key server).  Such
            #  implementations always trim local certifications from any
            #  key they handle."
            raise LocalCertificationSignature

        signature['exportable'] = exportable
    elif sub.subtype == 5:
        signature['trust_level'] = int(sub.data[0])
        signature['trust_amount'] = int(sub.data[1])
    elif sub.subtype == 6:
        # Null-terminated
        regex = sub.data[:-1].decode('ascii', 'replace')
        try:
            validate_subpacket_regex(regex)
        except RegexValueError:
            return
        signature.setdefault('regexes', [])
        # The specifcation doesn't cover it, but it is reasonable that a
        # signature might specify multiple regular expressions.
        #
        # "In most cases, an implementation SHOULD use the last subpacket
        #  in the signature, but MAY use any conflict resolution scheme
        #  that makes more sense."
        signature['regexes'].append({
            'regex': regex,
            'hashed': signature_hashed or sub.hashed
        })
    elif sub.subtype == 7:
        signature['revocable'] = bool(sub.data[0])
    elif sub.subtype == 9:
        # "This is found only on a self-signature."
        if not signature['selfsig']:
            return

        # Raw expiration time is actually the time from the creation time
        # that expiration occurs in seconds, rather than a unix timestamp
        # as its name might imply
        exp_time = utils.long_to_int(sub.data, 0)
        if exp_time == 0:
            return
        signature['key_expiration_seconds'] = exp_time
    elif sub.subtype == 11:
        # "This is found only on a self-signature."
        if not signature['selfsig']:
            return
        signature['preferred_sym_algorithms'] = list(map(int, sub.data))
    elif sub.subtype == 12:
        if not bool(sub.data[0] & 0x80):
            # 0x80 MUST be set
            return
        # If this sensitive key is included when we receive it, it must
        # be for a reason. It would not be exported to be given to us
        # otherwise.
        #
        # "If this flag is set, implementations SHOULD NOT export this
        #  signature to other users except in cases where the data needs
        #  to be available: when the signature is being sent to the
        #  designated revoker, or when it is accompanied by a revocation
        #  signature from that revoker."
        signature['revocation_key_sensitive'] = bool(sub.data[0] & 0x40)
        signature['revocation_key_pub_algorithm_type'] = sub.data[1]
        signature['revocation_key'] = \
            utils.bytearray_to_hex(sub.data[2:])
    elif sub.subtype == 16:
        # "Some apparent conflicts may actually make sense -- for example,
        #  suppose a keyholder has a V3 key and a V4 key that share the
        #  same RSA key material.  Either of these keys can verify a
        #  signature created by the other, and it may be reasonable for a
        #  signature to contain an issuer subpacket for each key, as a way
        #  of explicitly tying those keys to the signature."
        signature.setdefault('key_ids', [])
        signature['key_ids'].append({
            'key_id':
            utils.bytearray_to_hex(sub.data, 0, 8).upper(),
            'hashed':
            signature_hashed or sub.hashed
        })
    elif sub.subtype == 20:
        notation = parse_notation(sub.data, signature_hashed or sub.hashed)
        if notation:
            if sub.critical:
                # "If there is a critical notation, the criticality
                #  applies to that specific notation and not to notations
                #  in general."
                raise CannotParseCriticalNotation(notation['name'])
            signature.setdefault('notations', [])
            signature['notations'].append(notation)

    elif sub.subtype == 21:
        # "This is found only on a self-signature."
        if not signature['selfsig']:
            return
        signature['preferred_hash_algorithms'] = list(map(int, sub.data))
    elif sub.subtype == 22:
        # "This is found only on a self-signature."
        if not signature['selfsig']:
            return
        signature['preferred_compression_algorithms'] = \
            list(map(int, sub.data))
    elif sub.subtype == 23:
        if not signature['selfsig']:
            # "This is found only on a self-signature."
            return
        # "All undefined flags MUST be zero."
        if sub.data[0] & 0x7f or any(sub.data[1:]):
            return

        signature['key_server_no_modify'] = bool(sub.data[0] & 0x80)
    elif sub.subtype == 24:
        signature['preferred_key_server'] = \
            sub.data.decode('utf8', 'replace')
    elif sub.subtype == 25:
        # "This is a flag in a User ID's self-signature"
        # ...
        # "there are two different and independent "primaries" -- one for
        #  User IDs, and one for User Attributes."
        if not signature['selfsig']:
            return
        if signature_owner_type not in (13, 17):
            return
        # Store the actual integer value. If we have multiple "primaries"
        # we can use this to resolve priority.
        #
        # "If more than one User ID in a key is marked as primary, the
        #  implementation may resolve the ambiguity in any way it sees
        #  fit, but it is RECOMMENDED that priority be given to the User
        #  ID with the most recent self-signature."
        signature['primary'] = int(sub.data[0])
    elif sub.subtype == 26:
        signature['policy_uri'] = sub.data.decode('utf8', 'replace')
    elif sub.subtype == 27:
        flags = sub.data[0]
        signature['may_certify_others'] = bool(flags & 0x01)
        signature['may_sign_data'] = bool(flags & 0x02)
        signature['may_encrypt_comms'] = bool(flags & 0x04)
        signature['may_encrypt_storage'] = bool(flags & 0x08)
        signature['may_be_used_for_auth'] = bool(flags & 0x20)
        # "The "split key" (0x10) and "group key" (0x80) flags are placed
        #  on a self-signature only; they are meaningless on a
        #  certification signature.  They SHOULD be placed only on a
        #  direct-key signature (type 0x1F) or a subkey signature
        #  (type 0x18), one that refers to the key the flag applies to."
        if signature['selfsig'] and signature['sig_type'] in (0x1f, 0x18):
            signature['may_have_been_split'] = bool(flags & 0x10)
            signature['may_have_multiple_owners'] = bool(flags & 0x80)
    elif sub.subtype == 28:
        signature['user_id'] = sub.data.decode('utf8', 'replace')
    elif sub.subtype == 29:
        # "This subpacket is used only in key revocation and certification
        #  revocation signatures."
        if signature['sig_type'] not in (0x20, 0x28):
            return
        revocation_code = int(sub.data[0])
        if (revocation_code in (0, 1, 2, 3, 32)
                or (revocation_code > 99 and revocation_code < 111)):
            signature['revocation_code'] = revocation_code
            signature['revocation_reason'] = \
                sub.data[1:].decode('utf8', 'replace')
    elif sub.subtype == 30:
        # "This subpacket is similar to a preferences subpacket, and only
        #  appears in a self-signature."
        if not signature['selfsig']:
            return
        if sub.data[0] & 0xfe:
            return
        if any(sub.data[1:]):
            return
        supports_modification = sub.data[0] & 0x01
        signature['supports_modification_detection'] = supports_modification
    elif sub.subtype == 31:
        signature['target_pub_key_algorithm'] = sub.data[0]
        signature['target_hash_algorithm'] = sub.data[1]
        # Store the target hash as hex
        signature['target_hash'] = utils.bytearray_to_hex(sub.data[2:])
    elif sub.subtype == 32:
        subsignature = parse_embedded_signature(sub.data, signature_hashed
                                                or sub.hashed)
        signature.setdefault('embedded_signatures', [])
        signature['embedded_signatures'].append(subsignature)

    # Unparseable signature subpackets
    # "If a subpacket is encountered that is marked critical but is
    #  unknown to the evaluating software, the evaluator SHOULD consider
    #  the signature to be in error."

    elif sub.subtype > 99 and sub.subtype < 111:
        # private / experimental
        if sub.critical:
            raise CannotParseCritical(sub.subtype)
    elif sub.subtype in (0, 1, 8, 13, 14, 15, 17, 18, 19):
        # "An implementation SHOULD ignore any subpacket of a type that it
        #  does not recognize."
        # raise ReservedSignatureSubpacket(sub.subtype)
        if sub.critical:
            raise CannotParseCritical(sub.subtype)
    else:
        # "An implementation SHOULD ignore any subpacket of a type that it
        #  does not recognize."
        # raise InvalidSignatureSubpacket(sub.subtype)
        if sub.critical:
            raise CannotParseCritical(sub.subtype)
Ejemplo n.º 16
0
def parse_signature_packet(
        p,
        parent_type,
        parent_key_ids=None,
        sig_hashed=False,
        # For testing
        parse_signature_subpacket=parse_signature_subpacket):
    """Parse a single pgpdump signature packet into a Python dictionary
    of values. sig_hashed should be set to True for signature packets
    which are embedded as hashed signature subpackets.
    """

    signature = {}
    signature['validated'] = None
    signature['hash_algorithm_type'] = p.raw_hash_algorithm
    signature['pub_algorithm_type'] = p.raw_pub_algorithm
    signature['sig_type'] = p.raw_sig_type
    signature['sig_version'] = p.sig_version
    signature['hash2'] = utils.bytearray_to_hex(p.hash2)

    # "If a subpacket is not hashed, then the information in it cannot be
    #  considered definitive because it is not part of the signature proper."
    #
    # For convenience, include the fields that are always hashed.
    signature['hashed'] = [
        'hash_algorithm_type',
        'pub_algorithm_type',
        'sig_type',
        'sig_version',
    ]

    if p.sig_version in (2, 3):
        # Only trust these explicitly if the version is < 4. If the version is
        # 4 or greater, these values may have come from unhashed subpackets
        # and could have been manipulated.

        signature['key_ids'] = [{
            'key_id': p.key_id.upper(),
            'hashed': True,
        }]
        signature['selfsig'] = p.key_id.upper() in parent_key_ids
        signature['creation_time'] = p.raw_creation_time
        signature['hashed'].extend([
            'creation_time',
        ])

    elif p.sig_version >= 4:
        hashed = {}

        # Parse Key IDs first so we know if it's a selfsignature or not
        for sub in p.subpackets:
            if sub.subtype == 16:
                parse_signature_subpacket(sub, signature, parent_type)

        sig_key_ids = set(
            map(lambda k: k['key_id'], signature.get('key_ids', [])))
        signature['selfsig'] = bool(sig_key_ids & set(parent_key_ids))

        for sub in p.subpackets:
            if sub.subtype == 16:
                continue
            parse_signature_subpacket(sub, signature, parent_type)
            if sub.subtype not in (6, 16, 20, 32):
                hashed[sub.subtype] = sig_hashed or sub.hashed

        for k, v in hashed.items():
            if not k or not v:
                continue
            keys = subpacket_type_to_keys(k)
            signature['hashed'].extend(keys)
    else:
        raise UnsupportedSignatureVersion(p.sig_version)

    return signature
Ejemplo n.º 17
0
 def from_subpacket_content(cls, type_, critical, sub_data):
     strong_request = bool(sub_data[0] & 0x80)
     public_key_algorithm = sub_data[1]
     fingerprint = utils.bytearray_to_hex(sub_data, 2, 20)
     return cls(critical, strong_request, public_key_algorithm, fingerprint)