From 4c59906fd9fca4e2b5d63f6832ccbb3ada45a271 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 21 Dec 2013 09:06:40 -0800 Subject: [PATCH] Use PyCrypto for decrypting symmetric encryption Maintained by Dwayne C. Litzenberger [1,2,3]. Although there is a MODE_OPENGP in PyCrypto since v2.6 [4], it seems like v4 secret key bodies are actually encrypted using plain-vanilla MODE_CFB. [1]: http://www.pycrypto.org/ [2]: https://www.dlitz.net/software/pycrypto/ [3]: https://github.com/dlitz/pycrypto/ [4]: https://github.com/dlitz/pycrypto/blob/master/ChangeLog --- gpg-migrate.py | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/gpg-migrate.py b/gpg-migrate.py index 785f78e..9f775e1 100755 --- a/gpg-migrate.py +++ b/gpg-migrate.py @@ -7,6 +7,11 @@ import re as _re import subprocess as _subprocess import struct as _struct +import Crypto.Cipher.AES as _crypto_cipher_aes +import Crypto.Cipher.Blowfish as _crypto_cipher_blowfish +import Crypto.Cipher.CAST as _crypto_cipher_cast +import Crypto.Cipher.DES3 as _crypto_cipher_des3 + def _get_stdout(args, stdin=None): stdin_pipe = None @@ -109,6 +114,15 @@ class PGPPacket (dict): 'cast5': 64, } + _crypto_module = { + 'aes with 128-bit key': _crypto_cipher_aes, + 'aes with 192-bit key': _crypto_cipher_aes, + 'aes with 256-bit key': _crypto_cipher_aes, + 'blowfish': _crypto_cipher_blowfish, + 'cast5': _crypto_cipher_cast, + 'tripledes': _crypto_cipher_des3, + } + _compression_algorithms = { 0: 'uncompressed', 1: 'zip', @@ -790,7 +804,31 @@ class PGPPacket (dict): return b''.join(chunks) def decrypt_symmetric_encryption(self, data): - raise NotImplementedError('decrypt symmetric encryption') + """Decrypt OpenPGP's Cipher Feedback mode""" + algorithm = self['symmetric-encryption-algorithm'] + module = self._crypto_module[algorithm] + key_size = self._key_size[algorithm] + segment_size_bits = self._cipher_block_size[algorithm] + if segment_size_bits % 8: + raise NotImplementedError( + ('{}-bit segment size for {} is not an integer number of bytes' + ).format(segment_size_bits, algorithm)) + segment_size_bytes = segment_size_bits // 8 + padding = segment_size_bytes - len(data) % segment_size_bytes + if padding: + data += b'\x00' * padding + passphrase = _getpass.getpass( + 'passphrase for {}: '.format(self['fingerprint'][-8:])) + passphrase = passphrase.encode('ascii') + cipher = module.new( + key=passphrase, + mode=module.MODE_CFB, + IV=self['initial-vector'], + segment_size=segment_size_bits) + plaintext = cipher.decrypt(data) + if padding: + plaintext = plaintext[:-padding] + return plaintext def packets_from_bytes(data): -- 2.26.2