Seguridad del esquema de encriptación híbrido

2

Quiero usar un esquema de cifrado híbrido para el cifrado de archivos. El esquema debe usar PKCS # 1 v1.5, que es vulnerable a los ataques de Oracle oracle, porque muchas tarjetas inteligentes (por ejemplo, la tarjeta OpenPGP) solo son compatibles con PKCS # 1 v1.5.

Así que se me ocurrió el siguiente esquema (las claves RSA tienen un tamaño de 4096 bits, el algoritmo simétrico es AES-256-CTR o ChaCha-20 con claves de 256 bits y, por supuesto, se utilizan diferentes claves para la firma y el cifrado Y, por supuesto, sé que para los cifrados de flujo, los IVs absolutamente únicos deben usarse con la misma clave):

Encriptación:

var privateSigningKey;
var publicEncryptionKey;

---   var encryptedBlock = encryptAsym(sessionKey, iV) + encryptSym(data + hash(data));
+++   var encryptedBlock = encryptAsym(sessionKey, iV) + encryptSym(data + sign(data));
var signatureBlock = sign(encryptedBlock);
var encryptedData = encryptedBlock + signatureBlock;

return encryptedData;

Descifrado:

var trustedPublicKeys[];
var signatureBlock = encryptedData.getSignatureBlock();
var encryptedBlock = encryptedData.getEncryptedBlock();
var sender = "";

for each trustedPublicKey do {
    bool signatureValid = verifySignature(encryptedBlock, signatureBlock, trustedPublicKeys[i].getSigningKey();
    if (signatureValid) sender = trustedPublicKeys[i].getName();
}

if (sender == "") exit("Untrusted signature; stopping to prevent padding oracle attacks.");

var plainDataBlock = decryptHybrid(encryptedBlock);
---   var hash = plainDataBlock.getHash();
+++   var signature = plainDataBlock.getHash();
var plainData = plainDataBlock.getPlainData();

---   if (hash != hash(plainData)) exit("Decryption failed.");
+++   if (signature != sign(plainData)) exit("Decryption failed; untrusted signature.");

return plainData;

¿Se puede considerar seguro este esquema? ¿O hay algún problema de seguridad o mejores alternativas?

Sé que normalmente no haces tu propio cripto; pero no puedo usar el formato OpenPGP porque no evita los ataques de relleno (consulte esta pregunta ) y no conozco ninguna alternativa mejor.

Editar: Esquema actualizado

    
pregunta K. Biermann 20.02.2015 - 13:16
fuente

1 respuesta

1

Su esquema no protege contra un adversario que elige el mismo SigningKey como una parte honesta. Incluso si eso no es un problema, si un adversario podría o no "obtener crédito" por error
Un texto simple aún depende de los detalles de PKCS # 1 v1.5, ya que tendría que ser inviable.
para que un adversario encuentre un SigningKey malicioso diferente para el cual es probable que acepte un
versión mutilada de un par de firma de mensaje de privateSigningKey del remitente honesto.



Del mismo modo, su esquema no protege contra un adversario que elige el mismo
publicEncryptionKey como una fiesta honesta. Incluso si eso no fuera un problema, sea o no
su esquema proporciona autenticidad aún dependería de los detalles de PKCS # 1 v1.5,
ya que no sería factible que un adversario ganara el siguiente juego:


el adversario recibe un SigningKey y publicEncryptionKey generados honestamente
el adversario genera un texto simple data una supuesta clave de cifrado pública RSA maliciousEncryptKey
sessionKey y iV se generan honestamente

utilizando malwareEncryptKey,
encryptedBlock = encryptAsym(sessionKey, iV) + encryptSym(data + sign(data))

el adversario gana si y solo si plainDataBlock = data + sign(data)


No tengo conocimiento de ninguna alternativa "aprobada". Sin embargo, la mayoría de una buena alternativa es:


tener encryptSym ser alguna implementación de cifrado autenticado por única vez

permita que las listas entre corchetes denoten alguna forma de indicar inequívocamente todas las entradas de la lista
(Si todos menos uno de ellos tienen una longitud conocida, entonces la concatenación funcionaría,
de lo contrario, se podría usar algo como prefixfree (x) || prefixfree (y) || z.)

vamos a to_be_signed = [receiver's_name,receiver's_public_keys,data]

vamos a encryptedBlock =
[encryptAsym(receiver's_public_encryption_key,sessionKey), encryptSym([sender's_name,sender's_public_keys,to_be_signed,sign(to_be_signed)]

omita signatureBlock , y simplemente devuelve encryptedBlock



Si no es necesario que encryptedBlock oculte sender's_name , entonces eso y
sender's_public_keys se puede hacer datos asociados en lugar de estar encriptados.

Si uno confía en la seguridad CCA del cifrado de clave pública, la parte de esta respuesta
antes de esta oración pero después de la línea horizontal casi funcionará. (Ver el último párrafo de esta respuesta.)
De lo contrario, uno debería elegir valores de igual longitud distintos a y b (los bits 0 y 1
en cualquier orden funcionaría bien, pero podría ser más conveniente dejar que sean bytes enteros),
Reemplace to_be_signed con a || [receiver's_name,receiver's_public_keys,data] ,
y deje que signatureBlock = sign ( b || encryptedBlock ) en lugar de omitirlo.
Tenga en cuenta que en este caso, signatureBlock generalmente será suficiente para deducir sender's_name ,
lo que significa que usualmente hay un punto muy pequeño para que encryptedBlock oculte eso.


También es necesario asegurarse de que la operación de horquillado y el esquema de firma sean tales que la longitud de sus resultados no filtre más de lo necesario sobre la información que debe ser confidencial.
(ya que el cifrado puede filtrar la longitud del mensaje). Como condición suficiente, se mantendrán si
{la longitud de las representaciones de la lista depende solo de la longitud de las entradas y para los pares de claves de firma generados honestamente, la longitud de las firmas depende solo de la longitud de los mensajes correspondientes}.
Además, si desea permitir mensajes de varios receptores, las cosas son mucho más complicadas que
lo que he descrito en esta respuesta (aunque por razones no relacionadas con el resto de este párrafo).

    
respondido por el user49075 20.02.2015 - 14:15
fuente

Lea otras preguntas en las etiquetas