#!/usr/bin/python
+import re as _re
import subprocess as _subprocess
import struct as _struct
3: (None, None),
}
+ _packet_types = {
+ 0: 'reserved',
+ 1: 'public-key encrypted session key packet',
+ 2: 'signature packet',
+ 3: 'symmetric-key encrypted session key packet',
+ 4: 'one-pass signature packet',
+ 5: 'secret-key packet',
+ 6: 'public-key packet',
+ 7: 'secret-subkey packet',
+ 8: 'compressed data packet',
+ 9: 'symmetrically encrypted data packet',
+ 10: 'marker packet',
+ 11: 'literal data packet',
+ 12: 'trust packet',
+ 13: 'user id packet',
+ 14: 'public-subkey packet',
+ 17: 'user attribute packet',
+ 18: 'sym. encrypted and integrity protected data packet',
+ 19: 'modification detection code packet',
+ 60: 'private',
+ 61: 'private',
+ 62: 'private',
+ 63: 'private',
+ }
+
+ _public_key_algorithms = {
+ 1: 'rsa (encrypt or sign)',
+ 2: 'rsa encrypt-only',
+ 3: 'rsa sign-only',
+ 16: 'elgamal (encrypt-only)',
+ 17: 'dsa (digital signature algorithm)',
+ 18: 'reserved for elliptic curve',
+ 19: 'reserved for ecdsa',
+ 20: 'reserved (formerly elgamal encrypt or sign)',
+ 21: 'reserved for diffie-hellman',
+ 100: 'private',
+ 101: 'private',
+ 102: 'private',
+ 103: 'private',
+ 104: 'private',
+ 105: 'private',
+ 106: 'private',
+ 107: 'private',
+ 108: 'private',
+ 109: 'private',
+ 110: 'private',
+ }
+
+ _symmetric_key_algorithms = {
+ 0: 'plaintext or unencrypted data',
+ 1: 'idea',
+ 2: 'tripledes',
+ 3: 'cast5',
+ 4: 'blowfish',
+ 5: 'reserved',
+ 6: 'reserved',
+ 7: 'aes with 128-bit key',
+ 8: 'aes with 192-bit key',
+ 9: 'aes with 256-bit key',
+ 10: 'twofish',
+ 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._clean_type_regex.sub('_', self['type'])
+
def from_bytes(self, data):
offset = self._parse_header(data=data)
packet = data[offset:offset + self['length']]
raise ValueError('packet too short ({} < {})'.format(
len(packet), self['length']))
offset += self['length']
+ method_name = '_parse_{}'.format(self._clean_type())
+ method = getattr(self, method_name, None)
+ if not method:
+ raise NotImplementedError(
+ 'cannot parse packet type {!r}'.format(self['type']))
+ method(data=packet)
return offset
def _parse_header(self, data):
raise ValueError('most significant packet tag bit not set')
self['new-format'] = packet_tag & 1 << 6
if self['new-format']:
- self['packet-tag'] = packet_tag & 0b111111
+ type_code = packet_tag & 0b111111
raise NotImplementedError('new-format packet length')
else:
- self['packet-tag'] = packet_tag >> 2 & 0b1111
+ type_code = packet_tag >> 2 & 0b1111
self['length-type'] = packet_tag & 0b11
length_bytes, length_type = self._old_format_packet_length_type[
self['length-type']]
length_data = data[offset: offset + length_bytes]
offset += length_bytes
self['length'] = _struct.unpack(length_format, length_data)[0]
+ self['type'] = self._packet_types[type_code]
return offset
+ def _parse_public_key_packet(self, data):
+ self._parse_generic_public_key_packet(data=data)
+
+ def _parse_public_subkey_packet(self, data):
+ self._parse_generic_public_key_packet(data=data)
+
+ def _parse_generic_public_key_packet(self, data):
+ self['key-version'] = data[0]
+ offset = 1
+ if self['key-version'] != 4:
+ raise NotImplementedError(
+ 'public (sub)key packet version {}'.format(
+ self['key-version']))
+ length = 5
+ self['creation_time'], self['public-key-algorithm'] = _struct.unpack(
+ '>IB', data[offset: offset + length])
+ offset += length
+ self['key'] = data[offset:]
+
def to_bytes(self):
pass
['gpg', '--export', old_key])
old_key_packets = list(
packets_from_bytes(data=old_key_export))
+ if old_key_packets[0]['type'] != 'public-key packet':
+ raise ValueError(
+ '{} does not start with a public-key packet'.format(
+ old_key))
old_key_secret_export = _get_stdout(
['gpg', '--export-secret-keys', old_key])
old_key_secret_packets = list(