diff options
author | ume <ume@FreeBSD.org> | 2003-10-13 14:57:41 +0000 |
---|---|---|
committer | ume <ume@FreeBSD.org> | 2003-10-13 14:57:41 +0000 |
commit | fd41336ef5a14e7f322b0a8af359c45ce03d4cc8 (patch) | |
tree | 2a5d44fe2e98a214421d56ae5f3dea9a73bd2413 /sys/netinet6/esp_aesctr.c | |
parent | b24bb74b9ee088f076e89f68095367a214f7cc1d (diff) | |
download | FreeBSD-src-fd41336ef5a14e7f322b0a8af359c45ce03d4cc8.zip FreeBSD-src-fd41336ef5a14e7f322b0a8af359c45ce03d4cc8.tar.gz |
- support AES counter mode for ESP.
- use size_t as return type of schedlen(), as there's no error
check needed.
- clear key schedule buffer before freeing.
Obtained from: KAME
Diffstat (limited to 'sys/netinet6/esp_aesctr.c')
-rw-r--r-- | sys/netinet6/esp_aesctr.c | 463 |
1 files changed, 463 insertions, 0 deletions
diff --git a/sys/netinet6/esp_aesctr.c b/sys/netinet6/esp_aesctr.c new file mode 100644 index 0000000..9fbd615 --- /dev/null +++ b/sys/netinet6/esp_aesctr.c @@ -0,0 +1,463 @@ +/* $KAME: esp_aesctr.c,v 1.2 2003/07/20 00:29:37 itojun Exp $ */ + +/* + * Copyright (C) 1995, 1996, 1997, 1998 and 2003 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <sys/syslog.h> +#include <sys/mbuf.h> + +#include <net/if.h> +#include <net/route.h> + +#include <netinet/in.h> + +#include <netinet6/ipsec.h> +#include <netinet6/esp.h> +#include <netinet6/esp_aesctr.h> + +#include <netkey/key.h> + +#include <crypto/rijndael/rijndael.h> + +#include <net/net_osdep.h> + +#define AES_BLOCKSIZE 16 + +#define NONCESIZE 4 +union cblock { + struct { + u_int8_t nonce[4]; + u_int8_t iv[16]; + u_int32_t ctr; + } v __attribute__((__packed__)); + u_int8_t cblock[16]; +}; + +typedef struct { + u_int32_t r_ek[(RIJNDAEL_MAXNR+1)*4]; + int r_nr; /* key-length-dependent number of rounds */ +} aesctr_ctx; + +int +esp_aesctr_mature(sav) + struct secasvar *sav; +{ + int keylen; + const struct esp_algorithm *algo; + + algo = esp_algorithm_lookup(sav->alg_enc); + if (!algo) { + ipseclog((LOG_ERR, + "esp_aeesctr_mature %s: unsupported algorithm.\n", + algo->name)); + return 1; + } + + keylen = sav->key_enc->sadb_key_bits; + if (keylen < algo->keymin || algo->keymax < keylen) { + ipseclog((LOG_ERR, + "esp_aesctr_mature %s: invalid key length %d.\n", + algo->name, sav->key_enc->sadb_key_bits)); + return 1; + } + + /* rijndael key + nonce */ + if (!(keylen == 128 + 32 || keylen == 192 + 32 || keylen == 256 + 32)) { + ipseclog((LOG_ERR, + "esp_aesctr_mature %s: invalid key length %d.\n", + algo->name, keylen)); + return 1; + } + + return 0; +} + +size_t +esp_aesctr_schedlen(algo) + const struct esp_algorithm *algo; +{ + + return sizeof(aesctr_ctx); +} + +int +esp_aesctr_schedule(algo, sav) + const struct esp_algorithm *algo; + struct secasvar *sav; +{ + aesctr_ctx *ctx; + int keylen; + + /* SA key = AES key + nonce */ + keylen = _KEYLEN(sav->key_enc) * 8 - NONCESIZE * 8; + + ctx = (aesctr_ctx *)sav->sched; + if ((ctx->r_nr = rijndaelKeySetupEnc(ctx->r_ek, + (char *)_KEYBUF(sav->key_enc), keylen)) == 0) + return -1; + return 0; +} + +int +esp_aesctr_decrypt(m, off, sav, algo, ivlen) + struct mbuf *m; + size_t off; + struct secasvar *sav; + const struct esp_algorithm *algo; + int ivlen; +{ + struct mbuf *s; + struct mbuf *d, *d0 = NULL, *dp; + int soff, doff; /* offset from the head of chain, to head of this mbuf */ + int sn, dn; /* offset from the head of the mbuf, to meat */ + size_t ivoff, bodyoff; + union cblock cblock; + u_int8_t keystream[AES_BLOCKSIZE], *nonce; + u_int32_t ctr; + u_int8_t *ivp; + u_int8_t sbuf[AES_BLOCKSIZE], *sp, *dst; + struct mbuf *scut; + int scutoff; + int i; + int blocklen; + aesctr_ctx *ctx; + + if (ivlen != sav->ivlen) { + ipseclog((LOG_ERR, "esp_aesctr_decrypt %s: " + "unsupported ivlen %d\n", algo->name, ivlen)); + goto fail; + } + + /* assumes blocklen == padbound */ + blocklen = algo->padbound; + + ivoff = off + sizeof(struct newesp); + bodyoff = off + sizeof(struct newesp) + ivlen; + + /* setup counter block */ + nonce = _KEYBUF(sav->key_enc) + _KEYLEN(sav->key_enc) - NONCESIZE; + bcopy(nonce, cblock.v.nonce, NONCESIZE); + m_copydata(m, ivoff, ivlen, cblock.v.iv); + ctr = 1; + + if (m->m_pkthdr.len < bodyoff) { + ipseclog((LOG_ERR, "esp_aesctr_decrypt %s: bad len %d/%lu\n", + algo->name, m->m_pkthdr.len, (unsigned long)bodyoff)); + goto fail; + } + if ((m->m_pkthdr.len - bodyoff) % blocklen) { + ipseclog((LOG_ERR, "esp_aesctr_decrypt %s: " + "payload length must be multiple of %d\n", + algo->name, blocklen)); + goto fail; + } + + s = m; + d = d0 = dp = NULL; + soff = doff = sn = dn = 0; + ivp = sp = NULL; + + /* skip bodyoff */ + while (soff < bodyoff) { + if (soff + s->m_len > bodyoff) { + sn = bodyoff - soff; + break; + } + + soff += s->m_len; + s = s->m_next; + } + scut = s; + scutoff = sn; + + /* skip over empty mbuf */ + while (s && s->m_len == 0) + s = s->m_next; + + while (soff < m->m_pkthdr.len) { + /* source */ + if (sn + blocklen <= s->m_len) { + /* body is continuous */ + sp = mtod(s, u_int8_t *) + sn; + } else { + /* body is non-continuous */ + m_copydata(s, sn, blocklen, (caddr_t)sbuf); + sp = sbuf; + } + + /* destination */ + if (!d || dn + blocklen > d->m_len) { + if (d) + dp = d; + MGET(d, M_DONTWAIT, MT_DATA); + i = m->m_pkthdr.len - (soff + sn); + if (d && i > MLEN) { + MCLGET(d, M_DONTWAIT); + if ((d->m_flags & M_EXT) == 0) { + m_free(d); + d = NULL; + } + } + if (!d) { + goto nomem; + } + if (!d0) + d0 = d; + if (dp) + dp->m_next = d; + d->m_len = 0; + d->m_len = (M_TRAILINGSPACE(d) / blocklen) * blocklen; + if (d->m_len > i) + d->m_len = i; + dn = 0; + } + + /* put counter into counter block */ + cblock.v.ctr = htonl(ctr); + + /* setup keystream */ + ctx = (aesctr_ctx *)sav->sched; + rijndaelEncrypt(ctx->r_ek, ctx->r_nr, cblock.cblock, keystream); + + bcopy(sp, mtod(d, u_int8_t *) + dn, blocklen); + dst = mtod(d, u_int8_t *) + dn; + for (i = 0; i < blocklen; i++) + dst[i] ^= keystream[i]; + + ctr++; + + sn += blocklen; + dn += blocklen; + + /* find the next source block */ + while (s && sn >= s->m_len) { + sn -= s->m_len; + soff += s->m_len; + s = s->m_next; + } + + /* skip over empty mbuf */ + while (s && s->m_len == 0) + s = s->m_next; + } + + m_freem(scut->m_next); + scut->m_len = scutoff; + scut->m_next = d0; + + /* just in case */ + bzero(&cblock, sizeof(cblock)); + bzero(keystream, sizeof(keystream)); + + return 0; + +fail: + m_freem(m); + if (d0) + m_freem(d0); + return EINVAL; + +nomem: + m_freem(m); + if (d0) + m_freem(d0); + return ENOBUFS; +} + +int +esp_aesctr_encrypt(m, off, plen, sav, algo, ivlen) + struct mbuf *m; + size_t off; + size_t plen; + struct secasvar *sav; + const struct esp_algorithm *algo; + int ivlen; +{ + struct mbuf *s; + struct mbuf *d, *d0, *dp; + int soff, doff; /* offset from the head of chain, to head of this mbuf */ + int sn, dn; /* offset from the head of the mbuf, to meat */ + size_t ivoff, bodyoff; + union cblock cblock; + u_int8_t keystream[AES_BLOCKSIZE], *nonce; + u_int32_t ctr; + u_int8_t sbuf[AES_BLOCKSIZE], *sp, *dst; + struct mbuf *scut; + int scutoff; + int i; + int blocklen; + aesctr_ctx *ctx; + + if (ivlen != sav->ivlen) { + ipseclog((LOG_ERR, "esp_aesctr_encrypt %s: " + "unsupported ivlen %d\n", algo->name, ivlen)); + m_freem(m); + return EINVAL; + } + + /* assumes blocklen == padbound */ + blocklen = algo->padbound; + + ivoff = off + sizeof(struct newesp); + bodyoff = off + sizeof(struct newesp) + ivlen; + + /* put iv into the packet. */ + /* maybe it is better to overwrite dest, not source */ + m_copyback(m, ivoff, ivlen, sav->iv); + + /* setup counter block */ + nonce = _KEYBUF(sav->key_enc) + _KEYLEN(sav->key_enc) - NONCESIZE; + bcopy(nonce, cblock.v.nonce, NONCESIZE); + m_copydata(m, ivoff, ivlen, cblock.v.iv); + ctr = 1; + + if (m->m_pkthdr.len < bodyoff) { + ipseclog((LOG_ERR, "esp_aesctr_encrypt %s: bad len %d/%lu\n", + algo->name, m->m_pkthdr.len, (unsigned long)bodyoff)); + m_freem(m); + return EINVAL; + } + if ((m->m_pkthdr.len - bodyoff) % blocklen) { + ipseclog((LOG_ERR, "esp_aesctr_encrypt %s: " + "payload length must be multiple of %lu\n", + algo->name, (unsigned long)algo->padbound)); + m_freem(m); + return EINVAL; + } + + s = m; + d = d0 = dp = NULL; + soff = doff = sn = dn = 0; + sp = NULL; + + /* skip bodyoff */ + while (soff < bodyoff) { + if (soff + s->m_len > bodyoff) { + sn = bodyoff - soff; + break; + } + + soff += s->m_len; + s = s->m_next; + } + scut = s; + scutoff = sn; + + /* skip over empty mbuf */ + while (s && s->m_len == 0) + s = s->m_next; + + while (soff < m->m_pkthdr.len) { + /* source */ + if (sn + blocklen <= s->m_len) { + /* body is continuous */ + sp = mtod(s, u_int8_t *) + sn; + } else { + /* body is non-continuous */ + m_copydata(s, sn, blocklen, (caddr_t)sbuf); + sp = sbuf; + } + + /* destination */ + if (!d || dn + blocklen > d->m_len) { + if (d) + dp = d; + MGET(d, M_DONTWAIT, MT_DATA); + i = m->m_pkthdr.len - (soff + sn); + if (d && i > MLEN) { + MCLGET(d, M_DONTWAIT); + if ((d->m_flags & M_EXT) == 0) { + m_free(d); + d = NULL; + } + } + if (!d) { + m_freem(m); + if (d0) + m_freem(d0); + return ENOBUFS; + } + if (!d0) + d0 = d; + if (dp) + dp->m_next = d; + d->m_len = 0; + d->m_len = (M_TRAILINGSPACE(d) / blocklen) * blocklen; + if (d->m_len > i) + d->m_len = i; + dn = 0; + } + + /* put counter into counter block */ + cblock.v.ctr = htonl(ctr); + + /* setup keystream */ + ctx = (aesctr_ctx *)sav->sched; + rijndaelEncrypt(ctx->r_ek, ctx->r_nr, cblock.cblock, keystream); + + bcopy(sp, mtod(d, u_int8_t *) + dn, blocklen); + dst = mtod(d, u_int8_t *) + dn; + for (i = 0; i < blocklen; i++) + dst[i] ^= keystream[i]; + + ctr++; + + sn += blocklen; + dn += blocklen; + + /* find the next source block */ + while (s && sn >= s->m_len) { + sn -= s->m_len; + soff += s->m_len; + s = s->m_next; + } + + /* skip over empty mbuf */ + while (s && s->m_len == 0) + s = s->m_next; + } + + m_freem(scut->m_next); + scut->m_len = scutoff; + scut->m_next = d0; + + /* just in case */ + bzero(&cblock, sizeof(cblock)); + bzero(keystream, sizeof(keystream)); + + key_sa_stir_iv(sav); + + return 0; +} |