text: /*
   * Copyright 2011-2020 The OpenSSL Project Authors. All Rights Reserved.
   *
   * Licensed under the Apache License 2.0 (the "License").  You may not use
   * this file except in compliance with the License.  You can obtain a copy
   * in the file LICENSE in the source distribution or at
   * https://www.openssl.org/source/license.html
   */
  
  #include <string.h>
  #include <openssl/crypto.h>
  #include "crypto/modes.h"
  
  #ifndef STRICT_ALIGNMENT
  # ifdef __GNUC__
  typedef u64 u64_a1 __attribute((__aligned__(1)));
  # else
  typedef u64 u64_a1;
  # endif
  #endif
  
  /*
   * 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)
  {
      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)
  {
      unsigned int L = ctx->nonce.c[0] & 7; /* the L parameter */
  
      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
          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[0] &= ~0x40;   /* clear Adata flag */
      memcpy(&ctx->nonce.c[1], nonce, 14 - L);
  
      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;
      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 */
  
  /*
   * 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)
  {
      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] = flags0 & 7;
      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)
          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
          ctx->cmac.u[0] ^= ((u64_a1 *)inp)[0];
          ctx->cmac.u[1] ^= ((u64_a1 *)inp)[1];
  #endif
          (*block) (ctx->cmac.c, ctx->cmac.c, key);
          (*block) (ctx->nonce.c, scratch.c, key);
          ctr64_inc(ctx->nonce.c);
  #if defined(STRICT_ALIGNMENT)
          temp.u[0] ^= scratch.u[0];
          temp.u[1] ^= scratch.u[1];
          memcpy(out, temp.c, 16);
  #else
          ((u64_a1 *)out)[0] = scratch.u[0] ^ ((u64_a1 *)inp)[0];
          ((u64_a1 *)out)[1] = scratch.u[1] ^ ((u64_a1 *)inp)[1];
  #endif
          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)
  {
      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] = flags0 & 7;
      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(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
          ctx->cmac.u[0] ^= (((u64_a1 *)out)[0]
                              = scratch.u[0] ^ ((u64_a1 *)inp)[0]);
          ctx->cmac.u[1] ^= (((u64_a1 *)out)[1]
                              = scratch.u[1] ^ ((u64_a1 *)inp)[1]);
  #endif
          (*block) (ctx->cmac.c, ctx->cmac.c, key);
  
          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);
      }
  
      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;
  }
  
  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));
  }
  
  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] = flags0 & 7;
      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] = flags0 & 7;
      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;
  }
  