#include <string.h>
#include "tinydtls.h"
#include "global.h"
#include "numeric.h"
#include "ccm.h"
#ifdef HAVE_ASSERT_H
# include <assert.h>
#endif
#define CCM_FLAGS(A,M,L) (((A > 0) << 6) | (((M - 2)/2) << 3) | (L - 1))
#define MASK_L(_L) ((1 << 8 * _L) - 1)
#define SET_COUNTER(A,L,cnt,C) { \
unsigned int i_; \
memset((A) + DTLS_CCM_BLOCKSIZE - (L), 0, (L)); \
(C) = (cnt) & MASK_L(L); \
for (i_ = DTLS_CCM_BLOCKSIZE - 1; (C) && (i_ > (L)); --i_, (C) >>= 8) \
(A)[i_] |= (C) & 0xFF; \
}
static inline void
block0(size_t M,
size_t L,
size_t la,
size_t lm,
const unsigned char nonce[DTLS_CCM_BLOCKSIZE],
unsigned char *result) {
unsigned int i;
result[0] = CCM_FLAGS(la, M, L);
memcpy(result + 1, nonce, DTLS_CCM_BLOCKSIZE - L - 1);
for (i=0; i < L; i++) {
result[15-i] = lm & 0xff;
lm >>= 8;
}
}
static void
add_auth_data(rijndael_ctx *ctx, const unsigned char *msg, uint64_t la,
unsigned char B[DTLS_CCM_BLOCKSIZE],
unsigned char X[DTLS_CCM_BLOCKSIZE]) {
uint64_t i,j;
rijndael_encrypt(ctx, B, X);
memset(B, 0, DTLS_CCM_BLOCKSIZE);
if (!la)
return;
#ifndef WITH_CONTIKI
if (la < 0xFF00) {
j = 2;
dtls_int_to_uint16(B, la);
} else if (la <= UINT32_MAX) {
j = 6;
dtls_int_to_uint16(B, 0xFFFE);
dtls_int_to_uint32(B+2, la);
} else {
j = 10;
dtls_int_to_uint16(B, 0xFFFF);
dtls_int_to_uint64(B+2, la);
}
#else
assert(la < 0xFF00);
j = 2;
dtls_int_to_uint16(B, la);
#endif
i = min(DTLS_CCM_BLOCKSIZE - j, la);
memcpy(B + j, msg, i);
la -= i;
msg += i;
memxor(B, X, DTLS_CCM_BLOCKSIZE);
rijndael_encrypt(ctx, B, X);
while (la > DTLS_CCM_BLOCKSIZE) {
for (i = 0; i < DTLS_CCM_BLOCKSIZE; ++i)
B[i] = X[i] ^ *msg++;
la -= DTLS_CCM_BLOCKSIZE;
rijndael_encrypt(ctx, B, X);
}
if (la) {
memset(B, 0, DTLS_CCM_BLOCKSIZE);
memcpy(B, msg, la);
memxor(B, X, DTLS_CCM_BLOCKSIZE);
rijndael_encrypt(ctx, B, X);
}
}
static inline void
encrypt(rijndael_ctx *ctx, size_t L, unsigned long counter,
unsigned char *msg, size_t len,
unsigned char A[DTLS_CCM_BLOCKSIZE],
unsigned char S[DTLS_CCM_BLOCKSIZE]) {
static unsigned long counter_tmp;
SET_COUNTER(A, L, counter, counter_tmp);
rijndael_encrypt(ctx, A, S);
memxor(msg, S, len);
}
static inline void
mac(rijndael_ctx *ctx,
unsigned char *msg, size_t len,
unsigned char B[DTLS_CCM_BLOCKSIZE],
unsigned char X[DTLS_CCM_BLOCKSIZE]) {
size_t i;
for (i = 0; i < len; ++i)
B[i] = X[i] ^ msg[i];
rijndael_encrypt(ctx, B, X);
}
long int
dtls_ccm_encrypt_message(rijndael_ctx *ctx, size_t M, size_t L,
const unsigned char nonce[DTLS_CCM_BLOCKSIZE],
unsigned char *msg, size_t lm,
const unsigned char *aad, size_t la) {
size_t i, len;
unsigned long counter_tmp;
unsigned long counter = 1;
unsigned char A[DTLS_CCM_BLOCKSIZE];
unsigned char B[DTLS_CCM_BLOCKSIZE];
unsigned char S[DTLS_CCM_BLOCKSIZE];
unsigned char X[DTLS_CCM_BLOCKSIZE];
len = lm;
block0(M, L, la, lm, nonce, B);
add_auth_data(ctx, aad, la, B, X);
A[0] = L-1;
memcpy(A + 1, nonce, DTLS_CCM_BLOCKSIZE - L - 1);
while (lm >= DTLS_CCM_BLOCKSIZE) {
mac(ctx, msg, DTLS_CCM_BLOCKSIZE, B, X);
encrypt(ctx, L, counter, msg, DTLS_CCM_BLOCKSIZE, A, S);
lm -= DTLS_CCM_BLOCKSIZE;
msg += DTLS_CCM_BLOCKSIZE;
counter++;
}
if (lm) {
memcpy(B + lm, X + lm, DTLS_CCM_BLOCKSIZE - lm);
mac(ctx, msg, lm, B, X);
encrypt(ctx, L, counter, msg, lm, A, S);
msg += lm;
}
SET_COUNTER(A, L, 0, counter_tmp);
rijndael_encrypt(ctx, A, S);
for (i = 0; i < M; ++i)
*msg++ = X[i] ^ S[i];
return len + M;
}
long int
dtls_ccm_decrypt_message(rijndael_ctx *ctx, size_t M, size_t L,
const unsigned char nonce[DTLS_CCM_BLOCKSIZE],
unsigned char *msg, size_t lm,
const unsigned char *aad, size_t la) {
size_t len;
unsigned long counter_tmp;
unsigned long counter = 1;
unsigned char A[DTLS_CCM_BLOCKSIZE];
unsigned char B[DTLS_CCM_BLOCKSIZE];
unsigned char S[DTLS_CCM_BLOCKSIZE];
unsigned char X[DTLS_CCM_BLOCKSIZE];
if (lm < M)
goto error;
len = lm;
lm -= M;
block0(M, L, la, lm, nonce, B);
add_auth_data(ctx, aad, la, B, X);
A[0] = L-1;
memcpy(A + 1, nonce, DTLS_CCM_BLOCKSIZE - L - 1);
while (lm >= DTLS_CCM_BLOCKSIZE) {
encrypt(ctx, L, counter, msg, DTLS_CCM_BLOCKSIZE, A, S);
mac(ctx, msg, DTLS_CCM_BLOCKSIZE, B, X);
lm -= DTLS_CCM_BLOCKSIZE;
msg += DTLS_CCM_BLOCKSIZE;
counter++;
}
if (lm) {
encrypt(ctx, L, counter, msg, lm, A, S);
memcpy(B + lm, X + lm, DTLS_CCM_BLOCKSIZE - lm);
mac(ctx, msg, lm, B, X);
msg += lm;
}
SET_COUNTER(A, L, 0, counter_tmp);
rijndael_encrypt(ctx, A, S);
memxor(msg, S, M);
if (equals(X, msg, M))
return len - M;
error:
return -1;
}