inet6/pim: Return IPPROTO_DONE if the mbuf is freed.
[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 sbuf[AES_BLOCKSIZE], *sp, *dst;
140         struct mbuf *scut;
141         int scutoff;
142         int i;
143         int blocklen;
144         aesctr_ctx *ctx;
145
146         if (ivlen != sav->ivlen) {
147                 ipseclog((LOG_ERR, "esp_aesctr_decrypt %s: "
148                     "unsupported ivlen %d\n", algo->name, ivlen));
149                 goto fail;
150         }
151
152         /* assumes blocklen == padbound */
153         blocklen = algo->padbound;
154
155         ivoff = off + sizeof(struct newesp);
156         bodyoff = off + sizeof(struct newesp) + ivlen;
157
158         /* setup counter block */
159         nonce = _KEYBUF(sav->key_enc) + _KEYLEN(sav->key_enc) - NONCESIZE;
160         bcopy(nonce, cblock.v.nonce, NONCESIZE);
161         m_copydata(m, ivoff, ivlen, cblock.v.iv);
162         ctr = 1;
163
164         if (m->m_pkthdr.len < bodyoff) {
165                 ipseclog((LOG_ERR, "esp_aesctr_decrypt %s: bad len %d/%lu\n",
166                     algo->name, m->m_pkthdr.len, (unsigned long)bodyoff));
167                 goto fail;
168         }
169         if ((m->m_pkthdr.len - bodyoff) % blocklen) {
170                 ipseclog((LOG_ERR, "esp_aesctr_decrypt %s: "
171                     "payload length must be multiple of %d\n",
172                     algo->name, blocklen));
173                 goto fail;
174         }
175
176         s = m;
177         d = d0 = dp = NULL;
178         soff = doff = sn = dn = 0;
179         sp = NULL;
180
181         /* skip bodyoff */
182         while (soff < bodyoff) {
183                 if (soff + s->m_len > bodyoff) {
184                         sn = bodyoff - soff;
185                         break;
186                 }
187
188                 soff += s->m_len;
189                 s = s->m_next;
190         }
191         scut = s;
192         scutoff = sn;
193
194         /* skip over empty mbuf */
195         while (s && s->m_len == 0)
196                 s = s->m_next;
197
198         while (soff < m->m_pkthdr.len) {
199                 /* source */
200                 if (sn + blocklen <= s->m_len) {
201                         /* body is continuous */
202                         sp = mtod(s, u_int8_t *) + sn;
203                 } else {
204                         /* body is non-continuous */
205                         m_copydata(s, sn, blocklen, (caddr_t)sbuf);
206                         sp = sbuf;
207                 }
208
209                 /* destination */
210                 if (!d || dn + blocklen > d->m_len) {
211                         if (d)
212                                 dp = d;
213                         MGET(d, M_NOWAIT, MT_DATA);
214                         i = m->m_pkthdr.len - (soff + sn);
215                         if (d && i > MLEN) {
216                                 MCLGET(d, M_NOWAIT);
217                                 if ((d->m_flags & M_EXT) == 0) {
218                                         m_free(d);
219                                         d = NULL;
220                                 }
221                         }
222                         if (!d) {
223                                 goto nomem;
224                         }
225                         if (!d0)
226                                 d0 = d;
227                         if (dp)
228                                 dp->m_next = d;
229                         d->m_len = 0;
230                         d->m_len = (M_TRAILINGSPACE(d) / blocklen) * blocklen;
231                         if (d->m_len > i)
232                                 d->m_len = i;
233                         dn = 0;
234                 }
235
236                 /* put counter into counter block */
237                 cblock.v.ctr = htonl(ctr);
238
239                 /* setup keystream */
240                 ctx = (aesctr_ctx *)sav->sched;
241                 rijndaelEncrypt(ctx->r_ek, ctx->r_nr, cblock.cblock, keystream);
242
243                 bcopy(sp, mtod(d, u_int8_t *) + dn, blocklen);
244                 dst = mtod(d, u_int8_t *) + dn;
245                 for (i = 0; i < blocklen; i++)
246                         dst[i] ^= keystream[i];
247
248                 ctr++;
249
250                 sn += blocklen;
251                 dn += blocklen;
252
253                 /* find the next source block */
254                 while (s && sn >= s->m_len) {
255                         sn -= s->m_len;
256                         soff += s->m_len;
257                         s = s->m_next;
258                 }
259
260                 /* skip over empty mbuf */
261                 while (s && s->m_len == 0)
262                         s = s->m_next;
263         }
264
265         m_freem(scut->m_next);
266         scut->m_len = scutoff;
267         scut->m_next = d0;
268
269         /* just in case */
270         bzero(&cblock, sizeof(cblock));
271         bzero(keystream, sizeof(keystream));
272
273         return 0;
274
275 fail:
276         m_freem(m);
277         if (d0)
278                 m_freem(d0);
279         return EINVAL;
280
281 nomem:
282         m_freem(m);
283         if (d0)
284                 m_freem(d0);
285         return ENOBUFS;
286 }
287
288 int
289 esp_aesctr_encrypt(struct mbuf *m, size_t off, size_t plen, struct secasvar *sav,
290                 const struct esp_algorithm *algo, int ivlen)
291 {
292         struct mbuf *s;
293         struct mbuf *d, *d0, *dp;
294         int soff, doff; /* offset from the head of chain, to head of this mbuf */
295         int sn, dn;     /* offset from the head of the mbuf, to meat */
296         size_t ivoff, bodyoff;
297         union cblock cblock;
298         u_int8_t keystream[AES_BLOCKSIZE], *nonce;
299         u_int32_t ctr;
300         u_int8_t sbuf[AES_BLOCKSIZE], *sp, *dst;
301         struct mbuf *scut;
302         int scutoff;
303         int i;
304         int blocklen;
305         aesctr_ctx *ctx;
306
307         if (ivlen != sav->ivlen) {
308                 ipseclog((LOG_ERR, "esp_aesctr_encrypt %s: "
309                     "unsupported ivlen %d\n", algo->name, ivlen));
310                 m_freem(m);
311                 return EINVAL;
312         }
313
314         /* assumes blocklen == padbound */
315         blocklen = algo->padbound;
316
317         ivoff = off + sizeof(struct newesp);
318         bodyoff = off + sizeof(struct newesp) + ivlen;
319
320         /* put iv into the packet. */
321         /* maybe it is better to overwrite dest, not source */
322         m_copyback(m, ivoff, ivlen, sav->iv);
323
324         /* setup counter block */
325         nonce = _KEYBUF(sav->key_enc) + _KEYLEN(sav->key_enc) - NONCESIZE;
326         bcopy(nonce, cblock.v.nonce, NONCESIZE);
327         m_copydata(m, ivoff, ivlen, cblock.v.iv);
328         ctr = 1;
329
330         if (m->m_pkthdr.len < bodyoff) {
331                 ipseclog((LOG_ERR, "esp_aesctr_encrypt %s: bad len %d/%lu\n",
332                     algo->name, m->m_pkthdr.len, (unsigned long)bodyoff));
333                 m_freem(m);
334                 return EINVAL;
335         }
336         if ((m->m_pkthdr.len - bodyoff) % blocklen) {
337                 ipseclog((LOG_ERR, "esp_aesctr_encrypt %s: "
338                     "payload length must be multiple of %lu\n",
339                     algo->name, (unsigned long)algo->padbound));
340                 m_freem(m);
341                 return EINVAL;
342         }
343
344         s = m;
345         d = d0 = dp = NULL;
346         soff = doff = sn = dn = 0;
347         sp = NULL;
348
349         /* skip bodyoff */
350         while (soff < bodyoff) {
351                 if (soff + s->m_len > bodyoff) {
352                         sn = bodyoff - soff;
353                         break;
354                 }
355
356                 soff += s->m_len;
357                 s = s->m_next;
358         }
359         scut = s;
360         scutoff = sn;
361
362         /* skip over empty mbuf */
363         while (s && s->m_len == 0)
364                 s = s->m_next;
365
366         while (soff < m->m_pkthdr.len) {
367                 /* source */
368                 if (sn + blocklen <= s->m_len) {
369                         /* body is continuous */
370                         sp = mtod(s, u_int8_t *) + sn;
371                 } else {
372                         /* body is non-continuous */
373                         m_copydata(s, sn, blocklen, (caddr_t)sbuf);
374                         sp = sbuf;
375                 }
376
377                 /* destination */
378                 if (!d || dn + blocklen > d->m_len) {
379                         if (d)
380                                 dp = d;
381                         MGET(d, M_NOWAIT, MT_DATA);
382                         i = m->m_pkthdr.len - (soff + sn);
383                         if (d && i > MLEN) {
384                                 MCLGET(d, M_NOWAIT);
385                                 if ((d->m_flags & M_EXT) == 0) {
386                                         m_free(d);
387                                         d = NULL;
388                                 }
389                         }
390                         if (!d) {
391                                 m_freem(m);
392                                 if (d0)
393                                         m_freem(d0);
394                                 return ENOBUFS;
395                         }
396                         if (!d0)
397                                 d0 = d;
398                         if (dp)
399                                 dp->m_next = d;
400                         d->m_len = 0;
401                         d->m_len = (M_TRAILINGSPACE(d) / blocklen) * blocklen;
402                         if (d->m_len > i)
403                                 d->m_len = i;
404                         dn = 0;
405                 }
406
407                 /* put counter into counter block */
408                 cblock.v.ctr = htonl(ctr);
409
410                 /* setup keystream */
411                 ctx = (aesctr_ctx *)sav->sched;
412                 rijndaelEncrypt(ctx->r_ek, ctx->r_nr, cblock.cblock, keystream);
413
414                 bcopy(sp, mtod(d, u_int8_t *) + dn, blocklen);
415                 dst = mtod(d, u_int8_t *) + dn;
416                 for (i = 0; i < blocklen; i++)
417                         dst[i] ^= keystream[i];
418
419                 ctr++;
420
421                 sn += blocklen;
422                 dn += blocklen;
423
424                 /* find the next source block */
425                 while (s && sn >= s->m_len) {
426                         sn -= s->m_len;
427                         soff += s->m_len;
428                         s = s->m_next;
429                 }
430
431                 /* skip over empty mbuf */
432                 while (s && s->m_len == 0)
433                         s = s->m_next;
434         }
435
436         m_freem(scut->m_next);
437         scut->m_len = scutoff;
438         scut->m_next = d0;
439
440         /* just in case */
441         bzero(&cblock, sizeof(cblock));
442         bzero(keystream, sizeof(keystream));
443
444         key_sa_stir_iv(sav);
445
446         return 0;
447 }