1 /* $NetBSD: esp_aesctr.c,v 1.5.2.1 2010/09/12 21:16:46 bouyer Exp $ */
2 /* $KAME: esp_aesctr.c,v 1.2 2003/07/20 00:29:37 itojun Exp $ */
3
4 /*
5 * Copyright (C) 1995, 1996, 1997, 1998 and 2003 WIDE Project.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the project nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: esp_aesctr.c,v 1.5.2.1 2010/09/12 21:16:46 bouyer Exp $");
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/socket.h>
39 #include <sys/queue.h>
40 #include <sys/syslog.h>
41 #include <sys/mbuf.h>
42
43 #include <net/if.h>
44 #include <net/route.h>
45
46 #include <netinet/in.h>
47
48 #include <netinet6/ipsec.h>
49 #include <netinet6/esp.h>
50 #include <netinet6/esp_aesctr.h>
51
52 #include <netkey/key.h>
53
54 #include <crypto/rijndael/rijndael.h>
55
56 #include <net/net_osdep.h>
57
58 #define AES_BLOCKSIZE 16
59
60 #define NONCESIZE 4
61 union cblock {
62 struct {
63 u_int8_t nonce[4];
64 u_int8_t iv[8];
65 u_int32_t ctr;
66 } v __attribute__((__packed__));
67 u_int8_t cblock[16];
68 };
69
70 typedef struct {
71 u_int32_t r_ek[(RIJNDAEL_MAXNR+1)*4];
72 int r_nr; /* key-length-dependent number of rounds */
73 } aesctr_ctx;
74
75 int
76 esp_aesctr_mature(sav)
77 struct secasvar *sav;
78 {
79 int keylen;
80 const struct esp_algorithm *algo;
81
82 algo = esp_algorithm_lookup(sav->alg_enc);
83 if (algo == NULL) {
84 ipseclog((LOG_ERR,
85 "esp_aesctr_mature: unsupported encryption algorithm %d\n",
86 sav->alg_enc));
87 return 1;
88 }
89
90 keylen = sav->key_enc->sadb_key_bits;
91 if (keylen < algo->keymin || algo->keymax < keylen) {
92 ipseclog((LOG_ERR,
93 "esp_aesctr_mature %s: invalid key length %d.\n",
94 algo->name, sav->key_enc->sadb_key_bits));
95 return 1;
96 }
97
98 /* rijndael key + nonce */
99 if (!(keylen == 128 + 32 || keylen == 192 + 32 || keylen == 256 + 32)) {
100 ipseclog((LOG_ERR,
101 "esp_aesctr_mature %s: invalid key length %d.\n",
102 algo->name, keylen));
103 return 1;
104 }
105
106 return 0;
107 }
108
109 size_t
110 esp_aesctr_schedlen(const struct esp_algorithm *algo)
111 {
112
113 return sizeof(aesctr_ctx);
114 }
115
116 int
117 esp_aesctr_schedule(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(
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 {
309 struct mbuf *s;
310 struct mbuf *d, *d0, *dp;
311 int soff, doff; /* offset from the head of chain, to head of this mbuf */
312 int sn, dn; /* offset from the head of the mbuf, to meat */
313 size_t ivoff, bodyoff;
314 union cblock cblock;
315 u_int8_t keystream[AES_BLOCKSIZE], *nonce;
316 u_int32_t ctr;
317 u_int8_t sbuf[AES_BLOCKSIZE], *sp, *dst;
318 struct mbuf *scut;
319 int scutoff;
320 int i;
321 int blocklen;
322 aesctr_ctx *ctx;
323
324 if (ivlen != sav->ivlen) {
325 ipseclog((LOG_ERR, "esp_aesctr_encrypt %s: "
326 "unsupported ivlen %d\n", algo->name, ivlen));
327 m_freem(m);
328 return EINVAL;
329 }
330
331 /* assumes blocklen == padbound */
332 blocklen = algo->padbound;
333
334 ivoff = off + sizeof(struct newesp);
335 bodyoff = off + sizeof(struct newesp) + ivlen;
336
337 /* put iv into the packet. */
338 /* maybe it is better to overwrite dest, not source */
339 m_copyback(m, ivoff, ivlen, sav->iv);
340
341 /* setup counter block */
342 nonce = _KEYBUF(sav->key_enc) + _KEYLEN(sav->key_enc) - NONCESIZE;
343 bcopy(nonce, cblock.v.nonce, NONCESIZE);
344 m_copydata(m, ivoff, ivlen, cblock.v.iv);
345 ctr = 1;
346
347 if (m->m_pkthdr.len < bodyoff) {
348 ipseclog((LOG_ERR, "esp_aesctr_encrypt %s: bad len %d/%lu\n",
349 algo->name, m->m_pkthdr.len, (unsigned long)bodyoff));
350 m_freem(m);
351 return EINVAL;
352 }
353 if ((m->m_pkthdr.len - bodyoff) % blocklen) {
354 ipseclog((LOG_ERR, "esp_aesctr_encrypt %s: "
355 "payload length must be multiple of %lu\n",
356 algo->name, (unsigned long)algo->padbound));
357 m_freem(m);
358 return EINVAL;
359 }
360
361 s = m;
362 d = d0 = dp = NULL;
363 soff = doff = sn = dn = 0;
364 sp = NULL;
365
366 /* skip bodyoff */
367 while (soff < bodyoff) {
368 if (soff + s->m_len > bodyoff) {
369 sn = bodyoff - soff;
370 break;
371 }
372
373 soff += s->m_len;
374 s = s->m_next;
375 }
376 scut = s;
377 scutoff = sn;
378
379 /* skip over empty mbuf */
380 while (s && s->m_len == 0)
381 s = s->m_next;
382
383 while (soff < m->m_pkthdr.len) {
384 /* source */
385 if (sn + blocklen <= s->m_len) {
386 /* body is continuous */
387 sp = mtod(s, u_int8_t *) + sn;
388 } else {
389 /* body is non-continuous */
390 m_copydata(s, sn, blocklen, (caddr_t)sbuf);
391 sp = sbuf;
392 }
393
394 /* destination */
395 if (!d || dn + blocklen > d->m_len) {
396 if (d)
397 dp = d;
398 MGET(d, M_DONTWAIT, MT_DATA);
399 i = m->m_pkthdr.len - (soff + sn);
400 if (d && i > MLEN) {
401 MCLGET(d, M_DONTWAIT);
402 if ((d->m_flags & M_EXT) == 0) {
403 m_free(d);
404 d = NULL;
405 }
406 }
407 if (!d) {
408 m_freem(m);
409 if (d0)
410 m_freem(d0);
411 return ENOBUFS;
412 }
413 if (!d0)
414 d0 = d;
415 if (dp)
416 dp->m_next = d;
417 d->m_len = 0;
418 d->m_len = (M_TRAILINGSPACE(d) / blocklen) * blocklen;
419 if (d->m_len > i)
420 d->m_len = i;
421 dn = 0;
422 }
423
424 /* put counter into counter block */
425 cblock.v.ctr = htonl(ctr);
426
427 /* setup keystream */
428 ctx = (aesctr_ctx *)sav->sched;
429 rijndaelEncrypt(ctx->r_ek, ctx->r_nr, cblock.cblock, keystream);
430
431 bcopy(sp, mtod(d, u_int8_t *) + dn, blocklen);
432 dst = mtod(d, u_int8_t *) + dn;
433 for (i = 0; i < blocklen; i++)
434 dst[i] ^= keystream[i];
435
436 ctr++;
437
438 sn += blocklen;
439 dn += blocklen;
440
441 /* find the next source block */
442 while (s && sn >= s->m_len) {
443 sn -= s->m_len;
444 soff += s->m_len;
445 s = s->m_next;
446 }
447
448 /* skip over empty mbuf */
449 while (s && s->m_len == 0)
450 s = s->m_next;
451 }
452
453 m_freem(scut->m_next);
454 scut->m_len = scutoff;
455 scut->m_next = d0;
456
457 /* just in case */
458 bzero(&cblock, sizeof(cblock));
459 bzero(keystream, sizeof(keystream));
460
461 key_sa_stir_iv(sav);
462
463 return 0;
464 }
Cache object: c9f10d02382fac46ef9af38e12e77ca0
|