3

所以我一直在广泛地寻找 CBC 和 GCM 的不同 AES 实现,我不想自己实现这个,以防我出错,所以我找到了以下 AES CBC 代码并在我的 RX63NB 上测试了它们的速度(瑞萨测试板)。

                    Encrypt                 Decrypt 
                    bytes   speed (us)      bytes   speed (us)
Tiny AES            64      1500            64      8900
                    128     2880            128     17820
aes-byte-29-08-08   64      1250            64      4900
                    128     1220            128     9740
Cyclone             64      230             64      237
                    128     375             128     387

我对 Cyclone 的速度有多快感到惊讶,为了澄清我从CycloneSSL 获取了 AES、CBC 和 Endian 文件并且只使用了这些文件。

然后我尝试了 CycloneSSl 的 GCM,这是输出:

                    Encrypt                 Decrypt 
                    bytes   speed μs        bytes   speed μs
Cyclone   GCM       64      9340            64      9340
                    128     14900           128     14900

我检查了 HMAC 时间(来自 CycloneSSL),看看需要多少时间:

HMAC        bytes   speed μs
Sha1        64      746
            128     857
Sha224      64      918
            128     1066
Sha256      64      918
            128     1066
Sha384      64      2395
            128     2840
Sha512      64      2400
            128     2840
Sha512_224  64      2390
            128     2835
Sha512_356  64      2390
            128     2835
MD5         64      308
            128     345
Whirlpool   64      5630
            128     6420
Tiger       64      832
            128     952

其中最慢的是漩涡。

如果您将 128 字节的 cbc 加密时间添加到带有 128 字节的漩涡的 hmac 中,您将获得 6795 μs,这大约是 GCM 所需时间的一半。

现在我可以理解,由于 galios 字段等原因,GHASH 比 HMAC 需要更长的时间,但与我知道的最慢的 HASH 算法相比,它的速度要慢 2 倍,这太疯狂了。

所以我开始怀疑我是否做错了什么,或者 CycloneSLL gcm 的实现是否真的很明显。不幸的是,我还没有在 c 中找到其他易于使用的 GCM 实现来与之进行比较。

我使用的所有代码都可以在 pastebin上找到,不同的文件用 -------------------- 分隔

这是我用来用 GCM 加密的代码:

static void test_encrypt(void)
{
  uint8_t key[] = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
  uint8_t iv[]  = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
  uint8_t in[]  = { 0x48, 0x61, 0x6c, 0x6c, 0x6f, 0x20, 0x68, 0x6f, 0x65, 0x20, 0x67, 0x61, 0x61, 0x74, 0x20, 0x68,
                    0x65, 0x74, 0x20, 0x6d, 0x65, 0x74, 0x20, 0x6a, 0x6f, 0x75, 0x20, 0x76, 0x61, 0x6e, 0x64, 0x61,
                    0x61, 0x67, 0x2c, 0x20, 0x6d, 0x65, 0x74, 0x20, 0x6d, 0x69, 0x6a, 0x20, 0x67, 0x61, 0x61, 0x74,
                    0x20, 0x68, 0x65, 0x74, 0x20, 0x67, 0x6f, 0x65, 0x64, 0x20, 0x68, 0x6f, 0x6f, 0x72, 0x2e, 0x21,
                    0x48, 0x61, 0x6c, 0x6c, 0x6f, 0x20, 0x68, 0x6f, 0x65, 0x20, 0x67, 0x61, 0x61, 0x74, 0x20, 0x68,
                    0x65, 0x74, 0x20, 0x6d, 0x65, 0x74, 0x20, 0x6a, 0x6f, 0x75, 0x20, 0x76, 0x61, 0x6e, 0x64, 0x61,
                    0x61, 0x67, 0x2c, 0x20, 0x6d, 0x65, 0x74, 0x20, 0x6d, 0x69, 0x6a, 0x20, 0x67, 0x61, 0x61, 0x74,
                    0x20, 0x68, 0x65, 0x74, 0x20, 0x67, 0x6f, 0x65, 0x64, 0x20, 0x68, 0x6f, 0x6f, 0x72, 0x2e, 0x21};

  AesContext context;
  aesInit(&context, key, 16 ); // 16 byte = 128 bit      
  error_crypto_t error = gcmEncrypt(AES_CIPHER_ALGO, &context, iv, 16, 0, 0, in, in, 128, key, 16);
}

