0x50: 'third-party confirmation',
}
+ _signature_subpacket_types = {
+ 0: 'reserved',
+ 1: 'reserved',
+ 2: 'signature creation time',
+ 3: 'signature expiration time',
+ 4: 'exportable certification',
+ 5: 'trust signature',
+ 6: 'regular expression',
+ 7: 'revocable',
+ 8: 'reserved',
+ 9: 'key expiration time',
+ 10: 'placeholder for backward compatibility',
+ 11: 'preferred symmetric algorithms',
+ 12: 'revocation key',
+ 13: 'reserved',
+ 14: 'reserved',
+ 15: 'reserved',
+ 16: 'issuer',
+ 17: 'reserved',
+ 18: 'reserved',
+ 19: 'reserved',
+ 20: 'notation data',
+ 21: 'preferred hash algorithms',
+ 22: 'preferred compression algorithms',
+ 23: 'key server preferences',
+ 24: 'preferred key server',
+ 25: 'primary user id',
+ 26: 'policy uri',
+ 27: 'key flags',
+ 28: 'signer user id',
+ 29: 'reason for revocation',
+ 30: 'features',
+ 31: 'signature target',
+ 32: 'embedded signature',
+ 100: 'private',
+ 101: 'private',
+ 102: 'private',
+ 103: 'private',
+ 104: 'private',
+ 105: 'private',
+ 106: 'private',
+ 107: 'private',
+ 108: 'private',
+ 109: 'private',
+ 110: 'private',
+ }
+
_clean_type_regex = _re.compile('\W+')
def _clean_type(self):
return self['fingerprint'][-8:].upper()
def _str_signature_packet(self):
- return self['signature-type']
+ lines = [self['signature-type']]
+ if self['unhashed-subpackets']:
+ lines.append(' unhashed subpackets:')
+ for subpacket in self['unhashed-subpackets']:
+ lines.append(' {}'.format(subpacket['type']))
+ return '\n'.join(lines)
def _str_user_id_packet(self):
return self['user']
if key_end:
self['secret-key-checksum'] = data[key_end:]
+ def _parse_signature_subpackets(self, data):
+ offset = 0
+ while offset < len(data):
+ o, subpacket = self._parse_signature_subpacket(data=data[offset:])
+ offset += o
+ yield subpacket
+
+ def _parse_signature_subpacket(self, data):
+ subpacket = {}
+ first = data[0]
+ offset = 1
+ if first < 192:
+ length = first
+ elif first >= 192 and first < 255:
+ second = data[offset]
+ offset += 1
+ length = ((first - 192) << 8) + second + 192
+ else:
+ length = _struct.unpack(
+ '>I', data[offset: offset + 4])[0]
+ offset += 4
+ subpacket['type'] = self._signature_subpacket_types[data[offset]]
+ offset += 1
+ subpacket['data'] = data[offset: offset + length - 1]
+ offset += len(subpacket['data'])
+ return (offset, subpacket)
+
def _parse_signature_packet(self, data):
self['signature-version'] = data[0]
offset = 1
offset += hashed_count
unhashed_count = _struct.unpack('>H', data[offset: offset + 2])[0]
offset += 2
- self['unhashed-subpackets'] = data[offset: offset + unhashed_count]
+ self['unhashed-subpackets'] = list(self._parse_signature_subpackets(
+ data=data[offset: offset + unhashed_count]))
offset += unhashed_count
self['signed-hash-word'] = data[offset: offset + 2]
offset += 2
if self.public_packets:
lines.append(' public:')
for packet in self.public_packets:
- lines.append(' {}'.format(packet))
+ lines.extend(self._str_packet(packet=packet, prefix=' '))
if self.secret_packets:
lines.append(' secret:')
for packet in self.secret_packets:
- lines.append(' {}'.format(packet))
+ lines.extend(self._str_packet(packet=packet, prefix=' '))
return '\n'.join(lines)
+ def _str_packet(self, packet, prefix):
+ lines = str(packet).split('\n')
+ return [prefix + line for line in lines]
+
def import_from_gpg(self):
key_export = _get_stdout(
['gpg', '--export', self.fingerprint])
def export_to_gpg(self):
raise NotImplemetedError('export to gpg')
+ def import_from_key(self, key):
+ """Migrate the (sub)keys into this key"""
+ pass
+
def migrate(old_key, new_key):
"""Add the old key and sub-keys to the new key
old_key.import_from_gpg()
new_key = PGPKey(fingerprint=new_key)
new_key.import_from_gpg()
+ new_key.import_from_key(key=old_key)
print(old_key)
print(new_key)