Merge branch 'reimplement-tcp-ao-using-crypto-library'

Eric Biggers says:

====================
Reimplement TCP-AO using crypto library

This series can also be retrieved from:

    git fetch https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git tcp-ao-v2

This series is targeting net-next for 7.2.  To make this series
self-contained in the networking code, I dropped the patches that remove
support for transformation cloning from the crypto API, which is a
further negative 275-line cleanup and optimization this series enables.
That will be done as a follow-up, either through the crypto tree for
7.3, or still through net-next for 7.2 at maintainer preference.

This series refactors the TCP-AO (TCP Authentication Option) code to do
MAC and KDF computations using lib/crypto/ instead of crypto_ahash.
This greatly simplifies the code and makes it much more efficient.  The
entire tcp_sigpool mechanism becomes unnecessary and is removed, as the
problems it was designed to solve don't exist with the library APIs.

The crypto API's support for crypto transformation cloning also becomes
unnecessary and will be removed in follow-up patches.  Note that as part
of that, we'll be able to roll back the addition of the reference count
to crypto_tfm, which had regressed performance for all crypto API users.

To make this simplification and optimization possible, this series also
updates the TCP-AO code to support a specific set of algorithms, rather
than arbitrary algorithms that don't make sense and are very likely not
being used, e.g. CRC-32 and HMAC-MD5.

Specifically, this series retains the support for AES-128-CMAC,
HMAC-SHA1, and HMAC-SHA256.  AES-128-CMAC and HMAC-SHA1 are the only
algorithms that are actually standardized for use in TCP-AO, while
HMAC-SHA256 makes sense to continue supporting as a Linux extension.  Of
course, other algorithms can still be (re-)added later if ever needed.
It's worth noting that TCP-AO MACs are limited to 20 bytes by the TCP
options space, which limits the benefit of further algorithm upgrades.

This series passes the tcp_ao selftests
(sudo make -C tools/testing/selftests/net/tcp_ao/ run_tests).

To get a sense for how much more efficient this makes the TCP-AO code,
here's a microbenchmark for tcp_ao_hash_skb() with skb->len == 128:

        Algorithm       Avg cycles (before)     Avg cycles (after)
        ---------       -------------------     ------------------
        HMAC-SHA1       3319                    1256
        HMAC-SHA256     3311                    1344
        AES-128-CMAC    2720                    1107
====================

Link: https://patch.msgid.link/20260427172727.9310-1-ebiggers@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Paolo Abeni 2026-04-30 09:39:12 +02:00
commit fdd2c9a1d0
10 changed files with 432 additions and 914 deletions

View File

