title: CCM-AES from Linux Kernel
body: <p>I need to be compatible with Solaris crypto mech SUN_CKM_AES_CCM. In Linux, I believe I should setup an AEAD request to get "ccm(aes)" mech. Documentation for Linux Crypto does seem rather poor, the best example appears to be tcrypt.c test, and kernel sources.</p>
  
  <p>From Solaris, I did a test encryption of a 512 byte block, with 16 byte hmac, and 12 byte iv. This needs to stay the same, and hopefully the results be identical.</p>
  
  <p>However, what I <em>think</em> should would work, does not;</p>
  
  <pre><code>   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(&amp;result.completion);
    req = aead_request_alloc(tfm, GFP_KERNEL);
    aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
                            cipher_work_done, &amp;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 &lt; 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 &lt; 12; i++, d++)
      ivp[i] = d;
  
    sg_init_one(&amp;plaintext[0],  plaindata,  512);
    sg_init_one(&amp;ciphertext[0], cipherdata, 512);
    sg_init_one(&amp;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);
  </code></pre>
  
  <p>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.  There are lots of errors checking in the code, removed here, that confirm all prior call return success.</p>
  
  <p>As far as I can tell, I follow the tcrypt.c sources pretty close. However, I wonder if the forced <code>ivsize = 16</code> will mean I can not use the supplied algorithm anyway. That aside, it would be nice to see the encrypt call succeed and what is put in the cipherdata output.</p>
  
  <p>The code is put into a kernel module, and run at _init() time. Initially I used blkcipher "aes", which works, but is not the ccm-aes variant. This made me change to use aead, which I can not get to work.</p>