OpenPGP (RFC4880): ¿está de acuerdo con mi implementación de SimpleS2K (cadena a clave)?

2

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

  1. Cree dos hasher SHA1 (debido a que AES256 necesita una clave de 32 bytes, SHA1 produce un hash de 20 bytes)
  2. No cargues hashers [0]
  3. Actualice foo con 0x00
  4. Actualice hashers[1] con UTF-8 codificado hashers[0]
  5. Actualice foo con UTF-8 codificado hashers[0]
  6. Concatenar foo más hashers[0].digest
  7. 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!

    
pregunta Paul M Furley 27.03.2015 - 15:35
fuente

0 respuestas

Lea otras preguntas en las etiquetas