1 /* $KAME: esp_aesctr.c,v 1.2 2003/07/20 00:29:37 itojun Exp $ */
4 * Copyright (C) 1995, 1996, 1997, 1998 and 2003 WIDE Project.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
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>
42 #include <net/route.h>
44 #include <netinet/in.h>
46 #include <netinet6/ipsec.h>
47 #include <netinet6/esp.h>
48 #include <netinet6/esp_aesctr.h>
50 #include <netkey/key.h>
52 #include <crypto/rijndael/rijndael.h>
54 #include <net/net_osdep.h>
56 #define AES_BLOCKSIZE 16
64 } v __attribute__((__packed__));
69 u_int32_t r_ek[(RIJNDAEL_MAXNR+1)*4];
70 int r_nr; /* key-length-dependent number of rounds */
74 esp_aesctr_mature(sav)
78 const struct esp_algorithm *algo;
80 algo = esp_algorithm_lookup(sav->alg_enc);
83 "esp_aeesctr_mature %s: unsupported algorithm.\n",
88 keylen = sav->key_enc->sadb_key_bits;
89 if (keylen < algo->keymin || algo->keymax < keylen) {
91 "esp_aesctr_mature %s: invalid key length %d.\n",
92 algo->name, sav->key_enc->sadb_key_bits));
96 /* rijndael key + nonce */
97 if (!(keylen == 128 + 32 || keylen == 192 + 32 || keylen == 256 + 32)) {
99 "esp_aesctr_mature %s: invalid key length %d.\n",
100 algo->name, keylen));
108 esp_aesctr_schedlen(algo)
109 const struct esp_algorithm *algo;
112 return sizeof(aesctr_ctx);
116 esp_aesctr_schedule(algo, sav)
117 const struct esp_algorithm *algo;
118 struct secasvar *sav;
123 /* SA key = AES key + nonce */
124 keylen = _KEYLEN(sav->key_enc) * 8 - NONCESIZE * 8;
126 ctx = (aesctr_ctx *)sav->sched;
127 if ((ctx->r_nr = rijndaelKeySetupEnc(ctx->r_ek,
128 (char *)_KEYBUF(sav->key_enc), keylen)) == 0)
134 esp_aesctr_decrypt(m, off, sav, algo, ivlen)
137 struct secasvar *sav;
138 const struct esp_algorithm *algo;
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;
147 u_int8_t keystream[AES_BLOCKSIZE], *nonce;
150 u_int8_t sbuf[AES_BLOCKSIZE], *sp, *dst;
157 if (ivlen != sav->ivlen) {
158 ipseclog((LOG_ERR, "esp_aesctr_decrypt %s: "
159 "unsupported ivlen %d\n", algo->name, ivlen));
163 /* assumes blocklen == padbound */
164 blocklen = algo->padbound;
166 ivoff = off + sizeof(struct newesp);
167 bodyoff = off + sizeof(struct newesp) + ivlen;
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);
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));
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));
189 soff = doff = sn = dn = 0;
193 while (soff < bodyoff) {
194 if (soff + s->m_len > bodyoff) {
205 /* skip over empty mbuf */
206 while (s && s->m_len == 0)
209 while (soff < m->m_pkthdr.len) {
211 if (sn + blocklen <= s->m_len) {
212 /* body is continuous */
213 sp = mtod(s, u_int8_t *) + sn;
215 /* body is non-continuous */
216 m_copydata(s, sn, blocklen, (caddr_t)sbuf);
221 if (!d || dn + blocklen > d->m_len) {
224 MGET(d, M_DONTWAIT, MT_DATA);
225 i = m->m_pkthdr.len - (soff + sn);
227 MCLGET(d, M_DONTWAIT);
228 if ((d->m_flags & M_EXT) == 0) {
241 d->m_len = (M_TRAILINGSPACE(d) / blocklen) * blocklen;
247 /* put counter into counter block */
248 cblock.v.ctr = htonl(ctr);
250 /* setup keystream */
251 ctx = (aesctr_ctx *)sav->sched;
252 rijndaelEncrypt(ctx->r_ek, ctx->r_nr, cblock.cblock, keystream);
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];
264 /* find the next source block */
265 while (s && sn >= s->m_len) {
271 /* skip over empty mbuf */
272 while (s && s->m_len == 0)
276 m_freem(scut->m_next);
277 scut->m_len = scutoff;
281 bzero(&cblock, sizeof(cblock));
282 bzero(keystream, sizeof(keystream));
300 esp_aesctr_encrypt(m, off, plen, sav, algo, ivlen)
304 struct secasvar *sav;
305 const struct esp_algorithm *algo;
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;
314 u_int8_t keystream[AES_BLOCKSIZE], *nonce;
316 u_int8_t sbuf[AES_BLOCKSIZE], *sp, *dst;
323 if (ivlen != sav->ivlen) {
324 ipseclog((LOG_ERR, "esp_aesctr_encrypt %s: "
325 "unsupported ivlen %d\n", algo->name, ivlen));
330 /* assumes blocklen == padbound */
331 blocklen = algo->padbound;
333 ivoff = off + sizeof(struct newesp);
334 bodyoff = off + sizeof(struct newesp) + ivlen;
336 /* put iv into the packet. */
337 /* maybe it is better to overwrite dest, not source */
338 m_copyback(m, ivoff, ivlen, sav->iv);
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);
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));
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));
362 soff = doff = sn = dn = 0;
366 while (soff < bodyoff) {
367 if (soff + s->m_len > bodyoff) {
378 /* skip over empty mbuf */
379 while (s && s->m_len == 0)
382 while (soff < m->m_pkthdr.len) {
384 if (sn + blocklen <= s->m_len) {
385 /* body is continuous */
386 sp = mtod(s, u_int8_t *) + sn;
388 /* body is non-continuous */
389 m_copydata(s, sn, blocklen, (caddr_t)sbuf);
394 if (!d || dn + blocklen > d->m_len) {
397 MGET(d, M_DONTWAIT, MT_DATA);
398 i = m->m_pkthdr.len - (soff + sn);
400 MCLGET(d, M_DONTWAIT);
401 if ((d->m_flags & M_EXT) == 0) {
417 d->m_len = (M_TRAILINGSPACE(d) / blocklen) * blocklen;
423 /* put counter into counter block */
424 cblock.v.ctr = htonl(ctr);
426 /* setup keystream */
427 ctx = (aesctr_ctx *)sav->sched;
428 rijndaelEncrypt(ctx->r_ek, ctx->r_nr, cblock.cblock, keystream);
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];
440 /* find the next source block */
441 while (s && sn >= s->m_len) {
447 /* skip over empty mbuf */
448 while (s && s->m_len == 0)
452 m_freem(scut->m_next);
453 scut->m_len = scutoff;
457 bzero(&cblock, sizeof(cblock));
458 bzero(keystream, sizeof(keystream));