Version 0.5.0 of tinydtls

Dependents:   tinydtls_test_cellular tinydtls_test_ethernet tiny-dtls

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ccm.c Source File

ccm.c

00001 /* dtls -- a very basic DTLS implementation
00002  *
00003  * Copyright (C) 2011--2012 Olaf Bergmann <bergmann@tzi.org>
00004  *
00005  * Permission is hereby granted, free of charge, to any person
00006  * obtaining a copy of this software and associated documentation
00007  * files (the "Software"), to deal in the Software without
00008  * restriction, including without limitation the rights to use, copy,
00009  * modify, merge, publish, distribute, sublicense, and/or sell copies
00010  * of the Software, and to permit persons to whom the Software is
00011  * furnished to do so, subject to the following conditions:
00012  *
00013  * The above copyright notice and this permission notice shall be
00014  * included in all copies or substantial portions of the Software.
00015  *
00016  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00017  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00018  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00019  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
00020  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
00021  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
00022  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
00023  * SOFTWARE.
00024  */
00025 
00026 #include <string.h>
00027 
00028 #include "global.h"
00029 #include "numeric.h"
00030 #include "ccm.h"
00031 
00032 #define CCM_FLAGS(A,M,L) (((A > 0) << 6) | (((M - 2)/2) << 3) | (L - 1))
00033 
00034 #define MASK_L(_L) ((1 << 8 * _L) - 1)
00035 
00036 #define SET_COUNTER(A,L,cnt,C) {                    \
00037     int i;                              \
00038     memset((A) + DTLS_CCM_BLOCKSIZE - (L), 0, (L));         \
00039     (C) = (cnt) & MASK_L(L);                        \
00040     for (i = DTLS_CCM_BLOCKSIZE - 1; (C) && (i > (L)); --i, (C) >>= 8)  \
00041       (A)[i] |= (C) & 0xFF;                     \
00042   }
00043 
00044 static inline void 
00045 block0(size_t M,       /* number of auth bytes */
00046        size_t L,       /* number of bytes to encode message length */
00047        size_t la,      /* l(a) octets additional authenticated data */
00048        size_t lm,      /* l(m) message length */
00049        unsigned char nonce[DTLS_CCM_BLOCKSIZE],
00050        unsigned char *result) {
00051   int i;
00052 
00053   result[0] = CCM_FLAGS(la, M, L);
00054 
00055   /* copy the nonce */
00056   memcpy(result + 1, nonce, DTLS_CCM_BLOCKSIZE - L);
00057   
00058   for (i=0; i < L; i++) {
00059     result[15-i] = lm & 0xff;
00060     lm >>= 8;
00061   }
00062 }
00063 
00064 /** 
00065  * Creates the CBC-MAC for the additional authentication data that
00066  * is sent in cleartext. 
00067  *
00068  * \param ctx  The crypto context for the AES encryption.
00069  * \param msg  The message starting with the additional authentication data.
00070  * \param la   The number of additional authentication bytes in \p msg.
00071  * \param B    The input buffer for crypto operations. When this function
00072  *             is called, \p B must be initialized with \c B0 (the first
00073  *             authentication block.
00074  * \param X    The output buffer where the result of the CBC calculation
00075  *             is placed.
00076  * \return     The result is written to \p X.
00077  */
00078 static void
00079 add_auth_data(rijndael_ctx *ctx, const unsigned char *msg, size_t la,
00080           unsigned char B[DTLS_CCM_BLOCKSIZE], 
00081           unsigned char X[DTLS_CCM_BLOCKSIZE]) {
00082   size_t i,j; 
00083 
00084   rijndael_encrypt(ctx, B, X);
00085 
00086   memset(B, 0, DTLS_CCM_BLOCKSIZE);
00087 
00088   if (!la)
00089     return;
00090 
00091 #ifndef WITH_CONTIKI
00092     if (la < 0xFF00) {      /* 2^16 - 2^8 */
00093       j = 2;
00094       dtls_int_to_uint16(B, la);
00095   } else if (la <= UINT32_MAX) {
00096       j = 6;
00097       dtls_int_to_uint16(B, 0xFFFE);
00098       dtls_int_to_uint32(B+2, la);
00099     } else {
00100       j = 10;
00101       dtls_int_to_uint16(B, 0xFFFF);
00102       dtls_ulong_to_uint64(B+2, la);
00103     }
00104 #else /* WITH_CONTIKI */
00105   /* With Contiki, we are building for small devices and thus
00106    * anticipate that the number of additional authentication bytes
00107    * will not exceed 65280 bytes (0xFF00) and we can skip the
00108    * workarounds required for j=6 and j=10 on devices with a word size
00109    * of 32 bits or 64 bits, respectively.
00110    */
00111 
00112   assert(la < 0xFF00);
00113   j = 2;
00114   dtls_int_to_uint16(B, la);
00115 #endif /* WITH_CONTIKI */
00116 
00117     i = min(DTLS_CCM_BLOCKSIZE - j, la);
00118     memcpy(B + j, msg, i);
00119     la -= i;
00120     msg += i;
00121     
00122     memxor(B, X, DTLS_CCM_BLOCKSIZE);
00123   
00124   rijndael_encrypt(ctx, B, X);
00125   
00126   while (la > DTLS_CCM_BLOCKSIZE) {
00127     for (i = 0; i < DTLS_CCM_BLOCKSIZE; ++i)
00128       B[i] = X[i] ^ *msg++;
00129     la -= DTLS_CCM_BLOCKSIZE;
00130 
00131     rijndael_encrypt(ctx, B, X);
00132   }
00133   
00134   if (la) {
00135     memset(B, 0, DTLS_CCM_BLOCKSIZE);
00136     memcpy(B, msg, la);
00137     memxor(B, X, DTLS_CCM_BLOCKSIZE);
00138 
00139     rijndael_encrypt(ctx, B, X);  
00140   } 
00141 }
00142 
00143 static inline void
00144 encrypt(rijndael_ctx *ctx, size_t L, unsigned long counter,
00145     unsigned char *msg, size_t len,
00146     unsigned char A[DTLS_CCM_BLOCKSIZE],
00147     unsigned char S[DTLS_CCM_BLOCKSIZE]) {
00148 
00149   static unsigned long counter_tmp;
00150 
00151   SET_COUNTER(A, L, counter, counter_tmp);    
00152   rijndael_encrypt(ctx, A, S);
00153   memxor(msg, S, len);
00154 }
00155 
00156 static inline void
00157 mac(rijndael_ctx *ctx, 
00158     unsigned char *msg, size_t len,
00159     unsigned char B[DTLS_CCM_BLOCKSIZE],
00160     unsigned char X[DTLS_CCM_BLOCKSIZE]) {
00161   size_t i;
00162 
00163   for (i = 0; i < len; ++i)
00164     B[i] = X[i] ^ msg[i];
00165 
00166   rijndael_encrypt(ctx, B, X);
00167 
00168 }
00169 
00170 long int
00171 dtls_ccm_encrypt_message(rijndael_ctx *ctx, size_t M, size_t L, 
00172              unsigned char nonce[DTLS_CCM_BLOCKSIZE], 
00173              unsigned char *msg, size_t lm, 
00174              const unsigned char *aad, size_t la) {
00175   size_t i, len;
00176   unsigned long counter_tmp;
00177   unsigned long counter = 1; /* \bug does not work correctly on ia32 when
00178                          lm >= 2^16 */
00179   unsigned char A[DTLS_CCM_BLOCKSIZE]; /* A_i blocks for encryption input */
00180   unsigned char B[DTLS_CCM_BLOCKSIZE]; /* B_i blocks for CBC-MAC input */
00181   unsigned char S[DTLS_CCM_BLOCKSIZE]; /* S_i = encrypted A_i blocks */
00182   unsigned char X[DTLS_CCM_BLOCKSIZE]; /* X_i = encrypted B_i blocks */
00183 
00184   len = lm;         /* save original length */
00185   /* create the initial authentication block B0 */
00186   block0(M, L, la, lm, nonce, B);
00187   add_auth_data(ctx, aad, la, B, X);
00188 
00189   /* initialize block template */
00190   A[0] = L-1;
00191 
00192   /* copy the nonce */
00193   memcpy(A + 1, nonce, DTLS_CCM_BLOCKSIZE - L);
00194   
00195   while (lm >= DTLS_CCM_BLOCKSIZE) {
00196     /* calculate MAC */
00197     mac(ctx, msg, DTLS_CCM_BLOCKSIZE, B, X);
00198 
00199     /* encrypt */
00200     encrypt(ctx, L, counter, msg, DTLS_CCM_BLOCKSIZE, A, S);
00201 
00202     /* update local pointers */
00203     lm -= DTLS_CCM_BLOCKSIZE;
00204     msg += DTLS_CCM_BLOCKSIZE;
00205     counter++;
00206   }
00207 
00208   if (lm) {
00209     /* Calculate MAC. The remainder of B must be padded with zeroes, so
00210      * B is constructed to contain X ^ msg for the first lm bytes (done in
00211      * mac() and X ^ 0 for the remaining DTLS_CCM_BLOCKSIZE - lm bytes
00212      * (i.e., we can use memcpy() here).
00213      */
00214     memcpy(B + lm, X + lm, DTLS_CCM_BLOCKSIZE - lm);
00215     mac(ctx, msg, lm, B, X);
00216 
00217     /* encrypt */
00218     encrypt(ctx, L, counter, msg, lm, A, S);
00219 
00220     /* update local pointers */
00221     msg += lm;
00222   }
00223   
00224   /* calculate S_0 */  
00225   SET_COUNTER(A, L, 0, counter_tmp);
00226   rijndael_encrypt(ctx, A, S);
00227 
00228   for (i = 0; i < M; ++i)
00229     *msg++ = X[i] ^ S[i];
00230 
00231   return len + M;
00232 }
00233 
00234 long int
00235 dtls_ccm_decrypt_message(rijndael_ctx *ctx, size_t M, size_t L,
00236              unsigned char nonce[DTLS_CCM_BLOCKSIZE], 
00237              unsigned char *msg, size_t lm, 
00238              const unsigned char *aad, size_t la) {
00239   
00240   size_t len;
00241   unsigned long counter_tmp;
00242   unsigned long counter = 1; /* \bug does not work correctly on ia32 when
00243                          lm >= 2^16 */
00244   unsigned char A[DTLS_CCM_BLOCKSIZE]; /* A_i blocks for encryption input */
00245   unsigned char B[DTLS_CCM_BLOCKSIZE]; /* B_i blocks for CBC-MAC input */
00246   unsigned char S[DTLS_CCM_BLOCKSIZE]; /* S_i = encrypted A_i blocks */
00247   unsigned char X[DTLS_CCM_BLOCKSIZE]; /* X_i = encrypted B_i blocks */
00248 
00249   if (lm < M)
00250     goto error;
00251 
00252   len = lm;       /* save original length */
00253   lm -= M;        /* detract MAC size*/
00254 
00255   /* create the initial authentication block B0 */
00256   block0(M, L, la, lm, nonce, B);
00257   add_auth_data(ctx, aad, la, B, X);
00258 
00259   /* initialize block template */
00260   A[0] = L-1;
00261 
00262   /* copy the nonce */
00263   memcpy(A + 1, nonce, DTLS_CCM_BLOCKSIZE - L);
00264   
00265   while (lm >= DTLS_CCM_BLOCKSIZE) {
00266     /* decrypt */
00267     encrypt(ctx, L, counter, msg, DTLS_CCM_BLOCKSIZE, A, S);
00268     
00269     /* calculate MAC */
00270     mac(ctx, msg, DTLS_CCM_BLOCKSIZE, B, X);
00271 
00272     /* update local pointers */
00273     lm -= DTLS_CCM_BLOCKSIZE;
00274     msg += DTLS_CCM_BLOCKSIZE;
00275     counter++;
00276   }
00277 
00278   if (lm) {
00279     /* decrypt */
00280     encrypt(ctx, L, counter, msg, lm, A, S);
00281 
00282     /* Calculate MAC. Note that msg ends in the MAC so we must
00283      * construct B to contain X ^ msg for the first lm bytes (done in
00284      * mac() and X ^ 0 for the remaining DTLS_CCM_BLOCKSIZE - lm bytes
00285      * (i.e., we can use memcpy() here).
00286      */
00287     memcpy(B + lm, X + lm, DTLS_CCM_BLOCKSIZE - lm);
00288     mac(ctx, msg, lm, B, X); 
00289 
00290     /* update local pointers */
00291     msg += lm;
00292   }
00293   
00294   /* calculate S_0 */  
00295   SET_COUNTER(A, L, 0, counter_tmp);
00296   rijndael_encrypt(ctx, A, S);
00297 
00298   memxor(msg, S, M);
00299 
00300   /* return length if MAC is valid, otherwise continue with error handling */
00301   if (memcmp(X, msg, M) == 0) 
00302     return len - M;
00303   
00304  error:
00305   return -1;
00306 }