]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
net/tcp-ao: Use crypto library API instead of crypto_ahash
authorEric Biggers <ebiggers@kernel.org>
Mon, 27 Apr 2026 17:27:24 +0000 (10:27 -0700)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 30 Apr 2026 07:38:56 +0000 (09:38 +0200)
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.

Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
Link: https://patch.msgid.link/20260427172727.9310-3-ebiggers@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
include/net/tcp_ao.h
net/ipv4/Kconfig
net/ipv4/tcp_ao.c
net/ipv6/tcp_ao.c
tools/testing/selftests/net/tcp_ao/config

index 1e9e27d6e06ba1be185910d7618ef6453e72ba38..20997aef3b0d81e96d3a1c3f864db21aa06891e8 100644 (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,7 +183,6 @@ 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)
@@ -176,6 +190,8 @@ struct tcp_sigpool;
 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,
@@ -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);
@@ -234,7 +250,7 @@ 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,
index 21e5164e30db604c7f7d119e37316e22d894a447..77b053b445a08df6190360c17a82c487be80d3bf 100644 (file)
@@ -746,9 +746,10 @@ config TCP_SIGPOOL
 
 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),
index b21bd69b4e829ec34a399777bd77e298fe6cfc34..0d24cbd66c9a1250a176c85b3230350f13b7d8f7 100644 (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>
 
 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)
-{
-       struct scatterlist sg;
-       int ret;
+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)
+{
+       for (size_t i = 0; i < ARRAY_SIZE(tcp_ao_algos); i++) {
+               const struct tcp_ao_algo *algo = &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) == 0)
+                       return algo;
+       }
+       return NULL;
+}
 
-       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 = 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. */
+       }
+}
 
-       ret = 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. */
+       }
+}
 
-       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. */
+       }
+}
 
-       ret = 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;
 
-       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. */
+       }
 }
 
 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);
@@ -346,29 +446,22 @@ 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;
-
-       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;
+       } __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 */
+       };
+
+       tcp_ao_calc_traffic_key(mkt, key, &input, sizeof(input));
+       return 0;
 }
 
 int tcp_v4_ao_calc_key_sk(struct tcp_ao_key *mkt, u8 *key,
@@ -435,40 +528,37 @@ static int tcp_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
        return -EAFNOSUPPORT;
 }
 
-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 = {
+               .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));
+       return 0;
 }
 
 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,
+                       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 == 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
@@ -478,13 +568,13 @@ static int tcp_ao_hash_pseudoheader(unsigned short int family,
        if (family == AF_INET) {
                const struct iphdr *iph = ip_hdr(skb);
 
-               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 == AF_INET6) {
                const struct ipv6hdr *iph = ipv6_hdr(skb);
 
-               return tcp_v6_ao_hash_pseudoheader(hp, &iph->daddr,
+               return tcp_v6_ao_hash_pseudoheader(mac_ctx, &iph->daddr,
                                &iph->saddr, skb->len);
 #endif
        }
@@ -506,31 +596,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;
-
-       bp = (__be32 *)hp->scratch;
-       *bp = htonl(sne);
+       __be32 sne_be32 = 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 +629,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 +638,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;
-
-       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;
+       struct tcp_ao_mac_ctx mac_ctx;
+       u8 hash_buf[TCP_AO_MAX_MAC_LEN];
 
-       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;
-
-       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;
+       struct tcp_ao_mac_ctx mac_ctx;
+       u8 hash_buf[TCP_AO_MAX_MAC_LEN];
 
-       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;
 }
 
@@ -1280,7 +1338,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 +1393,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 +1432,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;
-       }
-
-       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;
+       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;
        }
 
-       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,54 +1556,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)";
-       else if (strcmp("hmac(sha1)", algo) &&
-                strcmp("hmac(sha256)", algo) &&
-                (strcmp("cmac(aes)", algo) || cmd->keylen != 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 = tcp_sigpool_alloc_ahash(algo, 60);
-       if (pool_id < 0)
-               return ERR_PTR(pool_id);
-
-       err = tcp_sigpool_start(pool_id, &hp);
-       if (err)
-               goto err_free_pool;
+       if (strcmp(cmd->alg_name, "cmac(aes)") == 0 &&
+           cmd->keylen == AES_KEYSIZE_128)
+               strscpy(cmd->alg_name, "cmac(aes128)");
 
-       tfm = crypto_ahash_reqtfm(hp.req);
-       digest_size = crypto_ahash_digestsize(tfm);
-       tcp_sigpool_end(&hp);
+       algo = tcp_ao_find_algo(cmd->alg_name);
+       if (!algo)
+               return ERR_PTR(-ENOENT);
 
-       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,
@@ -1757,7 +1743,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)
@@ -2288,7 +2273,11 @@ match:
                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,
index 3c09ac26206e3a4f13857eb20ff31a51bfa02017..2dcfe9dda7f4a295b37788ad1252b3b018379ade 100644 (file)
@@ -7,7 +7,6 @@
  *             Francesco Ruggeri <fruggeri@arista.com>
  *             Salam Noureddine <noureddine@arista.com>
  */
-#include <crypto/hash.h>
 #include <linux/tcp.h>
 
 #include <net/tcp.h>
@@ -24,29 +23,22 @@ static int tcp_v6_ao_calc_key(struct tcp_ao_key *mkt, u8 *key,
                u8                      label[6];
                struct tcp6_ao_context  ctx;
                __be16                  outlen;
-       } __packed * tmp;
-       struct tcp_sigpool hp;
-       int err;
-
-       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;
+       } __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 */
+       };
+
+       tcp_ao_calc_traffic_key(mkt, key, &input, sizeof(input));
+       return 0;
 }
 
 int tcp_v6_ao_calc_key_skb(struct tcp_ao_key *mkt, u8 *key,
@@ -112,23 +104,20 @@ 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,
+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 = 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);
-
-       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 = {
+               .saddr = *saddr,
+               .daddr = *daddr,
+               .len = cpu_to_be32(nbytes),
+               .protocol = cpu_to_be32(IPPROTO_TCP),
+       };
+
+       tcp_ao_mac_update(mac_ctx, &phdr, sizeof(phdr));
+       return 0;
 }
 
 int tcp_v6_ao_hash_skb(char *ao_hash, struct tcp_ao_key *key,
index 47228a7d0b90ea0435de342a1f6269b6c28dc437..1b120bfd89c403477f76fd758f5efc9baddecf59 100644 (file)
@@ -1,6 +1,3 @@
-CONFIG_CRYPTO_CMAC=y
-CONFIG_CRYPTO_HMAC=y
-CONFIG_CRYPTO_SHA1=y
 CONFIG_IPV6=y
 CONFIG_IPV6_MULTIPLE_TABLES=y
 CONFIG_NET_L3_MASTER_DEV=y