static void test_decrypt(void)
{
  uint8_t key[] = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
  uint8_t tag[] = { 0x56, 0x56, 0x5C, 0xCD, 0x5C, 0x57, 0x36, 0x66, 0x73, 0xF7, 0xFF, 0x2A, 0x17, 0x49, 0x0E, 0xC4};
  uint8_t iv[]  = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
  uint8_t out[] = { 0x05, 0x7C, 0x51, 0xFF, 0xE4, 0x9F, 0x8C, 0x90, 0xF1, 0x7D, 0x56, 0xFB, 0x87, 0xB9, 0x44, 0x79,
                    0xB1, 0x04, 0x32, 0x39, 0x78, 0xFF, 0x51, 0x60, 0x48, 0x0B, 0x21, 0x77, 0xF2, 0x26, 0x0B, 0x94,
                    0x7B, 0xA7, 0x26, 0x74, 0x87, 0xA8, 0x2C, 0x5A, 0xA1, 0x19, 0x03, 0x17, 0x66, 0x3A, 0x46, 0x9F,
                    0xE6, 0x1D, 0x3B, 0x65, 0xFD, 0xC0, 0xBA, 0xC0, 0xD9, 0x45, 0xE7, 0x17, 0x74, 0x0F, 0xB7, 0x4B,
                    0x0F, 0xF0, 0x16, 0xF6, 0xE8, 0x4F, 0xFD, 0x96, 0x64, 0x5E, 0xDB, 0x9E, 0x3A, 0x0B, 0x93, 0x8F,
                    0x87, 0x83, 0x90, 0xF8, 0xF9, 0xE6, 0xA3, 0xE7, 0x5E, 0x72, 0x3C, 0xB5, 0x98, 0x54, 0x11, 0xD7,
                    0xB4, 0x7C, 0xFF, 0xA3, 0x51, 0x1A, 0xB0, 0x69, 0x4F, 0x57, 0xBB, 0x83, 0x40, 0x2A, 0xE6, 0x75,
                    0x8B, 0xB5, 0xCA, 0xA4, 0x84, 0x82, 0x1D, 0xA8, 0x94, 0x03, 0x77, 0x9C, 0x3B, 0xF8, 0xA0, 0x60};

  AesContext context;
  aesInit(&context, key, 16 ); // 16 byte = 128 bit
  error_crypto_t error = gcmDecrypt(AES_CIPHER_ALGO, &context, iv, 16, 0, 0, out, out, 128, tag, 16);
}

out[] 中的数据是来自 in[] 的 gcm 加密数据,并且一切正常。(正确解密并通过身份验证。

问题

  • 所有 GCM 实现都这么慢吗?
  • 还有其他(更好的)GCM 实现吗?
  • 如果我想要快速加密+验证,我应该只使用 HMAC 吗?

编辑

我已经能够从mbedTLS (PolarSSL)获得 GCM 方法,它比旋风分离器快 11 倍(加密/解密 128 个字节需要 880us)。它产生与 cylcone GCM 相同的输出,所以我相信它可以正常工作。

gcm_context gcm_ctx;
gcm_init(&gcm_ctx, POLARSSL_CIPHER_ID_AES,key, 128);
int error = gcm_auth_decrypt(&gcm_ctx, 128,iv, 16, NULL, 0, tag, 16, out, buffer );
4

1 回答 1

0

你的数字看起来很奇怪,aes-byte-29-08-08 的 128 字节比 64 字节的加密时间少?

假设 RX63N 与 Cortex-M 相当(它们都是 32 位的,没有向量单元,而且很难找到关于 RX63N 的信息):

SharkSSL声称的基准使 CBC 的速度比 GCM 快两倍多,如果针对速度进行了优化,则为 2.6。9340比340大很多。

Cifra的基准测试显示他们的 AES 和 AES-GCM 之间存在 10 倍的差异,尽管 GCM 测试还包括 auth-data。仍然远未接近直接 AES 和 GCM 之间的差异。

所以相对而言,回答 1,我不认为所有的 GCM 实现都那么慢,相对于普通的 AES。

至于其他的 GCM 实现,还有前面提到的 Cifra(虽然我直到现在才听说它,而且它在 GitHub 上只有 3 颗星(如果这意味着什么的话),所以审核级别可能相当低) ,也许您可​​以从FreeBSD中删除 AES-GCM 实现。尽管在您的平台上绝对不能谈论性能。

不管实现如何,HMAC 在没有硬件支持(如 AES-NI 支持(CLMUL))的平台上可能会更快。这对性能有多重要?您必须使用 AES 还是分组密码?也许 ChaCha20+Poly1305 更适合您的性能需求(请参阅 Cifra 的性能数据)。现在在OpenSSH中使用- chacha.* 和 poly1305.*

注意旁道攻击。AES 的软件实现可能对缓存时序攻击很敏感,尽管我认为这不适用于所有东西都在 SRAM 中的微控制器。

*Salsa20 是 ChaCha20 的前身

于 2015-03-27T06:38:45.117 回答