@ -2027,40 +2027,6 @@ struct tcp6_pseudohdr {
__be32 protocol; /* including padding */
};
/*
* struct tcp_sigpool - per-CPU pool of ahash_requests
* @scratch: per-CPU temporary area, that can be used between
* tcp_sigpool_start() and tcp_sigpool_end() to perform
* crypto request
* @req: pre-allocated ahash request
*/
struct tcp_sigpool {
void *scratch;
struct ahash_request *req;
};
int tcp_sigpool_alloc_ahash(const char *alg, size_t scratch_size);
void tcp_sigpool_get(unsigned int id);
void tcp_sigpool_release(unsigned int id);
int tcp_sigpool_hash_skb_data(struct tcp_sigpool *hp,
const struct sk_buff *skb,
unsigned int header_len);
/**
* tcp_sigpool_start - disable bh and start using tcp_sigpool_ahash
* @id: tcp_sigpool that was previously allocated by tcp_sigpool_alloc_ahash()
* @c: returned tcp_sigpool for usage (uninitialized on failure)
*
* Returns: 0 on success, error otherwise.
*/
int tcp_sigpool_start(unsigned int id, struct tcp_sigpool *c);
/**
* tcp_sigpool_end - enable bh and stop using tcp_sigpool
* @c: tcp_sigpool context that was returned by tcp_sigpool_start()
*/
void tcp_sigpool_end(struct tcp_sigpool *c);
size_t tcp_sigpool_algo(unsigned int id, char *buf, size_t buf_len);
/* - functions */
void tcp_v4_md5_hash_skb(char *md5_hash, const struct tcp_md5sig_key *key,
const struct sock *sk, const struct sk_buff *skb);
int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr,
@ -2520,9 +2486,9 @@ struct tcp_sock_af_ops {
struct tcp_ao_key *(*ao_lookup)(const struct sock *sk,
struct sock *addr_sk,
int sndid, int rcvid);
int (*ao_calc_key_sk)(struct tcp_ao_key *mkt, u8 *key,
const struct sock *sk,
__be32 sisn, __be32 disn, bool send);
void (*ao_calc_key_sk)(struct tcp_ao_key *mkt, u8 *key,
const struct sock *sk,
__be32 sisn, __be32 disn, bool send);
int (*calc_ao_hash)(char *location, struct tcp_ao_key *ao,
const struct sock *sk, const struct sk_buff *skb,
const u8 *tkey, int hash_offset, u32 sne);
@ -2543,7 +2509,7 @@ struct tcp_request_sock_ops {
struct tcp_ao_key *(*ao_lookup)(const struct sock *sk,
struct request_sock *req,
int sndid, int rcvid);
int (*ao_calc_key)(struct tcp_ao_key *mkt, u8 *key, struct request_sock *sk);
void (*ao_calc_key)(struct tcp_ao_key *mkt, u8 *key, struct request_sock *sk);
int (*ao_synack_hash)(char *ao_hash, struct tcp_ao_key *mkt,
struct request_sock *req, const struct sk_buff *skb,
int hash_offset, u32 sne);

View File

@ -2,8 +2,7 @@
#ifndef _TCP_AO_H
#define _TCP_AO_H
#define TCP_AO_KEY_ALIGN 1
#define __tcp_ao_key_align __aligned(TCP_AO_KEY_ALIGN)
#include <crypto/sha2.h> /* for SHA256_DIGEST_SIZE */
union tcp_ao_addr {
struct in_addr a4;
@ -32,11 +31,27 @@ struct tcp_ao_counters {
atomic64_t dropped_icmp;
};
enum tcp_ao_algo_id {
TCP_AO_ALGO_HMAC_SHA1 = 1, /* specified by RFC 5926 */
TCP_AO_ALGO_HMAC_SHA256, /* Linux extension */
TCP_AO_ALGO_AES_128_CMAC, /* specified by RFC 5926 */
};
/*
* This is the maximum untruncated MAC length, in bytes. Note that the MACs
* actually get truncated to 20 or fewer bytes to fit in the TCP options space.
*/
#define TCP_AO_MAX_MAC_LEN SHA256_DIGEST_SIZE
#define TCP_AO_MAX_TRAFFIC_KEY_LEN SHA256_DIGEST_SIZE
struct tcp_ao_mac_ctx;
struct tcp_ao_key {
struct hlist_node node;
union tcp_ao_addr addr;
u8 key[TCP_AO_MAXKEYLEN] __tcp_ao_key_align;
unsigned int tcp_sigpool_id;
u8 key[TCP_AO_MAXKEYLEN];
enum tcp_ao_algo_id algo;
unsigned int digest_size;
int l3index;
u8 prefixlen;
@ -168,14 +183,15 @@ struct tcp6_ao_context {
__be32 disn;
};
struct tcp_sigpool;
/* Established states are fast-path and there always is current_key/rnext_key */
#define TCP_AO_ESTABLISHED (TCPF_ESTABLISHED | TCPF_FIN_WAIT1 | TCPF_FIN_WAIT2 | \
TCPF_CLOSE_WAIT | TCPF_LAST_ACK | TCPF_CLOSING)
int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
struct tcp_ao_key *key, struct tcphdr *th,
__u8 *hash_location);
void tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
struct tcp_ao_key *key, struct tcphdr *th,
__u8 *hash_location);
void tcp_ao_mac_update(struct tcp_ao_mac_ctx *mac_ctx, const void *data,
size_t data_len);
int tcp_ao_hash_skb(unsigned short int family,
char *ao_hash, struct tcp_ao_key *key,
const struct sock *sk, const struct sk_buff *skb,
@ -188,8 +204,8 @@ struct tcp_ao_key *tcp_ao_established_key(const struct sock *sk,
int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk,
struct request_sock *req, struct sk_buff *skb,
int family);
int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
unsigned int len, struct tcp_sigpool *hp);
void tcp_ao_calc_traffic_key(const struct tcp_ao_key *mkt, u8 *traffic_key,
const void *input, unsigned int input_len);
void tcp_ao_destroy_sock(struct sock *sk, bool twsk);
void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp);
bool tcp_ao_ignore_icmp(const struct sock *sk, int family, int type, int code);
@ -222,11 +238,11 @@ struct tcp_ao_key *tcp_v4_ao_lookup(const struct sock *sk, struct sock *addr_sk,
int tcp_v4_ao_synack_hash(char *ao_hash, struct tcp_ao_key *mkt,
struct request_sock *req, const struct sk_buff *skb,
int hash_offset, u32 sne);
int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
const struct sock *sk,
__be32 sisn, __be32 disn, bool send);
int tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
struct request_sock *req);
void tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
const struct sock *sk,
__be32 sisn, __be32 disn, bool send);
void tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
struct request_sock *req);
struct tcp_ao_key *tcp_v4_ao_lookup_rsk(const struct sock *sk,
struct request_sock *req,
int sndid, int rcvid);
@ -234,16 +250,17 @@ int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
const struct sock *sk, const struct sk_buff *skb,
const u8 *tkey, int hash_offset, u32 sne);
/* ipv6 specific functions */
int tcp_v6_ao_hash_pseudoheader(struct tcp_sigpool *hp,
const struct in6_addr *daddr,
const struct in6_addr *saddr, int nbytes);
int tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
const struct sk_buff *skb, __be32 sisn, __be32 disn);
int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
const struct sock *sk, __be32 sisn,
__be32 disn, bool send);
int tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
struct request_sock *req);
void tcp_v6_ao_hash_pseudoheader(struct tcp_ao_mac_ctx *mac_ctx,
const struct in6_addr *daddr,
const struct in6_addr *saddr, int nbytes);
void tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
const struct sk_buff *skb, __be32 sisn,
__be32 disn);
void tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
const struct sock *sk, __be32 sisn,
__be32 disn, bool send);
void tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
struct request_sock *req);
struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk,
struct sock *addr_sk, int sndid, int rcvid);
struct tcp_ao_key *tcp_v6_ao_lookup_rsk(const struct sock *sk,
@ -263,11 +280,10 @@ void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb,
struct request_sock *req, unsigned short int family);
#else /* CONFIG_TCP_AO */
static inline int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
struct tcp_ao_key *key, struct tcphdr *th,
__u8 *hash_location)
static inline void tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
struct tcp_ao_key *key,
struct tcphdr *th, __u8 *hash_location)
{
return 0;
}
static inline void tcp_ao_syncookie(struct sock *sk, const struct sk_buff *skb,

View File

@ -741,14 +741,12 @@ config DEFAULT_TCP_CONG
default "bbr" if DEFAULT_BBR
default "cubic"
config TCP_SIGPOOL
tristate
config TCP_AO
bool "TCP: Authentication Option (RFC5925)"
select CRYPTO
select CRYPTO_LIB_AES_CBC_MACS
select CRYPTO_LIB_SHA1
select CRYPTO_LIB_SHA256
select CRYPTO_LIB_UTILS
select TCP_SIGPOOL
depends on 64BIT # seq-number extension needs WRITE_ONCE(u64)
help
TCP-AO specifies the use of stronger Message Authentication Codes (MACs),

View File

@ -60,7 +60,6 @@ obj-$(CONFIG_TCP_CONG_SCALABLE) += tcp_scalable.o
obj-$(CONFIG_TCP_CONG_LP) += tcp_lp.o
obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah.o
obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o
obj-$(CONFIG_TCP_SIGPOOL) += tcp_sigpool.o
obj-$(CONFIG_NET_SOCK_MSG) += tcp_bpf.o
obj-$(CONFIG_BPF_SYSCALL) += udp_bpf.o
obj-$(CONFIG_NETLABEL) += cipso_ipv4.o

View File

@ -9,7 +9,9 @@
*/
#define pr_fmt(fmt) "TCP: " fmt
#include <crypto/hash.h>
#include <crypto/aes-cbc-macs.h>
#include <crypto/sha1.h>
#include <crypto/sha2.h>
#include <crypto/utils.h>
#include <linux/inetdevice.h>
#include <linux/tcp.h>
@ -21,32 +23,133 @@
DEFINE_STATIC_KEY_DEFERRED_FALSE(tcp_ao_needed, HZ);
int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx,
unsigned int len, struct tcp_sigpool *hp)
static const struct tcp_ao_algo {
const char *name;
unsigned int digest_size;
} tcp_ao_algos[] = {
[TCP_AO_ALGO_HMAC_SHA1] = {
.name = "hmac(sha1)",
.digest_size = SHA1_DIGEST_SIZE,
},
[TCP_AO_ALGO_HMAC_SHA256] = {
.name = "hmac(sha256)",
.digest_size = SHA256_DIGEST_SIZE,
},
[TCP_AO_ALGO_AES_128_CMAC] = {
.name = "cmac(aes128)",
.digest_size = AES_BLOCK_SIZE, /* same as AES_KEYSIZE_128 */
},
};
struct tcp_ao_mac_ctx {
enum tcp_ao_algo_id algo;
union {
struct hmac_sha1_ctx hmac_sha1;
struct hmac_sha256_ctx hmac_sha256;
struct {
struct aes_cmac_key key;
struct aes_cmac_ctx ctx;
} aes_cmac;
};
};
static const struct tcp_ao_algo *tcp_ao_find_algo(const char *name)
{
struct scatterlist sg;
int ret;
for (size_t i = 0; i < ARRAY_SIZE(tcp_ao_algos); i++) {
const struct tcp_ao_algo *algo = &tcp_ao_algos[i];
if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp->req),
mkt->key, mkt->keylen))
goto clear_hash;
if (!algo->name)
continue;
if (WARN_ON_ONCE(algo->digest_size > TCP_AO_MAX_MAC_LEN ||
algo->digest_size >
TCP_AO_MAX_TRAFFIC_KEY_LEN))
continue;
if (strcmp(name, algo->name) == 0)
return algo;
}
return NULL;
}
ret = crypto_ahash_init(hp->req);
if (ret)
goto clear_hash;
static void tcp_ao_mac_init(struct tcp_ao_mac_ctx *mac_ctx,
enum tcp_ao_algo_id algo, const u8 *traffic_key)
{
mac_ctx->algo = algo;
switch (mac_ctx->algo) {
case TCP_AO_ALGO_HMAC_SHA1:
hmac_sha1_init_usingrawkey(&mac_ctx->hmac_sha1, traffic_key,
SHA1_DIGEST_SIZE);
return;
case TCP_AO_ALGO_HMAC_SHA256:
hmac_sha256_init_usingrawkey(&mac_ctx->hmac_sha256, traffic_key,
SHA256_DIGEST_SIZE);
return;
case TCP_AO_ALGO_AES_128_CMAC:
aes_cmac_preparekey(&mac_ctx->aes_cmac.key, traffic_key,
AES_KEYSIZE_128);
aes_cmac_init(&mac_ctx->aes_cmac.ctx, &mac_ctx->aes_cmac.key);
return;
default:
WARN_ON_ONCE(1); /* algo was validated earlier. */
}
}
sg_init_one(&sg, ctx, len);
ahash_request_set_crypt(hp->req, &sg, key, len);
crypto_ahash_update(hp->req);
void tcp_ao_mac_update(struct tcp_ao_mac_ctx *mac_ctx, const void *data,
size_t data_len)
{
switch (mac_ctx->algo) {
case TCP_AO_ALGO_HMAC_SHA1:
hmac_sha1_update(&mac_ctx->hmac_sha1, data, data_len);
return;
case TCP_AO_ALGO_HMAC_SHA256:
hmac_sha256_update(&mac_ctx->hmac_sha256, data, data_len);
return;
case TCP_AO_ALGO_AES_128_CMAC:
aes_cmac_update(&mac_ctx->aes_cmac.ctx, data, data_len);
return;
default:
WARN_ON_ONCE(1); /* algo was validated earlier. */
}
}
ret = crypto_ahash_final(hp->req);
if (ret)
goto clear_hash;
static void tcp_ao_mac_final(struct tcp_ao_mac_ctx *mac_ctx, u8 *out)
{
switch (mac_ctx->algo) {
case TCP_AO_ALGO_HMAC_SHA1:
hmac_sha1_final(&mac_ctx->hmac_sha1, out);
return;
case TCP_AO_ALGO_HMAC_SHA256:
hmac_sha256_final(&mac_ctx->hmac_sha256, out);
return;
case TCP_AO_ALGO_AES_128_CMAC:
aes_cmac_final(&mac_ctx->aes_cmac.ctx, out);
return;
default:
WARN_ON_ONCE(1); /* algo was validated earlier. */
}
}
return 0;
clear_hash:
memset(key, 0, tcp_ao_digest_size(mkt));
return 1;
void tcp_ao_calc_traffic_key(const struct tcp_ao_key *mkt, u8 *traffic_key,
const void *input, unsigned int input_len)
{
switch (mkt->algo) {
case TCP_AO_ALGO_HMAC_SHA1:
hmac_sha1_usingrawkey(mkt->key, mkt->keylen, input, input_len,
traffic_key);
return;
case TCP_AO_ALGO_HMAC_SHA256:
hmac_sha256_usingrawkey(mkt->key, mkt->keylen, input, input_len,
traffic_key);
return;
case TCP_AO_ALGO_AES_128_CMAC: {
struct aes_cmac_key k;
aes_cmac_preparekey(&k, mkt->key, AES_KEYSIZE_128);
aes_cmac(&k, input, input_len, traffic_key);
return;
}
default:
WARN_ON_ONCE(1); /* algo was validated earlier. */
}
}
bool tcp_ao_ignore_icmp(const struct sock *sk, int family, int type, int code)
@ -254,7 +357,6 @@ static struct tcp_ao_key *tcp_ao_copy_key(struct sock *sk,
*new_key = *key;
INIT_HLIST_NODE(&new_key->node);
tcp_sigpool_get(new_key->tcp_sigpool_id);
atomic64_set(&new_key->pkt_good, 0);
atomic64_set(&new_key->pkt_bad, 0);
@ -265,7 +367,6 @@ static void tcp_ao_key_free_rcu(struct rcu_head *head)
{
struct tcp_ao_key *key = container_of(head, struct tcp_ao_key, rcu);
tcp_sigpool_release(key->tcp_sigpool_id);
kfree_sensitive(key);
}
@ -276,7 +377,6 @@ static void tcp_ao_info_free(struct tcp_ao_info *ao)
hlist_for_each_entry_safe(key, n, &ao->head, node) {
hlist_del(&key->node);
tcp_sigpool_release(key->tcp_sigpool_id);
kfree_sensitive(key);
}
kfree(ao);
@ -335,10 +435,10 @@ void tcp_ao_time_wait(struct tcp_timewait_sock *tcptw, struct tcp_sock *tp)
}
/* 4 tuple and ISNs are expected in NBO */
static int tcp_v4_ao_calc_key(struct tcp_ao_key *mkt, u8 *key,
__be32 saddr, __be32 daddr,
__be16 sport, __be16 dport,
__be32 sisn, __be32 disn)
static void tcp_v4_ao_calc_key(struct tcp_ao_key *mkt, u8 *key,
__be32 saddr, __be32 daddr,
__be16 sport, __be16 dport,
__be32 sisn, __be32 disn)
{
/* See RFC5926 3.1.1 */
struct kdf_input_block {
@ -346,148 +446,146 @@ static int tcp_v4_ao_calc_key(struct tcp_ao_key *mkt, u8 *key,
u8 label[6];
struct tcp4_ao_context ctx;
__be16 outlen;
} __packed * tmp;
struct tcp_sigpool hp;
int err;
} __packed input = {
.counter = 1,
.label = "TCP-AO",
.ctx = {
.saddr = saddr,
.daddr = daddr,
.sport = sport,
.dport = dport,
.sisn = sisn,
.disn = disn,
},
.outlen = htons(tcp_ao_digest_size(mkt) * 8), /* in bits */
};
err = tcp_sigpool_start(mkt->tcp_sigpool_id, &hp);
if (err)
return err;
tmp = hp.scratch;
tmp->counter = 1;
memcpy(tmp->label, "TCP-AO", 6);
tmp->ctx.saddr = saddr;
tmp->ctx.daddr = daddr;
tmp->ctx.sport = sport;
tmp->ctx.dport = dport;
tmp->ctx.sisn = sisn;
tmp->ctx.disn = disn;
tmp->outlen = htons(tcp_ao_digest_size(mkt) * 8); /* in bits */
err = tcp_ao_calc_traffic_key(mkt, key, tmp, sizeof(*tmp), &hp);
tcp_sigpool_end(&hp);
return err;
tcp_ao_calc_traffic_key(mkt, key, &input, sizeof(input));
}
int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
const struct sock *sk,
__be32 sisn, __be32 disn, bool send)
void tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
const struct sock *sk,
__be32 sisn, __be32 disn, bool send)
{
if (send)
return tcp_v4_ao_calc_key(mkt, key, sk->sk_rcv_saddr,
sk->sk_daddr, htons(sk->sk_num),
sk->sk_dport, sisn, disn);
tcp_v4_ao_calc_key(mkt, key, sk->sk_rcv_saddr, sk->sk_daddr,
htons(sk->sk_num), sk->sk_dport, sisn, disn);
else
return tcp_v4_ao_calc_key(mkt, key, sk->sk_daddr,
sk->sk_rcv_saddr, sk->sk_dport,
htons(sk->sk_num), disn, sisn);
tcp_v4_ao_calc_key(mkt, key, sk->sk_daddr, sk->sk_rcv_saddr,
sk->sk_dport, htons(sk->sk_num), disn, sisn);
}
static int tcp_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
const struct sock *sk,
__be32 sisn, __be32 disn, bool send)
{
if (mkt->family == AF_INET)
return tcp_v4_ao_calc_key_sk(mkt, key, sk, sisn, disn, send);
if (mkt->family == AF_INET) {
tcp_v4_ao_calc_key_sk(mkt, key, sk, sisn, disn, send);
return 0;
}
#if IS_ENABLED(CONFIG_IPV6)
else if (mkt->family == AF_INET6)
return tcp_v6_ao_calc_key_sk(mkt, key, sk, sisn, disn, send);
if (mkt->family == AF_INET6) {
tcp_v6_ao_calc_key_sk(mkt, key, sk, sisn, disn, send);
return 0;
}
#endif
else
return -EOPNOTSUPP;
return -EOPNOTSUPP;
}
int tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
struct request_sock *req)
void tcp_v4_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
struct request_sock *req)
{
struct inet_request_sock *ireq = inet_rsk(req);
return tcp_v4_ao_calc_key(mkt, key,
ireq->ir_loc_addr, ireq->ir_rmt_addr,
htons(ireq->ir_num), ireq->ir_rmt_port,
htonl(tcp_rsk(req)->snt_isn),
htonl(tcp_rsk(req)->rcv_isn));
tcp_v4_ao_calc_key(mkt, key, ireq->ir_loc_addr, ireq->ir_rmt_addr,
htons(ireq->ir_num), ireq->ir_rmt_port,
htonl(tcp_rsk(req)->snt_isn),
htonl(tcp_rsk(req)->rcv_isn));
}
static int tcp_v4_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
const struct sk_buff *skb,
__be32 sisn, __be32 disn)
static void tcp_v4_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
const struct sk_buff *skb,
__be32 sisn, __be32 disn)
{
const struct iphdr *iph = ip_hdr(skb);
const struct tcphdr *th = tcp_hdr(skb);
return tcp_v4_ao_calc_key(mkt, key, iph->saddr, iph->daddr,
th->source, th->dest, sisn, disn);
tcp_v4_ao_calc_key(mkt, key, iph->saddr, iph->daddr, th->source,
th->dest, sisn, disn);
}
static int tcp_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
const struct sk_buff *skb,
__be32 sisn, __be32 disn, int family)
{
if (family == AF_INET)
return tcp_v4_ao_calc_key_skb(mkt, key, skb, sisn, disn);
if (family == AF_INET) {
tcp_v4_ao_calc_key_skb(mkt, key, skb, sisn, disn);
return 0;
}
#if IS_ENABLED(CONFIG_IPV6)
else if (family == AF_INET6)
return tcp_v6_ao_calc_key_skb(mkt, key, skb, sisn, disn);
if (family == AF_INET6) {
tcp_v6_ao_calc_key_skb(mkt, key, skb, sisn, disn);
return 0;
}
#endif
return -EAFNOSUPPORT;
}
static int tcp_v4_ao_hash_pseudoheader(struct tcp_sigpool *hp,
__be32 daddr, __be32 saddr,
int nbytes)
static void tcp_v4_ao_hash_pseudoheader(struct tcp_ao_mac_ctx *mac_ctx,
__be32 daddr, __be32 saddr, int nbytes)
{
struct tcp4_pseudohdr *bp;
struct scatterlist sg;
struct tcp4_pseudohdr phdr = {
.saddr = saddr,
.daddr = daddr,
.pad = 0,
.protocol = IPPROTO_TCP,
.len = cpu_to_be16(nbytes),
};
bp = hp->scratch;
bp->saddr = saddr;
bp->daddr = daddr;
bp->pad = 0;
bp->protocol = IPPROTO_TCP;
bp->len = cpu_to_be16(nbytes);
sg_init_one(&sg, bp, sizeof(*bp));
ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp));
return crypto_ahash_update(hp->req);
tcp_ao_mac_update(mac_ctx, &phdr, sizeof(phdr));
}
static int tcp_ao_hash_pseudoheader(unsigned short int family,
const struct sock *sk,
const struct sk_buff *skb,
struct tcp_sigpool *hp, int nbytes)
struct tcp_ao_mac_ctx *mac_ctx, int nbytes)
{
const struct tcphdr *th = tcp_hdr(skb);
/* TODO: Can we rely on checksum being zero to mean outbound pkt? */
if (!th->check) {
if (family == AF_INET)
return tcp_v4_ao_hash_pseudoheader(hp, sk->sk_daddr,
sk->sk_rcv_saddr, skb->len);
if (family == AF_INET) {
tcp_v4_ao_hash_pseudoheader(mac_ctx, sk->sk_daddr,
sk->sk_rcv_saddr, skb->len);
return 0;
}
#if IS_ENABLED(CONFIG_IPV6)
else if (family == AF_INET6)
return tcp_v6_ao_hash_pseudoheader(hp, &sk->sk_v6_daddr,
&sk->sk_v6_rcv_saddr, skb->len);
if (family == AF_INET6) {
tcp_v6_ao_hash_pseudoheader(mac_ctx, &sk->sk_v6_daddr,
&sk->sk_v6_rcv_saddr,
skb->len);
return 0;
}
#endif
else
return -EAFNOSUPPORT;
return -EAFNOSUPPORT;
}
if (family == AF_INET) {
const struct iphdr *iph = ip_hdr(skb);
return tcp_v4_ao_hash_pseudoheader(hp, iph->daddr,
iph->saddr, skb->len);
tcp_v4_ao_hash_pseudoheader(mac_ctx, iph->daddr, iph->saddr,
skb->len);
return 0;
}
#if IS_ENABLED(CONFIG_IPV6)
} else if (family == AF_INET6) {
if (family == AF_INET6) {
const struct ipv6hdr *iph = ipv6_hdr(skb);
return tcp_v6_ao_hash_pseudoheader(hp, &iph->daddr,
&iph->saddr, skb->len);
#endif
tcp_v6_ao_hash_pseudoheader(mac_ctx, &iph->daddr, &iph->saddr,
skb->len);
return 0;
}
#endif
return -EAFNOSUPPORT;
}
@ -506,31 +604,20 @@ u32 tcp_ao_compute_sne(u32 next_sne, u32 next_seq, u32 seq)
return sne;
}
/* tcp_ao_hash_sne(struct tcp_sigpool *hp)
* @hp - used for hashing
* @sne - sne value
*/
static int tcp_ao_hash_sne(struct tcp_sigpool *hp, u32 sne)
static void tcp_ao_hash_sne(struct tcp_ao_mac_ctx *mac_ctx, u32 sne)
{
struct scatterlist sg;
__be32 *bp;
__be32 sne_be32 = htonl(sne);
bp = (__be32 *)hp->scratch;
*bp = htonl(sne);
sg_init_one(&sg, bp, sizeof(*bp));
ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp));
return crypto_ahash_update(hp->req);
tcp_ao_mac_update(mac_ctx, &sne_be32, sizeof(sne_be32));
}
static int tcp_ao_hash_header(struct tcp_sigpool *hp,
const struct tcphdr *th,
bool exclude_options, u8 *hash,
int hash_offset, int hash_len)
static void tcp_ao_hash_header(struct tcp_ao_mac_ctx *mac_ctx,
const struct tcphdr *th, bool exclude_options,
u8 *hash, int hash_offset, int hash_len)
{
struct scatterlist sg;
u8 *hdr = hp->scratch;
int err, len;
/* Full TCP header (th->doff << 2) should fit into scratch area. */
u8 hdr[60];
int len;
/* We are not allowed to change tcphdr, make a local copy */
if (exclude_options) {
@ -550,11 +637,7 @@ static int tcp_ao_hash_header(struct tcp_sigpool *hp,
memset(hdr + hash_offset, 0, hash_len);
}
sg_init_one(&sg, hdr, len);
ahash_request_set_crypt(hp->req, &sg, NULL, len);
err = crypto_ahash_update(hp->req);
WARN_ON_ONCE(err != 0);
return err;
tcp_ao_mac_update(mac_ctx, hdr, len);
}
int tcp_ao_hash_hdr(unsigned short int family, char *ao_hash,
@ -563,109 +646,92 @@ int tcp_ao_hash_hdr(unsigned short int family, char *ao_hash,
const union tcp_ao_addr *saddr,
const struct tcphdr *th, u32 sne)
{
int tkey_len = tcp_ao_digest_size(key);
int hash_offset = ao_hash - (char *)th;
struct tcp_sigpool hp;
void *hash_buf = NULL;
struct tcp_ao_mac_ctx mac_ctx;
u8 hash_buf[TCP_AO_MAX_MAC_LEN];
hash_buf = kmalloc(tkey_len, GFP_ATOMIC);
if (!hash_buf)
goto clear_hash_noput;
if (tcp_sigpool_start(key->tcp_sigpool_id, &hp))
goto clear_hash_noput;
if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp.req), tkey, tkey_len))
goto clear_hash;
if (crypto_ahash_init(hp.req))
goto clear_hash;
if (tcp_ao_hash_sne(&hp, sne))
goto clear_hash;
tcp_ao_mac_init(&mac_ctx, key->algo, tkey);
tcp_ao_hash_sne(&mac_ctx, sne);
if (family == AF_INET) {
if (tcp_v4_ao_hash_pseudoheader(&hp, daddr->a4.s_addr,
saddr->a4.s_addr, th->doff * 4))
goto clear_hash;
tcp_v4_ao_hash_pseudoheader(&mac_ctx, daddr->a4.s_addr,
saddr->a4.s_addr, th->doff * 4);
#if IS_ENABLED(CONFIG_IPV6)
} else if (family == AF_INET6) {
if (tcp_v6_ao_hash_pseudoheader(&hp, &daddr->a6,
&saddr->a6, th->doff * 4))
goto clear_hash;
tcp_v6_ao_hash_pseudoheader(&mac_ctx, &daddr->a6,
&saddr->a6, th->doff * 4);
#endif
} else {
WARN_ON_ONCE(1);
goto clear_hash;
}
if (tcp_ao_hash_header(&hp, th,
!!(key->keyflags & TCP_AO_KEYF_EXCLUDE_OPT),
ao_hash, hash_offset, tcp_ao_maclen(key)))
goto clear_hash;
ahash_request_set_crypt(hp.req, NULL, hash_buf, 0);
if (crypto_ahash_final(hp.req))
goto clear_hash;
tcp_ao_hash_header(&mac_ctx, th,
!!(key->keyflags & TCP_AO_KEYF_EXCLUDE_OPT),
ao_hash, hash_offset, tcp_ao_maclen(key));
tcp_ao_mac_final(&mac_ctx, hash_buf);
memcpy(ao_hash, hash_buf, tcp_ao_maclen(key));
tcp_sigpool_end(&hp);
kfree(hash_buf);
return 0;
clear_hash:
tcp_sigpool_end(&hp);
clear_hash_noput:
memset(ao_hash, 0, tcp_ao_maclen(key));
kfree(hash_buf);
return 1;
}
static void tcp_ao_hash_skb_data(struct tcp_ao_mac_ctx *mac_ctx,
const struct sk_buff *skb,
unsigned int header_len)
{
const unsigned int head_data_len = skb_headlen(skb) > header_len ?
skb_headlen(skb) - header_len : 0;
const struct skb_shared_info *shi = skb_shinfo(skb);
struct sk_buff *frag_iter;
unsigned int i;
tcp_ao_mac_update(mac_ctx, (const u8 *)tcp_hdr(skb) + header_len,
head_data_len);
for (i = 0; i < shi->nr_frags; ++i) {
const skb_frag_t *f = &shi->frags[i];
u32 p_off, p_len, copied;
const void *vaddr;
struct page *p;
skb_frag_foreach_page(f, skb_frag_off(f), skb_frag_size(f),
p, p_off, p_len, copied) {
vaddr = kmap_local_page(p);
tcp_ao_mac_update(mac_ctx, vaddr + p_off, p_len);
kunmap_local(vaddr);
}
}
skb_walk_frags(skb, frag_iter)
tcp_ao_hash_skb_data(mac_ctx, frag_iter, 0);
}
int tcp_ao_hash_skb(unsigned short int family,
char *ao_hash, struct tcp_ao_key *key,
const struct sock *sk, const struct sk_buff *skb,
const u8 *tkey, int hash_offset, u32 sne)
{
const struct tcphdr *th = tcp_hdr(skb);
int tkey_len = tcp_ao_digest_size(key);
struct tcp_sigpool hp;
void *hash_buf = NULL;
struct tcp_ao_mac_ctx mac_ctx;
u8 hash_buf[TCP_AO_MAX_MAC_LEN];
hash_buf = kmalloc(tkey_len, GFP_ATOMIC);
if (!hash_buf)
goto clear_hash_noput;
if (tcp_sigpool_start(key->tcp_sigpool_id, &hp))
goto clear_hash_noput;
if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp.req), tkey, tkey_len))
goto clear_hash;
/* For now use sha1 by default. Depends on alg in tcp_ao_key */
if (crypto_ahash_init(hp.req))
goto clear_hash;
if (tcp_ao_hash_sne(&hp, sne))
goto clear_hash;
if (tcp_ao_hash_pseudoheader(family, sk, skb, &hp, skb->len))
goto clear_hash;
if (tcp_ao_hash_header(&hp, th,
!!(key->keyflags & TCP_AO_KEYF_EXCLUDE_OPT),
ao_hash, hash_offset, tcp_ao_maclen(key)))
goto clear_hash;
if (tcp_sigpool_hash_skb_data(&hp, skb, th->doff << 2))
goto clear_hash;
ahash_request_set_crypt(hp.req, NULL, hash_buf, 0);
if (crypto_ahash_final(hp.req))
tcp_ao_mac_init(&mac_ctx, key->algo, tkey);
tcp_ao_hash_sne(&mac_ctx, sne);
if (tcp_ao_hash_pseudoheader(family, sk, skb, &mac_ctx, skb->len))
goto clear_hash;
tcp_ao_hash_header(&mac_ctx, th,
!!(key->keyflags & TCP_AO_KEYF_EXCLUDE_OPT),
ao_hash, hash_offset, tcp_ao_maclen(key));
tcp_ao_hash_skb_data(&mac_ctx, skb, th->doff << 2);
tcp_ao_mac_final(&mac_ctx, hash_buf);
memcpy(ao_hash, hash_buf, tcp_ao_maclen(key));
tcp_sigpool_end(&hp);
kfree(hash_buf);
return 0;
clear_hash:
tcp_sigpool_end(&hp);
clear_hash_noput:
memset(ao_hash, 0, tcp_ao_maclen(key));
kfree(hash_buf);
return 1;
}
@ -681,22 +747,12 @@ int tcp_v4_ao_synack_hash(char *ao_hash, struct tcp_ao_key *ao_key,
struct request_sock *req, const struct sk_buff *skb,
int hash_offset, u32 sne)
{
void *hash_buf = NULL;
int err;
u8 tkey_buf[TCP_AO_MAX_TRAFFIC_KEY_LEN];
hash_buf = kmalloc(tcp_ao_digest_size(ao_key), GFP_ATOMIC);
if (!hash_buf)
return -ENOMEM;
tcp_v4_ao_calc_key_rsk(ao_key, tkey_buf, req);
err = tcp_v4_ao_calc_key_rsk(ao_key, hash_buf, req);
if (err)
goto out;
err = tcp_ao_hash_skb(AF_INET, ao_hash, ao_key, req_to_sk(req), skb,
hash_buf, hash_offset, sne);
out:
kfree(hash_buf);
return err;
return tcp_ao_hash_skb(AF_INET, ao_hash, ao_key, req_to_sk(req), skb,
tkey_buf, hash_offset, sne);
}
struct tcp_ao_key *tcp_v4_ao_lookup_rsk(const struct sock *sk,
@ -806,14 +862,14 @@ int tcp_ao_prepare_reset(const struct sock *sk, struct sk_buff *skb,
return 0;
}
int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
struct tcp_ao_key *key, struct tcphdr *th,
__u8 *hash_location)
void tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
struct tcp_ao_key *key, struct tcphdr *th,
__u8 *hash_location)
{
struct tcp_skb_cb *tcb = TCP_SKB_CB(skb);
u8 tkey_buf[TCP_AO_MAX_TRAFFIC_KEY_LEN];
struct tcp_sock *tp = tcp_sk(sk);
struct tcp_ao_info *ao;
void *tkey_buf = NULL;
u8 *traffic_key;
u32 sne;
@ -825,9 +881,6 @@ int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
if (!(tcb->tcp_flags & TCPHDR_ACK)) {
disn = 0;
tkey_buf = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC);
if (!tkey_buf)
return -ENOMEM;
traffic_key = tkey_buf;
} else {
disn = ao->risn;
@ -839,8 +892,6 @@ int tcp_ao_transmit_skb(struct sock *sk, struct sk_buff *skb,
ntohl(th->seq));
tp->af_specific->calc_ao_hash(hash_location, key, sk, skb, traffic_key,
hash_location - (u8 *)th, sne);
kfree(tkey_buf);
return 0;
}
static struct tcp_ao_key *tcp_ao_inbound_lookup(unsigned short int family,
@ -905,7 +956,7 @@ tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb,
{
const struct tcphdr *th = tcp_hdr(skb);
u8 maclen = tcp_ao_hdr_maclen(aoh);
void *hash_buf = NULL;
u8 hash_buf[TCP_AO_MAX_MAC_LEN];
if (maclen != tcp_ao_maclen(key)) {
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOBAD);
@ -916,10 +967,6 @@ tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb,
return SKB_DROP_REASON_TCP_AOFAILURE;
}
hash_buf = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC);
if (!hash_buf)
return SKB_DROP_REASON_NOT_SPECIFIED;
/* XXX: make it per-AF callback? */
tcp_ao_hash_skb(family, hash_buf, key, sk, skb, traffic_key,
(phash - (u8 *)th), sne);
@ -929,13 +976,11 @@ tcp_ao_verify_hash(const struct sock *sk, const struct sk_buff *skb,
atomic64_inc(&key->pkt_bad);
trace_tcp_ao_mismatch(sk, skb, aoh->keyid,
aoh->rnext_keyid, maclen);
kfree(hash_buf);
return SKB_DROP_REASON_TCP_AOFAILURE;
}
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOGOOD);
atomic64_inc(&info->counters.pkt_good);
atomic64_inc(&key->pkt_good);
kfree(hash_buf);
return SKB_NOT_DROPPED_YET;
}
@ -944,11 +989,11 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb,
unsigned short int family, const struct request_sock *req,
int l3index, const struct tcp_ao_hdr *aoh)
{
u8 tkey_buf[TCP_AO_MAX_TRAFFIC_KEY_LEN];
const struct tcphdr *th = tcp_hdr(skb);
u8 maclen = tcp_ao_hdr_maclen(aoh);
u8 *phash = (u8 *)(aoh + 1); /* hash goes just after the header */
struct tcp_ao_info *info;
enum skb_drop_reason ret;
struct tcp_ao_key *key;
__be32 sisn, disn;
u8 *traffic_key;
@ -1056,14 +1101,9 @@ tcp_inbound_ao_hash(struct sock *sk, const struct sk_buff *skb,
return SKB_DROP_REASON_TCP_AOFAILURE;
}
verify_hash:
traffic_key = kmalloc(tcp_ao_digest_size(key), GFP_ATOMIC);
if (!traffic_key)
return SKB_DROP_REASON_NOT_SPECIFIED;
tcp_ao_calc_key_skb(key, traffic_key, skb, sisn, disn, family);
ret = tcp_ao_verify_hash(sk, skb, family, info, aoh, key,
traffic_key, phash, sne, l3index);
kfree(traffic_key);
return ret;
tcp_ao_calc_key_skb(key, tkey_buf, skb, sisn, disn, family);
return tcp_ao_verify_hash(sk, skb, family, info, aoh, key,
tkey_buf, phash, sne, l3index);
key_not_found:
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAOKEYNOTFOUND);
@ -1280,7 +1320,6 @@ int tcp_ao_copy_all_matching(const struct sock *sk, struct sock *newsk,
free_and_exit:
hlist_for_each_entry_safe(key, key_head, &new_ao->head, node) {
hlist_del(&key->node);
tcp_sigpool_release(key->tcp_sigpool_id);
atomic_sub(tcp_ao_sizeof_key(key), &newsk->sk_omem_alloc);
kfree_sensitive(key);
}
@ -1336,23 +1375,10 @@ static int tcp_ao_verify_ipv4(struct sock *sk, struct tcp_ao_add *cmd,
return 0;
}
static int tcp_ao_parse_crypto(struct tcp_ao_add *cmd, struct tcp_ao_key *key)
static int tcp_ao_parse_crypto(const struct tcp_ao_add *cmd,
struct tcp_ao_key *key)
{
unsigned int syn_tcp_option_space;
bool is_kdf_aes_128_cmac = false;
struct crypto_ahash *tfm;
struct tcp_sigpool hp;
void *tmp_key = NULL;
int err;
/* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */
if (!strcmp("cmac(aes128)", cmd->alg_name)) {
strscpy(cmd->alg_name, "cmac(aes)", sizeof(cmd->alg_name));
is_kdf_aes_128_cmac = (cmd->keylen != 16);
tmp_key = kmalloc(cmd->keylen, GFP_KERNEL);
if (!tmp_key)
return -ENOMEM;
}
key->maclen = cmd->maclen ?: 12; /* 12 is the default in RFC5925 */
@ -1388,64 +1414,27 @@ static int tcp_ao_parse_crypto(struct tcp_ao_add *cmd, struct tcp_ao_key *key)
syn_tcp_option_space -= TCPOLEN_MSS_ALIGNED;
syn_tcp_option_space -= TCPOLEN_TSTAMP_ALIGNED;
syn_tcp_option_space -= TCPOLEN_WSCALE_ALIGNED;
if (tcp_ao_len_aligned(key) > syn_tcp_option_space) {
err = -EMSGSIZE;
goto err_kfree;
if (tcp_ao_len_aligned(key) > syn_tcp_option_space)
return -EMSGSIZE;
if (key->algo == TCP_AO_ALGO_AES_128_CMAC &&
cmd->keylen != AES_KEYSIZE_128) {
/* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */
static const u8 zeroes[AES_KEYSIZE_128];
struct aes_cmac_key extractor;
aes_cmac_preparekey(&extractor, zeroes, AES_KEYSIZE_128);
aes_cmac(&extractor, cmd->key, cmd->keylen, key->key);
key->keylen = AES_KEYSIZE_128;
} else {
memcpy(key->key, cmd->key, cmd->keylen);
key->keylen = cmd->keylen;
}
key->keylen = cmd->keylen;
memcpy(key->key, cmd->key, cmd->keylen);
err = tcp_sigpool_start(key->tcp_sigpool_id, &hp);
if (err)
goto err_kfree;
tfm = crypto_ahash_reqtfm(hp.req);
if (is_kdf_aes_128_cmac) {
void *scratch = hp.scratch;
struct scatterlist sg;
memcpy(tmp_key, cmd->key, cmd->keylen);
sg_init_one(&sg, tmp_key, cmd->keylen);
/* Using zero-key of 16 bytes as described in RFC5926 */
memset(scratch, 0, 16);
err = crypto_ahash_setkey(tfm, scratch, 16);
if (err)
goto err_pool_end;
err = crypto_ahash_init(hp.req);
if (err)
goto err_pool_end;
ahash_request_set_crypt(hp.req, &sg, key->key, cmd->keylen);
err = crypto_ahash_update(hp.req);
if (err)
goto err_pool_end;
err |= crypto_ahash_final(hp.req);
if (err)
goto err_pool_end;
key->keylen = 16;
}
err = crypto_ahash_setkey(tfm, key->key, key->keylen);
if (err)
goto err_pool_end;
tcp_sigpool_end(&hp);
kfree_sensitive(tmp_key);
if (tcp_ao_maclen(key) > key->digest_size)
return -EINVAL;
return 0;
err_pool_end:
tcp_sigpool_end(&hp);
err_kfree:
kfree_sensitive(tmp_key);
return err;
}
#if IS_ENABLED(CONFIG_IPV6)
@ -1549,50 +1538,33 @@ static struct tcp_ao_info *getsockopt_ao_info(struct sock *sk)
static struct tcp_ao_key *tcp_ao_key_alloc(struct sock *sk,
struct tcp_ao_add *cmd)
{
const char *algo = cmd->alg_name;
unsigned int digest_size;
struct crypto_ahash *tfm;
const struct tcp_ao_algo *algo;
struct tcp_ao_key *key;
struct tcp_sigpool hp;
int err, pool_id;
size_t size;
/* Force null-termination of alg_name */
cmd->alg_name[ARRAY_SIZE(cmd->alg_name) - 1] = '\0';
/* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */
if (!strcmp("cmac(aes128)", algo))
algo = "cmac(aes)";
/* Full TCP header (th->doff << 2) should fit into scratch area,
* see tcp_ao_hash_header().
/*
* For backwards compatibility, accept "cmac(aes)" as an alias for
* "cmac(aes128)", provided that the key length is exactly 128 bits.
*/
pool_id = tcp_sigpool_alloc_ahash(algo, 60);
if (pool_id < 0)
return ERR_PTR(pool_id);
if (strcmp(cmd->alg_name, "cmac(aes)") == 0 &&
cmd->keylen == AES_KEYSIZE_128)
strscpy(cmd->alg_name, "cmac(aes128)");
err = tcp_sigpool_start(pool_id, &hp);
if (err)
goto err_free_pool;
algo = tcp_ao_find_algo(cmd->alg_name);
if (!algo)
return ERR_PTR(-ENOENT);
tfm = crypto_ahash_reqtfm(hp.req);
digest_size = crypto_ahash_digestsize(tfm);
tcp_sigpool_end(&hp);
size = sizeof(struct tcp_ao_key) + (digest_size << 1);
size = sizeof(struct tcp_ao_key) + (algo->digest_size << 1);
key = sock_kmalloc(sk, size, GFP_KERNEL);
if (!key) {
err = -ENOMEM;
goto err_free_pool;
}
if (!key)
return ERR_PTR(-ENOMEM);
key->tcp_sigpool_id = pool_id;
key->digest_size = digest_size;
key->algo = algo - tcp_ao_algos;
key->digest_size = algo->digest_size;
return key;
err_free_pool:
tcp_sigpool_release(pool_id);
return ERR_PTR(err);
}
static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family,
@ -1753,7 +1725,6 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family,
err_free_sock:
atomic_sub(tcp_ao_sizeof_key(key), &sk->sk_omem_alloc);
tcp_sigpool_release(key->tcp_sigpool_id);
kfree_sensitive(key);
err_free_ao:
if (first)
@ -2284,7 +2255,11 @@ static int tcp_ao_copy_mkts_to_user(const struct sock *sk,
opt_out.pkt_good = atomic64_read(&key->pkt_good);
opt_out.pkt_bad = atomic64_read(&key->pkt_bad);
memcpy(&opt_out.key, key->key, key->keylen);
tcp_sigpool_algo(key->tcp_sigpool_id, opt_out.alg_name, 64);
if (key->algo == TCP_AO_ALGO_AES_128_CMAC)
/* This is needed for backwards compatibility. */
strscpy(opt_out.alg_name, "cmac(aes)");
else
strscpy(opt_out.alg_name, tcp_ao_algos[key->algo].name);
/* Copy key to user */
if (copy_to_sockptr_offset(optval, out_offset,

View File

@ -1657,14 +1657,8 @@ static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,
key.md5_key, sk, skb);
#endif
} else if (tcp_key_is_ao(&key)) {
int err;
err = tcp_ao_transmit_skb(sk, skb, key.ao_key, th,
opts.hash_location);
if (err) {
sk_skb_reason_drop(sk, skb, SKB_DROP_REASON_NOT_SPECIFIED);
return -ENOMEM;
}
tcp_ao_transmit_skb(sk, skb, key.ao_key, th,
opts.hash_location);
}
/* BPF prog is the last one writing header option */

View File

@ -1,366 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <crypto/hash.h>
#include <linux/cpu.h>
#include <linux/kref.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/percpu.h>
#include <linux/workqueue.h>
#include <net/tcp.h>
static size_t __scratch_size;
struct sigpool_scratch {
local_lock_t bh_lock;
void __rcu *pad;
};
static DEFINE_PER_CPU(struct sigpool_scratch, sigpool_scratch) = {
.bh_lock = INIT_LOCAL_LOCK(bh_lock),
};
struct sigpool_entry {
struct crypto_ahash *hash;
const char *alg;
struct kref kref;
uint16_t needs_key:1,
reserved:15;
};
#define CPOOL_SIZE (PAGE_SIZE / sizeof(struct sigpool_entry))
static struct sigpool_entry cpool[CPOOL_SIZE];
static unsigned int cpool_populated;
static DEFINE_MUTEX(cpool_mutex);
/* Slow-path */
struct scratches_to_free {
struct rcu_head rcu;
unsigned int cnt;
void *scratches[];
};
static void free_old_scratches(struct rcu_head *head)
{
struct scratches_to_free *stf;
stf = container_of(head, struct scratches_to_free, rcu);
while (stf->cnt--)
kfree(stf->scratches[stf->cnt]);
kfree(stf);
}
/**
* sigpool_reserve_scratch - re-allocates scratch buffer, slow-path
* @size: request size for the scratch/temp buffer
*/
static int sigpool_reserve_scratch(size_t size)
{
struct scratches_to_free *stf;
size_t stf_sz = struct_size(stf, scratches, num_possible_cpus());
int cpu, err = 0;
lockdep_assert_held(&cpool_mutex);
if (__scratch_size >= size)
return 0;
stf = kmalloc(stf_sz, GFP_KERNEL);
if (!stf)
return -ENOMEM;
stf->cnt = 0;
size = max(size, __scratch_size);
cpus_read_lock();
for_each_possible_cpu(cpu) {
void *scratch, *old_scratch;
scratch = kmalloc_node(size, GFP_KERNEL, cpu_to_node(cpu));
if (!scratch) {
err = -ENOMEM;
break;
}
old_scratch = rcu_replace_pointer(per_cpu(sigpool_scratch.pad, cpu),
scratch, lockdep_is_held(&cpool_mutex));
if (!cpu_online(cpu) || !old_scratch) {
kfree(old_scratch);
continue;
}
stf->scratches[stf->cnt++] = old_scratch;
}
cpus_read_unlock();
if (!err)
__scratch_size = size;
call_rcu(&stf->rcu, free_old_scratches);
return err;
}
static void sigpool_scratch_free(void)
{
int cpu;
for_each_possible_cpu(cpu)
kfree(rcu_replace_pointer(per_cpu(sigpool_scratch.pad, cpu),
NULL, lockdep_is_held(&cpool_mutex)));
__scratch_size = 0;
}
static int __cpool_try_clone(struct crypto_ahash *hash)
{
struct crypto_ahash *tmp;
tmp = crypto_clone_ahash(hash);
if (IS_ERR(tmp))
return PTR_ERR(tmp);
crypto_free_ahash(tmp);
return 0;
}
static int __cpool_alloc_ahash(struct sigpool_entry *e, const char *alg)
{
struct crypto_ahash *cpu0_hash;
int ret;
e->alg = kstrdup(alg, GFP_KERNEL);
if (!e->alg)
return -ENOMEM;
cpu0_hash = crypto_alloc_ahash(alg, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(cpu0_hash)) {
ret = PTR_ERR(cpu0_hash);
goto out_free_alg;
}
e->needs_key = crypto_ahash_get_flags(cpu0_hash) & CRYPTO_TFM_NEED_KEY;
ret = __cpool_try_clone(cpu0_hash);
if (ret)
goto out_free_cpu0_hash;
e->hash = cpu0_hash;
kref_init(&e->kref);
return 0;
out_free_cpu0_hash:
crypto_free_ahash(cpu0_hash);
out_free_alg:
kfree(e->alg);
e->alg = NULL;
return ret;
}
/**
* tcp_sigpool_alloc_ahash - allocates pool for ahash requests
* @alg: name of async hash algorithm
* @scratch_size: reserve a tcp_sigpool::scratch buffer of this size
*/
int tcp_sigpool_alloc_ahash(const char *alg, size_t scratch_size)
{
int i, ret;
/* slow-path */
mutex_lock(&cpool_mutex);
ret = sigpool_reserve_scratch(scratch_size);
if (ret)
goto out;
for (i = 0; i < cpool_populated; i++) {
if (!cpool[i].alg)
continue;
if (strcmp(cpool[i].alg, alg))
continue;
/* pairs with tcp_sigpool_release() */
if (!kref_get_unless_zero(&cpool[i].kref))
kref_init(&cpool[i].kref);
ret = i;
goto out;
}
for (i = 0; i < cpool_populated; i++) {
if (!cpool[i].alg)
break;
}
if (i >= CPOOL_SIZE) {
ret = -ENOSPC;
goto out;
}
ret = __cpool_alloc_ahash(&cpool[i], alg);
if (!ret) {
ret = i;
if (i == cpool_populated)
cpool_populated++;
}
out:
mutex_unlock(&cpool_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(tcp_sigpool_alloc_ahash);
static void __cpool_free_entry(struct sigpool_entry *e)
{
crypto_free_ahash(e->hash);
kfree(e->alg);
memset(e, 0, sizeof(*e));
}
static void cpool_cleanup_work_cb(struct work_struct *work)
{
bool free_scratch = true;
unsigned int i;
mutex_lock(&cpool_mutex);
for (i = 0; i < cpool_populated; i++) {
if (kref_read(&cpool[i].kref) > 0) {
free_scratch = false;
continue;
}
if (!cpool[i].alg)
continue;
__cpool_free_entry(&cpool[i]);
}
if (free_scratch)
sigpool_scratch_free();
mutex_unlock(&cpool_mutex);
}
static DECLARE_WORK(cpool_cleanup_work, cpool_cleanup_work_cb);
static void cpool_schedule_cleanup(struct kref *kref)
{
schedule_work(&cpool_cleanup_work);
}
/**
* tcp_sigpool_release - decreases number of users for a pool. If it was
* the last user of the pool, releases any memory that was consumed.
* @id: tcp_sigpool that was previously allocated by tcp_sigpool_alloc_ahash()
*/
void tcp_sigpool_release(unsigned int id)
{
if (WARN_ON_ONCE(id >= cpool_populated || !cpool[id].alg))
return;
/* slow-path */
kref_put(&cpool[id].kref, cpool_schedule_cleanup);
}
EXPORT_SYMBOL_GPL(tcp_sigpool_release);
/**
* tcp_sigpool_get - increases number of users (refcounter) for a pool
* @id: tcp_sigpool that was previously allocated by tcp_sigpool_alloc_ahash()
*/
void tcp_sigpool_get(unsigned int id)
{
if (WARN_ON_ONCE(id >= cpool_populated || !cpool[id].alg))
return;
kref_get(&cpool[id].kref);
}
EXPORT_SYMBOL_GPL(tcp_sigpool_get);
int tcp_sigpool_start(unsigned int id, struct tcp_sigpool *c) __cond_acquires(0, RCU_BH)
{
struct crypto_ahash *hash;
rcu_read_lock_bh();
if (WARN_ON_ONCE(id >= cpool_populated || !cpool[id].alg)) {
rcu_read_unlock_bh();
return -EINVAL;
}
hash = crypto_clone_ahash(cpool[id].hash);
if (IS_ERR(hash)) {
rcu_read_unlock_bh();
return PTR_ERR(hash);
}
c->req = ahash_request_alloc(hash, GFP_ATOMIC);
if (!c->req) {
crypto_free_ahash(hash);
rcu_read_unlock_bh();
return -ENOMEM;
}
ahash_request_set_callback(c->req, 0, NULL, NULL);
/* Pairs with tcp_sigpool_reserve_scratch(), scratch area is
* valid (allocated) until tcp_sigpool_end().
*/
local_lock_nested_bh(&sigpool_scratch.bh_lock);
c->scratch = rcu_dereference_bh(*this_cpu_ptr(&sigpool_scratch.pad));
return 0;
}
EXPORT_SYMBOL_GPL(tcp_sigpool_start);
void tcp_sigpool_end(struct tcp_sigpool *c) __releases(RCU_BH)
{
struct crypto_ahash *hash = crypto_ahash_reqtfm(c->req);
local_unlock_nested_bh(&sigpool_scratch.bh_lock);
rcu_read_unlock_bh();
ahash_request_free(c->req);
crypto_free_ahash(hash);
}
EXPORT_SYMBOL_GPL(tcp_sigpool_end);
/**
* tcp_sigpool_algo - return algorithm of tcp_sigpool
* @id: tcp_sigpool that was previously allocated by tcp_sigpool_alloc_ahash()
* @buf: buffer to return name of algorithm
* @buf_len: size of @buf
*/
size_t tcp_sigpool_algo(unsigned int id, char *buf, size_t buf_len)
{
if (WARN_ON_ONCE(id >= cpool_populated || !cpool[id].alg))
return -EINVAL;
return strscpy(buf, cpool[id].alg, buf_len);
}
EXPORT_SYMBOL_GPL(tcp_sigpool_algo);
/**
* tcp_sigpool_hash_skb_data - hash data in skb with initialized tcp_sigpool
* @hp: tcp_sigpool pointer
* @skb: buffer to add sign for
* @header_len: TCP header length for this segment
*/
int tcp_sigpool_hash_skb_data(struct tcp_sigpool *hp,
const struct sk_buff *skb,
unsigned int header_len)
{
const unsigned int head_data_len = skb_headlen(skb) > header_len ?
skb_headlen(skb) - header_len : 0;
const struct skb_shared_info *shi = skb_shinfo(skb);
const struct tcphdr *tp = tcp_hdr(skb);
struct ahash_request *req = hp->req;
struct sk_buff *frag_iter;
struct scatterlist sg;
unsigned int i;
sg_init_table(&sg, 1);
sg_set_buf(&sg, ((u8 *)tp) + header_len, head_data_len);
ahash_request_set_crypt(req, &sg, NULL, head_data_len);
if (crypto_ahash_update(req))
return 1;
for (i = 0; i < shi->nr_frags; ++i) {
const skb_frag_t *f = &shi->frags[i];
unsigned int offset = skb_frag_off(f);
struct page *page;
page = skb_frag_page(f) + (offset >> PAGE_SHIFT);
sg_set_page(&sg, page, skb_frag_size(f), offset_in_page(offset));
ahash_request_set_crypt(req, &sg, NULL, skb_frag_size(f));
if (crypto_ahash_update(req))
return 1;
}
skb_walk_frags(skb, frag_iter)
if (tcp_sigpool_hash_skb_data(hp, frag_iter, 0))
return 1;
return 0;
}
EXPORT_SYMBOL(tcp_sigpool_hash_skb_data);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Per-CPU pool of crypto requests");

View File

@ -7,84 +7,73 @@
* Francesco Ruggeri <fruggeri@arista.com>
* Salam Noureddine <noureddine@arista.com>
*/
#include <crypto/hash.h>
#include <linux/tcp.h>
#include <net/tcp.h>
#include <net/ipv6.h>
static int tcp_v6_ao_calc_key(struct tcp_ao_key *mkt, u8 *key,
const struct in6_addr *saddr,
const struct in6_addr *daddr,
__be16 sport, __be16 dport,
__be32 sisn, __be32 disn)
static void tcp_v6_ao_calc_key(struct tcp_ao_key *mkt, u8 *key,
const struct in6_addr *saddr,
const struct in6_addr *daddr,
__be16 sport, __be16 dport,
__be32 sisn, __be32 disn)
{
struct kdf_input_block {
u8 counter;
u8 label[6];
struct tcp6_ao_context ctx;
__be16 outlen;
} __packed * tmp;
struct tcp_sigpool hp;
int err;
} __packed input = {
.counter = 1,
.label = "TCP-AO",
.ctx = {
.saddr = *saddr,
.daddr = *daddr,
.sport = sport,
.dport = dport,
.sisn = sisn,
.disn = disn,
},
.outlen = htons(tcp_ao_digest_size(mkt) * 8), /* in bits */
};
err = tcp_sigpool_start(mkt->tcp_sigpool_id, &hp);
if (err)
return err;
tmp = hp.scratch;
tmp->counter = 1;
memcpy(tmp->label, "TCP-AO", 6);
tmp->ctx.saddr = *saddr;
tmp->ctx.daddr = *daddr;
tmp->ctx.sport = sport;
tmp->ctx.dport = dport;
tmp->ctx.sisn = sisn;
tmp->ctx.disn = disn;
tmp->outlen = htons(tcp_ao_digest_size(mkt) * 8); /* in bits */
err = tcp_ao_calc_traffic_key(mkt, key, tmp, sizeof(*tmp), &hp);
tcp_sigpool_end(&hp);
return err;
tcp_ao_calc_traffic_key(mkt, key, &input, sizeof(input));
}
int tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
const struct sk_buff *skb,
__be32 sisn, __be32 disn)
void tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
const struct sk_buff *skb, __be32 sisn, __be32 disn)
{
const struct ipv6hdr *iph = ipv6_hdr(skb);
const struct tcphdr *th = tcp_hdr(skb);
return tcp_v6_ao_calc_key(mkt, key, &iph->saddr,
&iph->daddr, th->source,
th->dest, sisn, disn);
tcp_v6_ao_calc_key(mkt, key, &iph->saddr, &iph->daddr, th->source,
th->dest, sisn, disn);
}
int tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
const struct sock *sk, __be32 sisn,
__be32 disn, bool send)
void tcp_v6_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
const struct sock *sk, __be32 sisn,
__be32 disn, bool send)
{
if (send)
return tcp_v6_ao_calc_key(mkt, key, &sk->sk_v6_rcv_saddr,
&sk->sk_v6_daddr, htons(sk->sk_num),
sk->sk_dport, sisn, disn);
tcp_v6_ao_calc_key(mkt, key, &sk->sk_v6_rcv_saddr,
&sk->sk_v6_daddr, htons(sk->sk_num),
sk->sk_dport, sisn, disn);
else
return tcp_v6_ao_calc_key(mkt, key, &sk->sk_v6_daddr,
&sk->sk_v6_rcv_saddr, sk->sk_dport,
htons(sk->sk_num), disn, sisn);
tcp_v6_ao_calc_key(mkt, key, &sk->sk_v6_daddr,
&sk->sk_v6_rcv_saddr, sk->sk_dport,
htons(sk->sk_num), disn, sisn);
}
int tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
struct request_sock *req)
void tcp_v6_ao_calc_key_rsk(struct tcp_ao_key *mkt, u8 *key,
struct request_sock *req)
{
struct inet_request_sock *ireq = inet_rsk(req);
return tcp_v6_ao_calc_key(mkt, key,
&ireq->ir_v6_loc_addr, &ireq->ir_v6_rmt_addr,
htons(ireq->ir_num), ireq->ir_rmt_port,
htonl(tcp_rsk(req)->snt_isn),
htonl(tcp_rsk(req)->rcv_isn));
tcp_v6_ao_calc_key(mkt, key,
&ireq->ir_v6_loc_addr, &ireq->ir_v6_rmt_addr,
htons(ireq->ir_num), ireq->ir_rmt_port,
htonl(tcp_rsk(req)->snt_isn),
htonl(tcp_rsk(req)->rcv_isn));
}
struct tcp_ao_key *tcp_v6_ao_lookup(const struct sock *sk,
@ -112,23 +101,19 @@ struct tcp_ao_key *tcp_v6_ao_lookup_rsk(const struct sock *sk,
AF_INET6, sndid, rcvid);
}
int tcp_v6_ao_hash_pseudoheader(struct tcp_sigpool *hp,
const struct in6_addr *daddr,
const struct in6_addr *saddr, int nbytes)
void tcp_v6_ao_hash_pseudoheader(struct tcp_ao_mac_ctx *mac_ctx,
const struct in6_addr *daddr,
const struct in6_addr *saddr, int nbytes)
{
struct tcp6_pseudohdr *bp;
struct scatterlist sg;
bp = hp->scratch;
/* 1. TCP pseudo-header (RFC2460) */
bp->saddr = *saddr;
bp->daddr = *daddr;
bp->len = cpu_to_be32(nbytes);
bp->protocol = cpu_to_be32(IPPROTO_TCP);
struct tcp6_pseudohdr phdr = {
.saddr = *saddr,
.daddr = *daddr,
.len = cpu_to_be32(nbytes),
.protocol = cpu_to_be32(IPPROTO_TCP),
};
sg_init_one(&sg, bp, sizeof(*bp));
ahash_request_set_crypt(hp->req, &sg, NULL, sizeof(*bp));
return crypto_ahash_update(hp->req);
tcp_ao_mac_update(mac_ctx, &phdr, sizeof(phdr));
}
int tcp_v6_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
@ -149,20 +134,10 @@ int tcp_v6_ao_synack_hash(char *ao_hash, struct tcp_ao_key *ao_key,
struct request_sock *req, const struct sk_buff *skb,
int hash_offset, u32 sne)
{
void *hash_buf = NULL;
int err;
u8 tkey_buf[TCP_AO_MAX_TRAFFIC_KEY_LEN];
hash_buf = kmalloc(tcp_ao_digest_size(ao_key), GFP_ATOMIC);
if (!hash_buf)
return -ENOMEM;
tcp_v6_ao_calc_key_rsk(ao_key, tkey_buf, req);
err = tcp_v6_ao_calc_key_rsk(ao_key, hash_buf, req);
if (err)
goto out;
err = tcp_ao_hash_skb(AF_INET6, ao_hash, ao_key, req_to_sk(req), skb,
hash_buf, hash_offset, sne);
out:
kfree(hash_buf);
return err;
return tcp_ao_hash_skb(AF_INET6, ao_hash, ao_key, req_to_sk(req), skb,
tkey_buf, hash_offset, sne);
}

