| /* dtls -- a very basic DTLS implementation |
| * |
| * Copyright (C) 2011--2014 Olaf Bergmann <bergmann@tzi.org> |
| * |
| * Permission is hereby granted, free of charge, to any person |
| * obtaining a copy of this software and associated documentation |
| * files (the "Software"), to deal in the Software without |
| * restriction, including without limitation the rights to use, copy, |
| * modify, merge, publish, distribute, sublicense, and/or sell copies |
| * of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be |
| * included in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
| * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
| * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| */ |
| |
| #include <string.h> |
| |
| #include "dtls_config.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) { \ |
| 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, /* number of auth bytes */ |
| size_t L, /* number of bytes to encode message length */ |
| size_t la, /* l(a) octets additional authenticated data */ |
| size_t lm, /* l(m) message length */ |
| unsigned char nonce[DTLS_CCM_BLOCKSIZE], |
| unsigned char *result) { |
| int i; |
| |
| result[0] = CCM_FLAGS(la, M, L); |
| |
| /* copy the nonce */ |
| memcpy(result + 1, nonce, DTLS_CCM_BLOCKSIZE - L); |
| |
| for (i=0; i < L; i++) { |
| result[15-i] = lm & 0xff; |
| lm >>= 8; |
| } |
| } |
| |
| /** |
| * Creates the CBC-MAC for the additional authentication data that |
| * is sent in cleartext. |
| * |
| * \param ctx The crypto context for the AES encryption. |
| * \param msg The message starting with the additional authentication data. |
| * \param la The number of additional authentication bytes in \p msg. |
| * \param B The input buffer for crypto operations. When this function |
| * is called, \p B must be initialized with \c B0 (the first |
| * authentication block. |
| * \param X The output buffer where the result of the CBC calculation |
| * is placed. |
| * \return The result is written to \p X. |
| */ |
| static void |
| add_auth_data(rijndael_ctx *ctx, const unsigned char *msg, size_t la, |
| unsigned char B[DTLS_CCM_BLOCKSIZE], |
| unsigned char X[DTLS_CCM_BLOCKSIZE]) { |
| size_t i,j; |
| |
| rijndael_encrypt(ctx, B, X); |
| |
| memset(B, 0, DTLS_CCM_BLOCKSIZE); |
| |
| if (!la) |
| return; |
| |
| #ifndef WITH_CONTIKI |
| if (la < 0xFF00) { /* 2^16 - 2^8 */ |
| 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 /* WITH_CONTIKI */ |
| /* With Contiki, we are building for small devices and thus |
| * anticipate that the number of additional authentication bytes |
| * will not exceed 65280 bytes (0xFF00) and we can skip the |
| * workarounds required for j=6 and j=10 on devices with a word size |
| * of 32 bits or 64 bits, respectively. |
| */ |
| |
| assert(la < 0xFF00); |
| j = 2; |
| dtls_int_to_uint16(B, la); |
| #endif /* WITH_CONTIKI */ |
| |
| 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, |
| 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; /* \bug does not work correctly on ia32 when |
| lm >= 2^16 */ |
| unsigned char A[DTLS_CCM_BLOCKSIZE]; /* A_i blocks for encryption input */ |
| unsigned char B[DTLS_CCM_BLOCKSIZE]; /* B_i blocks for CBC-MAC input */ |
| unsigned char S[DTLS_CCM_BLOCKSIZE]; /* S_i = encrypted A_i blocks */ |
| unsigned char X[DTLS_CCM_BLOCKSIZE]; /* X_i = encrypted B_i blocks */ |
| |
| len = lm; /* save original length */ |
| /* create the initial authentication block B0 */ |
| block0(M, L, la, lm, nonce, B); |
| add_auth_data(ctx, aad, la, B, X); |
| |
| /* initialize block template */ |
| A[0] = L-1; |
| |
| /* copy the nonce */ |
| memcpy(A + 1, nonce, DTLS_CCM_BLOCKSIZE - L); |
| |
| while (lm >= DTLS_CCM_BLOCKSIZE) { |
| /* calculate MAC */ |
| mac(ctx, msg, DTLS_CCM_BLOCKSIZE, B, X); |
| |
| /* encrypt */ |
| encrypt(ctx, L, counter, msg, DTLS_CCM_BLOCKSIZE, A, S); |
| |
| /* update local pointers */ |
| lm -= DTLS_CCM_BLOCKSIZE; |
| msg += DTLS_CCM_BLOCKSIZE; |
| counter++; |
| } |
| |
| if (lm) { |
| /* Calculate MAC. The remainder of B must be padded with zeroes, so |
| * B is constructed to contain X ^ msg for the first lm bytes (done in |
| * mac() and X ^ 0 for the remaining DTLS_CCM_BLOCKSIZE - lm bytes |
| * (i.e., we can use memcpy() here). |
| */ |
| memcpy(B + lm, X + lm, DTLS_CCM_BLOCKSIZE - lm); |
| mac(ctx, msg, lm, B, X); |
| |
| /* encrypt */ |
| encrypt(ctx, L, counter, msg, lm, A, S); |
| |
| /* update local pointers */ |
| msg += lm; |
| } |
| |
| /* calculate S_0 */ |
| 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, |
| 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; /* \bug does not work correctly on ia32 when |
| lm >= 2^16 */ |
| unsigned char A[DTLS_CCM_BLOCKSIZE]; /* A_i blocks for encryption input */ |
| unsigned char B[DTLS_CCM_BLOCKSIZE]; /* B_i blocks for CBC-MAC input */ |
| unsigned char S[DTLS_CCM_BLOCKSIZE]; /* S_i = encrypted A_i blocks */ |
| unsigned char X[DTLS_CCM_BLOCKSIZE]; /* X_i = encrypted B_i blocks */ |
| |
| if (lm < M) |
| goto error; |
| |
| len = lm; /* save original length */ |
| lm -= M; /* detract MAC size*/ |
| |
| /* create the initial authentication block B0 */ |
| block0(M, L, la, lm, nonce, B); |
| add_auth_data(ctx, aad, la, B, X); |
| |
| /* initialize block template */ |
| A[0] = L-1; |
| |
| /* copy the nonce */ |
| memcpy(A + 1, nonce, DTLS_CCM_BLOCKSIZE - L); |
| |
| while (lm >= DTLS_CCM_BLOCKSIZE) { |
| /* decrypt */ |
| encrypt(ctx, L, counter, msg, DTLS_CCM_BLOCKSIZE, A, S); |
| |
| /* calculate MAC */ |
| mac(ctx, msg, DTLS_CCM_BLOCKSIZE, B, X); |
| |
| /* update local pointers */ |
| lm -= DTLS_CCM_BLOCKSIZE; |
| msg += DTLS_CCM_BLOCKSIZE; |
| counter++; |
| } |
| |
| if (lm) { |
| /* decrypt */ |
| encrypt(ctx, L, counter, msg, lm, A, S); |
| |
| /* Calculate MAC. Note that msg ends in the MAC so we must |
| * construct B to contain X ^ msg for the first lm bytes (done in |
| * mac() and X ^ 0 for the remaining DTLS_CCM_BLOCKSIZE - lm bytes |
| * (i.e., we can use memcpy() here). |
| */ |
| memcpy(B + lm, X + lm, DTLS_CCM_BLOCKSIZE - lm); |
| mac(ctx, msg, lm, B, X); |
| |
| /* update local pointers */ |
| msg += lm; |
| } |
| |
| /* calculate S_0 */ |
| SET_COUNTER(A, L, 0, counter_tmp); |
| rijndael_encrypt(ctx, A, S); |
| |
| memxor(msg, S, M); |
| |
| /* return length if MAC is valid, otherwise continue with error handling */ |
| if (equals(X, msg, M)) |
| return len - M; |
| |
| error: |
| return -1; |
| } |