modes/ocb128.c: ocb_lookup_l to allow non-contiguous lookup
[openssl.git] / crypto / modes / ccm128.c
index ced0a2b5d73bb247b6b370b3e22ab8353889de6a..c1ded0f9148ae06cfdc4b910556f542d26780d66 100644 (file)
@@ -6,7 +6,7 @@
  * are met:
  *
  * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer. 
+ *    notice, this list of conditions and the following disclaimer.
  *
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in
 #endif
 #include <assert.h>
 
-typedef struct {
-       union { u8 c[16]; size_t s[16/sizeof(size_t)]; } nonce, cmac,
-                                                        scratch, inp;
-       u64 blocks;
-       block128_f block;
-       void *key;
-} CCM128_CONTEXT;
-
-/* First you setup M and L parameters and pass the key schedule */
+/*
+ * First you setup M and L parameters and pass the key schedule. This is
+ * called once per session setup...
+ */
 void CRYPTO_ccm128_init(CCM128_CONTEXT *ctx,
-       unsigned int M,unsigned int L,void *key,block128_f block)
+                        unsigned int M, unsigned int L, void *key,
+                        block128_f block)
 {
-       memset(ctx->nonce.c,0,sizeof(ctx->nonce.c));
-       ctx->nonce.c[0] = ((u8)(L-1)&7) | (u8)(((M-2)/2)&7)<<3;
-       ctx->blocks = 0;
-       ctx->block = block;
-       ctx->key = key;
+    memset(ctx->nonce.c, 0, sizeof(ctx->nonce.c));
+    ctx->nonce.c[0] = ((u8)(L - 1) & 7) | (u8)(((M - 2) / 2) & 7) << 3;
+    ctx->blocks = 0;
+    ctx->block = block;
+    ctx->key = key;
 }
 
 /* !!! Following interfaces are to be called *once* per packet !!! */
 
 /* Then you setup per-message nonce and pass the length of the message */
 int CRYPTO_ccm128_setiv(CCM128_CONTEXT *ctx,
-       const unsigned char *nonce,size_t nlen,size_t mlen)
+                        const unsigned char *nonce, size_t nlen, size_t mlen)
 {
-       unsigned int L = ctx->nonce.c[0]&7;     /* the L parameter */
+    unsigned int L = ctx->nonce.c[0] & 7; /* the L parameter */
 
-       if (nlen<(14-L)) return -1;             /* nonce is too short */
+    if (nlen < (14 - L))
+        return -1;              /* nonce is too short */
 
-       if (sizeof(mlen)==8 && L>=3) {
-               ctx->nonce.c[8]  = (u8)(mlen>>(56%(sizeof(mlen)*8)));
-               ctx->nonce.c[9]  = (u8)(mlen>>(48%(sizeof(mlen)*8)));
-               ctx->nonce.c[10] = (u8)(mlen>>(40%(sizeof(mlen)*8)));
-               ctx->nonce.c[11] = (u8)(mlen>>(32%(sizeof(mlen)*8)));
-       }
-       else
-               *((size_t *)&ctx->nonce.s[8]) = 0;
+    if (sizeof(mlen) == 8 && L >= 3) {
+        ctx->nonce.c[8] = (u8)(mlen >> (56 % (sizeof(mlen) * 8)));
+        ctx->nonce.c[9] = (u8)(mlen >> (48 % (sizeof(mlen) * 8)));
+        ctx->nonce.c[10] = (u8)(mlen >> (40 % (sizeof(mlen) * 8)));
+        ctx->nonce.c[11] = (u8)(mlen >> (32 % (sizeof(mlen) * 8)));
+    } else
+        ctx->nonce.u[1] = 0;
 
-       ctx->nonce.c[12] = (u8)(mlen>>24);
-       ctx->nonce.c[13] = (u8)(mlen>>16);
-       ctx->nonce.c[14] = (u8)(mlen>>8);
-       ctx->nonce.c[15] = (u8)mlen;
+    ctx->nonce.c[12] = (u8)(mlen >> 24);
+    ctx->nonce.c[13] = (u8)(mlen >> 16);
+    ctx->nonce.c[14] = (u8)(mlen >> 8);
+    ctx->nonce.c[15] = (u8)mlen;
 
-       ctx->nonce.c[0] &= ~0x40;       /* clear Adata flag */
-       memcpy(&ctx->nonce.c[1],nonce,14-L);
+    ctx->nonce.c[0] &= ~0x40;   /* clear Adata flag */
+    memcpy(&ctx->nonce.c[1], nonce, 14 - L);
 
-       return 0;
+    return 0;
 }
 
 /* Then you pass additional authentication data, this is optional */
 void CRYPTO_ccm128_aad(CCM128_CONTEXT *ctx,
-       const unsigned char *aad,size_t alen)
-{      unsigned int i;
-
-       if (alen==0) return;
-
-       ctx->nonce.c[0] |= 0x40;        /* set Adata flag */
-       (*ctx->block)(ctx->nonce.c,ctx->cmac.c,ctx->key),
-       ctx->blocks++;
-
-       if (alen<(0x10000-0x100)) {
-               ctx->cmac.c[0] ^= (u8)(alen>>8);
-               ctx->cmac.c[1] ^= (u8)alen;
-               i=2;
-       }
-       else if (sizeof(alen)==8 && alen>=(size_t)1<<32) {
-               ctx->cmac.c[0] ^= 0xFF;
-               ctx->cmac.c[1] ^= 0xFF;
-               ctx->cmac.c[2] ^= (u8)(alen>>(56%(sizeof(alen)*8)));
-               ctx->cmac.c[3] ^= (u8)(alen>>(48%(sizeof(alen)*8)));
-               ctx->cmac.c[4] ^= (u8)(alen>>(40%(sizeof(alen)*8)));
-               ctx->cmac.c[5] ^= (u8)(alen>>(32%(sizeof(alen)*8)));
-               ctx->cmac.c[6] ^= (u8)(alen>>24);
-               ctx->cmac.c[7] ^= (u8)(alen>>16);
-               ctx->cmac.c[8] ^= (u8)(alen>>8);
-               ctx->cmac.c[9] ^= (u8)alen;
-               i=10;
-       }
-       else {
-               ctx->cmac.c[0] ^= 0xFF;
-               ctx->cmac.c[1] ^= 0xFE;
-               ctx->cmac.c[2] ^= (u8)(alen>>24);
-               ctx->cmac.c[3] ^= (u8)(alen>>16);
-               ctx->cmac.c[4] ^= (u8)(alen>>8);
-               ctx->cmac.c[5] ^= (u8)alen;
-               i=6;
-       }
-
-       do {
-               for(;i<16 && alen;++i,++aad,--alen)
-                       ctx->cmac.c[i] ^= *aad;
-               (*ctx->block)(ctx->cmac.c,ctx->cmac.c,ctx->key),
-               ctx->blocks++;
-               i=0;
-       } while (alen);
+                       const unsigned char *aad, size_t alen)
+{
+    unsigned int i;
+    block128_f block = ctx->block;
+
+    if (alen == 0)
+        return;
+
+    ctx->nonce.c[0] |= 0x40;    /* set Adata flag */
+    (*block) (ctx->nonce.c, ctx->cmac.c, ctx->key), ctx->blocks++;
+
+    if (alen < (0x10000 - 0x100)) {
+        ctx->cmac.c[0] ^= (u8)(alen >> 8);
+        ctx->cmac.c[1] ^= (u8)alen;
+        i = 2;
+    } else if (sizeof(alen) == 8
+               && alen >= (size_t)1 << (32 % (sizeof(alen) * 8))) {
+        ctx->cmac.c[0] ^= 0xFF;
+        ctx->cmac.c[1] ^= 0xFF;
+        ctx->cmac.c[2] ^= (u8)(alen >> (56 % (sizeof(alen) * 8)));
+        ctx->cmac.c[3] ^= (u8)(alen >> (48 % (sizeof(alen) * 8)));
+        ctx->cmac.c[4] ^= (u8)(alen >> (40 % (sizeof(alen) * 8)));
+        ctx->cmac.c[5] ^= (u8)(alen >> (32 % (sizeof(alen) * 8)));
+        ctx->cmac.c[6] ^= (u8)(alen >> 24);
+        ctx->cmac.c[7] ^= (u8)(alen >> 16);
+        ctx->cmac.c[8] ^= (u8)(alen >> 8);
+        ctx->cmac.c[9] ^= (u8)alen;
+        i = 10;
+    } else {
+        ctx->cmac.c[0] ^= 0xFF;
+        ctx->cmac.c[1] ^= 0xFE;
+        ctx->cmac.c[2] ^= (u8)(alen >> 24);
+        ctx->cmac.c[3] ^= (u8)(alen >> 16);
+        ctx->cmac.c[4] ^= (u8)(alen >> 8);
+        ctx->cmac.c[5] ^= (u8)alen;
+        i = 6;
+    }
+
+    do {
+        for (; i < 16 && alen; ++i, ++aad, --alen)
+            ctx->cmac.c[i] ^= *aad;
+        (*block) (ctx->cmac.c, ctx->cmac.c, ctx->key), ctx->blocks++;
+        i = 0;
+    } while (alen);
 }
 
 /* Finally you encrypt or decrypt the message */
 
