简体   繁体   English

来自Linux内核的CCM-AES

[英]CCM-AES from Linux Kernel

I need to be compatible with Solaris crypto mech SUN_CKM_AES_CCM. 我需要与Solaris加密机器SUN_CKM_AES_CCM兼容。 In Linux, I believe I should setup an AEAD request to get "ccm(aes)" mech. 在Linux中,我相信我应该设置一个AEAD请求以获得“ccm(aes)”机制。 Documentation for Linux Crypto does seem rather poor, the best example appears to be tcrypt.c test, and kernel sources. Linux Crypto的文档看起来确实很差,最好的例子似乎是tcrypt.c测试和内核源代码。

From Solaris, I did a test encryption of a 512 byte block, with 16 byte hmac, and 12 byte iv. 在Solaris中,我对512字节块进行了测试加密,具有16字节hmac和12字节iv。 This needs to stay the same, and hopefully the results be identical. 这需要保持不变,希望结果是相同的。

However, what I think should would work, does not; 但是,我认为应该起作用,不会;

   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);

And what we get back is that ivsize is 16 (and I see no way to set it to 12), and that encrypt fails with "-22" or EINVAL. 我们得到的是ivsize是16(并且我认为没有办法将其设置为12),并且加密失败了“-22”或EINVAL。 There are lots of errors checking in the code, removed here, that confirm all prior call return success. 代码中有很多错误检查,在这里删除,确认所有先前的呼叫返回成功。

As far as I can tell, I follow the tcrypt.c sources pretty close. 据我所知,我非常接近tcrypt.c来源。 However, I wonder if the forced ivsize = 16 will mean I can not use the supplied algorithm anyway. 但是,我想知道强制ivsize = 16是否意味着无论如何我都不能使用提供的算法。 That aside, it would be nice to see the encrypt call succeed and what is put in the cipherdata output. 除此之外,看到加密调用成功以及加密数据输出中的内容会很高兴。

The code is put into a kernel module, and run at _init() time. 代码放入内核模块,并在_init()时运行。 Initially I used blkcipher "aes", which works, but is not the ccm-aes variant. 最初我使用blkcipher“aes”,它起作用,但不是ccm-aes变体。 This made me change to use aead, which I can not get to work. 这让我改变使用aead,我无法工作。

Ok, this is what I have learnt. 好的,这就是我所学到的。

1) Let's call the Application's iv nonce . 1)让我们调用应用程序的iv nonce And let's call the internal crypto's iv iv . 让我们称之为内部加密iv iv It turns out that the Solaris code is using nonce-len=12 , but the CCM-AES algorithm still uses iv-len=16 . 事实证明,Solaris代码使用的是nonce-len=12 ,但CCM-AES算法仍然使用iv-len=16

From Solaris kernel sources, iv is made up with: 从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

So, on Linux I want "ccm(aes)" with ivlen 16, and prepare the iv from nonce properly. 因此,在Linux上我想用ivlen 16“ccm(aes)”,并正确地从nonce准备iv

2) When calling crypto_aead_encrypt() the prior call of aead_request_set_assoc() is ignored, and HMAC is put at the end of the cipher buffer. 2)当调用crypto_aead_encrypt() ,忽略先前调用aead_request_set_assoc() ,并将HMAC放在密码缓冲区的末尾。 In my case, at ciphertext[512], for 16 bytes. 就我而言,在密文[512]中,为16个字节。 So the input needed to be +16 in length. 所以输入需要长度为+16。

Using scatterlist, the HMAC "at the end" can be somewhere different if set up correctly. 使用分散列表,如果设置正确,HMAC“在末尾”可以是不同的地方。

3) When calling crypto_aead_decrypt() the cryptolen should be +16 (cipherinputlen + maclen). 3)当调用crypto_aead_decrypt() ,cryptolen应为+16(cipherinputlen + maclen)。 The MAC is read from end of input buffer, ie, ciphertext[512] for 16 bytes. 从输入缓冲区的末尾读取MAC,即密文[512]为16字节。 Which can also be a separate buffer using scatterlist. 它也可以是使用scatterlist的单独缓冲区。

4) crypto_aead_setauthsize() checks that the len given is correct, then does nothing with it. 4) crypto_aead_setauthsize()检查len给出的是否正确,然后对它做任何事情。 Don't think this actually sets the size! 不要以为这实际上设定了尺寸!

5) aead_request_set_assoc() has to be set, even if it is just to a buffer of zeros. 5)必须设置aead_request_set_assoc() ,即使它只是一个零的缓冲区。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM