sctp: Use HMAC-SHA1 and HMAC-SHA256 library for chunk authentication

For SCTP chunk authentication, use the HMAC-SHA1 and HMAC-SHA256 library
functions instead of crypto_shash.  This is simpler and faster.  There's
no longer any need to pre-allocate 'crypto_shash' objects; the SCTP code
now simply calls into the HMAC code directly.

As part of this, make SCTP always support both HMAC-SHA1 and
HMAC-SHA256.  Previously, it only guaranteed support for HMAC-SHA1.
However, HMAC-SHA256 tended to be supported too anyway, as it was
supported if CONFIG_CRYPTO_SHA256 was enabled elsewhere in the kconfig.

Acked-by: Xin Long <lucien.xin@gmail.com>
Signed-off-by: Eric Biggers <ebiggers@kernel.org>
Link: https://patch.msgid.link/20250818205426.30222-4-ebiggers@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Eric Biggers 2025-08-18 13:54:24 -07:00 committed by Jakub Kicinski
parent dd91c79e4f
commit bf40785fa4
9 changed files with 48 additions and 177 deletions

View File

@ -22,16 +22,11 @@ struct sctp_endpoint;
struct sctp_association; struct sctp_association;
struct sctp_authkey; struct sctp_authkey;
struct sctp_hmacalgo; struct sctp_hmacalgo;
struct crypto_shash;
/* /* Defines an HMAC algorithm supported by SCTP chunk authentication */
* Define a generic struct that will hold all the info
* necessary for an HMAC transform
*/
struct sctp_hmac { struct sctp_hmac {
__u16 hmac_id; /* one of the above ids */ __u16 hmac_id; /* one of SCTP_AUTH_HMAC_ID_* */
char *hmac_name; /* name for loading */ __u16 hmac_len; /* length of the HMAC value in bytes */
__u16 hmac_len; /* length of the signature */
}; };
/* This is generic structure that containst authentication bytes used /* This is generic structure that containst authentication bytes used
@ -78,9 +73,9 @@ int sctp_auth_asoc_copy_shkeys(const struct sctp_endpoint *ep,
struct sctp_association *asoc, struct sctp_association *asoc,
gfp_t gfp); gfp_t gfp);
int sctp_auth_init_hmacs(struct sctp_endpoint *ep, gfp_t gfp); int sctp_auth_init_hmacs(struct sctp_endpoint *ep, gfp_t gfp);
void sctp_auth_destroy_hmacs(struct crypto_shash *auth_hmacs[]); const struct sctp_hmac *sctp_auth_get_hmac(__u16 hmac_id);
struct sctp_hmac *sctp_auth_get_hmac(__u16 hmac_id); const struct sctp_hmac *
struct sctp_hmac *sctp_auth_asoc_get_hmac(const struct sctp_association *asoc); sctp_auth_asoc_get_hmac(const struct sctp_association *asoc);
void sctp_auth_asoc_set_default_hmac(struct sctp_association *asoc, void sctp_auth_asoc_set_default_hmac(struct sctp_association *asoc,
struct sctp_hmac_algo_param *hmacs); struct sctp_hmac_algo_param *hmacs);
int sctp_auth_asoc_verify_hmac_id(const struct sctp_association *asoc, int sctp_auth_asoc_verify_hmac_id(const struct sctp_association *asoc,

View File

@ -417,16 +417,12 @@ enum {
SCTP_AUTH_HMAC_ID_RESERVED_0, SCTP_AUTH_HMAC_ID_RESERVED_0,
SCTP_AUTH_HMAC_ID_SHA1, SCTP_AUTH_HMAC_ID_SHA1,
SCTP_AUTH_HMAC_ID_RESERVED_2, SCTP_AUTH_HMAC_ID_RESERVED_2,
#if defined (CONFIG_CRYPTO_SHA256) || defined (CONFIG_CRYPTO_SHA256_MODULE)
SCTP_AUTH_HMAC_ID_SHA256, SCTP_AUTH_HMAC_ID_SHA256,
#endif
__SCTP_AUTH_HMAC_MAX __SCTP_AUTH_HMAC_MAX
}; };
#define SCTP_AUTH_HMAC_ID_MAX __SCTP_AUTH_HMAC_MAX - 1 #define SCTP_AUTH_HMAC_ID_MAX __SCTP_AUTH_HMAC_MAX - 1
#define SCTP_AUTH_NUM_HMACS __SCTP_AUTH_HMAC_MAX #define SCTP_AUTH_NUM_HMACS __SCTP_AUTH_HMAC_MAX
#define SCTP_SHA1_SIG_SIZE 20
#define SCTP_SHA256_SIG_SIZE 32
/* SCTP-AUTH, Section 3.2 /* SCTP-AUTH, Section 3.2
* The chunk types for INIT, INIT-ACK, SHUTDOWN-COMPLETE and AUTH chunks * The chunk types for INIT, INIT-ACK, SHUTDOWN-COMPLETE and AUTH chunks

View File

@ -1329,11 +1329,6 @@ struct sctp_endpoint {
/* rcvbuf acct. policy. */ /* rcvbuf acct. policy. */
__u32 rcvbuf_policy; __u32 rcvbuf_policy;
/* SCTP AUTH: array of the HMACs that will be allocated
* we need this per association so that we don't serialize
*/
struct crypto_shash **auth_hmacs;
/* SCTP-AUTH: hmacs for the endpoint encoded into parameter */ /* SCTP-AUTH: hmacs for the endpoint encoded into parameter */
struct sctp_hmac_algo_param *auth_hmacs_list; struct sctp_hmac_algo_param *auth_hmacs_list;

View File

@ -7,9 +7,9 @@ menuconfig IP_SCTP
tristate "The SCTP Protocol" tristate "The SCTP Protocol"
depends on INET depends on INET
depends on IPV6 || IPV6=n depends on IPV6 || IPV6=n
select CRYPTO select CRYPTO_LIB_SHA1
select CRYPTO_HMAC select CRYPTO_LIB_SHA256
select CRYPTO_SHA1 select CRYPTO_LIB_UTILS
select NET_CRC32C select NET_CRC32C
select NET_UDP_TUNNEL select NET_UDP_TUNNEL
help help
@ -79,15 +79,17 @@ config SCTP_COOKIE_HMAC_MD5
bool "Enable optional MD5 hmac cookie generation" bool "Enable optional MD5 hmac cookie generation"
help help
Enable optional MD5 hmac based SCTP cookie generation Enable optional MD5 hmac based SCTP cookie generation
select CRYPTO_HMAC if SCTP_COOKIE_HMAC_MD5 select CRYPTO
select CRYPTO_MD5 if SCTP_COOKIE_HMAC_MD5 select CRYPTO_HMAC
select CRYPTO_MD5
config SCTP_COOKIE_HMAC_SHA1 config SCTP_COOKIE_HMAC_SHA1
bool "Enable optional SHA1 hmac cookie generation" bool "Enable optional SHA1 hmac cookie generation"
help help
Enable optional SHA1 hmac based SCTP cookie generation Enable optional SHA1 hmac based SCTP cookie generation
select CRYPTO_HMAC if SCTP_COOKIE_HMAC_SHA1 select CRYPTO
select CRYPTO_SHA1 if SCTP_COOKIE_HMAC_SHA1 select CRYPTO_HMAC
select CRYPTO_SHA1
config INET_SCTP_DIAG config INET_SCTP_DIAG
depends on INET_DIAG depends on INET_DIAG

View File

@ -12,36 +12,37 @@
* Vlad Yasevich <vladislav.yasevich@hp.com> * Vlad Yasevich <vladislav.yasevich@hp.com>
*/ */
#include <crypto/hash.h> #include <crypto/sha1.h>
#include <crypto/sha2.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/scatterlist.h>
#include <net/sctp/sctp.h> #include <net/sctp/sctp.h>
#include <net/sctp/auth.h> #include <net/sctp/auth.h>
static struct sctp_hmac sctp_hmac_list[SCTP_AUTH_NUM_HMACS] = { static const struct sctp_hmac sctp_hmac_list[SCTP_AUTH_NUM_HMACS] = {
{ {
/* id 0 is reserved. as all 0 */ /* id 0 is reserved. as all 0 */
.hmac_id = SCTP_AUTH_HMAC_ID_RESERVED_0, .hmac_id = SCTP_AUTH_HMAC_ID_RESERVED_0,
}, },
{ {
.hmac_id = SCTP_AUTH_HMAC_ID_SHA1, .hmac_id = SCTP_AUTH_HMAC_ID_SHA1,
.hmac_name = "hmac(sha1)", .hmac_len = SHA1_DIGEST_SIZE,
.hmac_len = SCTP_SHA1_SIG_SIZE,
}, },
{ {
/* id 2 is reserved as well */ /* id 2 is reserved as well */
.hmac_id = SCTP_AUTH_HMAC_ID_RESERVED_2, .hmac_id = SCTP_AUTH_HMAC_ID_RESERVED_2,
}, },
#if IS_ENABLED(CONFIG_CRYPTO_SHA256)
{ {
.hmac_id = SCTP_AUTH_HMAC_ID_SHA256, .hmac_id = SCTP_AUTH_HMAC_ID_SHA256,
.hmac_name = "hmac(sha256)", .hmac_len = SHA256_DIGEST_SIZE,
.hmac_len = SCTP_SHA256_SIG_SIZE,
} }
#endif
}; };
static bool sctp_hmac_supported(__u16 hmac_id)
{
return hmac_id < ARRAY_SIZE(sctp_hmac_list) &&
sctp_hmac_list[hmac_id].hmac_len != 0;
}
void sctp_auth_key_put(struct sctp_auth_bytes *key) void sctp_auth_key_put(struct sctp_auth_bytes *key)
{ {
@ -444,76 +445,7 @@ struct sctp_shared_key *sctp_auth_get_shkey(
return NULL; return NULL;
} }
/* const struct sctp_hmac *sctp_auth_get_hmac(__u16 hmac_id)
* Initialize all the possible digest transforms that we can use. Right
* now, the supported digests are SHA1 and SHA256. We do this here once
* because of the restrictiong that transforms may only be allocated in
* user context. This forces us to pre-allocated all possible transforms
* at the endpoint init time.
*/
int sctp_auth_init_hmacs(struct sctp_endpoint *ep, gfp_t gfp)
{
struct crypto_shash *tfm = NULL;
__u16 id;
/* If the transforms are already allocated, we are done */
if (ep->auth_hmacs)
return 0;
/* Allocated the array of pointers to transorms */
ep->auth_hmacs = kcalloc(SCTP_AUTH_NUM_HMACS,
sizeof(struct crypto_shash *),
gfp);
if (!ep->auth_hmacs)
return -ENOMEM;
for (id = 0; id < SCTP_AUTH_NUM_HMACS; id++) {
/* See is we support the id. Supported IDs have name and
* length fields set, so that we can allocated and use
* them. We can safely just check for name, for without the
* name, we can't allocate the TFM.
*/
if (!sctp_hmac_list[id].hmac_name)
continue;
/* If this TFM has been allocated, we are all set */
if (ep->auth_hmacs[id])
continue;
/* Allocate the ID */
tfm = crypto_alloc_shash(sctp_hmac_list[id].hmac_name, 0, 0);
if (IS_ERR(tfm))
goto out_err;
ep->auth_hmacs[id] = tfm;
}
return 0;
out_err:
/* Clean up any successful allocations */
sctp_auth_destroy_hmacs(ep->auth_hmacs);
ep->auth_hmacs = NULL;
return -ENOMEM;
}
/* Destroy the hmac tfm array */
void sctp_auth_destroy_hmacs(struct crypto_shash *auth_hmacs[])
{
int i;
if (!auth_hmacs)
return;
for (i = 0; i < SCTP_AUTH_NUM_HMACS; i++) {
crypto_free_shash(auth_hmacs[i]);
}
kfree(auth_hmacs);
}
struct sctp_hmac *sctp_auth_get_hmac(__u16 hmac_id)
{ {
return &sctp_hmac_list[hmac_id]; return &sctp_hmac_list[hmac_id];
} }
@ -521,7 +453,8 @@ struct sctp_hmac *sctp_auth_get_hmac(__u16 hmac_id)
/* Get an hmac description information that we can use to build /* Get an hmac description information that we can use to build
* the AUTH chunk * the AUTH chunk
*/ */
struct sctp_hmac *sctp_auth_asoc_get_hmac(const struct sctp_association *asoc) const struct sctp_hmac *
sctp_auth_asoc_get_hmac(const struct sctp_association *asoc)
{ {
struct sctp_hmac_algo_param *hmacs; struct sctp_hmac_algo_param *hmacs;
__u16 n_elt; __u16 n_elt;
@ -543,26 +476,10 @@ struct sctp_hmac *sctp_auth_asoc_get_hmac(const struct sctp_association *asoc)
sizeof(struct sctp_paramhdr)) >> 1; sizeof(struct sctp_paramhdr)) >> 1;
for (i = 0; i < n_elt; i++) { for (i = 0; i < n_elt; i++) {
id = ntohs(hmacs->hmac_ids[i]); id = ntohs(hmacs->hmac_ids[i]);
if (sctp_hmac_supported(id))
/* Check the id is in the supported range. And return &sctp_hmac_list[id];
* see if we support the id. Supported IDs have name and
* length fields set, so that we can allocate and use
* them. We can safely just check for name, for without the
* name, we can't allocate the TFM.
*/
if (id > SCTP_AUTH_HMAC_ID_MAX ||
!sctp_hmac_list[id].hmac_name) {
id = 0;
continue;
}
break;
} }
return NULL;
if (id == 0)
return NULL;
return &sctp_hmac_list[id];
} }
static int __sctp_auth_find_hmacid(__be16 *hmacs, int n_elts, __be16 hmac_id) static int __sctp_auth_find_hmacid(__be16 *hmacs, int n_elts, __be16 hmac_id)
@ -606,7 +523,6 @@ int sctp_auth_asoc_verify_hmac_id(const struct sctp_association *asoc,
void sctp_auth_asoc_set_default_hmac(struct sctp_association *asoc, void sctp_auth_asoc_set_default_hmac(struct sctp_association *asoc,
struct sctp_hmac_algo_param *hmacs) struct sctp_hmac_algo_param *hmacs)
{ {
struct sctp_endpoint *ep;
__u16 id; __u16 id;
int i; int i;
int n_params; int n_params;
@ -617,16 +533,9 @@ void sctp_auth_asoc_set_default_hmac(struct sctp_association *asoc,
n_params = (ntohs(hmacs->param_hdr.length) - n_params = (ntohs(hmacs->param_hdr.length) -
sizeof(struct sctp_paramhdr)) >> 1; sizeof(struct sctp_paramhdr)) >> 1;
ep = asoc->ep;
for (i = 0; i < n_params; i++) { for (i = 0; i < n_params; i++) {
id = ntohs(hmacs->hmac_ids[i]); id = ntohs(hmacs->hmac_ids[i]);
if (sctp_hmac_supported(id)) {
/* Check the id is in the supported range */
if (id > SCTP_AUTH_HMAC_ID_MAX)
continue;
/* If this TFM has been allocated, use this id */
if (ep->auth_hmacs[id]) {
asoc->default_hmac_id = id; asoc->default_hmac_id = id;
break; break;
} }
@ -709,10 +618,9 @@ void sctp_auth_calculate_hmac(const struct sctp_association *asoc,
struct sctp_shared_key *ep_key, gfp_t gfp) struct sctp_shared_key *ep_key, gfp_t gfp)
{ {
struct sctp_auth_bytes *asoc_key; struct sctp_auth_bytes *asoc_key;
struct crypto_shash *tfm;
__u16 key_id, hmac_id; __u16 key_id, hmac_id;
unsigned char *end;
int free_key = 0; int free_key = 0;
size_t data_len;
__u8 *digest; __u8 *digest;
/* Extract the info we need: /* Extract the info we need:
@ -733,19 +641,17 @@ void sctp_auth_calculate_hmac(const struct sctp_association *asoc,
free_key = 1; free_key = 1;
} }
/* set up scatter list */ data_len = skb_tail_pointer(skb) - (unsigned char *)auth;
end = skb_tail_pointer(skb);
tfm = asoc->ep->auth_hmacs[hmac_id];
digest = (u8 *)(&auth->auth_hdr + 1); digest = (u8 *)(&auth->auth_hdr + 1);
if (crypto_shash_setkey(tfm, &asoc_key->data[0], asoc_key->len)) if (hmac_id == SCTP_AUTH_HMAC_ID_SHA1) {
goto free; hmac_sha1_usingrawkey(asoc_key->data, asoc_key->len,
(const u8 *)auth, data_len, digest);
} else {
WARN_ON_ONCE(hmac_id != SCTP_AUTH_HMAC_ID_SHA256);
hmac_sha256_usingrawkey(asoc_key->data, asoc_key->len,
(const u8 *)auth, data_len, digest);
}
crypto_shash_tfm_digest(tfm, (u8 *)auth, end - (unsigned char *)auth,
digest);
free:
if (free_key) if (free_key)
sctp_auth_key_put(asoc_key); sctp_auth_key_put(asoc_key);
} }
@ -788,14 +694,11 @@ int sctp_auth_ep_set_hmacs(struct sctp_endpoint *ep,
for (i = 0; i < hmacs->shmac_num_idents; i++) { for (i = 0; i < hmacs->shmac_num_idents; i++) {
id = hmacs->shmac_idents[i]; id = hmacs->shmac_idents[i];
if (id > SCTP_AUTH_HMAC_ID_MAX) if (!sctp_hmac_supported(id))
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (SCTP_AUTH_HMAC_ID_SHA1 == id) if (SCTP_AUTH_HMAC_ID_SHA1 == id)
has_sha1 = 1; has_sha1 = 1;
if (!sctp_hmac_list[id].hmac_name)
return -EOPNOTSUPP;
} }
if (!has_sha1) if (!has_sha1)
@ -1021,8 +924,6 @@ int sctp_auth_deact_key_id(struct sctp_endpoint *ep,
int sctp_auth_init(struct sctp_endpoint *ep, gfp_t gfp) int sctp_auth_init(struct sctp_endpoint *ep, gfp_t gfp)
{ {
int err = -ENOMEM;
/* Allocate space for HMACS and CHUNKS authentication /* Allocate space for HMACS and CHUNKS authentication
* variables. There are arrays that we encode directly * variables. There are arrays that we encode directly
* into parameters to make the rest of the operations easier. * into parameters to make the rest of the operations easier.
@ -1060,13 +961,6 @@ int sctp_auth_init(struct sctp_endpoint *ep, gfp_t gfp)
ep->auth_chunk_list = auth_chunks; ep->auth_chunk_list = auth_chunks;
} }
/* Allocate and initialize transorms arrays for supported
* HMACs.
*/
err = sctp_auth_init_hmacs(ep, gfp);
if (err)
goto nomem;
return 0; return 0;
nomem: nomem:
@ -1075,7 +969,7 @@ int sctp_auth_init(struct sctp_endpoint *ep, gfp_t gfp)
kfree(ep->auth_chunk_list); kfree(ep->auth_chunk_list);
ep->auth_hmacs_list = NULL; ep->auth_hmacs_list = NULL;
ep->auth_chunk_list = NULL; ep->auth_chunk_list = NULL;
return err; return -ENOMEM;
} }
void sctp_auth_free(struct sctp_endpoint *ep) void sctp_auth_free(struct sctp_endpoint *ep)
@ -1084,6 +978,4 @@ void sctp_auth_free(struct sctp_endpoint *ep)
kfree(ep->auth_chunk_list); kfree(ep->auth_chunk_list);
ep->auth_hmacs_list = NULL; ep->auth_hmacs_list = NULL;
ep->auth_chunk_list = NULL; ep->auth_chunk_list = NULL;
sctp_auth_destroy_hmacs(ep->auth_hmacs);
ep->auth_hmacs = NULL;
} }

View File

@ -184,7 +184,8 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
* DATA. * DATA.
*/ */
if (sctp_auth_send_cid(SCTP_CID_DATA, asoc)) { if (sctp_auth_send_cid(SCTP_CID_DATA, asoc)) {
struct sctp_hmac *hmac_desc = sctp_auth_asoc_get_hmac(asoc); const struct sctp_hmac *hmac_desc =
sctp_auth_asoc_get_hmac(asoc);
if (hmac_desc) if (hmac_desc)
max_data -= SCTP_PAD4(sizeof(struct sctp_auth_chunk) + max_data -= SCTP_PAD4(sizeof(struct sctp_auth_chunk) +

View File

@ -1320,7 +1320,7 @@ struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc,
__u16 key_id) __u16 key_id)
{ {
struct sctp_authhdr auth_hdr; struct sctp_authhdr auth_hdr;
struct sctp_hmac *hmac_desc; const struct sctp_hmac *hmac_desc;
struct sctp_chunk *retval; struct sctp_chunk *retval;
/* Get the first hmac that the peer told us to use */ /* Get the first hmac that the peer told us to use */

View File

@ -4362,7 +4362,7 @@ static enum sctp_ierror sctp_sf_authenticate(
struct sctp_shared_key *sh_key = NULL; struct sctp_shared_key *sh_key = NULL;
struct sctp_authhdr *auth_hdr; struct sctp_authhdr *auth_hdr;
__u8 *save_digest, *digest; __u8 *save_digest, *digest;
struct sctp_hmac *hmac; const struct sctp_hmac *hmac;
unsigned int sig_len; unsigned int sig_len;
__u16 key_id; __u16 key_id;

View File

@ -9581,16 +9581,6 @@ static int sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
if (err) if (err)
return err; return err;
/* New ep's auth_hmacs should be set if old ep's is set, in case
* that net->sctp.auth_enable has been changed to 0 by users and
* new ep's auth_hmacs couldn't be set in sctp_endpoint_init().
*/
if (oldsp->ep->auth_hmacs) {
err = sctp_auth_init_hmacs(newsp->ep, GFP_KERNEL);
if (err)
return err;
}
sctp_auto_asconf_init(newsp); sctp_auto_asconf_init(newsp);
/* Move any messages in the old socket's receive queue that are for the /* Move any messages in the old socket's receive queue that are for the