1366145b5ac7385dd1a0a5cd4e11ffec17e44af6
[platform/upstream/iotivity.git] / extlibs / tinydtls / ccm.c
1 /* dtls -- a very basic DTLS implementation
2  *
3  * Copyright (C) 2011--2014 Olaf Bergmann <bergmann@tzi.org>
4  *
5  * Permission is hereby granted, free of charge, to any person
6  * obtaining a copy of this software and associated documentation
7  * files (the "Software"), to deal in the Software without
8  * restriction, including without limitation the rights to use, copy,
9  * modify, merge, publish, distribute, sublicense, and/or sell copies
10  * of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  */
25
26 #include <string.h>
27
28 #include "dtls_config.h"
29 #include "global.h"
30 #include "numeric.h"
31 #include "ccm.h"
32
33 #ifdef HAVE_ASSERT_H
34 # include <assert.h>
35 #endif
36
37 #define CCM_FLAGS(A,M,L) (((A > 0) << 6) | (((M - 2)/2) << 3) | (L - 1))
38
39 #define MASK_L(_L) ((1 << 8 * _L) - 1)
40
41 #define SET_COUNTER(A,L,cnt,C) {                                        \
42     int i;                                                              \
43     memset((A) + DTLS_CCM_BLOCKSIZE - (L), 0, (L));                     \
44     (C) = (cnt) & MASK_L(L);                                            \
45     for (i = DTLS_CCM_BLOCKSIZE - 1; (C) && (i > (L)); --i, (C) >>= 8)  \
46       (A)[i] |= (C) & 0xFF;                                             \
47   }
48
49 INLINE_API void 
50 block0(size_t M,       /* number of auth bytes */
51        size_t L,       /* number of bytes to encode message length */
52        size_t la,      /* l(a) octets additional authenticated data */
53        size_t lm,      /* l(m) message length */
54        unsigned char nonce[DTLS_CCM_BLOCKSIZE],
55        unsigned char *result) {
56   int i;
57
58   result[0] = CCM_FLAGS(la, M, L);
59
60   /* copy the nonce */
61   memcpy(result + 1, nonce, DTLS_CCM_BLOCKSIZE - L);
62   
63   for (i=0; i < L; i++) {
64     result[15-i] = lm & 0xff;
65     lm >>= 8;
66   }
67 }
68
69 /** 
70  * Creates the CBC-MAC for the additional authentication data that
71  * is sent in cleartext. 
72  *
73  * \param ctx  The crypto context for the AES encryption.
74  * \param msg  The message starting with the additional authentication data.
75  * \param la   The number of additional authentication bytes in \p msg.
76  * \param B    The input buffer for crypto operations. When this function
77  *             is called, \p B must be initialized with \c B0 (the first
78  *             authentication block.
79  * \param X    The output buffer where the result of the CBC calculation
80  *             is placed.
81  * \return     The result is written to \p X.
82  */
83 static void
84 add_auth_data(rijndael_ctx *ctx, const unsigned char *msg, size_t la,
85               unsigned char B[DTLS_CCM_BLOCKSIZE], 
86               unsigned char X[DTLS_CCM_BLOCKSIZE]) {
87   size_t i,j; 
88
89   rijndael_encrypt(ctx, B, X);
90
91   memset(B, 0, DTLS_CCM_BLOCKSIZE);
92
93   if (!la)
94     return;
95
96 #ifndef WITH_CONTIKI
97     if (la < 0xFF00) {          /* 2^16 - 2^8 */
98       j = 2;
99       dtls_int_to_uint16(B, la);
100   } else if (la <= UINT32_MAX) {
101       j = 6;
102       dtls_int_to_uint16(B, 0xFFFE);
103       dtls_int_to_uint32(B+2, la);
104     } else {
105       j = 10;
106       dtls_int_to_uint16(B, 0xFFFF);
107       dtls_int_to_uint64(B+2, la);
108     }
109 #else /* WITH_CONTIKI */
110   /* With Contiki, we are building for small devices and thus
111    * anticipate that the number of additional authentication bytes
112    * will not exceed 65280 bytes (0xFF00) and we can skip the
113    * workarounds required for j=6 and j=10 on devices with a word size
114    * of 32 bits or 64 bits, respectively.
115    */
116
117   assert(la < 0xFF00);
118   j = 2;
119   dtls_int_to_uint16(B, la);
120 #endif /* WITH_CONTIKI */
121
122     i = min(DTLS_CCM_BLOCKSIZE - j, la);
123     memcpy(B + j, msg, i);
124     la -= i;
125     msg += i;
126     
127     memxor(B, X, DTLS_CCM_BLOCKSIZE);
128   
129   rijndael_encrypt(ctx, B, X);
130   
131   while (la > DTLS_CCM_BLOCKSIZE) {
132     for (i = 0; i < DTLS_CCM_BLOCKSIZE; ++i)
133       B[i] = X[i] ^ *msg++;
134     la -= DTLS_CCM_BLOCKSIZE;
135
136     rijndael_encrypt(ctx, B, X);
137   }
138   
139   if (la) {
140     memset(B, 0, DTLS_CCM_BLOCKSIZE);
141     memcpy(B, msg, la);
142     memxor(B, X, DTLS_CCM_BLOCKSIZE);
143
144     rijndael_encrypt(ctx, B, X);  
145   } 
146 }
147
148 INLINE_API void
149 encrypt(rijndael_ctx *ctx, size_t L, unsigned long counter,
150         unsigned char *msg, size_t len,
151         unsigned char A[DTLS_CCM_BLOCKSIZE],
152         unsigned char S[DTLS_CCM_BLOCKSIZE]) {
153
154   static unsigned long counter_tmp;
155
156   SET_COUNTER(A, L, counter, counter_tmp);    
157   rijndael_encrypt(ctx, A, S);
158   memxor(msg, S, len);
159 }
160
161 INLINE_API void
162 mac(rijndael_ctx *ctx, 
163     unsigned char *msg, size_t len,
164     unsigned char B[DTLS_CCM_BLOCKSIZE],
165     unsigned char X[DTLS_CCM_BLOCKSIZE]) {
166   size_t i;
167
168   for (i = 0; i < len; ++i)
169     B[i] = X[i] ^ msg[i];
170
171   rijndael_encrypt(ctx, B, X);
172
173 }
174
175 long int
176 dtls_ccm_encrypt_message(rijndael_ctx *ctx, size_t M, size_t L, 
177                          unsigned char nonce[DTLS_CCM_BLOCKSIZE], 
178                          unsigned char *msg, size_t lm, 
179                          const unsigned char *aad, size_t la) {
180   size_t i, len;
181   unsigned long counter_tmp;
182   unsigned long counter = 1; /* \bug does not work correctly on ia32 when
183                                      lm >= 2^16 */
184   unsigned char A[DTLS_CCM_BLOCKSIZE]; /* A_i blocks for encryption input */
185   unsigned char B[DTLS_CCM_BLOCKSIZE]; /* B_i blocks for CBC-MAC input */
186   unsigned char S[DTLS_CCM_BLOCKSIZE]; /* S_i = encrypted A_i blocks */
187   unsigned char X[DTLS_CCM_BLOCKSIZE]; /* X_i = encrypted B_i blocks */
188
189   len = lm;                     /* save original length */
190   /* create the initial authentication block B0 */
191   block0(M, L, la, lm, nonce, B);
192   add_auth_data(ctx, aad, la, B, X);
193
194   /* initialize block template */
195   A[0] = L-1;
196
197   /* copy the nonce */
198   memcpy(A + 1, nonce, DTLS_CCM_BLOCKSIZE - L);
199   
200   while (lm >= DTLS_CCM_BLOCKSIZE) {
201     /* calculate MAC */
202     mac(ctx, msg, DTLS_CCM_BLOCKSIZE, B, X);
203
204     /* encrypt */
205     encrypt(ctx, L, counter, msg, DTLS_CCM_BLOCKSIZE, A, S);
206
207     /* update local pointers */
208     lm -= DTLS_CCM_BLOCKSIZE;
209     msg += DTLS_CCM_BLOCKSIZE;
210     counter++;
211   }
212
213   if (lm) {
214     /* Calculate MAC. The remainder of B must be padded with zeroes, so
215      * B is constructed to contain X ^ msg for the first lm bytes (done in
216      * mac() and X ^ 0 for the remaining DTLS_CCM_BLOCKSIZE - lm bytes
217      * (i.e., we can use memcpy() here).
218      */
219     memcpy(B + lm, X + lm, DTLS_CCM_BLOCKSIZE - lm);
220     mac(ctx, msg, lm, B, X);
221
222     /* encrypt */
223     encrypt(ctx, L, counter, msg, lm, A, S);
224
225     /* update local pointers */
226     msg += lm;
227   }
228   
229   /* calculate S_0 */  
230   SET_COUNTER(A, L, 0, counter_tmp);
231   rijndael_encrypt(ctx, A, S);
232
233   for (i = 0; i < M; ++i)
234     *msg++ = X[i] ^ S[i];
235
236   return len + M;
237 }
238
239 long int
240 dtls_ccm_decrypt_message(rijndael_ctx *ctx, size_t M, size_t L,
241                          unsigned char nonce[DTLS_CCM_BLOCKSIZE], 
242                          unsigned char *msg, size_t lm, 
243                          const unsigned char *aad, size_t la) {
244   
245   size_t len;
246   unsigned long counter_tmp;
247   unsigned long counter = 1; /* \bug does not work correctly on ia32 when
248                                      lm >= 2^16 */
249   unsigned char A[DTLS_CCM_BLOCKSIZE]; /* A_i blocks for encryption input */
250   unsigned char B[DTLS_CCM_BLOCKSIZE]; /* B_i blocks for CBC-MAC input */
251   unsigned char S[DTLS_CCM_BLOCKSIZE]; /* S_i = encrypted A_i blocks */
252   unsigned char X[DTLS_CCM_BLOCKSIZE]; /* X_i = encrypted B_i blocks */
253
254   if (lm < M)
255     goto error;
256
257   len = lm;           /* save original length */
258   lm -= M;            /* detract MAC size*/
259
260   /* create the initial authentication block B0 */
261   block0(M, L, la, lm, nonce, B);
262   add_auth_data(ctx, aad, la, B, X);
263
264   /* initialize block template */
265   A[0] = L-1;
266
267   /* copy the nonce */
268   memcpy(A + 1, nonce, DTLS_CCM_BLOCKSIZE - L);
269   
270   while (lm >= DTLS_CCM_BLOCKSIZE) {
271     /* decrypt */
272     encrypt(ctx, L, counter, msg, DTLS_CCM_BLOCKSIZE, A, S);
273     
274     /* calculate MAC */
275     mac(ctx, msg, DTLS_CCM_BLOCKSIZE, B, X);
276
277     /* update local pointers */
278     lm -= DTLS_CCM_BLOCKSIZE;
279     msg += DTLS_CCM_BLOCKSIZE;
280     counter++;
281   }
282
283   if (lm) {
284     /* decrypt */
285     encrypt(ctx, L, counter, msg, lm, A, S);
286
287     /* Calculate MAC. Note that msg ends in the MAC so we must
288      * construct B to contain X ^ msg for the first lm bytes (done in
289      * mac() and X ^ 0 for the remaining DTLS_CCM_BLOCKSIZE - lm bytes
290      * (i.e., we can use memcpy() here).
291      */
292     memcpy(B + lm, X + lm, DTLS_CCM_BLOCKSIZE - lm);
293     mac(ctx, msg, lm, B, X); 
294
295     /* update local pointers */
296     msg += lm;
297   }
298   
299   /* calculate S_0 */  
300   SET_COUNTER(A, L, 0, counter_tmp);
301   rijndael_encrypt(ctx, A, S);
302
303   memxor(msg, S, M);
304
305   /* return length if MAC is valid, otherwise continue with error handling */
306   if (equals(X, msg, M))
307     return len - M;
308   
309  error:
310   return -1;
311 }