View File

@ -1,7 +1,3 @@
CONFIG_CRYPTO_CMAC=y
CONFIG_CRYPTO_HMAC=y
CONFIG_CRYPTO_RMD160=y
CONFIG_CRYPTO_SHA1=y
CONFIG_IPV6=y
CONFIG_IPV6_MULTIPLE_TABLES=y
CONFIG_NET_L3_MASTER_DEV=y

View File

@ -380,31 +380,6 @@ static void check_listen_socket(void)
close(sk);
}
static const char *fips_fpath = "/proc/sys/crypto/fips_enabled";
static bool is_fips_enabled(void)
{
static int fips_checked = -1;
FILE *fenabled;
int enabled;
if (fips_checked >= 0)
return !!fips_checked;
if (access(fips_fpath, R_OK)) {
if (errno != ENOENT)
test_error("Can't open %s", fips_fpath);
fips_checked = 0;
return false;
}
fenabled = fopen(fips_fpath, "r");
if (!fenabled)
test_error("Can't open %s", fips_fpath);
if (fscanf(fenabled, "%d", &enabled) != 1)
test_error("Can't read from %s", fips_fpath);
fclose(fenabled);
fips_checked = !!enabled;
return !!fips_checked;
}
struct test_key {
char password[TCP_AO_MAXKEYLEN];
const char *alg;
@ -430,14 +405,7 @@ struct key_collection {
static struct key_collection collection;
#define TEST_MAX_MACLEN 16
const char *test_algos[] = {
"cmac(aes128)",
"hmac(sha1)", "hmac(sha512)", "hmac(sha384)", "hmac(sha256)",
"hmac(sha224)", "hmac(sha3-512)",
/* only if !CONFIG_FIPS */
#define TEST_NON_FIPS_ALGOS 2
"hmac(rmd160)", "hmac(md5)"
};
const char *test_algos[] = { "cmac(aes128)", "hmac(sha1)", "hmac(sha256)" };
const unsigned int test_maclens[] = { 1, 4, 12, 16 };
#define MACLEN_SHIFT 2
#define ALGOS_SHIFT 4
@ -452,7 +420,7 @@ static unsigned int make_mask(unsigned int shift, unsigned int prev_shift)
static void init_key_in_collection(unsigned int index, bool randomized)
{
struct test_key *key = &collection.keys[index];
unsigned int algos_nr, algos_index;
unsigned int algos_index;
/* Same for randomized and non-randomized test flows */
key->client_keyid = index;
@ -474,10 +442,7 @@ static void init_key_in_collection(unsigned int index, bool randomized)
key->maclen = test_maclens[index & make_mask(shift, 0)];
algos_index = index & make_mask(ALGOS_SHIFT, shift);
}
algos_nr = ARRAY_SIZE(test_algos);
if (is_fips_enabled())
algos_nr -= TEST_NON_FIPS_ALGOS;
key->alg = test_algos[algos_index % algos_nr];
key->alg = test_algos[algos_index % ARRAY_SIZE(test_algos)];
}
static int init_default_key_collection(unsigned int nr_keys, bool randomized)