diff --git a/include/net/tcp.h b/include/net/tcp.h index fb2bc9edc7de..3c4e6adb0dbd 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -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); diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 1e9e27d6e06b..29fd7b735afa 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -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 /* 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, diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index 21e5164e30db..301b47660305 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -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), diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index 7f9f98813986..7964234f0d08 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -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 diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index a97cdf3e6af4..36a64c1cd8c9 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -9,7 +9,9 @@ */ #define pr_fmt(fmt) "TCP: " fmt -#include +#include +#include +#include #include #include #include @@ -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, diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index c8d7b5d20d18..75eb18a2ad4e 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -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 */ diff --git a/net/ipv4/tcp_sigpool.c b/net/ipv4/tcp_sigpool.c deleted file mode 100644 index 10b2e5970c40..000000000000 --- a/net/ipv4/tcp_sigpool.c +++ /dev/null @@ -1,366 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -#include -#include -#include -#include -#include -#include -#include -#include - -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"); diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c index 3c09ac26206e..01a8472805d1 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -7,84 +7,73 @@ * Francesco Ruggeri * Salam Noureddine */ -#include #include #include #include -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); } diff --git a/tools/testing/selftests/net/tcp_ao/config b/tools/testing/selftests/net/tcp_ao/config index f22148512365..1b120bfd89c4 100644 --- a/tools/testing/selftests/net/tcp_ao/config +++ b/tools/testing/selftests/net/tcp_ao/config @@ -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 diff --git a/tools/testing/selftests/net/tcp_ao/key-management.c b/tools/testing/selftests/net/tcp_ao/key-management.c index 69d9a7a05d5c..d86bb380b79f 100644 --- a/tools/testing/selftests/net/tcp_ao/key-management.c +++ b/tools/testing/selftests/net/tcp_ao/key-management.c @@ -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)