Antecedentes: estoy escribiendo un GPL Python OpenPGP para el analizador JSON que estoy probando en archivos generados con GPG 1.4.16.
Si se le da una frase de contraseña, el analizador generará claves utilizando los métodos de cadena a clave y, en última instancia, descifrar mensajes.
Estoy empezando con mensajes de cifrado simétricos:
echo "hello" | gpg --s2k-mode=0 --symmetric > symmetric.simples2k.gpg
... y usando "foo" como frase de contraseña.
Esto genera un paquete con un paquete SymmetricKeyEncryptedSessionKeyPacket
y un paquete% co_de, como se esperaba.
Los parámetros de S2K que GPG creó son: S2K simple ( enlace ) con hash SHA1 y Cifrado simétrico AES256.
Problema: Cuando obtengo la clave de la frase de contraseña SymmetricEncryptedandIntegrityProtectedDataPacket
usando SimpleS2K, luego intento descifrar con AES256, no se descifra correctamente. Así que la parte 1 de mi investigación es verificar que estoy haciendo el S2K correctamente.
Este es mi entendimiento de cómo generar la clave desde la frase de contraseña foo
usando SimpleS2K
- Cree dos hasher SHA1 (debido a que AES256 necesita una clave de 32 bytes, SHA1 produce un hash de 20 bytes)
- No cargues hashers [0]
- Actualice
foo
con 0x00 - Actualice
hashers[1]
con UTF-8 codificadohashers[0]
- Actualice
foo
con UTF-8 codificadohashers[0]
- Concatenar
foo
máshashers[0].digest
- Tome los primeros 32 bytes del resultado (es decir, elimine los últimos 8 bytes)
Aquí hay una implementación de demostración en Python 3:
import hashlib
from Crypto.Cipher import AES # using pycrypto==2.6.1
def format_octets(octets):
return ' '.join(['{:02x}'.format(x) for x in octets])
hasher_0 = hashlib.sha1()
hasher_1 = hashlib.sha1()
hasher_1.update(bytes([0x0]))
hasher_0.update('foo'.encode('utf-8'))
hasher_1.update('foo'.encode('utf-8'))
key = (hasher_0.digest() + hasher_1.digest())[0:32]
print('s2k key: {}'.format(format_octets(key)))
encrypted_data = bytes([
0x0b, 0x1e, 0xcf, 0x86, 0x33, 0x08, 0xfd, 0x66, 0x9a, 0xf0, 0xbe, 0x48,
0x62, 0xa4, 0xa5, 0x42, 0x4f, 0xd8, 0x20, 0xfe, 0x16, 0xe4, 0x4c, 0xdb,
0x80, 0x89, 0xee, 0x34, 0x72, 0xef, 0x52, 0x36, 0x70, 0x15, 0x01, 0x82,
0xd5, 0x0e, 0xeb, 0x61, 0xba, 0xe7, 0x71, 0x4a, 0x8c, 0x22, 0x6a, 0x9c,
0x79, 0x8f, 0xe3, 0xda, 0x31, 0xfc, 0xad, 0x14, 0xeb, 0x8a])
block_size = 16 # AES
iv = bytes([0x0] * block_size) # all-zero initialisation vector
obj = AES.new(key, AES.MODE_CFB, iv)
decrypted = obj.decrypt(encrypted_data)
random_octets = decrypted[0: block_size]
repeated_octets = decrypted[block_size: block_size + 2]
plaintext_packets = decrypted[block_size + 2:-20]
sha1 = decrypted[-20:]
print('random octets: {}'.format(format_octets(random_octets)))
print('repeated octets: {}'.format(format_octets(repeated_octets)))
print('plaintext_packets: {}'.format(format_octets(plaintext_packets)))
print('sha1: {}'.format(format_octets(sha1)))
Qué resultados
s2k key: 0b ee c7 b5 ea 3f 0f db c9 5d 0d d4 7f 3c 5b c2 75 da 8a 33 5a 8c aa 40 39 fd bc 02 c0 1a 64 9c
random octets: 48 9b 65 84 01 f7 33 23 0a 6e e4 32 9d c4 ef 99
repeated octets: 56 a8
plaintext_packets: b9 fd 5a 05 6e d2 83 22 22 fe e5 d7 77 dd 93 49 4f 03 fe 13
sha1: ce 7d 93 34 59 f6 71 70 c3 bd 06 94 ad 97 f3 0a 59 72 1d 93
La salida completa de JSON está aquí: enlace
Esperemos que podamos descartar la parte S2K y acceder a la parte AES :)
¡Gracias!