-static void ctr128_inc(unsigned char *counter) {
-       unsigned int n=16;
-       u8  c;
-
-       do {
-               --n;
-               c = counter[n];
-               ++c;
-               counter[n] = c;
-               if (c) return;
-       } while (n);
+/*
+ * counter part of nonce may not be larger than L*8 bits, L is not larger
+ * than 8, therefore 64-bit counter...
+ */
+static void ctr64_inc(unsigned char *counter)
+{
+    unsigned int n = 8;
+    u8 c;
+
+    counter += 8;
+    do {
+        --n;
+        c = counter[n];
+        ++c;
+        counter[n] = c;
+        if (c)
+            return;
+    } while (n);
 }
 
 int CRYPTO_ccm128_encrypt(CCM128_CONTEXT *ctx,
-       const unsigned char *inp, unsigned char *out,
-       size_t len)
+                          const unsigned char *inp, unsigned char *out,
+                          size_t len)
 {
-       size_t          n;
-       unsigned int    i;
-       unsigned char   flags = ctx->nonce.c[0];
-
-       if (!(flags&0x40))
-               (*ctx->block)(ctx->nonce.c,ctx->cmac.c,ctx->key),
-               ctx->blocks++;
-
-       flags &= 7;     /* extract the L parameter */
-       for (n=0,i=15-flags;i<15;++i) {
-               n |= ctx->nonce.c[i]; ctx->nonce.c[i]=0;
-               n <<= 8;
-       }
-       n |= ctx->nonce.c[15];  /* reconstructed length */
-       ctx->nonce.c[15]=1;
-
-       if (n!=len) return -1;  /* length mismatch */
-
-       ctx->blocks += ((len+15)>>3)|1;
-       if (ctx->blocks > (U64(1)<<61)) return -2; /* too much data */
-
-       while (len>=16) {
+    size_t n;
+    unsigned int i, L;
+    unsigned char flags0 = ctx->nonce.c[0];
+    block128_f block = ctx->block;
+    void *key = ctx->key;
+    union {
+        u64 u[2];
+        u8 c[16];
+    } scratch;
+
+    if (!(flags0 & 0x40))
+        (*block) (ctx->nonce.c, ctx->cmac.c, key), ctx->blocks++;
+
+    ctx->nonce.c[0] = L = flags0 & 7;
+    for (n = 0, i = 15 - L; i < 15; ++i) {
+        n |= ctx->nonce.c[i];
+        ctx->nonce.c[i] = 0;
+        n <<= 8;
+    }
+    n |= ctx->nonce.c[15];      /* reconstructed length */
+    ctx->nonce.c[15] = 1;
+
+    if (n != len)
+        return -1;              /* length mismatch */
+
+    ctx->blocks += ((len + 15) >> 3) | 1;
+    if (ctx->blocks > (U64(1) << 61))
+        return -2;              /* too much data */
+
+    while (len >= 16) {
 #if defined(STRICT_ALIGNMENT)
-               memcpy (ctx->inp.c,inp,16);
-               for (i=0; i<16/sizeof(size_t); ++i)
-                       ctx->cmac.s[i] ^= ctx->inp.s[i];
+        union {
+            u64 u[2];
+            u8 c[16];
+        } temp;
+
+        memcpy(temp.c, inp, 16);
+        ctx->cmac.u[0] ^= temp.u[0];
+        ctx->cmac.u[1] ^= temp.u[1];
 #else
-               for (i=0; i<16/sizeof(size_t); ++i)
-                       ctx->cmac.s[i] ^= ((size_t*)inp)[i];
+        ctx->cmac.u[0] ^= ((u64 *)inp)[0];
+        ctx->cmac.u[1] ^= ((u64 *)inp)[1];
 #endif
-               (*ctx->block)(ctx->cmac.c,ctx->cmac.c,ctx->key);
-               (*ctx->block)(ctx->nonce.c,ctx->scratch.c,ctx->key);
-               ctr128_inc(ctx->nonce.c);
+        (*block) (ctx->cmac.c, ctx->cmac.c, key);
+        (*block) (ctx->nonce.c, scratch.c, key);
+        ctr64_inc(ctx->nonce.c);
 #if defined(STRICT_ALIGNMENT)
-               for (i=0; i<16/sizeof(size_t); ++i)
-                       ctx->inp.s[i] ^= ctx->scratch.s[i];
-               memcpy(out,ctx->inp.c,16);
+        temp.u[0] ^= scratch.u[0];
+        temp.u[1] ^= scratch.u[1];
+        memcpy(out, temp.c, 16);
 #else
-               for (i=0; i<16/sizeof(size_t); ++i)
-                       ((size_t*)out)[i] = ctx->scratch.s[i]^((size_t*)inp)[i];
+        ((u64 *)out)[0] = scratch.u[0] ^ ((u64 *)inp)[0];
+        ((u64 *)out)[1] = scratch.u[1] ^ ((u64 *)inp)[1];
 #endif
-               inp += 16;
-               out += 16;
-               len -= 16;
-       }
-
-       if (len) {
-               for (i=0; i<len; ++i) ctx->cmac.c[i] ^= inp[i];
-               (*ctx->block)(ctx->cmac.c,ctx->cmac.c,ctx->key);
-               (*ctx->block)(ctx->nonce.c,ctx->scratch.c,ctx->key);
-               for (i=0; i<len; ++i) out[i] = ctx->scratch.c[i]^inp[i];
-       }
-
-       for (i=15-flags;i<16;++i)
-               ctx->nonce.c[i]=0;
-
-       (*ctx->block)(ctx->nonce.c,ctx->scratch.c,ctx->key);
-       for (i=0; i<16/sizeof(size_t); ++i)
-               ctx->cmac.s[i] ^= ctx->scratch.s[i];
-
-       return 0;
+        inp += 16;
+        out += 16;
+        len -= 16;
+    }
+
+    if (len) {
+        for (i = 0; i < len; ++i)
+            ctx->cmac.c[i] ^= inp[i];
+        (*block) (ctx->cmac.c, ctx->cmac.c, key);
+        (*block) (ctx->nonce.c, scratch.c, key);
+        for (i = 0; i < len; ++i)
+            out[i] = scratch.c[i] ^ inp[i];
+    }
+
+    for (i = 15 - L; i < 16; ++i)
+        ctx->nonce.c[i] = 0;
+
+    (*block) (ctx->nonce.c, scratch.c, key);
+    ctx->cmac.u[0] ^= scratch.u[0];
+    ctx->cmac.u[1] ^= scratch.u[1];
+
+    ctx->nonce.c[0] = flags0;
+
+    return 0;
 }
 
 int CRYPTO_ccm128_decrypt(CCM128_CONTEXT *ctx,
-       const unsigned char *inp, unsigned char *out,
-       size_t len)
+                          const unsigned char *inp, unsigned char *out,
+                          size_t len)
 {
-       size_t          n;
-       unsigned int    i;
-       unsigned char   flags = ctx->nonce.c[0];
-
-       if (!(flags&0x40))
-               (*ctx->block)(ctx->nonce.c,ctx->cmac.c,ctx->key);
-
-       flags &= 7;     /* extract the L parameter */
-       for (n=0,i=15-flags;i<15;++i) {
-               n |= ctx->nonce.c[i]; ctx->nonce.c[i]=0;
-               n <<= 8;
-       }
-       n |= ctx->nonce.c[15];  /* reconstructed length */
-       ctx->nonce.c[15]=1;
-
-       if (n!=len) return -1;
-
-       while (len>=16) {
-               (*ctx->block)(ctx->nonce.c,ctx->scratch.c,ctx->key);
-               ctr128_inc(ctx->nonce.c);
+    size_t n;
+    unsigned int i, L;
+    unsigned char flags0 = ctx->nonce.c[0];
+    block128_f block = ctx->block;
+    void *key = ctx->key;
+    union {
+        u64 u[2];
+        u8 c[16];
+    } scratch;
+
+    if (!(flags0 & 0x40))
+        (*block) (ctx->nonce.c, ctx->cmac.c, key);
+
+    ctx->nonce.c[0] = L = flags0 & 7;
+    for (n = 0, i = 15 - L; i < 15; ++i) {
+        n |= ctx->nonce.c[i];
+        ctx->nonce.c[i] = 0;
+        n <<= 8;
+    }
+    n |= ctx->nonce.c[15];      /* reconstructed length */
+    ctx->nonce.c[15] = 1;
+
+    if (n != len)
+        return -1;
+
+    while (len >= 16) {
+#if defined(STRICT_ALIGNMENT)
+        union {
+            u64 u[2];
+            u8 c[16];
+        } temp;
+#endif
+        (*block) (ctx->nonce.c, scratch.c, key);
+        ctr64_inc(ctx->nonce.c);
 #if defined(STRICT_ALIGNMENT)
-               memcpy (ctx->inp.c,inp,16);
-               for (i=0; i<16/sizeof(size_t); ++i)
-                       ctx->cmac.s[i] ^= (ctx->scratch.s[i] ^= ctx->inp.s[i]);
-               memcpy (out,ctx->scratch,16);
+        memcpy(temp.c, inp, 16);
+        ctx->cmac.u[0] ^= (scratch.u[0] ^= temp.u[0]);
+        ctx->cmac.u[1] ^= (scratch.u[1] ^= temp.u[1]);
+        memcpy(out, scratch.c, 16);
 #else
-               for (i=0; i<16/sizeof(size_t); ++i)
-                       ctx->cmac.s[i] ^= ((size_t*)out)[i] = ctx->scratch.s[i]^((size_t*)inp)[i];
+        ctx->cmac.u[0] ^= (((u64 *)out)[0] = scratch.u[0] ^ ((u64 *)inp)[0]);
+        ctx->cmac.u[1] ^= (((u64 *)out)[1] = scratch.u[1] ^ ((u64 *)inp)[1]);
 #endif
-               (*ctx->block)(ctx->cmac.c,ctx->cmac.c,ctx->key);
+        (*block) (ctx->cmac.c, ctx->cmac.c, key);
+
+        inp += 16;
+        out += 16;
+        len -= 16;
+    }
 
-               inp += 16;
-               out += 16;
-               len -= 16;
-       }
+    if (len) {
+        (*block) (ctx->nonce.c, scratch.c, key);
+        for (i = 0; i < len; ++i)
+            ctx->cmac.c[i] ^= (out[i] = scratch.c[i] ^ inp[i]);
+        (*block) (ctx->cmac.c, ctx->cmac.c, key);
+    }
 
-       if (len) {
-               (*ctx->block)(ctx->nonce.c,ctx->scratch.c,ctx->key);
-               for (i=0; i<len; ++len)
-                       ctx->cmac.c[i] ^= (out[i] = ctx->scratch.c[i]^inp[i]);
-               (*ctx->block)(ctx->cmac.c,ctx->cmac.c,ctx->key);
-       }
+    for (i = 15 - L; i < 16; ++i)
+        ctx->nonce.c[i] = 0;
 
-       for (i=15-flags;i<16;++i)
-               ctx->nonce.c[i]=0;
+    (*block) (ctx->nonce.c, scratch.c, key);
+    ctx->cmac.u[0] ^= scratch.u[0];
+    ctx->cmac.u[1] ^= scratch.u[1];
 
-       (*ctx->block)(ctx->nonce.c,ctx->scratch.c,ctx->key);
-       for (i=0; i<16/sizeof(size_t); ++i)
-               ctx->cmac.s[i] ^= ctx->scratch.s[i];
+    ctx->nonce.c[0] = flags0;
 
-       return 0;
+    return 0;
 }
 
