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 read_gpg_key(data,base64decode=True): if base64decode==True: binary = base64.b64decode(data) else: binary = data info = pgpdump.BinaryData(binary) info_key = {} info_key_id = {} info_keys_tmp = [] info_keys_out = [] packets = list(info.packets()) for packet in packets: #print(packet) if isinstance(packet, PublicSubkeyPacket) or isinstance(packet, PublicKeyPacket): info_key['key_id'] = packet.key_id info_key['raw_pub_algorithm'] = packet.raw_pub_algorithm info_key['modulus_bitlen'] = packet.modulus_bitlen info_key['exponent'] = packet.exponent info_key['modulus'] = packet.modulus info_keys_tmp.append(info_key) elif isinstance(packet, UserIDPacket): info_key_id['user_name'] = packet.user_name info_key_id['user_email'] = packet.user_email for info_key in info_keys_tmp: info_key['user_name'] = info_key_id['user_name'] info_key['user_email'] = info_key_id['user_email'] info_keys_out.append(info_key) return info_keys_out
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 render(self, value, request): if not value: return super(PGPKeyField, self).render(_('No public key.\nPaste your ASCII-armored pubkey here.'), request) data = pgpdump.BinaryData(value) packets = list(data.packets()) if packets: value = 'Imported Key:\n0x'+(packets[0].key_id.decode('utf-8')) else: value = '' return super(PGPKeyField, self).render(value, request)
def parse(settingsdir=None): if settingsdir == None: settingsdir = GnuPGProperties.path secring_file = os.path.join(settingsdir, GnuPGProperties.secring) if not os.path.exists(secring_file): return dict() rawdata = GnuPGProperties.load_data(secring_file) try: data = pgpdump.BinaryData(rawdata) except pgpdump.utils.PgpdumpException, e: print("gnupg: %s" % (e)) return dict()
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 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 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