From 152f5fc0e8b216b2ea0bd96438ad936894c8422a Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 21 Dec 2013 10:25:31 -0800 Subject: [PATCH] Add PGPPacket._string_to_key for calculating decryption keys So far we only implement the 'simple' string-to-key type. From RFC 4880 [1]: Simple S2K hashes the passphrase to produce the session key. The manner in which this is done depends on the size of the session key (which will depend on the cipher used) and the size of the hash algorithm's output. If the hash size is greater than the session key size, the high-order (leftmost) octets of the hash are used as the key. If the hash size is less than the key size, multiple instances of the hash context are created -- enough to produce the required key data. These instances are preloaded with 0, 1, 2, ... octets of zeros (that is to say, the first instance has no preloading, the second gets preloaded with 1 octet of zero, the third is preloaded with two octets of zeros, and so forth). As the data is hashed, it is given independently to each hash context. Since the contexts have been initialized differently, they will each produce different hash output. Once the passphrase is hashed, the output data from the multiple hashes is concatenated, first hash leftmost, to produce the key data, with any excess octets on the right discarded. [1]: http://tools.ietf.org/search/rfc4880#section-3.7.1.1 --- gpg-migrate.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/gpg-migrate.py b/gpg-migrate.py index 393d460..0bf1133 100755 --- a/gpg-migrate.py +++ b/gpg-migrate.py @@ -848,6 +848,32 @@ class PGPPacket (dict): self['public-key-algorithm'])) return b''.join(chunks) + def _string_to_key(self, string, key_size): + if key_size % 8: + raise ValueError( + '{}-bit key is not an integer number of bytes'.format( + key_size)) + key_size_bytes = key_size // 8 + hash_name = self._hashlib_name[ + self['string-to-key-hash-algorithm']] + string_hash = _hashlib.new(hash_name) + hashes = _math.ceil(key_size_bytes / string_hash.digest_size) + key = b'' + if self['string-to-key-type'] == 'simple': + update_bytes = string + else: + raise NotImplementedError( + 'key calculation for string-to-key type {}'.format( + self['string-to-key-type'])) + for padding in range(hashes): + string_hash = _hashlib.new(hash_name) + string_hash.update(padding * b'\x00') + if self['string-to-key-type'] == 'simple': + string_hash.update(update_bytes) + key += string_hash.digest() + key = key[:key_size_bytes] + return key + def decrypt_symmetric_encryption(self, data): """Decrypt OpenPGP's Cipher Feedback mode""" algorithm = self['symmetric-encryption-algorithm'] @@ -865,8 +891,9 @@ class PGPPacket (dict): passphrase = _getpass.getpass( 'passphrase for {}: '.format(self['fingerprint'][-8:])) passphrase = passphrase.encode('ascii') + key = self._string_to_key(string=passphrase, key_size=key_size) cipher = module.new( - key=passphrase, + key=key, mode=module.MODE_CFB, IV=self['initial-vector'], segment_size=segment_size_bits) -- 2.26.2