来自 Linux 内核的 CCM-AES

标签 c linux cryptography kernel aes

我需要与 Solaris 加密机制 SUN_CKM_AES_CCM 兼容。在 Linux 中,我相信我应该设置一个 AEAD 请求来获取“ccm(aes)”机械。 Linux Crypto 的文档看起来确实很差,最好的例子似乎是 tcrypt.c 测试和内核源代码。

我在 Solaris 上对一个 512 字节的 block 进行了测试加密,其中包含 16 字节的 hmac 和 12 字节的 iv。这需要保持不变,并希望结果相同。

但是,我认为应该会起作用,但实际上并没有;

   struct crypto_aead *tfm = NULL;
   struct aead_request *req;
   unsigned char key[16] = {
    0x5c, 0x95, 0x64, 0x42, 0x00, 0x82, 0x1c, 0x9e,
    0xd4, 0xac, 0x01, 0x83, 0xc4, 0x9c, 0x14, 0x97
   };
  unsigned int ivsize;
  int ret;
  struct scatterlist plaintext[1];
  struct scatterlist ciphertext[1];
  struct scatterlist hmactext[1];
  unsigned char *plaindata = NULL;
  unsigned char *cipherdata = NULL;
  unsigned char *hmacdata = NULL;
  unsigned char *ivp = NULL;
  int i;
  unsigned char d;
  struct tcrypt_result result;

  tfm = crypto_alloc_aead("ccm(aes)", 0, 0);
  init_completion(&result.completion);
  req = aead_request_alloc(tfm, GFP_KERNEL);
  aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
                          cipher_work_done, &result);

  crypto_aead_clear_flags(tfm, ~0);

  ret = crypto_aead_setkey(tfm, key, sizeof(key));

  ret = crypto_aead_setauthsize(tfm, 16); // authsize is hmac?

  ivsize = crypto_aead_ivsize(tfm);
  if (ivsize != 12) {
    printk("ivsize is not 12 %d - this needs to be fixed\n", ivsize);
  }

  plaindata  = kmalloc(512, GFP_KERNEL);
  cipherdata = kmalloc(512, GFP_KERNEL);
  hmacdata   = kmalloc(16, GFP_KERNEL);
  ivp        = kmalloc(ivsize, GFP_KERNEL);

  if (!plaindata || !cipherdata || !hmacdata || !ivp) goto out;

  // put 00 01 02 03 ... in the input buffer...
  for (i = 0, d = 0; i < 512; i++, d++)
    plaindata[i] = d;

  memset(cipherdata, 0, 512);
  memset(hmacdata, 0, 16);
  memset(ivp, 0, ivsize);

  // Put a8 a9 aa .... in iv
  for (i = 0,d=0xa8; i < 12; i++, d++)
    ivp[i] = d;

  sg_init_one(&plaintext[0],  plaindata,  512);
  sg_init_one(&ciphertext[0], cipherdata, 512);
  sg_init_one(&hmactext[0],   hmacdata,   16);

  aead_request_set_crypt(req, plaintext, ciphertext, 512, ivp);

  aead_request_set_assoc(req, hmactext, 16);

  ret = crypto_aead_encrypt(req);

  printk("cipher call returns %d \n", ret);

我们得到的结果是 ivsize 是 16(我看不出有什么办法可以将它设置为 12),并且加密失败并返回“-22”或 EINVAL。代码中有很多错误检查,已在此处删除,确认所有先前的调用返回成功。

据我所知,我非常关注 tcrypt.c 源代码。但是,我想知道强制 ivsize = 16 是否意味着我无论如何都无法使用提供的算法。除此之外,很高兴看到加密调用成功以及密码数据输出中放入了什么。

代码被放入内核模块,并在 _init() 时运行。最初我使用 blkcipher“aes”,它可以工作,但不是 ccm-aes 变体。这让我改用 aead,但我无法开始工作。

最佳答案

好的,这就是我学到的。

1) 让我们调用应用程序的 iv nonce。让我们调用内部加密的 iv iv。事实证明,Solaris 代码使用的是 nonce-len=12,但 CCM-AES 算法仍然使用 iv-len=16

在 Solaris 内核源代码中,iv 由以下内容组成:

iv[0] = 1..7, based on ivlen 16 - noncelen 12 = 2.
iv[1] = the nonce data (12 bytes).
iv[14] = 0
iv[15] = 1

所以,在 Linux 上,我想要“ccm(aes)”和 ivlen 16,并从 nonce 中正确准备 iv

2) 当调用 crypto_aead_encrypt() 时,先前调用的 aead_request_set_assoc() 被忽略,HMAC 被放在密码缓冲区的末尾。在我的例子中,在 ciphertext[512],16 个字节。因此输入的长度需要为 +16。

使用分散列表,如果设置正确,“末尾”的 HMAC 可能会有所不同。

3) 当调用 crypto_aead_decrypt() 时,cryptolen 应该是 +16 (cipherinputlen + maclen)。从输入缓冲区的末尾读取 MAC,即 16 字节的 ciphertext[512]。这也可以是使用 scatterlist 的单独缓冲区。

4) crypto_aead_setauthsize() 检查给定的 len 是否正确,然后不对其执行任何操作。不要认为这实际上设置了大小!

5) aead_request_set_assoc() 必须设置,即使它只是一个零缓冲区。

关于来自 Linux 内核的 CCM-AES,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13284986/

相关文章:

c - 将从/dev/urandom读取的内容移植到非Unix平台

c - 打开CV乘以巨大的矩阵分割错误

c++ - 连接汇编和 C 问题

Python - 我如何将长命令添加到子进程,当我在一行中应用时,整个命令不起作用

java - Linux 上的 Jubula(Centos 7)

typescript - Dart 中的 Crypto.subtle?

c - Autotools - 选择要链接的库版本

c - 引用数组指针中的值 (c)

linux - 检测 block 设备是本地磁盘还是移动U盘

encryption - 密码导出限制的现状如何?