def add(request): '''add a key to the keyserver ''' if request.method != 'POST': raise Http404 form = forms.KeyServerAddForm(request.POST) c = {} try: if not form.is_valid(): raise __AddException keytext = form.cleaned_data['keytext'] # check keytext try: pgp = pgpdump.AsciiData(keytext.encode("utf-8", "ignore")) except: raise __AddException keys = utils.parse_public_key_packets(pgp) keytexts = [] for data, packets in keys: if not utils.is_valid_packets(packets): raise __AddException keytext = utils.encode_ascii_armor(data) keytexts.append(keytext) pgpkeys = [] for keytext in keytexts: pgpkey = models.PGPKeyModel.objects.save_to_storage(None, keytext) pgpkeys.append(pgpkey) c = { 'pgpkeys': pgpkeys, } except __AddException: content = render(request, 'pgpdb/add_invalid_post.html') return HttpResponseBadRequest(content) return render(request, 'pgpdb/added.html', c)
def _get_keydata(self, data): results = [] try: if "-----BEGIN" in data: ak = pgpdump.AsciiData(data) else: ak = pgpdump.BinaryData(data) except TypeError: return [] now = time.time() for m in ak.packets(): if isinstance(m, pgpdump.packet.PublicKeyPacket): results.append({ "fingerprint": m.fingerprint, "created": m.datetime, "validity": ('e' if (0 < (int(m.expiration_time or 0)) < now) else ''), "keytype_name": (m.pub_algorithm or '').split()[0], "keysize": str(int(1.024 * round(len('%x' % m.modulus) / 0.256))), "uids": [], }) if isinstance(m, pgpdump.packet.UserIDPacket): results[-1]["uids"].append({ "name": m.user_name, "email": m.user_email }) return results
def _get_keydata(self, data): results = {} try: if "-----BEGIN" in data: ak = pgpdump.AsciiData(data) else: ak = pgpdump.BinaryData(data) except TypeError: return [] curfp = None for m in ak.packets(): if isinstance(m, pgpdump.packet.PublicKeyPacket): curfp = m.fingerprint results[curfp] = { "fingerprint": m.fingerprint, "expires": m.expiration_time, "created": m.datetime, "uids": [], } if isinstance(m, pgpdump.packet.UserIDPacket): results[curfp]["uids"].append({"name": m.user_name, "email": m.user_email}) return results
def calc_fingerprint(asciiArmored): a = pgpdump.AsciiData(asciiArmored) p = a.packets() try: # python 2 compatibility n = p.next() except AttributeError: n = next(p) fpr = "0x" + n.fingerprint.lower().decode("utf-8") log.debug("calc_fingerprint:", fpr) return fpr
def get_modulus_from_gpg(cert): import pgpdump, types try: data = pgpdump.BinaryData(cert) except pgpdump.utils.PgpdumpException: data = pgpdump.AsciiData(cert) public_gpg_keys = [ packet for packet in data.packets() if type(packet) == pgpdump.packet.PublicKeyPacket ] if len(public_gpg_keys) != 1: raise (Exception( "Found {} gpg public keys, require exactly 1, aborting.".format( len(public_gpg_keys)))) return public_gpg_keys[0].modulus
def change_passphrase(keydata, old_pass, new_pass='', fast=False, decoding_charset='utf-8'): """ Accepts a PGP key (armored or binary) and attempt to change the passphrase of any secret keys within. If new_pass is false (blank or None), the output will be an unprotected key. If fast is True, the new passphrase is assumed to already have high entropy and we use a minimal number of iterations when deriving the actual encryption key. Returns the new key in binary form, raises an exception on error. """ keydata = bytes(keydata) if b'-----BEGIN PGP' in keydata: packet_iter = pgpdump.AsciiData(keydata).packets() else: packet_iter = pgpdump.BinaryData(keydata).packets() try: # Ensure correct encoding on Python 2 if not isinstance(old_pass, unicode): old_pass = old_pass.decode('utf-8') if not isinstance(new_pass, unicode): new_pass = new_pass.decode('utf-8') except NameError: pass output = [] for packet in packet_iter: if packet.raw in (5, 7): packet = _unlock_secret_key(packet, old_pass, decoding_charset) if new_pass: packet = _lock_secret_key(packet, new_pass, fast) if packet is not None: output.append(packet) newkey = b'' for p in output: newkey += _pgp_header(p.raw, len(p.data)) + p.data return newkey
def _get_keydata(data): results = [] try: if "-----BEGIN" in data: ak = pgpdump.AsciiData(data) else: ak = pgpdump.BinaryData(data) packets = list(ak.packets()) except (TypeError, IndexError, PgpdumpException): return [] now = time.time() for m in packets: try: if isinstance(m, pgpdump.packet.PublicKeyPacket): size = str( int(1.024 * round(len('%x' % (m.modulus or 0)) / 0.256))) validity = ('e' if (0 < (int(m.expiration_time or 0)) < now) else '') results.append({ "fingerprint": m.fingerprint, "created": _get_creation_time(m), "validity": validity, "keytype_name": (m.pub_algorithm or '').split()[0], "keysize": size, "uids": [], }) if isinstance(m, pgpdump.packet.UserIDPacket) and results: # FIXME: This used to happen with results=[], does that imply # UIDs sometimes come before the PublicKeyPacket? results[-1]["uids"].append({ "name": m.user_name, "email": m.user_email }) except (TypeError, AttributeError, KeyError, IndexError, NameError): import traceback traceback.print_exc() # This will only return keys that have UIDs return [k for k in results if k['uids']]
def _get_keydata(self, data): results = [] try: if "-----BEGIN" in data: ak = pgpdump.AsciiData(data) else: ak = pgpdump.BinaryData(data) except TypeError: return [] now = time.time() for m in ak.packets(): try: if isinstance(m, pgpdump.packet.PublicKeyPacket): # FIXME m.datetime throws nasty error fails hard # Issue 1115 size = str( int(1.024 * round(len('%x' % m.modulus) / 0.256))) validity = ('e' if (0 < (int(m.expiration_time or 0)) < now) else '') results.append({ "fingerprint": m.fingerprint, "created": "1970-01-01 00:00:00", "validity": validity, "keytype_name": (m.pub_algorithm or '').split()[0], "keysize": size, "uids": [], }) if isinstance(m, pgpdump.packet.UserIDPacket): results[-1]["uids"].append({ "name": m.user_name, "email": m.user_email }) except (AttributeError, KeyError, IndexError, NameError): import traceback traceback.print_exc() return results
def eval(self, value, request): if '--' not in value: # best way i found to ignore unchanged fields. return IgnoreValue() try: data = pgpdump.AsciiData(bytes(value, 'utf-8')) except binascii.Error: raise ValidationError(_('Invalid PGP key')) packets = list(data.packets()) if not packets: return IgnoreValue() packet = packets[0] if not packet: raise ValidationError(_('Invalid PGP key (format)')) if self.require and not isinstance(packet, self.require): raise ValidationError(_('Invalid PGP key (type)')) return data.data
def scan(self, data, file, options, expire_at): self.event['total'] = { 'public_keys': 0, 'public_key_encrypted_session_keys': 0, 'signatures': 0, 'trusts': 0, 'user_attributes': 0, 'user_ids': 0, } self.event.setdefault('public_keys', []) self.event.setdefault('public_key_encrypted_session_keys', []) self.event.setdefault('signatures', []) self.event.setdefault('trusts', []) self.event.setdefault('user_attributes', []) self.event.setdefault('user_ids', []) try: data = pgpdump.AsciiData(data) for packet in data.packets(): if isinstance(packet, PublicKeyPacket): self.event['total']['public_keys'] += 1 public_key_entry = { 'key_id': getattr(packet, 'key_id', None), 'pubkey_version': getattr(packet, 'pubkey_version', None), 'fingerprint': getattr(packet, 'fingerprint', None), 'pub_algorithm_type': getattr(packet, 'pub_algorithm_type', None), 'key_value': getattr(packet, 'key_value', None), } creation_time = getattr(packet, 'creation_time', None) if creation_time is not None: public_key_entry[ 'creation_time'] = creation_time.isoformat() expiration_time = getattr(packet, 'expiration_time', None) if expiration_time is not None: public_key_entry[ 'expiration_time'] = expiration_time.isoformat() if public_key_entry not in self.event['public_keys']: self.event['public_keys'].append(public_key_entry) elif isinstance(packet, PublicKeyEncryptedSessionKeyPacket): self.event['total'][ 'public_key_encrypted_session_keys'] += 1 public_key_encrypted_session_key_entry = { 'session_key_version': getattr(packet, 'session_key_version', None), 'key_id': getattr(packet, 'key_id', None), 'pub_algorithm': getattr(packet, 'pub_algorithm', None), } if public_key_encrypted_session_key_entry not in self.event[ 'public_key_encrypted_session_keys']: self.event['public_key_encrypted_session_keys'].append( public_key_encrypted_session_key_entry) elif isinstance(packet, SignaturePacket): self.event['total']['signatures'] += 1 signature_packet_entry = { 'key_id': getattr(packet, 'key_id', None), 'sig_version': getattr(packet, 'sig_version', None), 'sig_type': getattr(packet, 'sig_type', None), 'hash_algorithm': getattr(packet, 'hash_algorithm', None), 'pub_algorithm': getattr(packet, 'pub_algorithm', None), 'length': getattr(packet, 'length', None), } creation_time = getattr(packet, 'creation_time', None) if creation_time is not None: signature_packet_entry[ 'creation_time'] = creation_time.isoformat() expiration_time = getattr(packet, 'expiration_time', None) if expiration_time is not None: signature_packet_entry[ 'expiration_time'] = expiration_time.isoformat() if signature_packet_entry not in self.event['signatures']: self.event['signatures'].append(signature_packet_entry) elif isinstance(packet, TrustPacket): self.event['total']['trusts'] += 1 trust_entry = { 'trusts': getattr(packet, 'trusts', None), } if trust_entry not in self.event['trusts']: self.event['trusts'].append(trust_entry) elif isinstance(packet, UserAttributePacket): self.event['total']['user_attributes'] += 1 user_attribute_entry = { 'image_format': getattr(packet, 'image_format', None), 'image_data': getattr(packet, 'image_data', None), } if user_attribute_entry not in self.event[ 'user_attributes']: self.event['user_attributes'].append( user_attribute_entry) elif isinstance(packet, UserIDPacket): self.event['total']['user_ids'] += 1 user_id_entry = { 'user': getattr(packet, 'user', None), 'user_name': getattr(packet, 'user_name', None), 'user_email': getattr(packet, 'user_email', None), } if user_id_entry not in self.event['user_ids']: self.event['user_ids'].append(user_id_entry) except TypeError: self.flags.append('type_error')
def minimal_key(keydata, user_id=None, subkey_id=None, binary_out=False): """ Accepts a PGP key (armored or binary) and returns a minimal PGP key containing exactly five packets (base64 or binary) defining a primary key, a single user id with one self-signature, and a single encryption subkey with one self-signature. Such a five packet key MUST be used in Autocrypt headers (Level 1 Spec section 2.1.1). The unrevoked user id with newest unexpired self-signature and the unrevoked encryption-capable subkey with newest unexpired self-signature are selected from the input key. If user_id is provided, a user id containing that string will be selected if there is one, otherwise any user id will be accepted. If subkey_id is specified, only a subkey with that id will be selected. Along with the new key, the selected user id and subkey id are returned. Returns None if there is a failure. """ def _get_int4(data, offset): '''Pull four bytes from data at offset and return as an integer.''' return ((data[offset] << 24) + (data[offset + 1] << 16) + (data[offset + 2] << 8) + data[offset + 3]) def _exp_time(creation_time, exp_time_subpacket_data): life_s = _get_int4(exp_time_subpacket_data, 0) if not life_s: return 0 return packet.creation_time + datetime.timedelta(seconds=life_s) def _pgp_header(type, body_length): if body_length < 192: return bytearray([type + 0xC0, body_length]) elif body_length < 8384: return bytearray([ type + 0xC0, (body_length - 192) // 256 + 192, (body_length - 192) % 256 ]) else: return bytearray([ type + 0xC0, 255, body_length // (1 << 24), body_length // (1 << 16) % 256, body_length // 1 << 8 % 256, body_length % 256 ]) pri_key = None u_id = None u_id_sig = None u_id_match = False s_key = None s_key_sig = None now = datetime.datetime.utcfromtimestamp(time.time()) if '-----BEGIN PGP PUBLIC KEY BLOCK-----' in keydata: packet_iter = pgpdump.AsciiData(keydata).packets() else: packet_iter = pgpdump.BinaryData(keydata).packets() try: packet = next(packet_iter) except: packet = None while packet: if packet.raw == 6 and pri_key: # Primary key must be the first break # and only the first packet. elif packet.raw != 6 and not pri_key: break elif packet.raw == 6: # Primary Public-Key Packet pri_key = packet elif packet.raw == 13: # User ID Packet u_id_try = packet u_id_sig_try = None u_id_try_match = not user_id or user_id in u_id_try.user #**FIXME Autocrypt spec 5.1 requires E-mail address canonicalization # Accept a nonmatching u_id IFF no other u_id matches. if u_id_match and not u_id_try_match: u_id_try = None for packet in packet_iter: if packet.raw != 2: # Signature Packet break elif not u_id_try: continue # User ID certification elif packet.raw_sig_type in (0x10, 0x11, 0x12, 0x13, 0x1F): if (packet.key_id in pri_key.fingerprint and (not packet.expiration_time or packet.expiration_time > now) and (not u_id_sig_try or u_id_sig_try.creation_time < packet.creation_time)): u_id_sig_try = packet # Certification revocation elif packet.raw_sig_type == 0x30: if packet.key_id in pri_key.fingerprint: u_id_try = None u_id_sig_try = None # Select unrevoked user id with newest unexpired self-signature if u_id_try and u_id_sig_try and ( not u_id or not u_id_sig or u_id_try_match and not u_id_match or u_id_sig_try.creation_time >= u_id_sig.creation_time): u_id = u_id_try u_id_sig = u_id_sig_try u_id_match = u_id_try_match continue # Skip next(packet_iter) - for has done it. elif packet.raw == 14: # Public-Subkey Packet s_key_try = packet s_key_sig_try = None # Honour a request for specific subkey and check for expiry. if (subkey_id and not subkey_id in s_key_try.fingerprint or s_key_try.expiration_time and s_key_try.expiration_time < now): s_key_try = None for packet in packet_iter: if packet.raw != 2: # Signature Packet break elif not s_key_try: continue # Subkey Binding Signature elif packet.raw_sig_type == 0x18: packet.key_expire_time = None if (packet.key_id in pri_key.fingerprint and not packet.expiration_time or packet.expiration_time >= now): can_encrypt = True # Assume encrypt if no flags. for subpacket in packet.subpackets: if subpacket.subtype == 9: # Key expiration # pgpdump should provide this!! packet.key_expire_time = _exp_time( packet.creation_time, subpacket.data) elif subpacket.subtype == 27: # Key flags can_encrypt |= subpacket.data[0] & 0x0C if can_encrypt and (not packet.key_expire_time or packet.key_expire_time >= now): s_key_sig_try = packet # Subkey revocation signature elif packet.raw_sig_type == 0x28: if packet.key_id in pri_key.fingerprint: s_key_try = None s_key_sig_try = None # Select unrevoked encryption-capable subkey with newest # unexpired self-signature (ignores newness of key itself). if s_key_try and s_key_sig_try and ( not s_key_sig or s_key_sig_try.creation_time >= s_key_sig.creation_time): s_key = s_key_try s_key_sig = s_key_sig_try continue # Skip next(packet_iter) - for has done it. try: packet = next(packet_iter) except: packet = None if not (pri_key and u_id and u_id_sig and s_key and s_key_sig): return '', None, None newkey = (_pgp_header(pri_key.raw, len(pri_key.data)) + pri_key.data + _pgp_header(u_id.raw, len(u_id.data)) + u_id.data + _pgp_header(u_id_sig.raw, len(u_id_sig.data)) + u_id_sig.data + _pgp_header(s_key.raw, len(s_key.data)) + s_key.data + _pgp_header(s_key_sig.raw, len(s_key_sig.data)) + s_key_sig.data) if not binary_out: newkey = base64.b64encode(newkey) return newkey, u_id.user, s_key.key_id
def post_save(self, sender, instance, created, **kwargs): if created: data = instance.file.read() pgp = None try: pgp = pgpdump.AsciiData(data) except PgpdumpException: pgp = pgpdump.BinaryData(data) instance.file.save(instance.file.name, ContentFile(pgp.data)) crc = crc24(pgp.data) crc_bin = crc.to_bytes(3, "big") instance.crc24 = base64.b64encode(crc_bin).decode("utf-8") instance.save() # parse packets index = 0 last_pubkey = None last_userid = None for packet in pgp.packets(): index += 1 if (not isinstance(packet, PublicKeyPacket) and not isinstance(packet, UserIDPacket) and not isinstance(packet, SignaturePacket)): continue if isinstance(packet, PublicKeyPacket): is_sub = isinstance(packet, PublicSubkeyPacket) creation_time = make_aware(packet.creation_time, utc) expir = None if packet.expiration_time is not None: expir = make_aware(packet.expiration_time, utc) algo = packet.raw_pub_algorithm bits = 0 if algo in (1, 2, 3): bits = len(bin(packet.modulus)[2:]) elif algo == 17: bits = len(bin(packet.prime)[2:]) elif algo in (16, 20): bits = len(bin(packet.prime)[2:]) fingerprint = packet.fingerprint.lower().decode('utf-8') keyid = packet.key_id.lower().decode('utf-8') last_pubkey = PGPPublicKeyModel.objects.create( index=index, key=instance, is_sub=is_sub, creation_time=creation_time, expiration_time=expir, algorithm=algo, bits=bits, fingerprint=fingerprint, keyid=keyid) elif isinstance(packet, UserIDPacket): userid = packet.data.decode("utf-8") last_userid = PGPUserIDModel.objects.create(index=index, key=instance, userid=userid) elif isinstance(packet, SignaturePacket) and last_userid: creation_time = make_aware(packet.creation_time, utc) expir = None if packet.expiration_time is not None: expir = make_aware(packet.expiration_time, utc) keyid = packet.key_id.lower().decode('utf-8') PGPSignatureModel.objects.create( index=index, key=instance, pkey=last_pubkey, userid=last_userid, type=packet.raw_sig_type, pka=packet.raw_pub_algorithm, hash=packet.raw_hash_algorithm, creation_time=creation_time, expiration_time=expir, keyid=keyid)
def get_keyinfo(data, autocrypt_header=None, key_info_class=KeyInfo, key_uid_class=KeyUID, key_source=None): """ This method will parse a stream of OpenPGP packets into a list of KeyInfo objects. Note: Signatures are not validated, this code only parses the data. """ try: if "-----BEGIN" in data: ak = pgpdump.AsciiData(data) else: ak = pgpdump.BinaryData(data) packets = list(ak.packets()) except (TypeError, IndexError, PgpdumpException): traceback.print_exc() return [] def _unixtime(packet, seconds=0, days=0): return (packet.raw_creation_time + (days or 0) * 24 * 3600 + (seconds or 0)) results = [] last_uid = key_uid_class() # Dummy last_key = key_info_class() # Dummy last_pubkeypacket = None main_key_id = None for m in packets: try: if isinstance(m, pgpdump.packet.PublicKeyPacket): size = str(int(1.024 * round(len('%x' % (m.modulus or 0)) / 0.256))) last_pubkeypacket = m last_key = key_info_class( key_source=key_source, fingerprint=m.fingerprint, keytype_name=m.pub_algorithm or '', keytype_code=m.raw_pub_algorithm, keysize=size) if isinstance(m, pgpdump.packet.PublicSubkeyPacket): last_key.is_subkey = True results[-1].subkeys.append(last_key) else: main_key_id = m.key_id results.append(last_key) # Older pgpdumps may fail here and cause traceback noise, but # the loop will limp onwards. last_key.created = _unixtime(m) if m.raw_days_valid > 0: last_key.expires = _unixtime(m, days=m.raw_days_valid) if last_key.expires == last_key.created: last_key.expires = 0 elif isinstance(m, pgpdump.packet.UserIDPacket) and results: last_uid = key_uid_class(name=m.user_name, email=m.user_email) last_key.uids.append(last_uid) elif isinstance(m, pgpdump.packet.SignaturePacket) and results: # Note: We don't actually check the signature; we trust # GnuPG will if we decide to use this key. if m.key_id == main_key_id: for s in m.subpackets: if s.subtype == 9: exp = _unixtime(last_pubkeypacket, seconds=get_int4(s.data, 0)) last_key.expires = max(last_key.expires, exp) elif s.subtype == 27: caps = set(c for c in last_key.capabilities) for flag, c in ((0x01, 'c'), (0x02, 's'), (0x0C, 'e'), (0x20, 'a')): if s.data[0] & flag: caps.add(c) last_key.capabilities = ''.join(caps) except (TypeError, AttributeError, KeyError, IndexError, NameError): traceback.print_exc() autocrypt_uid = None if autocrypt_header: # The autocrypt spec tells us that the visible addr= attribute # overrides whatever is on the key itself, so we synthesize a # fake UID here so the info is correct in an Autocrypt context. autocrypt_uid = key_uid_class( email=autocrypt_header['addr'], comment='Autocrypt') now = time.time() for keyinfo in results: keyinfo.synthesize_validity(now=now) keyinfo.add_subkey_capabilities(now=now) keyinfo.recalculate_expiration(now=now) if autocrypt_uid is not None: keyinfo.ensure_autocrypt_uid(autocrypt_uid) return results
def _return_rsa_key_from_pgp(self, pgp_key, secret): """ Accepts pgp_key bytes, process it to pgpdump packets, which is a Generator class with following consisting objects: @developer: tnanoba SecretKeyPacket object UserIDPacket object SignaturePacket object SecretKeyPacket object dict structure: { 'prime_p': '...', (p) 'prime_q': '...', (q) 'exponent': '...', (e) 'modulus': '...', (p * q = n) 'exponent_d': '...', (d) 'data': '...', 'raw_creation_time': '...', 's2k_type': '...', 'checksum': '...', 'pubkey_version': '...', 'exponent_x': '...', 's2k_id': '...', 's2k_cipher': '...', 'new': '...', 'creation_time': '...', 'fingerprint': '...', 'multiplicative_inverse': '...', 'raw': '...', 'prime': '...', 'length': '...', 'group_gen': '...', 'pub_algorithm_type': '...', 'raw_days_valid': '...', 'name': '...', 'key_id': '...', 's2k_hash': '...', 'raw_pub_algorithm': '...', 'expiration_time': '...', 's2k_iv': '...', 'group_order': '...' } :param pgp_key: bytes :param secret: bool :return: object """ try: packets = list(pgpdump.AsciiData(pgp_key).packets()) if secret: # Returns RSA private key instance return self.compute_rsa_private_key( packets[0].__dict__['prime_p'], packets[0].__dict__['prime_q'], packets[0].__dict__['exponent'], packets[0].__dict__['modulus'], packets[0].__dict__['exponent_d']) else: # Returns RSA public key instance return self.compute_rsa_public_key( packets[0].__dict__['exponent'], packets[0].__dict__['modulus'], ) except Exception as ERROR: self.logger.error(ERROR)
def load_key(key_asc): data = pgpdump.AsciiData(key_asc) entities = [] pubkey = None curkey = None curuid = None subkey_latest_selfsig = datetime.datetime.utcfromtimestamp(0) pubkey_latest_selfsig = datetime.datetime.utcfromtimestamp(0) uid_latest_selfsig = datetime.datetime.utcfromtimestamp(0) for packet in data.packets(): if isinstance(packet, pgpdump.packet.PublicKeyPacket) and not isinstance( packet, pgpdump.packet.SecretKeyPacket): if type(packet) == pgpdump.packet.PublicKeyPacket: pubkey_latest_selfsig = datetime.datetime.utcfromtimestamp(0) pubkey = models.PublicKey() curkey = pubkey # Ugh, BlobProperty wants str, not bytearray pubkey.key_data = str(data.data) else: subkey_latest_selfsig = datetime.datetime.utcfromtimestamp(0) curkey = models.PublicSubkey() entities.append(curkey) curkey.reversed_fingerprint = codecs.decode( packet.fingerprint.decode('ascii'), 'hex')[::-1] if type(packet) == pgpdump.packet.PublicKeyPacket: curkey.key = ndb.Key(models.PublicKey, curkey.stringid, namespace='hkp') else: curkey.key = ndb.Key(models.PublicSubkey, curkey.stringid, parent=pubkey.key, namespace='hkp') pubkey.subkeys.append(curkey.key) curkey.creation_time = packet.creation_time curkey.expiration_time = packet.expiration_time curkey.algorithm_type = packet.pub_algorithm_type curkey.bitlen = packet.modulus_bitlen elif isinstance(packet, pgpdump.packet.UserIDPacket): uid_latest_selfsig = datetime.datetime.utcfromtimestamp(0) curuid = models.Uid() entities.append(curuid) curuid.key = ndb.Key(models.Uid, packet.user, parent=pubkey.key, namespace='hkp') pubkey.uids.append(curuid.key) curuid.uid = uni_utils.compatibility_casefold(packet.user) elif isinstance(packet, pgpdump.packet.SignaturePacket): # self-sig if packet.key_id == pubkey.keyid: # At this point only interested in UID, subkey, or sig directly on key # TODO should record revocation as well if packet.raw_sig_type in (0x10, 0x11, 0x12, 0x13, 0x18, 0x1F): # From RFC4880: # Subpackets that appear in a certification self-signature # apply to the user name, and subpackets that appear in the subkey # self-signature apply to the subkey. Lastly, subpackets on the # direct-key signature apply to the entire key. # # NOTE while the certification subpackets should apply to the user name, # not the entire key, gpg seems to put properties of the public key in the # certification signature(s). So, no else here... if packet.raw_sig_type >= 0x10 and packet.raw_sig_type <= 0x13 and uid_latest_selfsig < packet.creation_time: uid_latest_selfsig = packet.creation_time curuid.creation_time = packet.creation_time curuid.expiration_time = packet.expiration_time if (packet.raw_sig_type == 0x18 and subkey_latest_selfsig < packet.creation_time ) or (packet.raw_sig_type != 0x18 and pubkey_latest_selfsig < packet.creation_time): # Should modify pubkey even if the direct-key sig packet happens after subkeys modkey = curkey if packet.raw_sig_type == 0x18 else pubkey for subpack in packet.subpackets: if subpack.subtype == 9: # Key Expiration Time modkey.expiration_time = modkey.creation_time + datetime.timedelta( seconds=pgpdump.utils.get_int4( subpack.data, 0)) elif subpack.subtype == 27: # Key Flags modkey.flags = subpack.data[0] elif subpack.subtype == 23: # Key Server Preferences (do we need these?) pass ndb.put_multi(entities)
class PublicKey(KeyBase): def __init__(self): super(PublicKey, self).__init__() self.uids = [] self.subkeys = [] self.key_data = bytearray() @property def asciiarmored(self): return utils.asciiarmor('PUBLIC KEY BLOCK', self.key_data) with open('mykey.asc', 'rb') as infile: data = pgpdump.AsciiData(infile.read()) pubkeys = [] pubkey = None curkey = None curuid = None subkey_latest_selfsig = datetime.datetime.utcfromtimestamp(0) pubkey_latest_selfsig = datetime.datetime.utcfromtimestamp(0) uid_latest_selfsig = datetime.datetime.utcfromtimestamp(0) for packet in data.packets(): print(str(type(packet))) pp.pprint(packet.__dict__) if isinstance(packet, pgpdump.packet.PublicKeyPacket) and not isinstance( packet, pgpdump.packet.SecretKeyPacket): if type(packet) == pgpdump.packet.PublicKeyPacket:
def scan(self, file_object, options): self.metadata["total"] = {"publicKeys": 0, "publicKeyEncryptedSessionKeys": 0, "signatures": 0, "trusts": 0, "userAttributes": 0, "userIds": 0} self.metadata.setdefault("publicKeys", []) self.metadata.setdefault("publicKeyEncryptedSessionKeys", []) self.metadata.setdefault("signatures", []) self.metadata.setdefault("trusts", []) self.metadata.setdefault("userAttributes", []) self.metadata.setdefault("userIds", []) try: data = pgpdump.AsciiData(file_object.data) for packet in data.packets(): if isinstance(packet, PublicKeyPacket): self.metadata["total"]["publicKeys"] += 1 public_key_entry = {} key_id = getattr(packet, "key_id", None) if key_id is not None: public_key_entry["keyId"] = key_id pubkey_version = getattr(packet, "pubkey_version", None) if pubkey_version is not None: public_key_entry["pubkeyVersion"] = pubkey_version fingerprint = getattr(packet, "fingerprint", None) if fingerprint is not None: public_key_entry["fingerprint"] = fingerprint pub_algorithm_type = getattr(packet, "pub_algorithm_type", None) if pub_algorithm_type is not None: public_key_entry["pubAlgorithmType"] = pub_algorithm_type key_value = getattr(packet, "key_value", None) if key_value is not None: public_key_entry["keyValue"] = key_value creation_time = getattr(packet, "creation_time", None) if creation_time is not None: public_key_entry["creationTime"] = creation_time.isoformat(timespec="seconds") expiration_time = getattr(packet, "expiration_time", None) if expiration_time is not None: public_key_entry["expirationTime"] = expiration_time.isoformat(timespec="seconds") if (public_key_entry and public_key_entry not in self.metadata["publicKeys"]): self.metadata["publicKeys"].append(public_key_entry) elif isinstance(packet, PublicKeyEncryptedSessionKeyPacket): self.metadata["total"]["publicKeyEncryptedSessionKeys"] += 1 public_key_encrypted_session_key_entry = {} session_key_version = getattr(packet, "session_key_version", None) if session_key_version is not None: public_key_encrypted_session_key_entry["sessionKeyVersion"] = session_key_version key_id = getattr(packet, "key_id", None) if key_id is not None: public_key_encrypted_session_key_entry["keyId"] = key_id pub_algorithm = getattr(packet, "pub_algorithm", None) if pub_algorithm is not None: public_key_encrypted_session_key_entry["pubAlgorithm"] = pub_algorithm if (public_key_encrypted_session_key_entry and public_key_encrypted_session_key_entry not in self.metadata["publicKeyEncryptedSessionKeys"]): self.metadata["publicKeyEncryptedSessionKeys"].append(public_key_encrypted_session_key_entry) elif isinstance(packet, SignaturePacket): self.metadata["total"]["signatures"] += 1 signature_packet_entry = {} key_id = getattr(packet, "key_id", None) if key_id is not None: signature_packet_entry["keyId"] = key_id sig_version = getattr(packet, "sig_version", None) if sig_version is not None: signature_packet_entry["sigVersion"] = sig_version sig_type = getattr(packet, "sig_type", None) if sig_type is not None: signature_packet_entry["sigType"] = sig_type hash_algorithm = getattr(packet, "hash_algorithm", None) if hash_algorithm is not None: signature_packet_entry["hashAlgorithm"] = hash_algorithm pub_algorithm = getattr(packet, "pub_algorithm", None) if pub_algorithm is not None: signature_packet_entry["pubAlgorithm"] = pub_algorithm creation_time = getattr(packet, "creation_time", None) if creation_time is not None: signature_packet_entry["creationTime"] = creation_time.isoformat(timespec="seconds") expiration_time = getattr(packet, "expiration_time", None) if expiration_time is not None: signature_packet_entry["expirationTime"] = expiration_time.isoformat(timespec="seconds") length = getattr(packet, "length", None) if length is not None: signature_packet_entry["length"] = length if (signature_packet_entry and signature_packet_entry not in self.metadata["signatures"]): self.metadata["signatures"].append(signature_packet_entry) elif isinstance(packet, TrustPacket): self.metadata["total"]["trusts"] += 1 trust_entry = {} trusts = getattr(packet, "trusts", None) if trusts is not None: trust_entry["trusts"] = trusts if (trust_entry and trust_entry not in self.metadata["trusts"]): self.metadata["trusts"].append(trust_entry) elif isinstance(packet, UserAttributePacket): self.metadata["total"]["userAttributes"] += 1 user_attribute_entry = {} image_format = getattr(packet, "image_format", None) if image_format is not None: user_attribute_entry["imageFormat"] = image_format image_data = getattr(packet, "image_data", None) if image_data is not None: user_attribute_entry["imageData"] = image_data if (user_attribute_entry and user_attribute_entry not in self.metadata["userAttributes"]): self.metadata["userAttributes"].append(user_attribute_entry) elif isinstance(packet, UserIDPacket): self.metadata["total"]["userIds"] += 1 user_id_entry = {} user = getattr(packet, "user", None) if user is not None: user_id_entry["user"] = user user_name = getattr(packet, "user_name", None) if user_name is not None: user_id_entry["userName"] = user_name user_email = getattr(packet, "user_email", None) if user_email is not None: user_id_entry["userEmail"] = user_email if (user_id_entry and user_id_entry not in self.metadata["userIds"]): self.metadata["userIds"].append(user_id_entry) except TypeError: file_object.flags.append(f"{self.scanner_name}::type_error")
def get_keydata(data, include_subkeys=False, autocrypt_header=None): results = [] try: if "-----BEGIN" in data: ak = pgpdump.AsciiData(data) else: ak = pgpdump.BinaryData(data) packets = list(ak.packets()) except (TypeError, IndexError, PgpdumpException): return [] if autocrypt_header: # The autocrypt spec tells us that the visible addr= attribute # overrides whatever is on the key itself, so we synthesize a # fake UID here so strict e-mail matches don't break Autocrypt. ac_uid = {'comment': 'Autocrypt', 'email': autocrypt_header['addr']} else: ac_uid = None now = time.time() for m in packets: try: if isinstance(m, pgpdump.packet.PublicKeyPacket): size = str( int(1.024 * round(len('%x' % (m.modulus or 0)) / 0.256))) validity = ('e' if (0 < (int(m.expiration_time or 0)) < now) else '') results.append({ "fingerprint": m.fingerprint, "created": _get_creation_time(m), "validity": validity, "keytype_name": (m.pub_algorithm or '').split()[0], "keysize": size, "uids": [] }) if isinstance(m, pgpdump.packet.UserIDPacket) and results: # FIXME: This used to happen with results=[], does that imply # UIDs sometimes come before the PublicKeyPacket? results[-1]["uids"].append({ "name": m.user_name, "email": m.user_email }) except (TypeError, AttributeError, KeyError, IndexError, NameError): import traceback traceback.print_exc() if not include_subkeys: # This will only return keys that have UIDs results = [k for k in results if k['uids']] # Ensure that all the keys we're returning have the Autocrypt UID, # if they have UIDs at all. if ac_uid is not None: for k in results: found = 0 for u in k['uids']: if u['email'] == ac_uid['email']: u['comment'] = u.get('comment', '') + '(Autocrypt)' found += 1 if k['uids'] and not found: k['uids'] += [ac_uid] return results