]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netinet6/esp_aesctr.c
This commit was generated by cvs2svn to compensate for changes in r155506,
[FreeBSD/FreeBSD.git] / sys / netinet6 / esp_aesctr.c
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$
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[8];
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 }