nataraid(4): Add devstat support.
[dragonfly.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 <netproto/key/key.h>
51
52 #include <crypto/rijndael/rijndael.h>
53
54 #define AES_BLOCKSIZE   16
55
56 #define NONCESIZE       4
57 union cblock {
58         struct {
59                 u_int8_t nonce[4];
60                 u_int8_t iv[8];
61                 u_int32_t ctr;
62         } __packed v;
63         u_int8_t cblock[16];
64 };
65
66 typedef struct {
67         u_int32_t       r_ek[(RIJNDAEL_MAXNR+1)*4];
68         int             r_nr; /* key-length-dependent number of rounds */
69 } aesctr_ctx;
70
71 int
72 esp_aesctr_mature(struct secasvar *sav)
73 {
74         int keylen;
75         const struct esp_algorithm *algo;
76
77         algo = esp_algorithm_lookup(sav->alg_enc);
78         if (!algo) {
79                 ipseclog((LOG_ERR,
80                     "esp_aeesctr_mature: unsupported algorithm %d\n",
81                     sav->alg_enc));
82                 return 1;
83         }
84
85         keylen = sav->key_enc->sadb_key_bits;
86         if (keylen < algo->keymin || algo->keymax < keylen) {
87                 ipseclog((LOG_ERR,
88                     "esp_aesctr_mature %s: invalid key length %d.\n",
89                     algo->name, sav->key_enc->sadb_key_bits));
90                 return 1;
91         }
92
93         /* rijndael key + nonce */
94         if (!(keylen == 128 + 32 || keylen == 192 + 32 || keylen == 256 + 32)) {
95                 ipseclog((LOG_ERR,
96                     "esp_aesctr_mature %s: invalid key length %d.\n",
97                     algo->name, keylen));
98                 return 1;
99         }
100
101         return 0;
102 }
103
104 size_t
105 esp_aesctr_schedlen(const struct esp_algorithm *algo)
106 {
107
108         return sizeof(aesctr_ctx);
109 }
110
111 int
112 esp_aesctr_schedule(const struct esp_algorithm *algo, struct secasvar *sav)
113 {
114         aesctr_ctx *ctx;
115         int keylen;
116
117         /* SA key = AES key + nonce */
118         keylen = _KEYLEN(sav->key_enc) * 8 - NONCESIZE * 8;
119
120         ctx = (aesctr_ctx *)sav->sched;
121         if ((ctx->r_nr = rijndaelKeySetupEnc(ctx->r_ek,
122             (char *)_KEYBUF(sav->key_enc), keylen)) == 0)
123                 return -1;
124         return 0;
125 }
126
127 int
128 esp_aesctr_decrypt(struct mbuf *m, size_t off, struct secasvar *sav,
129                 const struct esp_algorithm *algo, int ivlen)
130 {
131         struct mbuf *s;
132         struct mbuf *d, *d0 = NULL, *dp;
133         int soff, doff; /* offset from the head of chain, to head of this mbuf */
134         int sn, dn;     /* offset from the head of the mbuf, to meat */
135         size_t ivoff, bodyoff;
136         union cblock cblock;
137         u_int8_t keystream[AES_BLOCKSIZE], *nonce;
138         u_int32_t ctr;
139         u_int8_t *ivp;
140         u_int8_t sbuf[AES_BLOCKSIZE], *sp, *dst;
141         struct mbuf *scut;
142         int scutoff;
143         int i;
144         int blocklen;
145         aesctr_ctx *ctx;
146
147         if (ivlen != sav->ivlen) {
148                 ipseclog((LOG_ERR, "esp_aesctr_decrypt %s: "
149                     "unsupported ivlen %d\n", algo->name, ivlen));
150                 goto fail;
151         }
152
153         /* assumes blocklen == padbound */
154         blocklen = algo->padbound;
155
156         ivoff = off + sizeof(struct newesp);
157         bodyoff = off + sizeof(struct newesp) + ivlen;
158
159         /* setup counter block */
160         nonce = _KEYBUF(sav->key_enc) + _KEYLEN(sav->key_enc) - NONCESIZE;
161         bcopy(nonce, cblock.v.nonce, NONCESIZE);
162         m_copydata(m, ivoff, ivlen, cblock.v.iv);
163         ctr = 1;
164
165         if (m->m_pkthdr.len < bodyoff) {
166                 ipseclog((LOG_ERR, "esp_aesctr_decrypt %s: bad len %d/%lu\n",
167                     algo->name, m->m_pkthdr.len, (unsigned long)bodyoff));
168                 goto fail;
169         }
170         if ((m->m_pkthdr.len - bodyoff) % blocklen) {
171                 ipseclog((LOG_ERR, "esp_aesctr_decrypt %s: "
172                     "payload length must be multiple of %d\n",
173                     algo->name, blocklen));
174                 goto fail;
175         }
176
177         s = m;
178         d = d0 = dp = NULL;
179         soff = doff = sn = dn = 0;
180         ivp = sp = NULL;
181
182         /* skip bodyoff */
183         while (soff < bodyoff) {
184                 if (soff + s->m_len > bodyoff) {
185                         sn = bodyoff - soff;
186                         break;
187                 }
188
189                 soff += s->m_len;
190                 s = s->m_next;
191         }
192         scut = s;
193         scutoff = sn;
194
195         /* skip over empty mbuf */
196         while (s && s->m_len == 0)
197                 s = s->m_next;
198
199         while (soff < m->m_pkthdr.len) {
200                 /* source */
201                 if (sn + blocklen <= s->m_len) {
202                         /* body is continuous */
203                         sp = mtod(s, u_int8_t *) + sn;
204                 } else {
205                         /* body is non-continuous */
206                         m_copydata(s, sn, blocklen, (caddr_t)sbuf);
207                         sp = sbuf;
208                 }
209
210                 /* destination */
211                 if (!d || dn + blocklen > d->m_len) {
212                         if (d)
213                                 dp = d;
214                         MGET(d, M_NOWAIT, MT_DATA);
215                         i = m->m_pkthdr.len - (soff + sn);
216                         if (d && i > MLEN) {
217                                 MCLGET(d, M_NOWAIT);
218                                 if ((d->m_flags & M_EXT) == 0) {
219                                         m_free(d);
220                                         d = NULL;
221                                 }
222                         }
223                         if (!d) {
224                                 goto nomem;
225                         }
226                         if (!d0)
227                                 d0 = d;
228                         if (dp)
229                                 dp->m_next = d;
230                         d->m_len = 0;
231                         d->m_len = (M_TRAILINGSPACE(d) / blocklen) * blocklen;
232                         if (d->m_len > i)
233                                 d->m_len = i;
234                         dn = 0;
235                 }
236
237                 /* put counter into counter block */
238                 cblock.v.ctr = htonl(ctr);
239
240                 /* setup keystream */
241                 ctx = (aesctr_ctx *)sav->sched;
242                 rijndaelEncrypt(ctx->r_ek, ctx->r_nr, cblock.cblock, keystream);
243
244                 bcopy(sp, mtod(d, u_int8_t *) + dn, blocklen);
245                 dst = mtod(d, u_int8_t *) + dn;
246                 for (i = 0; i < blocklen; i++)
247                         dst[i] ^= keystream[i];
248
249                 ctr++;
250
251                 sn += blocklen;
252                 dn += blocklen;
253
254                 /* find the next source block */
255                 while (s && sn >= s->m_len) {
256                         sn -= s->m_len;
257                         soff += s->m_len;
258                         s = s->m_next;
259                 }
260
261                 /* skip over empty mbuf */
262                 while (s && s->m_len == 0)
263                         s = s->m_next;
264         }
265
266         m_freem(scut->m_next);
267         scut->m_len = scutoff;
268         scut->m_next = d0;
269
270         /* just in case */
271         bzero(&cblock, sizeof(cblock));
272         bzero(keystream, sizeof(keystream));
273
274         return 0;
275
276 fail:
277         m_freem(m);
278         if (d0)
279                 m_freem(d0);
280         return EINVAL;
281
282 nomem:
283         m_freem(m);
284         if (d0)
285                 m_freem(d0);
286         return ENOBUFS;
287 }
288
289 int
290 esp_aesctr_encrypt(struct mbuf *m, size_t off, size_t plen, struct secasvar *sav,
291                 const struct esp_algorithm *algo, int ivlen)
292 {
293         struct mbuf *s;
294         struct mbuf *d, *d0, *dp;
295         int soff, doff; /* offset from the head of chain, to head of this mbuf */
296         int sn, dn;     /* offset from the head of the mbuf, to meat */
297         size_t ivoff, bodyoff;
298         union cblock cblock;
299         u_int8_t keystream[AES_BLOCKSIZE], *nonce;
300         u_int32_t ctr;
301         u_int8_t sbuf[AES_BLOCKSIZE], *sp, *dst;
302         struct mbuf *scut;
303         int scutoff;
304         int i;
305         int blocklen;
306         aesctr_ctx *ctx;
307
308         if (ivlen != sav->ivlen) {
309                 ipseclog((LOG_ERR, "esp_aesctr_encrypt %s: "
310                     "unsupported ivlen %d\n", algo->name, ivlen));
311                 m_freem(m);
312                 return EINVAL;
313         }
314
315         /* assumes blocklen == padbound */
316         blocklen = algo->padbound;
317
318         ivoff = off + sizeof(struct newesp);
319         bodyoff = off + sizeof(struct newesp) + ivlen;
320
321         /* put iv into the packet. */
322         /* maybe it is better to overwrite dest, not source */
323         m_copyback(m, ivoff, ivlen, sav->iv);
324
325         /* setup counter block */
326         nonce = _KEYBUF(sav->key_enc) + _KEYLEN(sav->key_enc) - NONCESIZE;
327         bcopy(nonce, cblock.v.nonce, NONCESIZE);
328         m_copydata(m, ivoff, ivlen, cblock.v.iv);
329         ctr = 1;
330
331         if (m->m_pkthdr.len < bodyoff) {
332                 ipseclog((LOG_ERR, "esp_aesctr_encrypt %s: bad len %d/%lu\n",
333                     algo->name, m->m_pkthdr.len, (unsigned long)bodyoff));
334                 m_freem(m);
335                 return EINVAL;
336         }
337         if ((m->m_pkthdr.len - bodyoff) % blocklen) {
338                 ipseclog((LOG_ERR, "esp_aesctr_encrypt %s: "
339                     "payload length must be multiple of %lu\n",
340                     algo->name, (unsigned long)algo->padbound));
341                 m_freem(m);
342                 return EINVAL;
343         }
344
345         s = m;
346         d = d0 = dp = NULL;
347         soff = doff = sn = dn = 0;
348         sp = NULL;
349
350         /* skip bodyoff */
351         while (soff < bodyoff) {
352                 if (soff + s->m_len > bodyoff) {
353                         sn = bodyoff - soff;
354                         break;
355                 }
356
357                 soff += s->m_len;
358                 s = s->m_next;
359         }
360         scut = s;
361         scutoff = sn;
362
363         /* skip over empty mbuf */
364         while (s && s->m_len == 0)
365                 s = s->m_next;
366
367         while (soff < m->m_pkthdr.len) {
368                 /* source */
369                 if (sn + blocklen <= s->m_len) {
370                         /* body is continuous */
371                         sp = mtod(s, u_int8_t *) + sn;
372                 } else {
373                         /* body is non-continuous */
374                         m_copydata(s, sn, blocklen, (caddr_t)sbuf);
375                         sp = sbuf;
376                 }
377
378                 /* destination */
379                 if (!d || dn + blocklen > d->m_len) {
380                         if (d)
381                                 dp = d;
382                         MGET(d, M_NOWAIT, MT_DATA);
383                         i = m->m_pkthdr.len - (soff + sn);
384                         if (d && i > MLEN) {
385                                 MCLGET(d, M_NOWAIT);
386                                 if ((d->m_flags & M_EXT) == 0) {
387                                         m_free(d);
388                                         d = NULL;
389                                 }
390                         }
391                         if (!d) {
392                                 m_freem(m);
393                                 if (d0)
394                                         m_freem(d0);
395                                 return ENOBUFS;
396                         }
397                         if (!d0)
398                                 d0 = d;
399                         if (dp)
400                                 dp->m_next = d;
401                         d->m_len = 0;
402                         d->m_len = (M_TRAILINGSPACE(d) / blocklen) * blocklen;
403                         if (d->m_len > i)
404                                 d->m_len = i;
405                         dn = 0;
406                 }
407
408                 /* put counter into counter block */
409                 cblock.v.ctr = htonl(ctr);
410
411                 /* setup keystream */
412                 ctx = (aesctr_ctx *)sav->sched;
413                 rijndaelEncrypt(ctx->r_ek, ctx->r_nr, cblock.cblock, keystream);
414
415                 bcopy(sp, mtod(d, u_int8_t *) + dn, blocklen);
416                 dst = mtod(d, u_int8_t *) + dn;
417                 for (i = 0; i < blocklen; i++)
418                         dst[i] ^= keystream[i];
419
420                 ctr++;
421
422                 sn += blocklen;
423                 dn += blocklen;
424
425                 /* find the next source block */
426                 while (s && sn >= s->m_len) {
427                         sn -= s->m_len;
428                         soff += s->m_len;
429                         s = s->m_next;
430                 }
431
432                 /* skip over empty mbuf */
433                 while (s && s->m_len == 0)
434                         s = s->m_next;
435         }
436
437         m_freem(scut->m_next);
438         scut->m_len = scutoff;
439         scut->m_next = d0;
440
441         /* just in case */
442         bzero(&cblock, sizeof(cblock));
443         bzero(keystream, sizeof(keystream));
444
445         key_sa_stir_iv(sav);
446
447         return 0;
448 }