From nobody Thu Apr 9 15:36:00 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 930F331F9A6; Sat, 7 Mar 2026 22:46:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772923575; cv=none; b=Q3GrG6c55G26UcZgwXAZKZp071eE45LS3JeTLbgXvt0PqT2Ir0Jb4eOv9vm1Ji7itztLFHyw9mG4HcgKCiNDdU/8+g16Kpedl5LdLDwnNB6WZ7RIBcZQmv9QMg+fujUVtf8h1ieG+Vtno+o5e6UPZlhXJ+/A8ZWPIGslWqrb1oc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772923575; c=relaxed/simple; bh=JdP43UekUUIF5xjIRJ50CJU+NjrHRTlXAUqp4IINmQE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=CkDGCkjqTZnEB90RfAXOUtFvNNAiKY95vgYv1l3kQhDbcfeuK9lgFvSoZ1eTMDzukWOgXqZWCFWRAKYMCfs20RnCx/Yakx1qVyA0Xju8JWp73ixzCi8u451WtKICTDr/PAXcXAZcA74PYmTW/6afeIHXUSVO+GMOzQJMzZF6bow= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=QoU71WrA; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="QoU71WrA" Received: by smtp.kernel.org (Postfix) with ESMTPSA id DEC7CC2BCB1; Sat, 7 Mar 2026 22:46:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1772923575; bh=JdP43UekUUIF5xjIRJ50CJU+NjrHRTlXAUqp4IINmQE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=QoU71WrAMYeUuK4HXnzJGTotaK4TxnZdIkyyfpPLjQHOSbEXhB+hm4KI3wYXD6bVn riVxhNqcDb/gZvPUkHNOUpXNIwipaf6eNa7uxccRY1tVbUqX5VMCfRIjoDuU7pbxbr Fzqs/3jx65+CTIPBrEOH+q5uWO/L9pqWjya9vY12B0radrBvffQQgT1qpW0PUrIfXq jl6fNtqM0xiD0wSPZt0NPoVrOJnfeHbt1cZ1S7FxviWvlGT2bLyKVC459nVm8OQrAo flhDDFB/xQJBjapm1ONAbp39ThU1cL8bNIyjxTV+c3nG+mjWX2kD6b3b7Z8Ix94Cm9 yKKWyyQB7uCKg== From: Eric Biggers To: netdev@vger.kernel.org Cc: linux-crypto@vger.kernel.org, linux-kernel@vger.kernel.org, Eric Dumazet , Neal Cardwell , Kuniyuki Iwashima , "David S . Miller" , David Ahern , Jakub Kicinski , Paolo Abeni , Simon Horman , Ard Biesheuvel , "Jason A . Donenfeld" , Herbert Xu , Dmitry Safonov <0x7f454c46@gmail.com>, Eric Biggers Subject: [RFC PATCH 2/8] net/tcp-ao: Use crypto library API instead of crypto_ahash Date: Sat, 7 Mar 2026 14:43:35 -0800 Message-ID: <20260307224341.5644-3-ebiggers@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260307224341.5644-1-ebiggers@kernel.org> References: <20260307224341.5644-1-ebiggers@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Currently the kernel's TCP-AO implementation does the MAC and KDF computations using the crypto_ahash API. This API is inefficient and difficult to use, and it has required extensive workarounds in the form of per-CPU preallocated objects (tcp_sigpool) to work at all. Let's use lib/crypto/ instead. This means switching to straightforward stack-allocated structures, virtually addressed buffers, and direct function calls. It also means removing quite a bit of error handling. This makes TCP-AO quite a bit faster. This also enables many additional cleanups, which later commits will handle: removing tcp-sigpool, removing support for crypto_tfm cloning, removing more error handling, and replacing more dynamically-allocated buffers with stack buffers based on the now-statically-known limits. Signed-off-by: Eric Biggers --- include/net/tcp_ao.h | 27 +- net/ipv4/Kconfig | 5 +- net/ipv4/tcp_ao.c | 523 +++++++++++----------- net/ipv6/tcp_ao.c | 63 ++- tools/testing/selftests/net/tcp_ao/config | 2 - 5 files changed, 304 insertions(+), 316 deletions(-) diff --git a/include/net/tcp_ao.h b/include/net/tcp_ao.h index 1e9e27d6e06ba..f845bc631bc1e 100644 --- a/include/net/tcp_ao.h +++ b/include/net/tcp_ao.h @@ -1,11 +1,10 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef _TCP_AO_H #define _TCP_AO_H =20 -#define TCP_AO_KEY_ALIGN 1 -#define __tcp_ao_key_align __aligned(TCP_AO_KEY_ALIGN) +#include /* for SHA256_DIGEST_SIZE */ =20 union tcp_ao_addr { struct in_addr a4; #if IS_ENABLED(CONFIG_IPV6) struct in6_addr a6; @@ -30,15 +29,26 @@ struct tcp_ao_counters { atomic64_t key_not_found; atomic64_t ao_required; atomic64_t dropped_icmp; }; =20 +enum tcp_ao_algo_id { + TCP_AO_ALGO_HMAC_SHA1 =3D 1, /* specified by RFC 5926 */ + TCP_AO_ALGO_HMAC_SHA256 =3D 2, /* Linux extension */ + TCP_AO_ALGO_AES_128_CMAC =3D 3, /* specified by RFC 5926 */ +}; + +#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; u8 family; u8 keylen; @@ -166,18 +176,19 @@ struct tcp6_ao_context { __be16 dport; __be32 sisn; __be32 disn; }; =20 -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_W= AIT2 | \ TCPF_CLOSE_WAIT | TCPF_LAST_ACK | TCPF_CLOSING) =20 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_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, const u8 *tkey, int hash_offset, u32 sne); int tcp_parse_ao(struct sock *sk, int cmd, unsigned short int family, @@ -186,12 +197,12 @@ struct tcp_ao_key *tcp_ao_established_key(const struc= t sock *sk, struct tcp_ao_info *ao, int sndid, int rcvid); 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 c= ode); int tcp_ao_get_mkts(struct sock *sk, sockptr_t optval, sockptr_t optlen); int tcp_ao_get_sock_info(struct sock *sk, sockptr_t optval, sockptr_t optl= en); @@ -232,11 +243,11 @@ struct tcp_ao_key *tcp_v4_ao_lookup_rsk(const struct = sock *sk, int sndid, int rcvid); 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, +int tcp_v6_ao_hash_pseudoheader(struct tcp_ao_mac_ctx *mac_ctx, 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, diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig index df922f9f52891..0fa293527cee9 100644 --- a/net/ipv4/Kconfig +++ b/net/ipv4/Kconfig @@ -745,13 +745,14 @@ config DEFAULT_TCP_CONG config TCP_SIGPOOL tristate =20 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 && IPV6 !=3D m # seq-number extension needs WRITE_ONCE(u= 64) help TCP-AO specifies the use of stronger Message Authentication Codes (MACs= ), protects against replays for long-lived TCP connections, and provides more details on the association of security with TCP diff --git a/net/ipv4/tcp_ao.c b/net/ipv4/tcp_ao.c index b21bd69b4e829..0d24cbd66c9a1 100644 --- a/net/ipv4/tcp_ao.c +++ b/net/ipv4/tcp_ao.c @@ -7,11 +7,13 @@ * Francesco Ruggeri * Salam Noureddine */ #define pr_fmt(fmt) "TCP: " fmt =20 -#include +#include +#include +#include #include #include #include =20 #include @@ -19,36 +21,137 @@ #include #include =20 DEFINE_STATIC_KEY_DEFERRED_FALSE(tcp_ao_needed, HZ); =20 -int tcp_ao_calc_traffic_key(struct tcp_ao_key *mkt, u8 *key, void *ctx, - unsigned int len, struct tcp_sigpool *hp) -{ - struct scatterlist sg; - int ret; +static const struct tcp_ao_algo { + const char *name; + unsigned int digest_size; +} tcp_ao_algos[] =3D { + [TCP_AO_ALGO_HMAC_SHA1] =3D { + .name =3D "hmac(sha1)", + .digest_size =3D SHA1_DIGEST_SIZE, + }, + [TCP_AO_ALGO_HMAC_SHA256] =3D { + .name =3D "hmac(sha256)", + .digest_size =3D SHA256_DIGEST_SIZE, + }, + [TCP_AO_ALGO_AES_128_CMAC] =3D { + .name =3D "cmac(aes128)", + .digest_size =3D 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) +{ + for (size_t i =3D 0; i < ARRAY_SIZE(tcp_ao_algos); i++) { + const struct tcp_ao_algo *algo =3D &tcp_ao_algos[i]; + + 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) =3D=3D 0) + return algo; + } + return NULL; +} =20 - if (crypto_ahash_setkey(crypto_ahash_reqtfm(hp->req), - mkt->key, mkt->keylen)) - 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 =3D 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. */ + } +} =20 - ret =3D crypto_ahash_init(hp->req); - if (ret) - goto clear_hash; +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. */ + } +} =20 - sg_init_one(&sg, ctx, len); - ahash_request_set_crypt(hp->req, &sg, key, len); - crypto_ahash_update(hp->req); +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. */ + } +} =20 - ret =3D crypto_ahash_final(hp->req); - if (ret) - goto clear_hash; +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; =20 - return 0; -clear_hash: - memset(key, 0, tcp_ao_digest_size(mkt)); - return 1; + 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. */ + } } =20 bool tcp_ao_ignore_icmp(const struct sock *sk, int family, int type, int c= ode) { bool ignore_icmp =3D false; @@ -252,33 +355,30 @@ static struct tcp_ao_key *tcp_ao_copy_key(struct sock= *sk, if (!new_key) return NULL; =20 *new_key =3D *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); =20 return new_key; } =20 static void tcp_ao_key_free_rcu(struct rcu_head *head) { struct tcp_ao_key *key =3D container_of(head, struct tcp_ao_key, rcu); =20 - tcp_sigpool_release(key->tcp_sigpool_id); kfree_sensitive(key); } =20 static void tcp_ao_info_free(struct tcp_ao_info *ao) { struct tcp_ao_key *key; struct hlist_node *n; =20 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); static_branch_slow_dec_deferred(&tcp_ao_needed); } @@ -344,33 +444,26 @@ static int tcp_v4_ao_calc_key(struct tcp_ao_key *mkt,= u8 *key, struct kdf_input_block { u8 counter; u8 label[6]; struct tcp4_ao_context ctx; __be16 outlen; - } __packed * tmp; - struct tcp_sigpool hp; - int err; - - err =3D tcp_sigpool_start(mkt->tcp_sigpool_id, &hp); - if (err) - return err; - - tmp =3D hp.scratch; - tmp->counter =3D 1; - memcpy(tmp->label, "TCP-AO", 6); - tmp->ctx.saddr =3D saddr; - tmp->ctx.daddr =3D daddr; - tmp->ctx.sport =3D sport; - tmp->ctx.dport =3D dport; - tmp->ctx.sisn =3D sisn; - tmp->ctx.disn =3D disn; - tmp->outlen =3D htons(tcp_ao_digest_size(mkt) * 8); /* in bits */ - - err =3D tcp_ao_calc_traffic_key(mkt, key, tmp, sizeof(*tmp), &hp); - tcp_sigpool_end(&hp); - - return err; + } __packed input =3D { + .counter =3D 1, + .label =3D "TCP-AO", + .ctx =3D { + .saddr =3D saddr, + .daddr =3D daddr, + .sport =3D sport, + .dport =3D dport, + .sisn =3D sisn, + .disn =3D disn, + }, + .outlen =3D htons(tcp_ao_digest_size(mkt) * 8), /* in bits */ + }; + + tcp_ao_calc_traffic_key(mkt, key, &input, sizeof(input)); + return 0; } =20 int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key, const struct sock *sk, __be32 sisn, __be32 disn, bool send) @@ -433,60 +526,57 @@ static int tcp_ao_calc_key_skb(struct tcp_ao_key *mkt= , u8 *key, return tcp_v6_ao_calc_key_skb(mkt, key, skb, sisn, disn); #endif return -EAFNOSUPPORT; } =20 -static int tcp_v4_ao_hash_pseudoheader(struct tcp_sigpool *hp, +static int 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 =3D { + .saddr =3D saddr, + .daddr =3D daddr, + .pad =3D 0, + .protocol =3D IPPROTO_TCP, + .len =3D cpu_to_be16(nbytes), + }; =20 - bp =3D hp->scratch; - bp->saddr =3D saddr; - bp->daddr =3D daddr; - bp->pad =3D 0; - bp->protocol =3D IPPROTO_TCP; - bp->len =3D 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)); + return 0; } =20 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 =3D tcp_hdr(skb); =20 /* TODO: Can we rely on checksum being zero to mean outbound pkt? */ if (!th->check) { if (family =3D=3D AF_INET) - return tcp_v4_ao_hash_pseudoheader(hp, sk->sk_daddr, + return tcp_v4_ao_hash_pseudoheader(mac_ctx, sk->sk_daddr, sk->sk_rcv_saddr, skb->len); #if IS_ENABLED(CONFIG_IPV6) else if (family =3D=3D AF_INET6) - return tcp_v6_ao_hash_pseudoheader(hp, &sk->sk_v6_daddr, + return tcp_v6_ao_hash_pseudoheader(mac_ctx, &sk->sk_v6_daddr, &sk->sk_v6_rcv_saddr, skb->len); #endif else return -EAFNOSUPPORT; } =20 if (family =3D=3D AF_INET) { const struct iphdr *iph =3D ip_hdr(skb); =20 - return tcp_v4_ao_hash_pseudoheader(hp, iph->daddr, + return tcp_v4_ao_hash_pseudoheader(mac_ctx, iph->daddr, iph->saddr, skb->len); #if IS_ENABLED(CONFIG_IPV6) } else if (family =3D=3D AF_INET6) { const struct ipv6hdr *iph =3D ipv6_hdr(skb); =20 - return tcp_v6_ao_hash_pseudoheader(hp, &iph->daddr, + return tcp_v6_ao_hash_pseudoheader(mac_ctx, &iph->daddr, &iph->saddr, skb->len); #endif } return -EAFNOSUPPORT; } @@ -504,35 +594,24 @@ u32 tcp_ao_compute_sne(u32 next_sne, u32 next_seq, u3= 2 seq) } =20 return sne; } =20 -/* 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; - - bp =3D (__be32 *)hp->scratch; - *bp =3D htonl(sne); + __be32 sne_be32 =3D htonl(sne); =20 - 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)); } =20 -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 =3D hp->scratch; - int err, len; + /* Full TCP header (th->doff << 2) should fit into scratch area. */ + u8 hdr[60]; + int len; =20 /* We are not allowed to change tcphdr, make a local copy */ if (exclude_options) { len =3D sizeof(*th) + sizeof(struct tcp_ao_hdr) + hash_len; memcpy(hdr, th, sizeof(*th)); @@ -548,126 +627,105 @@ static int tcp_ao_hash_header(struct tcp_sigpool *h= p, /* zero out tcp-ao hash */ ((struct tcphdr *)hdr)->check =3D 0; memset(hdr + hash_offset, 0, hash_len); } =20 - sg_init_one(&sg, hdr, len); - ahash_request_set_crypt(hp->req, &sg, NULL, len); - err =3D crypto_ahash_update(hp->req); - WARN_ON_ONCE(err !=3D 0); - return err; + tcp_ao_mac_update(mac_ctx, hdr, len); } =20 int tcp_ao_hash_hdr(unsigned short int family, char *ao_hash, struct tcp_ao_key *key, const u8 *tkey, const union tcp_ao_addr *daddr, const union tcp_ao_addr *saddr, const struct tcphdr *th, u32 sne) { - int tkey_len =3D tcp_ao_digest_size(key); int hash_offset =3D ao_hash - (char *)th; - struct tcp_sigpool hp; - void *hash_buf =3D NULL; - - hash_buf =3D 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; + struct tcp_ao_mac_ctx mac_ctx; + u8 hash_buf[TCP_AO_MAX_MAC_LEN]; =20 - 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 =3D=3D 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 =3D=3D 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); =20 memcpy(ao_hash, hash_buf, tcp_ao_maclen(key)); - tcp_sigpool_end(&hp); - kfree(hash_buf); return 0; =20 clear_hash: - tcp_sigpool_end(&hp); -clear_hash_noput: memset(ao_hash, 0, tcp_ao_maclen(key)); - kfree(hash_buf); return 1; } =20 +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 =3D skb_headlen(skb) > header_len ? + skb_headlen(skb) - header_len : 0; + const struct skb_shared_info *shi =3D 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 =3D 0; i < shi->nr_frags; ++i) { + const skb_frag_t *f =3D &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 =3D 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 =3D tcp_hdr(skb); - int tkey_len =3D tcp_ao_digest_size(key); - struct tcp_sigpool hp; - void *hash_buf =3D NULL; - - hash_buf =3D 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; + struct tcp_ao_mac_ctx mac_ctx; + u8 hash_buf[TCP_AO_MAX_MAC_LEN]; =20 - 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); =20 memcpy(ao_hash, hash_buf, tcp_ao_maclen(key)); - tcp_sigpool_end(&hp); - kfree(hash_buf); return 0; =20 clear_hash: - tcp_sigpool_end(&hp); -clear_hash_noput: memset(ao_hash, 0, tcp_ao_maclen(key)); - kfree(hash_buf); return 1; } =20 int tcp_v4_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key, const struct sock *sk, const struct sk_buff *skb, @@ -1278,11 +1336,10 @@ int tcp_ao_copy_all_matching(const struct sock *sk,= struct sock *newsk, return 0; =20 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); } free_ao: kfree(new_ao); @@ -1334,27 +1391,14 @@ static int tcp_ao_verify_ipv4(struct sock *sk, stru= ct tcp_ao_add *cmd, =20 *addr =3D (union tcp_ao_addr *)&sin->sin_addr; return 0; } =20 -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 =3D false; - struct crypto_ahash *tfm; - struct tcp_sigpool hp; - void *tmp_key =3D 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 =3D (cmd->keylen !=3D 16); - tmp_key =3D kmalloc(cmd->keylen, GFP_KERNEL); - if (!tmp_key) - return -ENOMEM; - } =20 key->maclen =3D cmd->maclen ?: 12; /* 12 is the default in RFC5925 */ =20 /* Check: maclen + tcp-ao header <=3D (MAX_TCP_OPTION_SPACE - mss * - tstamp (including sackperm) @@ -1386,68 +1430,31 @@ static int tcp_ao_parse_crypto(struct tcp_ao_add *c= md, struct tcp_ao_key *key) */ syn_tcp_option_space =3D MAX_TCP_OPTION_SPACE; syn_tcp_option_space -=3D TCPOLEN_MSS_ALIGNED; syn_tcp_option_space -=3D TCPOLEN_TSTAMP_ALIGNED; syn_tcp_option_space -=3D TCPOLEN_WSCALE_ALIGNED; - if (tcp_ao_len_aligned(key) > syn_tcp_option_space) { - err =3D -EMSGSIZE; - goto err_kfree; - } - - key->keylen =3D cmd->keylen; - memcpy(key->key, cmd->key, cmd->keylen); - - err =3D tcp_sigpool_start(key->tcp_sigpool_id, &hp); - if (err) - goto err_kfree; - - tfm =3D crypto_ahash_reqtfm(hp.req); - if (is_kdf_aes_128_cmac) { - void *scratch =3D 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 =3D crypto_ahash_setkey(tfm, scratch, 16); - if (err) - goto err_pool_end; - - err =3D crypto_ahash_init(hp.req); - if (err) - goto err_pool_end; - - ahash_request_set_crypt(hp.req, &sg, key->key, cmd->keylen); - err =3D crypto_ahash_update(hp.req); - if (err) - goto err_pool_end; - - err |=3D crypto_ahash_final(hp.req); - if (err) - goto err_pool_end; - key->keylen =3D 16; + if (tcp_ao_len_aligned(key) > syn_tcp_option_space) + return -EMSGSIZE; + + if (key->algo =3D=3D TCP_AO_ALGO_AES_128_CMAC && + cmd->keylen !=3D 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 =3D AES_KEYSIZE_128; + } else { + memcpy(key->key, cmd->key, cmd->keylen); + key->keylen =3D cmd->keylen; } =20 - err =3D 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; =20 return 0; - -err_pool_end: - tcp_sigpool_end(&hp); -err_kfree: - kfree_sensitive(tmp_key); - return err; } =20 #if IS_ENABLED(CONFIG_IPV6) static int tcp_ao_verify_ipv6(struct sock *sk, struct tcp_ao_add *cmd, union tcp_ao_addr **paddr, @@ -1547,58 +1554,37 @@ static struct tcp_ao_info *getsockopt_ao_info(struc= t sock *sk) #define TCP_AO_GET_KEYF_VALID (TCP_AO_KEYF_IFINDEX) =20 static struct tcp_ao_key *tcp_ao_key_alloc(struct sock *sk, struct tcp_ao_add *cmd) { - const char *algo =3D 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; =20 /* Force null-termination of alg_name */ cmd->alg_name[ARRAY_SIZE(cmd->alg_name) - 1] =3D '\0'; =20 - /* RFC5926, 3.1.1.2. KDF_AES_128_CMAC */ - if (!strcmp("cmac(aes128)", algo)) - algo =3D "cmac(aes)"; - else if (strcmp("hmac(sha1)", algo) && - strcmp("hmac(sha256)", algo) && - (strcmp("cmac(aes)", algo) || cmd->keylen !=3D 16)) - return ERR_PTR(-ENOENT); - - /* 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 =3D tcp_sigpool_alloc_ahash(algo, 60); - if (pool_id < 0) - return ERR_PTR(pool_id); - - err =3D tcp_sigpool_start(pool_id, &hp); - if (err) - goto err_free_pool; + if (strcmp(cmd->alg_name, "cmac(aes)") =3D=3D 0 && + cmd->keylen =3D=3D AES_KEYSIZE_128) + strscpy(cmd->alg_name, "cmac(aes128)"); =20 - tfm =3D crypto_ahash_reqtfm(hp.req); - digest_size =3D crypto_ahash_digestsize(tfm); - tcp_sigpool_end(&hp); + algo =3D tcp_ao_find_algo(cmd->alg_name); + if (!algo) + return ERR_PTR(-ENOENT); =20 - size =3D sizeof(struct tcp_ao_key) + (digest_size << 1); + size =3D sizeof(struct tcp_ao_key) + (algo->digest_size << 1); key =3D sock_kmalloc(sk, size, GFP_KERNEL); - if (!key) { - err =3D -ENOMEM; - goto err_free_pool; - } + if (!key) + return ERR_PTR(-ENOMEM); =20 - key->tcp_sigpool_id =3D pool_id; - key->digest_size =3D digest_size; + key->algo =3D algo - tcp_ao_algos; + key->digest_size =3D algo->digest_size; return key; - -err_free_pool: - tcp_sigpool_release(pool_id); - return ERR_PTR(err); } =20 static int tcp_ao_add_cmd(struct sock *sk, unsigned short int family, sockptr_t optval, int optlen) { @@ -1755,11 +1741,10 @@ static int tcp_ao_add_cmd(struct sock *sk, unsigned= short int family, WRITE_ONCE(ao_info->rnext_key, key); return 0; =20 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) kfree(ao_info); return ret; @@ -2286,11 +2271,15 @@ static int tcp_ao_copy_mkts_to_user(const struct so= ck *sk, opt_out.keylen =3D key->keylen; opt_out.ifindex =3D key->l3index; opt_out.pkt_good =3D atomic64_read(&key->pkt_good); opt_out.pkt_bad =3D 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 =3D=3D 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); =20 /* Copy key to user */ if (copy_to_sockptr_offset(optval, out_offset, &opt_out, bytes_to_write)) return -EFAULT; diff --git a/net/ipv6/tcp_ao.c b/net/ipv6/tcp_ao.c index 3c09ac26206e3..2dcfe9dda7f4a 100644 --- a/net/ipv6/tcp_ao.c +++ b/net/ipv6/tcp_ao.c @@ -5,11 +5,10 @@ * * Authors: Dmitry Safonov * Francesco Ruggeri * Salam Noureddine */ -#include #include =20 #include #include =20 @@ -22,33 +21,26 @@ static int tcp_v6_ao_calc_key(struct tcp_ao_key *mkt, u= 8 *key, struct kdf_input_block { u8 counter; u8 label[6]; struct tcp6_ao_context ctx; __be16 outlen; - } __packed * tmp; - struct tcp_sigpool hp; - int err; - - err =3D tcp_sigpool_start(mkt->tcp_sigpool_id, &hp); - if (err) - return err; - - tmp =3D hp.scratch; - tmp->counter =3D 1; - memcpy(tmp->label, "TCP-AO", 6); - tmp->ctx.saddr =3D *saddr; - tmp->ctx.daddr =3D *daddr; - tmp->ctx.sport =3D sport; - tmp->ctx.dport =3D dport; - tmp->ctx.sisn =3D sisn; - tmp->ctx.disn =3D disn; - tmp->outlen =3D htons(tcp_ao_digest_size(mkt) * 8); /* in bits */ - - err =3D tcp_ao_calc_traffic_key(mkt, key, tmp, sizeof(*tmp), &hp); - tcp_sigpool_end(&hp); - - return err; + } __packed input =3D { + .counter =3D 1, + .label =3D "TCP-AO", + .ctx =3D { + .saddr =3D *saddr, + .daddr =3D *daddr, + .sport =3D sport, + .dport =3D dport, + .sisn =3D sisn, + .disn =3D disn, + }, + .outlen =3D htons(tcp_ao_digest_size(mkt) * 8), /* in bits */ + }; + + tcp_ao_calc_traffic_key(mkt, key, &input, sizeof(input)); + return 0; } =20 int tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key, const struct sk_buff *skb, __be32 sisn, __be32 disn) @@ -110,27 +102,24 @@ struct tcp_ao_key *tcp_v6_ao_lookup_rsk(const struct = sock *sk, l3index =3D l3mdev_master_ifindex_by_index(sock_net(sk), ireq->ir_iif); return tcp_ao_do_lookup(sk, l3index, (union tcp_ao_addr *)addr, AF_INET6, sndid, rcvid); } =20 -int tcp_v6_ao_hash_pseudoheader(struct tcp_sigpool *hp, +int 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 =3D hp->scratch; /* 1. TCP pseudo-header (RFC2460) */ - bp->saddr =3D *saddr; - bp->daddr =3D *daddr; - bp->len =3D cpu_to_be32(nbytes); - bp->protocol =3D 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); + struct tcp6_pseudohdr phdr =3D { + .saddr =3D *saddr, + .daddr =3D *daddr, + .len =3D cpu_to_be32(nbytes), + .protocol =3D cpu_to_be32(IPPROTO_TCP), + }; + + tcp_ao_mac_update(mac_ctx, &phdr, sizeof(phdr)); + return 0; } =20 int tcp_v6_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) diff --git a/tools/testing/selftests/net/tcp_ao/config b/tools/testing/self= tests/net/tcp_ao/config index 0ec38c167e6df..1b120bfd89c40 100644 --- a/tools/testing/selftests/net/tcp_ao/config +++ b/tools/testing/selftests/net/tcp_ao/config @@ -1,7 +1,5 @@ -CONFIG_CRYPTO_HMAC=3Dy -CONFIG_CRYPTO_SHA1=3Dy CONFIG_IPV6=3Dy CONFIG_IPV6_MULTIPLE_TABLES=3Dy CONFIG_NET_L3_MASTER_DEV=3Dy CONFIG_NET_VRF=3Dy CONFIG_TCP_AO=3Dy --=20 2.53.0