1 /* $KAME: esp_aesctr.c,v 1.2 2003/07/20 00:29:37 itojun Exp $ */
2
3 /*-
4 * Copyright (C) 1995, 1996, 1997, 1998 and 2003 WIDE Project.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the project nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 * $FreeBSD: releng/5.4/sys/netinet6/esp_aesctr.c 141090 2005-01-31 23:27:04Z imp $
32 */
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/socket.h>
37 #include <sys/queue.h>
38 #include <sys/syslog.h>
39 #include <sys/mbuf.h>
40
41 #include <net/if.h>
42 #include <net/route.h>
43
44 #include <netinet/in.h>
45
46 #include <netinet6/ipsec.h>
47 #include <netinet6/esp.h>
48 #include <netinet6/esp_aesctr.h>
49
50 #include <netkey/key.h>
51
52 #include <crypto/rijndael/rijndael.h>
53
54 #include <net/net_osdep.h>
55
56 #define AES_BLOCKSIZE 16
57
58 #define NONCESIZE 4
59 union cblock {
60 struct {
61 u_int8_t nonce[4];
62 u_int8_t iv[16];
63 u_int32_t ctr;
64 } v __attribute__((__packed__));
65 u_int8_t cblock[16];
66 };
67
68 typedef struct {
69 u_int32_t r_ek[(RIJNDAEL_MAXNR+1)*4];
70 int r_nr; /* key-length-dependent number of rounds */
71 } aesctr_ctx;
72
73 int
74 esp_aesctr_mature(sav)
75 struct secasvar *sav;
76 {
77 int keylen;
78 const struct esp_algorithm *algo;
79
80 algo = esp_algorithm_lookup(sav->alg_enc);
81 if (!algo) {
82 ipseclog((LOG_ERR,
83 "esp_aeesctr_mature %s: unsupported algorithm.\n",
84 algo->name));
85 return 1;
86 }
87
88 keylen = sav->key_enc->sadb_key_bits;
89 if (keylen < algo->keymin || algo->keymax < keylen) {
90 ipseclog((LOG_ERR,
91 "esp_aesctr_mature %s: invalid key length %d.\n",
92 algo->name, sav->key_enc->sadb_key_bits));
93 return 1;
94 }
95
96 /* rijndael key + nonce */
97 if (!(keylen == 128 + 32 || keylen == 192 + 32 || keylen == 256 + 32)) {
98 ipseclog((LOG_ERR,
99 "esp_aesctr_mature %s: invalid key length %d.\n",
100 algo->name, keylen));
101 return 1;
102 }
103
104 return 0;
105 }
106
107 size_t
108 esp_aesctr_schedlen(algo)
109 const struct esp_algorithm *algo;
110 {
111
112 return sizeof(aesctr_ctx);
113 }
114
115 int
116 esp_aesctr_schedule(algo, sav)
117 const struct esp_algorithm *algo;
118 struct secasvar *sav;
119 {
120 aesctr_ctx *ctx;
121 int keylen;
122
123 /* SA key = AES key + nonce */
124 keylen = _KEYLEN(sav->key_enc) * 8 - NONCESIZE * 8;
125
126 ctx = (aesctr_ctx *)sav->sched;
127 if ((ctx->r_nr = rijndaelKeySetupEnc(ctx->r_ek,
128 (char *)_KEYBUF(sav->key_enc), keylen)) == 0)
129 return -1;
130 return 0;
131 }
132
133 int
134 esp_aesctr_decrypt(m, off, sav, algo, ivlen)
135 struct mbuf *m;
136 size_t off;
137 struct secasvar *sav;
138 const struct esp_algorithm *algo;
139 int ivlen;
140 {
141 struct mbuf *s;
142 struct mbuf *d, *d0 = NULL, *dp;
143 int soff, doff; /* offset from the head of chain, to head of this mbuf */
144 int sn, dn; /* offset from the head of the mbuf, to meat */
145 size_t ivoff, bodyoff;
146 union cblock cblock;
147 u_int8_t keystream[AES_BLOCKSIZE], *nonce;
148 u_int32_t ctr;
149 u_int8_t *ivp;
150 u_int8_t sbuf[AES_BLOCKSIZE], *sp, *dst;
151 struct mbuf *scut;
152 int scutoff;
153 int i;
154 int blocklen;
155 aesctr_ctx *ctx;
156
157 if (ivlen != sav->ivlen) {
158 ipseclog((LOG_ERR, "esp_aesctr_decrypt %s: "
159 "unsupported ivlen %d\n", algo->name, ivlen));
160 goto fail;
161 }
162
163 /* assumes blocklen == padbound */
164 blocklen = algo->padbound;
165
166 ivoff = off + sizeof(struct newesp);
167 bodyoff = off + sizeof(struct newesp) + ivlen;
168
169 /* setup counter block */
170 nonce = _KEYBUF(sav->key_enc) + _KEYLEN(sav->key_enc) - NONCESIZE;
171 bcopy(nonce, cblock.v.nonce, NONCESIZE);
172 m_copydata(m, ivoff, ivlen, cblock.v.iv);
173 ctr = 1;
174
175 if (m->m_pkthdr.len < bodyoff) {
176 ipseclog((LOG_ERR, "esp_aesctr_decrypt %s: bad len %d/%lu\n",
177 algo->name, m->m_pkthdr.len, (unsigned long)bodyoff));
178 goto fail;
179 }
180 if ((m->m_pkthdr.len - bodyoff) % blocklen) {
181 ipseclog((LOG_ERR, "esp_aesctr_decrypt %s: "
182 "payload length must be multiple of %d\n",
183 algo->name, blocklen));
184 goto fail;
185 }
186
187 s = m;
188 d = d0 = dp = NULL;
189 soff = doff = sn = dn = 0;
190 ivp = sp = NULL;
191
192 /* skip bodyoff */
193 while (soff < bodyoff) {
194 if (soff + s->m_len > bodyoff) {
195 sn = bodyoff - soff;
196 break;
197 }
198
199 soff += s->m_len;
200 s = s->m_next;
201 }
202 scut = s;
203 scutoff = sn;
204
205 /* skip over empty mbuf */
206 while (s && s->m_len == 0)
207 s = s->m_next;
208
209 while (soff < m->m_pkthdr.len) {
210 /* source */
211 if (sn + blocklen <= s->m_len) {
212 /* body is continuous */
213 sp = mtod(s, u_int8_t *) + sn;
214 } else {
215 /* body is non-continuous */
216 m_copydata(s, sn, blocklen, (caddr_t)sbuf);
217 sp = sbuf;
218 }
219
220 /* destination */
221 if (!d || dn + blocklen > d->m_len) {
222 if (d)
223 dp = d;
224 MGET(d, M_DONTWAIT, MT_DATA);
225 i = m->m_pkthdr.len - (soff + sn);
226 if (d && i > MLEN) {
227 MCLGET(d, M_DONTWAIT);
228 if ((d->m_flags & M_EXT) == 0) {
229 m_free(d);
230 d = NULL;
231 }
232 }
233 if (!d) {
234 goto nomem;
235 }
236 if (!d0)
237 d0 = d;
238 if (dp)
239 dp->m_next = d;
240 d->m_len = 0;
241 d->m_len = (M_TRAILINGSPACE(d) / blocklen) * blocklen;
242 if (d->m_len > i)
243 d->m_len = i;
244 dn = 0;
245 }
246
247 /* put counter into counter block */
248 cblock.v.ctr = htonl(ctr);
249
250 /* setup keystream */
251 ctx = (aesctr_ctx *)sav->sched;
252 rijndaelEncrypt(ctx->r_ek, ctx->r_nr, cblock.cblock, keystream);
253
254 bcopy(sp, mtod(d, u_int8_t *) + dn, blocklen);
255 dst = mtod(d, u_int8_t *) + dn;
256 for (i = 0; i < blocklen; i++)
257 dst[i] ^= keystream[i];
258
259 ctr++;
260
261 sn += blocklen;
262 dn += blocklen;
263
264 /* find the next source block */
265 while (s && sn >= s->m_len) {
266 sn -= s->m_len;
267 soff += s->m_len;
268 s = s->m_next;
269 }
270
271 /* skip over empty mbuf */
272 while (s && s->m_len == 0)
273 s = s->m_next;
274 }
275
276 m_freem(scut->m_next);
277 scut->m_len = scutoff;
278 scut->m_next = d0;
279
280 /* just in case */
281 bzero(&cblock, sizeof(cblock));
282 bzero(keystream, sizeof(keystream));
283
284 return 0;
285
286 fail:
287 m_freem(m);
288 if (d0)
289 m_freem(d0);
290 return EINVAL;
291
292 nomem:
293 m_freem(m);
294 if (d0)
295 m_freem(d0);
296 return ENOBUFS;
297 }
298
299 int
300 esp_aesctr_encrypt(m, off, plen, sav, algo, ivlen)
301 struct mbuf *m;
302 size_t off;
303 size_t plen;
304 struct secasvar *sav;
305 const struct esp_algorithm *algo;
306 int ivlen;
307 {
308 struct mbuf *s;
309 struct mbuf *d, *d0, *dp;
310 int soff, doff; /* offset from the head of chain, to head of this mbuf */
311 int sn, dn; /* offset from the head of the mbuf, to meat */
312 size_t ivoff, bodyoff;
313 union cblock cblock;
314 u_int8_t keystream[AES_BLOCKSIZE], *nonce;
315 u_int32_t ctr;
316 u_int8_t sbuf[AES_BLOCKSIZE], *sp, *dst;
317 struct mbuf *scut;
318 int scutoff;
319 int i;
320 int blocklen;
321 aesctr_ctx *ctx;
322
323 if (ivlen != sav->ivlen) {
324 ipseclog((LOG_ERR, "esp_aesctr_encrypt %s: "
325 "unsupported ivlen %d\n", algo->name, ivlen));
326 m_freem(m);
327 return EINVAL;
328 }
329
330 /* assumes blocklen == padbound */
331 blocklen = algo->padbound;
332
333 ivoff = off + sizeof(struct newesp);
334 bodyoff = off + sizeof(struct newesp) + ivlen;
335
336 /* put iv into the packet. */
337 /* maybe it is better to overwrite dest, not source */
338 m_copyback(m, ivoff, ivlen, sav->iv);
339
340 /* setup counter block */
341 nonce = _KEYBUF(sav->key_enc) + _KEYLEN(sav->key_enc) - NONCESIZE;
342 bcopy(nonce, cblock.v.nonce, NONCESIZE);
343 m_copydata(m, ivoff, ivlen, cblock.v.iv);
344 ctr = 1;
345
346 if (m->m_pkthdr.len < bodyoff) {
347 ipseclog((LOG_ERR, "esp_aesctr_encrypt %s: bad len %d/%lu\n",
348 algo->name, m->m_pkthdr.len, (unsigned long)bodyoff));
349 m_freem(m);
350 return EINVAL;
351 }
352 if ((m->m_pkthdr.len - bodyoff) % blocklen) {
353 ipseclog((LOG_ERR, "esp_aesctr_encrypt %s: "
354 "payload length must be multiple of %lu\n",
355 algo->name, (unsigned long)algo->padbound));
356 m_freem(m);
357 return EINVAL;
358 }
359
360 s = m;
361 d = d0 = dp = NULL;
362 soff = doff = sn = dn = 0;
363 sp = NULL;
364
365 /* skip bodyoff */
366 while (soff < bodyoff) {
367 if (soff + s->m_len > bodyoff) {
368 sn = bodyoff - soff;
369 break;
370 }
371
372 soff += s->m_len;
373 s = s->m_next;
374 }
375 scut = s;
376 scutoff = sn;
377
378 /* skip over empty mbuf */
379 while (s && s->m_len == 0)
380 s = s->m_next;
381
382 while (soff < m->m_pkthdr.len) {
383 /* source */
384 if (sn + blocklen <= s->m_len) {
385 /* body is continuous */
386 sp = mtod(s, u_int8_t *) + sn;
387 } else {
388 /* body is non-continuous */
389 m_copydata(s, sn, blocklen, (caddr_t)sbuf);
390 sp = sbuf;
391 }
392
393 /* destination */
394 if (!d || dn + blocklen > d->m_len) {
395 if (d)
396 dp = d;
397 MGET(d, M_DONTWAIT, MT_DATA);
398 i = m->m_pkthdr.len - (soff + sn);
399 if (d && i > MLEN) {
400 MCLGET(d, M_DONTWAIT);
401 if ((d->m_flags & M_EXT) == 0) {
402 m_free(d);
403 d = NULL;
404 }
405 }
406 if (!d) {
407 m_freem(m);
408 if (d0)
409 m_freem(d0);
410 return ENOBUFS;
411 }
412 if (!d0)
413 d0 = d;
414 if (dp)
415 dp->m_next = d;
416 d->m_len = 0;
417 d->m_len = (M_TRAILINGSPACE(d) / blocklen) * blocklen;
418 if (d->m_len > i)
419 d->m_len = i;
420 dn = 0;
421 }
422
423 /* put counter into counter block */
424 cblock.v.ctr = htonl(ctr);
425
426 /* setup keystream */
427 ctx = (aesctr_ctx *)sav->sched;
428 rijndaelEncrypt(ctx->r_ek, ctx->r_nr, cblock.cblock, keystream);
429
430 bcopy(sp, mtod(d, u_int8_t *) + dn, blocklen);
431 dst = mtod(d, u_int8_t *) + dn;
432 for (i = 0; i < blocklen; i++)
433 dst[i] ^= keystream[i];
434
435 ctr++;
436
437 sn += blocklen;
438 dn += blocklen;
439
440 /* find the next source block */
441 while (s && sn >= s->m_len) {
442 sn -= s->m_len;
443 soff += s->m_len;
444 s = s->m_next;
445 }
446
447 /* skip over empty mbuf */
448 while (s && s->m_len == 0)
449 s = s->m_next;
450 }
451
452 m_freem(scut->m_next);
453 scut->m_len = scutoff;
454 scut->m_next = d0;
455
456 /* just in case */
457 bzero(&cblock, sizeof(cblock));
458 bzero(keystream, sizeof(keystream));
459
460 key_sa_stir_iv(sav);
461
462 return 0;
463 }
Cache object: fa060558586c6b3eac7cd231557b481c
|