#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/domain.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/syslog.h>
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/in_var.h>
#if INET6
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#include <netinet/icmp6.h>
#endif
#include <netinet6/ipsec.h>
#if INET6
#include <netinet6/ipsec6.h>
#endif
#include <netinet6/ah.h>
#if INET6
#include <netinet6/ah6.h>
#endif
#if IPSEC_ESP
#include <netinet6/esp.h>
#if INET6
#include <netinet6/esp6.h>
#endif
#endif
#include <net/pfkeyv2.h>
#include <netkey/keydb.h>
#include <libkern/crypto/md5.h>
#include <libkern/crypto/sha1.h>
#include <libkern/crypto/sha2.h>
#include <net/net_osdep.h>
#define HMACSIZE 16
static int ah_sumsiz_1216(struct secasvar *);
static int ah_sumsiz_zero(struct secasvar *);
static int ah_none_mature(struct secasvar *);
static int ah_none_init(struct ah_algorithm_state *, struct secasvar *);
static void ah_none_loop(struct ah_algorithm_state *, caddr_t, size_t);
static void ah_none_result(struct ah_algorithm_state *, caddr_t, size_t);
static int ah_keyed_md5_mature(struct secasvar *);
static int ah_keyed_md5_init(struct ah_algorithm_state *, struct secasvar *);
static void ah_keyed_md5_loop(struct ah_algorithm_state *, caddr_t, size_t);
static void ah_keyed_md5_result(struct ah_algorithm_state *, caddr_t, size_t);
static int ah_keyed_sha1_mature(struct secasvar *);
static int ah_keyed_sha1_init(struct ah_algorithm_state *, struct secasvar *);
static void ah_keyed_sha1_loop(struct ah_algorithm_state *, caddr_t, size_t);
static void ah_keyed_sha1_result(struct ah_algorithm_state *, caddr_t, size_t);
static int ah_hmac_md5_mature(struct secasvar *);
static int ah_hmac_md5_init(struct ah_algorithm_state *, struct secasvar *);
static void ah_hmac_md5_loop(struct ah_algorithm_state *, caddr_t, size_t);
static void ah_hmac_md5_result(struct ah_algorithm_state *, caddr_t, size_t);
static int ah_hmac_sha1_mature(struct secasvar *);
static int ah_hmac_sha1_init(struct ah_algorithm_state *, struct secasvar *);
static void ah_hmac_sha1_loop(struct ah_algorithm_state *, caddr_t, size_t);
static void ah_hmac_sha1_result(struct ah_algorithm_state *, caddr_t, size_t);
#if ALLCRYPTO
static int ah_sumsiz_sha2_256(struct secasvar *);
static int ah_hmac_sha2_256_mature(struct secasvar *);
static int ah_hmac_sha2_256_init(struct ah_algorithm_state *,
struct secasvar *);
static void ah_hmac_sha2_256_loop(struct ah_algorithm_state *, caddr_t, size_t);
static void ah_hmac_sha2_256_result(struct ah_algorithm_state *, caddr_t, size_t);
static int ah_sumsiz_sha2_384(struct secasvar *);
static int ah_hmac_sha2_384_mature(struct secasvar *);
static int ah_hmac_sha2_384_init(struct ah_algorithm_state *,
struct secasvar *);
static void ah_hmac_sha2_384_loop(struct ah_algorithm_state *, caddr_t, size_t);
static void ah_hmac_sha2_384_result(struct ah_algorithm_state *, caddr_t, size_t);
static int ah_sumsiz_sha2_512(struct secasvar *);
static int ah_hmac_sha2_512_mature(struct secasvar *);
static int ah_hmac_sha2_512_init(struct ah_algorithm_state *,
struct secasvar *);
static void ah_hmac_sha2_512_loop(struct ah_algorithm_state *, caddr_t, size_t);
static void ah_hmac_sha2_512_result(struct ah_algorithm_state *, caddr_t, size_t);
#endif
static void ah_update_mbuf(struct mbuf *, int, int,
const struct ah_algorithm *, struct ah_algorithm_state *);
const struct ah_algorithm *
ah_algorithm_lookup(idx)
int idx;
{
static struct ah_algorithm hmac_md5 =
{ ah_sumsiz_1216, ah_hmac_md5_mature, 128, 128, "hmac-md5",
ah_hmac_md5_init, ah_hmac_md5_loop,
ah_hmac_md5_result, };
static struct ah_algorithm keyed_md5 =
{ ah_sumsiz_1216, ah_keyed_md5_mature, 128, 128, "keyed-md5",
ah_keyed_md5_init, ah_keyed_md5_loop,
ah_keyed_md5_result, };
static struct ah_algorithm hmac_sha1 =
{ ah_sumsiz_1216, ah_hmac_sha1_mature, 160, 160, "hmac-sha1",
ah_hmac_sha1_init, ah_hmac_sha1_loop,
ah_hmac_sha1_result, };
static struct ah_algorithm keyed_sha1 =
{ ah_sumsiz_1216, ah_keyed_sha1_mature, 160, 160, "keyed-sha1",
ah_keyed_sha1_init, ah_keyed_sha1_loop,
ah_keyed_sha1_result, };
static struct ah_algorithm ah_none =
{ ah_sumsiz_zero, ah_none_mature, 0, 2048, "none",
ah_none_init, ah_none_loop, ah_none_result, };
#if ALLCRYPTO
static struct ah_algorithm hmac_sha2_256 =
{ ah_sumsiz_sha2_256, ah_hmac_sha2_256_mature, 256, 256,
"hmac-sha2-256",
ah_hmac_sha2_256_init, ah_hmac_sha2_256_loop,
ah_hmac_sha2_256_result, };
static struct ah_algorithm hmac_sha2_384 =
{ ah_sumsiz_sha2_384, ah_hmac_sha2_384_mature, 384, 384,
"hmac-sha2-384",
ah_hmac_sha2_384_init, ah_hmac_sha2_384_loop,
ah_hmac_sha2_384_result, };
static struct ah_algorithm hmac_sha2_512 =
{ ah_sumsiz_sha2_512, ah_hmac_sha2_512_mature, 512, 512,
"hmac-sha2-512",
ah_hmac_sha2_512_init, ah_hmac_sha2_512_loop,
ah_hmac_sha2_512_result, };
#endif
switch (idx) {
case SADB_AALG_MD5HMAC:
return &hmac_md5;
case SADB_AALG_SHA1HMAC:
return &hmac_sha1;
case SADB_X_AALG_MD5:
return &keyed_md5;
case SADB_X_AALG_SHA:
return &keyed_sha1;
case SADB_X_AALG_NULL:
return &ah_none;
#if ALLCRYPTO
case SADB_X_AALG_SHA2_256:
return &hmac_sha2_256;
case SADB_X_AALG_SHA2_384:
return &hmac_sha2_384;
case SADB_X_AALG_SHA2_512:
return &hmac_sha2_512;
#endif
default:
return NULL;
}
}
static int
ah_sumsiz_1216(sav)
struct secasvar *sav;
{
if (!sav)
return -1;
if (sav->flags & SADB_X_EXT_OLD)
return 16;
else
return 12;
}
static int
ah_sumsiz_zero(sav)
struct secasvar *sav;
{
if (!sav)
return -1;
return 0;
}
static int
ah_none_mature(sav)
struct secasvar *sav;
{
if (sav->sah->saidx.proto == IPPROTO_AH) {
ipseclog((LOG_ERR,
"ah_none_mature: protocol and algorithm mismatch.\n"));
return 1;
}
return 0;
}
static int
ah_none_init(
struct ah_algorithm_state *state,
__unused struct secasvar *sav)
{
state->foo = NULL;
return 0;
}
static void
ah_none_loop(
__unused struct ah_algorithm_state *state,
__unused caddr_t addr,
__unused size_t len)
{
}
static void
ah_none_result(
__unused struct ah_algorithm_state *state,
__unused caddr_t addr,
__unused size_t l)
{
}
static int
ah_keyed_md5_mature(
__unused struct secasvar *sav)
{
return 0;
}
static int
ah_keyed_md5_init(state, sav)
struct ah_algorithm_state *state;
struct secasvar *sav;
{
size_t padlen;
size_t keybitlen;
u_int8_t buf[32] __attribute__((aligned(4)));
if (!state)
panic("ah_keyed_md5_init: what?");
state->sav = sav;
state->foo = (void *)_MALLOC(sizeof(MD5_CTX), M_TEMP, M_NOWAIT);
if (state->foo == NULL)
return ENOBUFS;
MD5Init((MD5_CTX *)state->foo);
if (state->sav) {
MD5Update((MD5_CTX *)state->foo,
(u_int8_t *)_KEYBUF(state->sav->key_auth),
(u_int)_KEYLEN(state->sav->key_auth));
if (_KEYLEN(state->sav->key_auth) < 56)
padlen = 64 - 8 - _KEYLEN(state->sav->key_auth);
else
padlen = 64 + 64 - 8 - _KEYLEN(state->sav->key_auth);
keybitlen = _KEYLEN(state->sav->key_auth);
keybitlen *= 8;
buf[0] = 0x80;
MD5Update((MD5_CTX *)state->foo, &buf[0], 1);
padlen--;
bzero(buf, sizeof(buf));
while (sizeof(buf) < padlen) {
MD5Update((MD5_CTX *)state->foo, &buf[0], sizeof(buf));
padlen -= sizeof(buf);
}
if (padlen) {
MD5Update((MD5_CTX *)state->foo, &buf[0], padlen);
}
buf[0] = (keybitlen >> 0) & 0xff;
buf[1] = (keybitlen >> 8) & 0xff;
buf[2] = (keybitlen >> 16) & 0xff;
buf[3] = (keybitlen >> 24) & 0xff;
MD5Update((MD5_CTX *)state->foo, buf, 8);
}
return 0;
}
static void
ah_keyed_md5_loop(state, addr, len)
struct ah_algorithm_state *state;
caddr_t addr;
size_t len;
{
if (!state)
panic("ah_keyed_md5_loop: what?");
MD5Update((MD5_CTX *)state->foo, addr, len);
}
static void
ah_keyed_md5_result(state, addr, l)
struct ah_algorithm_state *state;
caddr_t addr;
size_t l;
{
u_char digest[16] __attribute__((aligned(4)));
if (!state)
panic("ah_keyed_md5_result: what?");
if (state->sav) {
MD5Update((MD5_CTX *)state->foo,
(u_int8_t *)_KEYBUF(state->sav->key_auth),
(u_int)_KEYLEN(state->sav->key_auth));
}
MD5Final(&digest[0], (MD5_CTX *)state->foo);
FREE(state->foo, M_TEMP);
bcopy(&digest[0], (void *)addr, sizeof(digest) > l ? l : sizeof(digest));
}
static int
ah_keyed_sha1_mature(sav)
struct secasvar *sav;
{
const struct ah_algorithm *algo;
if (!sav->key_auth) {
ipseclog((LOG_ERR, "ah_keyed_sha1_mature: no key is given.\n"));
return 1;
}
algo = ah_algorithm_lookup(sav->alg_auth);
if (!algo) {
ipseclog((LOG_ERR, "ah_keyed_sha1_mature: unsupported algorithm.\n"));
return 1;
}
if (sav->key_auth->sadb_key_bits < algo->keymin
|| algo->keymax < sav->key_auth->sadb_key_bits) {
ipseclog((LOG_ERR,
"ah_keyed_sha1_mature: invalid key length %d.\n",
sav->key_auth->sadb_key_bits));
return 1;
}
return 0;
}
static int
ah_keyed_sha1_init(state, sav)
struct ah_algorithm_state *state;
struct secasvar *sav;
{
SHA1_CTX *ctxt;
size_t padlen;
size_t keybitlen;
u_int8_t buf[32] __attribute__((aligned(4)));
if (!state)
panic("ah_keyed_sha1_init: what?");
state->sav = sav;
state->foo = (void *)_MALLOC(sizeof(SHA1_CTX), M_TEMP, M_NOWAIT);
if (!state->foo)
return ENOBUFS;
ctxt = (SHA1_CTX *)state->foo;
SHA1Init(ctxt);
if (state->sav) {
SHA1Update(ctxt, (u_int8_t *)_KEYBUF(state->sav->key_auth),
(u_int)_KEYLEN(state->sav->key_auth));
if (_KEYLEN(state->sav->key_auth) < 56)
padlen = 64 - 8 - _KEYLEN(state->sav->key_auth);
else
padlen = 64 + 64 - 8 - _KEYLEN(state->sav->key_auth);
keybitlen = _KEYLEN(state->sav->key_auth);
keybitlen *= 8;
buf[0] = 0x80;
SHA1Update(ctxt, &buf[0], 1);
padlen--;
bzero(buf, sizeof(buf));
while (sizeof(buf) < padlen) {
SHA1Update(ctxt, &buf[0], sizeof(buf));
padlen -= sizeof(buf);
}
if (padlen) {
SHA1Update(ctxt, &buf[0], padlen);
}
buf[0] = (keybitlen >> 0) & 0xff;
buf[1] = (keybitlen >> 8) & 0xff;
buf[2] = (keybitlen >> 16) & 0xff;
buf[3] = (keybitlen >> 24) & 0xff;
SHA1Update(ctxt, buf, 8);
}
return 0;
}
static void
ah_keyed_sha1_loop(state, addr, len)
struct ah_algorithm_state *state;
caddr_t addr;
size_t len;
{
SHA1_CTX *ctxt;
if (!state || !state->foo)
panic("ah_keyed_sha1_loop: what?");
ctxt = (SHA1_CTX *)state->foo;
SHA1Update(ctxt, (caddr_t)addr, (size_t)len);
}
static void
ah_keyed_sha1_result(state, addr, l)
struct ah_algorithm_state *state;
caddr_t addr;
size_t l;
{
u_char digest[SHA1_RESULTLEN] __attribute__((aligned(4)));
SHA1_CTX *ctxt;
if (!state || !state->foo)
panic("ah_keyed_sha1_result: what?");
ctxt = (SHA1_CTX *)state->foo;
if (state->sav) {
SHA1Update(ctxt, (u_int8_t *)_KEYBUF(state->sav->key_auth),
(u_int)_KEYLEN(state->sav->key_auth));
}
SHA1Final((caddr_t)&digest[0], ctxt);
bcopy(&digest[0], (void *)addr, sizeof(digest) > l ? l : sizeof(digest));
FREE(state->foo, M_TEMP);
}
static int
ah_hmac_md5_mature(sav)
struct secasvar *sav;
{
const struct ah_algorithm *algo;
if (!sav->key_auth) {
ipseclog((LOG_ERR, "ah_hmac_md5_mature: no key is given.\n"));
return 1;
}
algo = ah_algorithm_lookup(sav->alg_auth);
if (!algo) {
ipseclog((LOG_ERR, "ah_hmac_md5_mature: unsupported algorithm.\n"));
return 1;
}
if (sav->key_auth->sadb_key_bits < algo->keymin
|| algo->keymax < sav->key_auth->sadb_key_bits) {
ipseclog((LOG_ERR,
"ah_hmac_md5_mature: invalid key length %d.\n",
sav->key_auth->sadb_key_bits));
return 1;
}
return 0;
}
static int
ah_hmac_md5_init(state, sav)
struct ah_algorithm_state *state;
struct secasvar *sav;
{
u_char *ipad;
u_char *opad;
u_char tk[16] __attribute__((aligned(4)));
u_char *key;
size_t keylen;
size_t i;
MD5_CTX *ctxt;
if (!state)
panic("ah_hmac_md5_init: what?");
state->sav = sav;
state->foo = (void *)_MALLOC(64 + 64 + sizeof(MD5_CTX), M_TEMP, M_NOWAIT);
if (!state->foo)
return ENOBUFS;
ipad = (u_char *)state->foo;
opad = (u_char *)(ipad + 64);
ctxt = (MD5_CTX *)(void *)(opad + 64);
if (64 < _KEYLEN(state->sav->key_auth)) {
MD5Init(ctxt);
MD5Update(ctxt, _KEYBUF(state->sav->key_auth),
_KEYLEN(state->sav->key_auth));
MD5Final(&tk[0], ctxt);
key = &tk[0];
keylen = 16;
} else {
key = (u_char *) _KEYBUF(state->sav->key_auth);
keylen = _KEYLEN(state->sav->key_auth);
}
bzero(ipad, 64);
bzero(opad, 64);
bcopy(key, ipad, keylen);
bcopy(key, opad, keylen);
for (i = 0; i < 64; i++) {
ipad[i] ^= 0x36;
opad[i] ^= 0x5c;
}
MD5Init(ctxt);
MD5Update(ctxt, ipad, 64);
return 0;
}
static void
ah_hmac_md5_loop(state, addr, len)
struct ah_algorithm_state *state;
caddr_t addr;
size_t len;
{
MD5_CTX *ctxt;
if (!state || !state->foo)
panic("ah_hmac_md5_loop: what?");
ctxt = (MD5_CTX *)(void *)(((caddr_t)state->foo) + 128);
MD5Update(ctxt, addr, len);
}
static void
ah_hmac_md5_result(state, addr, l)
struct ah_algorithm_state *state;
caddr_t addr;
size_t l;
{
u_char digest[16] __attribute__((aligned(4)));
u_char *ipad;
u_char *opad;
MD5_CTX *ctxt;
if (!state || !state->foo)
panic("ah_hmac_md5_result: what?");
ipad = (u_char *)state->foo;
opad = (u_char *)(ipad + 64);
ctxt = (MD5_CTX *)(void *)(opad + 64);
MD5Final(&digest[0], ctxt);
MD5Init(ctxt);
MD5Update(ctxt, opad, 64);
MD5Update(ctxt, &digest[0], sizeof(digest));
MD5Final(&digest[0], ctxt);
bcopy(&digest[0], (void *)addr, sizeof(digest) > l ? l : sizeof(digest));
FREE(state->foo, M_TEMP);
}
static int
ah_hmac_sha1_mature(sav)
struct secasvar *sav;
{
const struct ah_algorithm *algo;
if (!sav->key_auth) {
ipseclog((LOG_ERR, "ah_hmac_sha1_mature: no key is given.\n"));
return 1;
}
algo = ah_algorithm_lookup(sav->alg_auth);
if (!algo) {
ipseclog((LOG_ERR, "ah_hmac_sha1_mature: unsupported algorithm.\n"));
return 1;
}
if (sav->key_auth->sadb_key_bits < algo->keymin
|| algo->keymax < sav->key_auth->sadb_key_bits) {
ipseclog((LOG_ERR,
"ah_hmac_sha1_mature: invalid key length %d.\n",
sav->key_auth->sadb_key_bits));
return 1;
}
return 0;
}
static int
ah_hmac_sha1_init(state, sav)
struct ah_algorithm_state *state;
struct secasvar *sav;
{
u_char *ipad;
u_char *opad;
SHA1_CTX *ctxt;
u_char tk[SHA1_RESULTLEN] __attribute__((aligned(4)));
u_char *key;
size_t keylen;
size_t i;
if (!state)
panic("ah_hmac_sha1_init: what?");
state->sav = sav;
state->foo = (void *)_MALLOC(64 + 64 + sizeof(SHA1_CTX),
M_TEMP, M_NOWAIT);
if (!state->foo)
return ENOBUFS;
ipad = (u_char *)state->foo;
opad = (u_char *)(ipad + 64);
ctxt = (SHA1_CTX *)(void *)(opad + 64);
if (64 < _KEYLEN(state->sav->key_auth)) {
SHA1Init(ctxt);
SHA1Update(ctxt, _KEYBUF(state->sav->key_auth),
_KEYLEN(state->sav->key_auth));
SHA1Final(&tk[0], ctxt);
key = &tk[0];
keylen = SHA1_RESULTLEN;
} else {
key = (u_char *) _KEYBUF(state->sav->key_auth);
keylen = _KEYLEN(state->sav->key_auth);
}
bzero(ipad, 64);
bzero(opad, 64);
bcopy(key, ipad, keylen);
bcopy(key, opad, keylen);
for (i = 0; i < 64; i++) {
ipad[i] ^= 0x36;
opad[i] ^= 0x5c;
}
SHA1Init(ctxt);
SHA1Update(ctxt, ipad, 64);
return 0;
}
static void
ah_hmac_sha1_loop(state, addr, len)
struct ah_algorithm_state *state;
caddr_t addr;
size_t len;
{
SHA1_CTX *ctxt;
if (!state || !state->foo)
panic("ah_hmac_sha1_loop: what?");
ctxt = (SHA1_CTX *)(void *)(((u_char *)state->foo) + 128);
SHA1Update(ctxt, (caddr_t)addr, (size_t)len);
}
static void
ah_hmac_sha1_result(state, addr, l)
struct ah_algorithm_state *state;
caddr_t addr;
size_t l;
{
u_char digest[SHA1_RESULTLEN] __attribute__((aligned(4)));
u_char *ipad;
u_char *opad;
SHA1_CTX *ctxt;
if (!state || !state->foo)
panic("ah_hmac_sha1_result: what?");
ipad = (u_char *)state->foo;
opad = (u_char *)(ipad + 64);
ctxt = (SHA1_CTX *)(void *)(opad + 64);
SHA1Final((caddr_t)&digest[0], ctxt);
SHA1Init(ctxt);
SHA1Update(ctxt, opad, 64);
SHA1Update(ctxt, (caddr_t)&digest[0], sizeof(digest));
SHA1Final((caddr_t)&digest[0], ctxt);
bcopy(&digest[0], (void *)addr, sizeof(digest) > l ? l : sizeof(digest));
FREE(state->foo, M_TEMP);
}
#if ALLCRYPTO
static int
ah_sumsiz_sha2_256(sav)
struct secasvar *sav;
{
if (!sav)
return -1;
return 16; }
static int
ah_hmac_sha2_256_mature(sav)
struct secasvar *sav;
{
const struct ah_algorithm *algo;
if (!sav->key_auth) {
ipseclog((LOG_ERR,
"ah_hmac_sha2_256_mature: no key is given.\n"));
return 1;
}
algo = ah_algorithm_lookup(sav->alg_auth);
if (!algo) {
ipseclog((LOG_ERR,
"ah_hmac_sha2_256_mature: unsupported algorithm.\n"));
return 1;
}
if (sav->key_auth->sadb_key_bits < algo->keymin ||
algo->keymax < sav->key_auth->sadb_key_bits) {
ipseclog((LOG_ERR,
"ah_hmac_sha2_256_mature: invalid key length %d.\n",
sav->key_auth->sadb_key_bits));
return 1;
}
return 0;
}
static int
ah_hmac_sha2_256_init(state, sav)
struct ah_algorithm_state *state;
struct secasvar *sav;
{
u_char *ipad;
u_char *opad;
SHA256_CTX *ctxt;
u_char tk[SHA256_DIGEST_LENGTH] __attribute__((aligned(4)));
u_char *key;
size_t keylen;
size_t i;
if (!state)
panic("ah_hmac_sha2_256_init: what?");
state->sav = sav;
state->foo = (void *)_MALLOC(64 + 64 + sizeof(SHA256_CTX),
M_TEMP, M_NOWAIT);
if (!state->foo)
return ENOBUFS;
ipad = (u_char *)state->foo;
opad = (u_char *)(ipad + 64);
ctxt = (SHA256_CTX *)(void *)(opad + 64);
if (64 < _KEYLEN(state->sav->key_auth)) {
bzero(tk, sizeof(tk));
bzero(ctxt, sizeof(*ctxt));
SHA256_Init(ctxt);
SHA256_Update(ctxt, (const u_int8_t *) _KEYBUF(state->sav->key_auth),
_KEYLEN(state->sav->key_auth));
SHA256_Final(&tk[0], ctxt);
key = &tk[0];
keylen = sizeof(tk) < 64 ? sizeof(tk) : 64;
} else {
key = (u_char *) _KEYBUF(state->sav->key_auth);
keylen = _KEYLEN(state->sav->key_auth);
}
bzero(ipad, 64);
bzero(opad, 64);
bcopy(key, ipad, keylen);
bcopy(key, opad, keylen);
for (i = 0; i < 64; i++) {
ipad[i] ^= 0x36;
opad[i] ^= 0x5c;
}
bzero(ctxt, sizeof(*ctxt));
SHA256_Init(ctxt);
SHA256_Update(ctxt, ipad, 64);
return 0;
}
static void
ah_hmac_sha2_256_loop(state, addr, len)
struct ah_algorithm_state *state;
caddr_t addr;
size_t len;
{
SHA256_CTX *ctxt;
if (!state || !state->foo)
panic("ah_hmac_sha2_256_loop: what?");
ctxt = (SHA256_CTX *)(void *)(((u_char *)state->foo) + 128);
SHA256_Update(ctxt, (const u_int8_t *)addr, (size_t)len);
}
static void
ah_hmac_sha2_256_result(state, addr, l)
struct ah_algorithm_state *state;
caddr_t addr;
size_t l;
{
u_char digest[SHA256_DIGEST_LENGTH] __attribute__((aligned(4)));
u_char *ipad;
u_char *opad;
SHA256_CTX *ctxt;
if (!state || !state->foo)
panic("ah_hmac_sha2_256_result: what?");
ipad = (u_char *)state->foo;
opad = (u_char *)(ipad + 64);
ctxt = (SHA256_CTX *)(void *)(opad + 64);
SHA256_Final((u_int8_t *)digest, ctxt);
SHA256_Init(ctxt);
SHA256_Update(ctxt, opad, 64);
SHA256_Update(ctxt, (const u_int8_t *)digest, sizeof(digest));
SHA256_Final((u_int8_t *)digest, ctxt);
bcopy(&digest[0], (void *)addr, sizeof(digest) > l ? l : sizeof(digest));
FREE(state->foo, M_TEMP);
}
static int
ah_sumsiz_sha2_384(sav)
struct secasvar *sav;
{
if (!sav)
return -1;
return 24; }
static int
ah_hmac_sha2_384_mature(sav)
struct secasvar *sav;
{
const struct ah_algorithm *algo;
if (!sav->key_auth) {
ipseclog((LOG_ERR,
"ah_hmac_sha2_384_mature: no key is given.\n"));
return 1;
}
algo = ah_algorithm_lookup(sav->alg_auth);
if (!algo) {
ipseclog((LOG_ERR,
"ah_hmac_sha2_384_mature: unsupported algorithm.\n"));
return 1;
}
if (sav->key_auth->sadb_key_bits < algo->keymin ||
algo->keymax < sav->key_auth->sadb_key_bits) {
ipseclog((LOG_ERR,
"ah_hmac_sha2_384_mature: invalid key length %d.\n",
sav->key_auth->sadb_key_bits));
return 1;
}
return 0;
}
static int
ah_hmac_sha2_384_init(state, sav)
struct ah_algorithm_state *state;
struct secasvar *sav;
{
u_char *ipad;
u_char *opad;
SHA384_CTX *ctxt;
u_char tk[SHA384_DIGEST_LENGTH] __attribute__((aligned(4)));
u_char *key;
size_t keylen;
size_t i;
if (!state)
panic("ah_hmac_sha2_384_init: what?");
state->sav = sav;
state->foo = (void *)_MALLOC(128 + 128 + sizeof(SHA384_CTX),
M_TEMP, M_NOWAIT);
if (!state->foo)
return ENOBUFS;
bzero(state->foo, 128 + 128 + sizeof(SHA384_CTX));
ipad = (u_char *)state->foo;
opad = (u_char *)(ipad + 128);
ctxt = (SHA384_CTX *)(void *)(opad + 128);
if (128 < _KEYLEN(state->sav->key_auth)) {
bzero(tk, sizeof(tk));
bzero(ctxt, sizeof(*ctxt));
SHA384_Init(ctxt);
SHA384_Update(ctxt, (const u_int8_t *) _KEYBUF(state->sav->key_auth),
_KEYLEN(state->sav->key_auth));
SHA384_Final(&tk[0], ctxt);
key = &tk[0];
keylen = sizeof(tk) < 128 ? sizeof(tk) : 128;
} else {
key = (u_char *) _KEYBUF(state->sav->key_auth);
keylen = _KEYLEN(state->sav->key_auth);
}
bzero(ipad, 128);
bzero(opad, 128);
bcopy(key, ipad, keylen);
bcopy(key, opad, keylen);
for (i = 0; i < 128; i++) {
ipad[i] ^= 0x36;
opad[i] ^= 0x5c;
}
bzero(ctxt, sizeof(*ctxt));
SHA384_Init(ctxt);
SHA384_Update(ctxt, ipad, 128);
return 0;
}
static void
ah_hmac_sha2_384_loop(state, addr, len)
struct ah_algorithm_state *state;
caddr_t addr;
size_t len;
{
SHA384_CTX *ctxt;
if (!state || !state->foo)
panic("ah_hmac_sha2_384_loop: what?");
ctxt = (SHA384_CTX *)(void *)(((u_char *)state->foo) + 256);
SHA384_Update(ctxt, (const u_int8_t *)addr, (size_t)len);
}
static void
ah_hmac_sha2_384_result(state, addr, l)
struct ah_algorithm_state *state;
caddr_t addr;
size_t l;
{
u_char digest[SHA384_DIGEST_LENGTH];
u_char *ipad;
u_char *opad;
SHA384_CTX *ctxt;
if (!state || !state->foo)
panic("ah_hmac_sha2_384_result: what?");
ipad = (u_char *)state->foo;
opad = (u_char *)(ipad + 128);
ctxt = (SHA384_CTX *)(void *)(opad + 128);
SHA384_Final((u_int8_t *)digest, ctxt);
SHA384_Init(ctxt);
SHA384_Update(ctxt, opad, 128);
SHA384_Update(ctxt, (const u_int8_t *)digest, sizeof(digest));
SHA384_Final((u_int8_t *)digest, ctxt);
bcopy(&digest[0], (void *)addr, sizeof(digest) > l ? l : sizeof(digest));
FREE(state->foo, M_TEMP);
}
static int
ah_sumsiz_sha2_512(sav)
struct secasvar *sav;
{
if (!sav)
return -1;
return 32; }
static int
ah_hmac_sha2_512_mature(sav)
struct secasvar *sav;
{
const struct ah_algorithm *algo;
if (!sav->key_auth) {
ipseclog((LOG_ERR,
"ah_hmac_sha2_512_mature: no key is given.\n"));
return 1;
}
algo = ah_algorithm_lookup(sav->alg_auth);
if (!algo) {
ipseclog((LOG_ERR,
"ah_hmac_sha2_512_mature: unsupported algorithm.\n"));
return 1;
}
if (sav->key_auth->sadb_key_bits < algo->keymin ||
algo->keymax < sav->key_auth->sadb_key_bits) {
ipseclog((LOG_ERR,
"ah_hmac_sha2_512_mature: invalid key length %d.\n",
sav->key_auth->sadb_key_bits));
return 1;
}
return 0;
}
static int
ah_hmac_sha2_512_init(state, sav)
struct ah_algorithm_state *state;
struct secasvar *sav;
{
u_char *ipad;
u_char *opad;
SHA512_CTX *ctxt;
u_char tk[SHA512_DIGEST_LENGTH] __attribute__((aligned(4)));
u_char *key;
size_t keylen;
size_t i;
if (!state)
panic("ah_hmac_sha2_512_init: what?");
state->sav = sav;
state->foo = (void *)_MALLOC(128 + 128 + sizeof(SHA512_CTX),
M_TEMP, M_NOWAIT);
if (!state->foo)
return ENOBUFS;
bzero(state->foo, 128 + 128 + sizeof(SHA512_CTX));
ipad = (u_char *)state->foo;
opad = (u_char *)(ipad + 128);
ctxt = (SHA512_CTX *)(void *)(opad + 128);
if (128 < _KEYLEN(state->sav->key_auth)) {
bzero(tk, sizeof(tk));
bzero(ctxt, sizeof(*ctxt));
SHA512_Init(ctxt);
SHA512_Update(ctxt, (const u_int8_t *) _KEYBUF(state->sav->key_auth),
_KEYLEN(state->sav->key_auth));
SHA512_Final(&tk[0], ctxt);
key = &tk[0];
keylen = sizeof(tk) < 128 ? sizeof(tk) : 128;
} else {
key = (u_char *) _KEYBUF(state->sav->key_auth);
keylen = _KEYLEN(state->sav->key_auth);
}
bzero(ipad, 128);
bzero(opad, 128);
bcopy(key, ipad, keylen);
bcopy(key, opad, keylen);
for (i = 0; i < 128; i++) {
ipad[i] ^= 0x36;
opad[i] ^= 0x5c;
}
bzero(ctxt, sizeof(*ctxt));
SHA512_Init(ctxt);
SHA512_Update(ctxt, ipad, 128);
return 0;
}
static void
ah_hmac_sha2_512_loop(state, addr, len)
struct ah_algorithm_state *state;
caddr_t addr;
size_t len;
{
SHA512_CTX *ctxt;
if (!state || !state->foo)
panic("ah_hmac_sha2_512_loop: what?");
ctxt = (SHA512_CTX *)(void *)(((u_char *)state->foo) + 256);
SHA512_Update(ctxt, (const u_int8_t *) addr, (size_t)len);
}
static void
ah_hmac_sha2_512_result(state, addr, l)
struct ah_algorithm_state *state;
caddr_t addr;
size_t l;
{
u_char digest[SHA512_DIGEST_LENGTH] __attribute__((aligned(4)));
u_char *ipad;
u_char *opad;
SHA512_CTX *ctxt;
if (!state || !state->foo)
panic("ah_hmac_sha2_512_result: what?");
ipad = (u_char *)state->foo;
opad = (u_char *)(ipad + 128);
ctxt = (SHA512_CTX *)(void *)(opad + 128);
SHA512_Final((u_int8_t *)digest, ctxt);
SHA512_Init(ctxt);
SHA512_Update(ctxt, opad, 128);
SHA512_Update(ctxt, (const u_int8_t *)digest, sizeof(digest));
SHA512_Final((u_int8_t *)digest, ctxt);
bcopy(&digest[0], (void *)addr, sizeof(digest) > l ? l : sizeof(digest));
FREE(state->foo, M_TEMP);
}
#endif
static void
ah_update_mbuf(m, off, len, algo, algos)
struct mbuf *m;
int off;
int len;
const struct ah_algorithm *algo;
struct ah_algorithm_state *algos;
{
struct mbuf *n;
int tlen;
if (off + len <= m->m_len) {
(algo->update)(algos, mtod(m, caddr_t) + off, len);
return;
}
for (n = m; n; n = n->m_next) {
if (off < n->m_len)
break;
off -= n->m_len;
}
if (!n)
panic("ah_update_mbuf: wrong offset specified");
for (; n && len > 0; n = n->m_next) {
if (n->m_len == 0)
continue;
if (n->m_len - off < len)
tlen = n->m_len - off;
else
tlen = len;
(algo->update)(algos, mtod(n, caddr_t) + off, tlen);
len -= tlen;
off = 0;
}
}
#if INET
int
ah4_calccksum(m, ahdat, len, algo, sav)
struct mbuf *m;
caddr_t ahdat;
size_t len;
const struct ah_algorithm *algo;
struct secasvar *sav;
{
int off;
int hdrtype;
size_t advancewidth;
struct ah_algorithm_state algos;
u_char sumbuf[AH_MAXSUMSIZE] __attribute__((aligned(4)));
int error = 0;
int ahseen;
struct mbuf *n = NULL;
if ((m->m_flags & M_PKTHDR) == 0)
return EINVAL;
ahseen = 0;
hdrtype = -1;
off = 0;
error = (algo->init)(&algos, sav);
if (error)
return error;
advancewidth = 0;
again:
switch (hdrtype) {
case -1:
{
struct ip iphdr;
size_t hlen;
m_copydata(m, off, sizeof(iphdr), (caddr_t)&iphdr);
#if _IP_VHL
hlen = IP_VHL_HL(iphdr.ip_vhl) << 2;
#else
hlen = iphdr.ip_hl << 2;
#endif
iphdr.ip_ttl = 0;
iphdr.ip_sum = htons(0);
if (ip4_ah_cleartos)
iphdr.ip_tos = 0;
iphdr.ip_off = htons(ntohs(iphdr.ip_off) & ip4_ah_offsetmask);
(algo->update)(&algos, (caddr_t)&iphdr, sizeof(struct ip));
if (hlen != sizeof(struct ip)) {
u_char *p;
int i, l, skip;
if (hlen > MCLBYTES) {
error = EMSGSIZE;
goto fail;
}
MGET(n, M_DONTWAIT, MT_DATA);
if (n && hlen > MLEN) {
MCLGET(n, M_DONTWAIT);
if ((n->m_flags & M_EXT) == 0) {
m_free(n);
n = NULL;
}
}
if (n == NULL) {
error = ENOBUFS;
goto fail;
}
m_copydata(m, off, hlen, mtod(n, caddr_t));
p = mtod(n, u_char *);
i = sizeof(struct ip);
while (i < hlen) {
if (i + IPOPT_OPTVAL >= hlen) {
ipseclog((LOG_ERR, "ah4_calccksum: "
"invalid IP option\n"));
error = EINVAL;
goto fail;
}
if (p[i + IPOPT_OPTVAL] == IPOPT_EOL ||
p[i + IPOPT_OPTVAL] == IPOPT_NOP ||
i + IPOPT_OLEN < hlen)
;
else {
ipseclog((LOG_ERR,
"ah4_calccksum: invalid IP option "
"(type=%02x)\n",
p[i + IPOPT_OPTVAL]));
error = EINVAL;
goto fail;
}
skip = 1;
switch (p[i + IPOPT_OPTVAL]) {
case IPOPT_EOL:
case IPOPT_NOP:
l = 1;
skip = 0;
break;
case IPOPT_SECURITY:
case 0x85:
case 0x86:
case 0x94:
case 0x95:
l = p[i + IPOPT_OLEN];
if (l < 2)
goto invalopt;
skip = 0;
break;
default:
l = p[i + IPOPT_OLEN];
if (l < 2)
goto invalopt;
skip = 1;
break;
}
if (l < 1 || hlen - i < l) {
invalopt:
ipseclog((LOG_ERR,
"ah4_calccksum: invalid IP option "
"(type=%02x len=%02x)\n",
p[i + IPOPT_OPTVAL],
p[i + IPOPT_OLEN]));
error = EINVAL;
goto fail;
}
if (skip)
bzero(p + i, l);
if (p[i + IPOPT_OPTVAL] == IPOPT_EOL)
break;
i += l;
}
p = mtod(n, u_char *) + sizeof(struct ip);
(algo->update)(&algos, (caddr_t)p, hlen - sizeof(struct ip));
m_free(n);
n = NULL;
}
hdrtype = (iphdr.ip_p) & 0xff;
advancewidth = hlen;
break;
}
case IPPROTO_AH:
{
struct ah ah;
int siz;
int hdrsiz;
int totlen;
m_copydata(m, off, sizeof(ah), (caddr_t)&ah);
hdrsiz = (sav->flags & SADB_X_EXT_OLD)
? sizeof(struct ah)
: sizeof(struct newah);
siz = (*algo->sumsiz)(sav);
totlen = (ah.ah_len + 2) << 2;
if (!ahseen) {
if (totlen > m->m_pkthdr.len - off ||
totlen > MCLBYTES) {
error = EMSGSIZE;
goto fail;
}
MGET(n, M_DONTWAIT, MT_DATA);
if (n && totlen > MLEN) {
MCLGET(n, M_DONTWAIT);
if ((n->m_flags & M_EXT) == 0) {
m_free(n);
n = NULL;
}
}
if (n == NULL) {
error = ENOBUFS;
goto fail;
}
m_copydata(m, off, totlen, mtod(n, caddr_t));
n->m_len = totlen;
bzero(mtod(n, caddr_t) + hdrsiz, siz);
(algo->update)(&algos, mtod(n, caddr_t), n->m_len);
m_free(n);
n = NULL;
} else
ah_update_mbuf(m, off, totlen, algo, &algos);
ahseen++;
hdrtype = ah.ah_nxt;
advancewidth = totlen;
break;
}
default:
ah_update_mbuf(m, off, m->m_pkthdr.len - off, algo, &algos);
advancewidth = m->m_pkthdr.len - off;
break;
}
off += advancewidth;
if (off < m->m_pkthdr.len)
goto again;
if (len < (*algo->sumsiz)(sav)) {
error = EINVAL;
goto fail;
}
(algo->result)(&algos, (caddr_t) &sumbuf[0], sizeof(sumbuf));
bcopy(&sumbuf[0], ahdat, (*algo->sumsiz)(sav));
if (n)
m_free(n);
return error;
fail:
if (n)
m_free(n);
return error;
}
#endif
#if INET6
int
ah6_calccksum(m, ahdat, len, algo, sav)
struct mbuf *m;
caddr_t ahdat;
size_t len;
const struct ah_algorithm *algo;
struct secasvar *sav;
{
int newoff, off;
int proto, nxt;
struct mbuf *n = NULL;
int error;
int ahseen;
struct ah_algorithm_state algos;
u_char sumbuf[AH_MAXSUMSIZE] __attribute__((aligned(4)));
if ((m->m_flags & M_PKTHDR) == 0)
return EINVAL;
error = (algo->init)(&algos, sav);
if (error)
return error;
off = 0;
proto = IPPROTO_IPV6;
nxt = -1;
ahseen = 0;
again:
newoff = ip6_nexthdr(m, off, proto, &nxt);
if (newoff < 0)
newoff = m->m_pkthdr.len;
else if (newoff <= off) {
error = EINVAL;
goto fail;
}
switch (proto) {
case IPPROTO_IPV6:
if (off == 0) {
struct ip6_hdr ip6copy;
if (newoff - off != sizeof(struct ip6_hdr)) {
error = EINVAL;
goto fail;
}
m_copydata(m, off, newoff - off, (caddr_t)&ip6copy);
ip6copy.ip6_flow = 0;
ip6copy.ip6_vfc &= ~IPV6_VERSION_MASK;
ip6copy.ip6_vfc |= IPV6_VERSION;
ip6copy.ip6_hlim = 0;
if (IN6_IS_ADDR_LINKLOCAL(&ip6copy.ip6_src))
ip6copy.ip6_src.s6_addr16[1] = 0x0000;
if (IN6_IS_ADDR_LINKLOCAL(&ip6copy.ip6_dst))
ip6copy.ip6_dst.s6_addr16[1] = 0x0000;
(algo->update)(&algos, (caddr_t)&ip6copy,
sizeof(struct ip6_hdr));
} else {
newoff = m->m_pkthdr.len;
ah_update_mbuf(m, off, m->m_pkthdr.len - off, algo,
&algos);
}
break;
case IPPROTO_AH:
{
int siz;
int hdrsiz;
hdrsiz = (sav->flags & SADB_X_EXT_OLD)
? sizeof(struct ah)
: sizeof(struct newah);
siz = (*algo->sumsiz)(sav);
if (!ahseen) {
if (newoff - off > MCLBYTES) {
error = EMSGSIZE;
goto fail;
}
MGET(n, M_DONTWAIT, MT_DATA);
if (n && newoff - off > MLEN) {
MCLGET(n, M_DONTWAIT);
if ((n->m_flags & M_EXT) == 0) {
m_free(n);
n = NULL;
}
}
if (n == NULL) {
error = ENOBUFS;
goto fail;
}
m_copydata(m, off, newoff - off, mtod(n, caddr_t));
n->m_len = newoff - off;
bzero(mtod(n, caddr_t) + hdrsiz, siz);
(algo->update)(&algos, mtod(n, caddr_t), n->m_len);
m_free(n);
n = NULL;
} else
ah_update_mbuf(m, off, newoff - off, algo, &algos);
ahseen++;
break;
}
case IPPROTO_HOPOPTS:
case IPPROTO_DSTOPTS:
{
struct ip6_ext *ip6e;
int hdrlen, optlen;
u_int8_t *p, *optend, *optp;
if (newoff - off > MCLBYTES) {
error = EMSGSIZE;
goto fail;
}
MGET(n, M_DONTWAIT, MT_DATA);
if (n && newoff - off > MLEN) {
MCLGET(n, M_DONTWAIT);
if ((n->m_flags & M_EXT) == 0) {
m_free(n);
n = NULL;
}
}
if (n == NULL) {
error = ENOBUFS;
goto fail;
}
m_copydata(m, off, newoff - off, mtod(n, caddr_t));
n->m_len = newoff - off;
ip6e = mtod(n, struct ip6_ext *);
hdrlen = (ip6e->ip6e_len + 1) << 3;
if (newoff - off < hdrlen) {
error = EINVAL;
m_free(n);
n = NULL;
goto fail;
}
p = mtod(n, u_int8_t *);
optend = p + hdrlen;
optp = p + 2;
while (optp < optend) {
if (optp[0] == IP6OPT_PAD1)
optlen = 1;
else {
if (optp + 2 > optend) {
error = EINVAL;
m_free(n);
n = NULL;
goto fail;
}
optlen = optp[1] + 2;
if (optp[0] & IP6OPT_MUTABLE)
bzero(optp + 2, optlen - 2);
}
optp += optlen;
}
(algo->update)(&algos, mtod(n, caddr_t), n->m_len);
m_free(n);
n = NULL;
break;
}
case IPPROTO_ROUTING:
default:
ah_update_mbuf(m, off, newoff - off, algo, &algos);
break;
}
if (newoff < m->m_pkthdr.len) {
proto = nxt;
off = newoff;
goto again;
}
if (len < (*algo->sumsiz)(sav)) {
error = EINVAL;
goto fail;
}
(algo->result)(&algos, (caddr_t) &sumbuf[0], sizeof(sumbuf));
bcopy(&sumbuf[0], ahdat, (*algo->sumsiz)(sav));
if (n)
m_free(n);
return 0;
fail:
if (n)
m_free(n);
return error;
}
#endif