c# - example - El modo CTS de Bouncy Castle para Blowfish Engine no funciona como se espera
bouncycastle maven (1)
Supongo que CTS y CBC siempre tendrán el mismo resultado si la entrada tiene una longitud de 8 bits. ¿Esto es solo suerte / coincidencia o es fundamentalmente verdad?
No, esta es una afirmación falsa.
Aquí está la cita de Wikipedia :
El robo de texto cifrado para el modo CBC no requiere necesariamente que el texto plano sea más largo que un bloque. En el caso de que el texto simple tenga un bloque de longitud o menos, el vector de Inicialización (IV) puede actuar como el bloque anterior de texto cifrado.
Así que incluso para su caso de entrada de 8 bytes, el algoritmo CTS entra en juego y afecta a la salida. Básicamente, su declaración sobre la igualdad de CTS y CBS podría revertirse:
CTS y CBC siempre tendrán el mismo resultado hasta los dos últimos bloques.
Podrías verificarlo con el siguiente ejemplo:
static byte[] EncryptData(byte[] input, string algorithm)
{
IBufferedCipher inCipher = CipherUtilities.GetCipher(algorithm);
var hashOfPrivateKey = HashValue(Encoding.ASCII.GetBytes("12345678"));
var key = new KeyParameter(hashOfPrivateKey);
var IV = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
var cipherParams = new ParametersWithIV(key, IV);
inCipher.Init(true, cipherParams);
return inCipher.DoFinal(input);
}
static void Main(string[] args)
{
var data = Encoding.ASCII.GetBytes("0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF");
var ctsResult = EncryptData(data, "BLOWFISH/CTS");
var cbcResult = EncryptData(data, "BLOWFISH/CBC");
var equalPartLength = data.Length - 2 * 8;
var equal = ctsResult.Take(equalPartLength).SequenceEqual(cbcResult.Take(equalPartLength));
}
Esta es básicamente la respuesta a tu pregunta principal. No debe esperar la misma salida para CTS y CBC en una entrada de 8 bytes.
Aquí están las respuestas (espero) a sus otras preguntas:
Si el modo CTS utilizado en el Compendio de cifrado de Delphi usa CBC junto con CTS. No pude encontrar documentado en ninguna parte.
Tampoco he encontrado ninguna documentación para el modo CTS en Delphi Encryption Compendium, sin embargo, hay tales comentarios en el código fuente :
cmCTSx = doble CBC, con relleno CFS8 de bloque final truncado
Los modos cmCTSx, cmCFSx, cmCFS8 son modos propietarios desarrollados por mí. Estos modos funcionan como cmCBCx, cmCFBx, cmCFB8 pero con doble XOR''ing de la corriente de entrada en el registro de Feedback.
Así que parece que el modo CTS se implementa de manera personalizada en Delphi Encryption Compendium que no será compatible con la implementación estándar de Bouncy Castle.
La diferencia entre llamar solo a DoFinal () y ProcessBytes () y luego a DoFinal () en Bouncy Castle, imagino que se requiere cuando el bloque de entrada es más grande que el tamaño del bloque del motor, en este caso son del mismo tamaño.
El par de llamadas ProcessBytes()
/ DoFinal()
se requiere si encripta los datos de forma secuencial. Podría ser necesario, por ejemplo, si se transmiten grandes datos. Sin embargo, si tiene una rutina que requiere una matriz de bytes completa para el cifrado, puede llamar a la siguiente sobrecarga conveniente de DoFinal()
una vez:
var encryptedData = inCipher.DoFinal(plainText);
Esta sobrecarga de DoFinal()
calculará el tamaño del búfer de salida y hará las llamadas requeridas de ProcessBytes()
y DoFinal()
bajo el capó .
Si el Compendio de cifrado de Delphi es correcto / incorrecto o Si Bouncy Castle es correcto / incorrecto. No tengo suficiente conocimiento en criptografía para entender la implementación, de lo contrario no haría una pregunta aquí (necesito orientación).
Resumamos aquí:
- No debe esperar la misma salida para CTS y CBC para la entrada de 8 bytes.
- Parece que Delphi Encryption Compendium usa un algoritmo personalizado para CTS. Dado que Bouncy Castle se implementa de acuerdo con los estándares, estas bibliotecas producirán resultados diferentes. Si no es necesario que su nueva aplicación sea compatible con datos encriptados producidos con la aplicación heredada de Delphi, entonces puede usar Bouncy Castle y estar bien. En otro caso, debe usar el mismo algoritmo CTS personalizado que utiliza Delphi Encryption Compendium, que requerirá el puerto de sus fuentes a C #, desafortunadamente.
ACTUALIZAR
(Más detalles sobre la implementación de Delphi Encryption Compendium en la versión 3.0)
Aquí hay un código de codificación CTS de la versión 3.0 de DEC:
S := @Source;
D := @Dest;
// ...
begin
while DataSize >= FBufSize do
begin
XORBuffers(S, FFeedback, FBufSize, D);
Encode(D);
XORBuffers(D, FFeedback, FBufSize, FFeedback);
Inc(S, FBufSize);
Inc(D, FBufSize);
Dec(DataSize, FBufSize);
end;
if DataSize > 0 then
begin
Move(FFeedback^, FBuffer^, FBufSize);
Encode(FBuffer);
XORBuffers(S, FBuffer, DataSize, D);
XORBuffers(FBuffer, FFeedback, FBufSize, FFeedback);
end;
end;
Aquí vemos el doble XOR''ing que fue mencionado en la documentación de DEC. Básicamente este código implementa el siguiente algoritmo:
C[i] = Encrypt( P[i] xor F[i-1] )
F[i] = F[i-1] xor C[i]
F[0] = IV
mientras que el algoritmo estándar sería:
C[i] = Encrypt( P[i] xor C[i-1] )
C[0] = IV
El paso F[i] = F[i-1] xor C[i]
es una invención del autor de DEC y hace que los resultados del cifrado sean diferentes. El manejo de los dos últimos bloques que es crucial para el modo CTS también se implementa no por un estándar.
Aquí hay un comentario de DEC v 3.0 ReadMe.txt que describe por qué el autor agregó dicha modificación:
Modo cmCTS, los datos de XOR antes y ahora después del cifrado. Esto tiene un mejor efecto de seguridad cuando se usa un InitVector, la salida es segura cuando se usa un InitVector malo, ca 1% de velocidad perdida
Es un error muy común cuando los autores de bibliotecas de seguridad intentan hacer que los algoritmos subyacentes sean "más seguros" con estas modificaciones ingenuas. En muchos casos, tales cambios tienen un efecto inverso y disminuyen la resistencia de protección. Otra desventaja, por supuesto, es que los datos cifrados no pueden ser descifrados por otras bibliotecas implementadas de acuerdo con el estándar, como en su caso.
Quizás mis expectativas estén equivocadas. No soy un experto en criptografía, solo soy un usuario simple. He intentado exhaustivamente hacer este trabajo sin éxito hasta ahora.
Información de fondo:
Estoy tratando de trasladar un cifrado heredado de Delphi Encryption Compendium que utiliza el motor Blowfish ( TCipher_Blowfish_
) con el modo de operación CTS ( cmCTS
). La clave privada es procesada por RipeMD256 ( THash_RipeMD256
).
Problemas:
La matriz de entrada de texto sin formato de bytes debe ser del mismo tamaño que CIPHER_BLOCK
. Por lo que puedo decir, no debería.
De Wikipedia:
En la criptografía, el robo de texto cifrado (CTS) es un método general de uso de un modo de operación de cifrado de bloque que permite el procesamiento de mensajes que no son divisibles uniformemente en bloques sin que se produzca una expansión del texto cifrado, a costa de una complejidad ligeramente mayor.
La salida no es la misma que la rutina anterior:
Estoy usando:
- Mismo iv
- Misma contraseña
- Misma entrada de texto sin formato
La aplicación heredada usa ANSI String, la nueva usa Unicode, así que para cada cadena de entrada he llamado Encoding.ASCII.GetBytes("plainText")
, Encoding.ASCII.GetBytes("privatepassword")
.
Los bytes de la contraseña privada son procesados por RipeMD256, he comprobado los bytes de salida y son los mismos.
Puedo confirmar que el problema es específico en el Bouncy Clastle (modo de operación o configuración / paso faltante) porque he descargado una segunda biblioteca Blowfish.cs y usando una entrada de 8 bytes (el mismo tamaño que el bloque de cifrado) y utilizando el Encrypt_CBC(bytes[])
con los mismos resultados de IV en la misma salida que el formato heredado.
Este es el bosquejo del código que estoy usando para Blowfish.cs
y Bouncy Castle
:
Compendio de cifrado de Delphi
var
IV: Array [0..7] of Byte (1,2,3,4,5,6,7,8);
Key: String = ''12345678'';
with TCipher_Blowfish.Create('''', nil) do
begin
try
InitKey(Key, @IV); //Key is auto hashed using RIPE256 here;
Result:= CodeString(''12345678'', paEncode, -1); //Output bytes is later encoded as MIME64 here, the result is the hash.
finally
Free;
end;
end;
Blofish.cs
var hashOfPrivateKey = HashValue(Encoding.ASCII.GetBytes("12345678"));
Blowfish b = new BlowFish(hashOfPrivateKey);
b.IV = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8};
var input = Encoding.ASCII.GetBytes("12345678");
var output = b.Encrypt_CBC(input);
Supongo que CTS y CBC siempre tendrán el mismo resultado si la entrada tiene una longitud de 8 bits. ¿Esto es solo suerte / coincidencia o es fundamentalmente verdad?
Castillo inflable
IBufferedCipher inCipher = CipherUtilities.GetCipher("BLOWFISH/CTS");
var hashOfPrivateKey = HashValue(Encoding.ASCII.GetBytes("12345678"));
var key = new KeyParameter(hashOfPrivateKey);
var IV = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8};
var cipherParams = new ParametersWithIV(key, IV);
inCipher.Init(true, cipherParams);
var input = Encoding.ASCII.GetBytes("12345678");
//try one: direct with DoFinal
var output = inCipher.DoFinal(input);
// output bytes different from expected
inCipher.Reset();
//try two: ProcessBytes then DoFinal
var outBytes = new byte[input.Length];
var res = inCipher.ProcessBytes(input, 0, input.Length, outBytes, 0);
var r = inCipher.DoFinal(outBytes, res);
// outBytes bytes different from expected
Como dije, estoy comparando CBC con CTS basándose en el supuesto de que, dada una entrada de 8 bytes, la salida será la misma. No puedo reenviar la implementación con Bouncy Castle si, incluso con la misma entrada, la salida no es la misma.
No lo sé:
- Si el modo CTS utilizado en el Compendio de cifrado de Delphi usa CBC junto con CTS. No pude encontrar documentado en ninguna parte.
- La diferencia entre llamar solo a DoFinal () y ProcessBytes () y luego a DoFinal () en Bouncy Castle, imagino que se requiere cuando el bloque de entrada es más grande que el tamaño del bloque del motor, en este caso son del mismo tamaño.
- Si el Compendio de cifrado de Delphi es correcto / incorrecto o Si Bouncy Castle es correcto / incorrecto. No tengo suficiente conocimiento en criptografía para entender la implementación, de lo contrario no haría una pregunta aquí (necesito orientación).