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 static inline void
block0(size_t M,size_t L,size_t la,size_t lm,unsigned char nonce[DTLS_CCM_BLOCKSIZE],unsigned char * result)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
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])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 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])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 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])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
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)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
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)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 }
312