| /* |
| * 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] = 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] = 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] = 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; |
| } |