c++ - cifrar - cifrado aes java
¿Diferencia de velocidad entre cifrado AES/CBC y descifrado? (3)
Al analizar la tabla, ¿la diferencia entre el cifrado y el descifrado implica que mi implementación es anormalmente lenta? ¿Hice algo malo?
Tres o cuatro cosas saltan hacia mí. Estoy de acuerdo con @JamesKPolk: los números se ven apagados. En primer lugar, las bibliotecas de criptografía suelen estar marcadas con el modo CTR, no el modo CBC. También vea los puntos de referencia SUPERCOP . Cualquiera tiene que usar ciclos por byte (cpb) para normalizar las unidades de medida en todas las máquinas. Decir "9 MB / s" sin contexto no significa nada.
En segundo lugar, necesitamos conocer la máquina y su frecuencia de CPU. Parece que está presionando datos a 9 MB / s para cifrado y 6.5 MB / s para descifrado. Una máquina iCore moderna, como un Core-i5 funcionando a 2.7 GHz , impulsará los datos del modo CBC a alrededor de 2.5 o 3.0 cpb, que es aproximadamente 980 MB / s o 1 GB / s. Incluso mi viejo Core2 Duo corriendo a 2.0 GHz mueve los datos más rápido de lo que está mostrando. El Core 2 mueve datos a 14.5 cpb o 130 MB / s.
En tercer lugar, descarta este código. Hay mucho margen de mejora por lo que no vale la pena criticar en este contexto; y el código recomendado está abajo. Vale la pena mencionar que está creando muchos objetos como ArraySource
y StreamTransformationFilter
. El filtro agrega relleno que perturba los puntos de referencia de cifrado y descifrado AES y sesga los resultados. Solo necesita un objeto de cifrado y solo necesita llamar a ProcessBlock
o ProcessString
.
CryptoPP::AES::Decryption aesDecryption(aesKey, ENCRYPTION_KEY_SIZE_AES);
CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, aesIv);
...
En cuarto lugar, el wiki de Crypto ++ tiene un artículo de Benchmarks con el siguiente código. Es una nueva sección y no estaba disponible cuando hiciste tu pregunta. A continuación, le mostramos cómo ejecutar su prueba.
AutoSeededRandomPool prng;
SecByteBlock key(16);
prng.GenerateBlock(key, key.size());
CTR_Mode<AES>::Encryption cipher;
cipher.SetKeyWithIV(key, key.size(), key);
const int BUF_SIZE = RoundUpToMultipleOf(2048U,
dynamic_cast<StreamTransformation&>(cipher).OptimalBlockSize());
AlignedSecByteBlock buf(BUF_SIZE);
prng.GenerateBlock(buf, buf.size());
const double runTimeInSeconds = 3.0;
const double cpuFreq = 2.7 * 1000 * 1000 * 1000;
double elapsedTimeInSeconds;
unsigned long i=0, blocks=1;
ThreadUserTimer timer;
timer.StartTimer();
do
{
blocks *= 2;
for (; i<blocks; i++)
cipher.ProcessString(buf, BUF_SIZE);
elapsedTimeInSeconds = timer.ElapsedTimeAsDouble();
}
while (elapsedTimeInSeconds < runTimeInSeconds);
const double bytes = static_cast<double>(BUF_SIZE) * blocks;
const double ghz = cpuFreq / 1000 / 1000 / 1000;
const double mbs = bytes / 1024 / 1024 / elapsedTimeInSeconds;
const double cpb = elapsedTimeInSeconds * cpuFreq / bytes;
std::cout << cipher.AlgorithmName() << " benchmarks..." << std::endl;
std::cout << " " << ghz << " GHz cpu frequency" << std::endl;
std::cout << " " << cpb << " cycles per byte (cpb)" << std::endl;
std::cout << " " << mbs << " MiB per second (MiB)" << std::endl;
Ejecutar el código en un Core-i5 6400 a 2.7 GHz da como resultado:
$ ./bench.exe
AES/CTR benchmarks...
2.7 GHz cpu frequency
0.58228 cycles per byte (cpb)
4422.13 MiB per second (MiB)
Quinto, cuando modifico el programa de referencia que se muestra arriba para operar bloques de 64 bytes:
const int BUF_SIZE = 64;
unsigned int blocks = 0;
...
do
{
blocks++;
cipher.ProcessString(buf, BUF_SIZE);
elapsedTimeInSeconds = timer.ElapsedTimeAsDouble();
}
while (elapsedTimeInSeconds < runTimeInSeconds);
Veo 3.4 cpb o 760 MB / s para el Core-i5 6400 a 2.7 GHz para bloques de 64 bytes. La biblioteca recibe un golpe para pequeños búferes, pero la mayoría de las bibliotecas (¿todas?) Lo hacen.
$ ./bench.exe
AES/CTR benchmarks...
2.7 GHz cpu frequency
3.39823 cycles per byte (cpb)
757.723 MiB per second (MiB)
En sexto lugar, debe sacar el procesador del modo de ahorro de energía o un estado de baja energía para obtener los mejores y más consistentes resultados. La biblioteca usa governor.sh
para hacerlo en Linux. Está disponible en el directorio TestScript/
.
Séptimo, cuando cambio al descifrado del modo CTR con:
CTR_Mode<AES>::Decryption cipher;
cipher.SetKeyWithIV(key, key.size(), key);
Entonces veo aproximadamente la misma velocidad para el descifrado masivo:
$ ./bench.exe
AES/CTR benchmarks...
2.7 GHz cpu frequency
0.579923 cycles per byte (cpb)
4440.11 MiB per second (MiB)
Octavo, aquí hay una colección de números de referencia en varias máquinas diferentes. Debería proporcionar un objetivo aproximado a medida que ajusta sus pruebas.
La placa de desarrollo de mi Beaglebone, que funciona a 980 MHz, está moviendo datos al doble de la velocidad que está informando. El Beaglebone logra un aburrido 40 cpb con 20 MB / s porque es staright C / C ++; y no optimizado para A-32.
Skylake Core-i5 @ 2.7 GHz
Core2 Duo @ 2.0 GHz
LeMaker HiKey Kirik SoC Aarch64 a 1.2 GHz
AMD Opteron Aarch64 @ 2.0 GHz
BananaPi Cortex-A7 dev-board @ 860MHz
Servidor IBM Power8 a 4.1 GHz
Mis conclusiones son:
- El cifrado y el descifrado a granel del modo CTR son casi los mismos en las máquinas modernas
- La configuración de la tecla del modo CTR no es la misma, el descifrado lleva un poco más de tiempo en las máquinas modernas
- Los tamaños de bloques pequeños son más caros que los tamaños de bloques grandes
Es todo lo que espero ver.
Creo que su próximo paso es recopilar algunos datos usando el programa de ejemplo en la wiki de Crypto ++ y luego evaluar los resultados.
Me pregunto, en teoría, cuánto más lento se compararía el descifrado AES / CBC con el cifrado AES / CBC con las siguientes condiciones:
- Clave de encriptación de 32 bytes (256 bits);
- Un tamaño de bloques de 16 bytes (128 bits).
La razón por la que pregunto es que quiero saber si la velocidad de descifrado de una implementación que tengo no es anormalmente lenta. He hecho algunas pruebas en bloques de memoria aleatoria de diferentes tamaños. Los resultados son los siguientes:
64B:
64 KB:
10 MB - 520 MB:
Todos los datos fueron almacenados en la memoria interna de mi sistema. La aplicación genera los datos para cifrar por sí mismo. La memoria virtual está desactivada en la PC de prueba para que no haya llamadas de E / S.
Al analizar la tabla, ¿la diferencia entre el cifrado y el descifrado implica que mi implementación es anormalmente lenta? ¿Hice algo malo?
Actualizar:
- Esta prueba se ejecuta en otra pc;
- Esta prueba se ejecuta con datos aleatorios;
- Crypto ++ se utiliza para el cifrado y descifrado AES / CBC.
La implementación de descifrado es la siguiente:
CryptoPP::AES::Decryption aesDecryption(aesKey, ENCRYPTION_KEY_SIZE_AES);
CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, aesIv);
CryptoPP::ArraySink * decSink = new CryptoPP::ArraySink(data, dataSizeMax);
CryptoPP::StreamTransformationFilter stfDecryptor(cbcDecryption, decSink);
stfDecryptor.Put(reinterpret_cast<const unsigned char*>(ciphertext), cipherSize);
stfDecryptor.MessageEnd();
*dataOutputSize = decSink->TotalPutLength();
Actualización 2:
- Resultado agregado para bloques de 64 bytes
Como el cifrado simétrico, el cifrado y el descifrado deberían ser bastante cercanos en velocidad . No estoy seguro acerca de su implementación, pero hay formas de optimizarlo si le preocupa cómo se usó el algoritmo . En experimentos, AES no es el más rápido y CBC agregará seguridad pero lo ralentizará. Aquí hay una comparación, ya que preguntas sobre los tamaños de clave y bloque:
Teóricamente, el descifrado de AES es un 30% más lento. Esta es una propiedad de los sistemas Rijndael en general.