-size_t CRYPTO_ccm128_tag(CCM128_CONTEXT *ctx,unsigned char *tag,size_t len)
-{      unsigned int M = (ctx->nonce.c[0]>>3)&7;        /* the M parameter */
+static void ctr64_add(unsigned char *counter, size_t inc)
+{
+    size_t n = 8, val = 0;
+
+    counter += 8;
+    do {
+        --n;
+        val += counter[n] + (inc & 0xff);
+        counter[n] = (unsigned char)val;
+        val >>= 8;              /* carry bit */
+        inc >>= 8;
+    } while (n && (inc || val));
+}
 
-       M *= 2; M += 2;
-       if (len<M)      return 0;
-       memcpy(tag,ctx->cmac.c,M);
-       return M;
+int CRYPTO_ccm128_encrypt_ccm64(CCM128_CONTEXT *ctx,
+                                const unsigned char *inp, unsigned char *out,
+                                size_t len, ccm128_f stream)
+{
+    size_t n;
+    unsigned int i, L;
+    unsigned char flags0 = ctx->nonce.c[0];
+    block128_f block = ctx->block;
+    void *key = ctx->key;
+    union {
+        u64 u[2];
+        u8 c[16];
+    } scratch;
+
+    if (!(flags0 & 0x40))
+        (*block) (ctx->nonce.c, ctx->cmac.c, key), ctx->blocks++;
+
+    ctx->nonce.c[0] = L = flags0 & 7;
+    for (n = 0, i = 15 - L; i < 15; ++i) {
+        n |= ctx->nonce.c[i];
+        ctx->nonce.c[i] = 0;
+        n <<= 8;
+    }
+    n |= ctx->nonce.c[15];      /* reconstructed length */
+    ctx->nonce.c[15] = 1;
+
+    if (n != len)
+        return -1;              /* length mismatch */
+
+    ctx->blocks += ((len + 15) >> 3) | 1;
+    if (ctx->blocks > (U64(1) << 61))
+        return -2;              /* too much data */
+
+    if ((n = len / 16)) {
+        (*stream) (inp, out, n, key, ctx->nonce.c, ctx->cmac.c);
+        n *= 16;
+        inp += n;
+        out += n;
+        len -= n;
+        if (len)
+            ctr64_add(ctx->nonce.c, n / 16);
+    }
+
+    if (len) {
+        for (i = 0; i < len; ++i)
+            ctx->cmac.c[i] ^= inp[i];
+        (*block) (ctx->cmac.c, ctx->cmac.c, key);
+        (*block) (ctx->nonce.c, scratch.c, key);
+        for (i = 0; i < len; ++i)
+            out[i] = scratch.c[i] ^ inp[i];
+    }
+
+    for (i = 15 - L; i < 16; ++i)
+        ctx->nonce.c[i] = 0;
+
+    (*block) (ctx->nonce.c, scratch.c, key);
+    ctx->cmac.u[0] ^= scratch.u[0];
+    ctx->cmac.u[1] ^= scratch.u[1];
+
+    ctx->nonce.c[0] = flags0;
+
+    return 0;
+}
+
+int CRYPTO_ccm128_decrypt_ccm64(CCM128_CONTEXT *ctx,
+                                const unsigned char *inp, unsigned char *out,
+                                size_t len, ccm128_f stream)
+{
+    size_t n;
+    unsigned int i, L;
+    unsigned char flags0 = ctx->nonce.c[0];
+    block128_f block = ctx->block;
+    void *key = ctx->key;
+    union {
+        u64 u[2];
+        u8 c[16];
+    } scratch;
+
+    if (!(flags0 & 0x40))
+        (*block) (ctx->nonce.c, ctx->cmac.c, key);
+
+    ctx->nonce.c[0] = L = flags0 & 7;
+    for (n = 0, i = 15 - L; i < 15; ++i) {
+        n |= ctx->nonce.c[i];
+        ctx->nonce.c[i] = 0;
+        n <<= 8;
+    }
+    n |= ctx->nonce.c[15];      /* reconstructed length */
+    ctx->nonce.c[15] = 1;
+
+    if (n != len)
+        return -1;
+
+    if ((n = len / 16)) {
+        (*stream) (inp, out, n, key, ctx->nonce.c, ctx->cmac.c);
+        n *= 16;
+        inp += n;
+        out += n;
+        len -= n;
+        if (len)
+            ctr64_add(ctx->nonce.c, n / 16);
+    }
+
+    if (len) {
+        (*block) (ctx->nonce.c, scratch.c, key);
+        for (i = 0; i < len; ++i)
+            ctx->cmac.c[i] ^= (out[i] = scratch.c[i] ^ inp[i]);
+        (*block) (ctx->cmac.c, ctx->cmac.c, key);
+    }
+
+    for (i = 15 - L; i < 16; ++i)
+        ctx->nonce.c[i] = 0;
+
+    (*block) (ctx->nonce.c, scratch.c, key);
+    ctx->cmac.u[0] ^= scratch.u[0];
+    ctx->cmac.u[1] ^= scratch.u[1];
+
+    ctx->nonce.c[0] = flags0;
+
+    return 0;
+}
+
+size_t CRYPTO_ccm128_tag(CCM128_CONTEXT *ctx, unsigned char *tag, size_t len)
+{
+    unsigned int M = (ctx->nonce.c[0] >> 3) & 7; /* the M parameter */
+
+    M *= 2;
+    M += 2;
+    if (len < M)
+        return 0;
+    memcpy(tag, ctx->cmac.c, M);
+    return M;
 }