]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
crypto: drbg - Use HMAC-SHA512 library API
authorEric Biggers <ebiggers@kernel.org>
Mon, 20 Apr 2026 06:34:06 +0000 (23:34 -0700)
committerHerbert Xu <herbert@gondor.apana.org.au>
Thu, 7 May 2026 08:10:00 +0000 (16:10 +0800)
Since the HMAC algorithm is now fixed at HMAC-SHA512, just use the
HMAC-SHA512 library API.  This is simpler and more efficient.

Remove error-handling code that is no longer needed.

Signed-off-by: Eric Biggers <ebiggers@kernel.org>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
crypto/Kconfig
crypto/drbg.c

index 1abb3d3564582e01db227c1e67b473b1baf2f92d..608b2c739193fe5512c7feb7fcddfdcf49bed288 100644 (file)
@@ -1115,10 +1115,9 @@ menu "Random number generation"
 
 config CRYPTO_DRBG
        tristate "NIST SP800-90A DRBG (Deterministic Random Bit Generator)"
-       select CRYPTO_HMAC
        select CRYPTO_JITTERENTROPY
+       select CRYPTO_LIB_SHA512
        select CRYPTO_RNG
-       select CRYPTO_SHA512
        help
          DRBG (Deterministic Random Bit Generator) (NIST SP800-90A)
 
index e62bde7aab43ff638d50fb5065a7cfe481379a0e..4f326385cf367758ae957ff4d3265a6c8e8af1f1 100644 (file)
@@ -4,6 +4,7 @@
  *       both with and without prediction resistance
  *
  * Copyright Stephan Mueller <smueller@chronox.de>, 2014
+ * Copyright 2026 Google LLC
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -90,7 +91,6 @@
 
 #include <crypto/internal/drbg.h>
 #include <crypto/internal/rng.h>
-#include <crypto/hash.h>
 #include <crypto/sha2.h>
 #include <linux/fips.h>
 #include <linux/kernel.h>
@@ -143,12 +143,11 @@ enum drbg_seed_state {
 struct drbg_state {
        struct mutex drbg_mutex;        /* lock around DRBG */
        u8 V[DRBG_STATE_LEN];           /* internal state -- 10.1.2.1 1a */
+       struct hmac_sha512_key key;     /* current key -- 10.1.2.1 1b */
        u8 C[DRBG_STATE_LEN];           /* current key -- 10.1.2.1 1b */
        /* Number of RNG requests since last reseed -- 10.1.2.1 1c */
        size_t reseed_ctr;
        size_t reseed_threshold;
-       void *priv_data;        /* Cipher handle */
-
        enum drbg_seed_state seeded;            /* DRBG fully seeded? */
        unsigned long last_seed_time;
        bool pr;                /* Prediction resistance enabled? */
@@ -186,94 +185,67 @@ static int drbg_uninstantiate(struct drbg_state *drbg);
  * HMAC DRBG functions
  ******************************************************************/
 
-static int drbg_kcapi_hash(struct drbg_state *drbg, unsigned char *outval,
-                          const struct list_head *in);
-static void drbg_kcapi_hmacsetkey(struct drbg_state *drbg,
-                                 const unsigned char *key);
-static int drbg_init_hash_kernel(struct drbg_state *drbg);
-static int drbg_fini_hash_kernel(struct drbg_state *drbg);
-
 MODULE_ALIAS_CRYPTO("drbg_pr_hmac_sha512");
 MODULE_ALIAS_CRYPTO("drbg_nopr_hmac_sha512");
 
 /* update function of HMAC DRBG as defined in 10.1.2.2 */
-static int drbg_hmac_update(struct drbg_state *drbg, struct list_head *seed,
-                           int reseed)
+static void drbg_hmac_update(struct drbg_state *drbg, struct list_head *seed,
+                            int reseed)
 {
-       int ret = -EFAULT;
        int i = 0;
-       struct drbg_string seed1, seed2, vdata;
-       LIST_HEAD(seedlist);
-       LIST_HEAD(vdatalist);
+       struct hmac_sha512_ctx hmac_ctx;
 
        if (!reseed) {
                /* 10.1.2.3 step 2 -- memset(0) of C is implicit with kzalloc */
                memset(drbg->V, 1, DRBG_STATE_LEN);
-               drbg_kcapi_hmacsetkey(drbg, drbg->C);
+               hmac_sha512_preparekey(&drbg->key, drbg->C, DRBG_STATE_LEN);
        }
 
-       drbg_string_fill(&seed1, drbg->V, DRBG_STATE_LEN);
-       list_add_tail(&seed1.list, &seedlist);
-       /* buffer of seed2 will be filled in for loop below with one byte */
-       drbg_string_fill(&seed2, NULL, 1);
-       list_add_tail(&seed2.list, &seedlist);
-       /* input data of seed is allowed to be NULL at this point */
-       if (seed)
-               list_splice_tail(seed, &seedlist);
-
-       drbg_string_fill(&vdata, drbg->V, DRBG_STATE_LEN);
-       list_add_tail(&vdata.list, &vdatalist);
        for (i = 2; 0 < i; i--) {
                /* first round uses 0x0, second 0x1 */
                unsigned char prefix = DRBG_PREFIX0;
                if (1 == i)
                        prefix = DRBG_PREFIX1;
                /* 10.1.2.2 step 1 and 4 -- concatenation and HMAC for key */
-               seed2.buf = &prefix;
-               ret = drbg_kcapi_hash(drbg, drbg->C, &seedlist);
-               if (ret)
-                       return ret;
-               drbg_kcapi_hmacsetkey(drbg, drbg->C);
+               hmac_sha512_init(&hmac_ctx, &drbg->key);
+               hmac_sha512_update(&hmac_ctx, drbg->V, DRBG_STATE_LEN);
+               hmac_sha512_update(&hmac_ctx, &prefix, 1);
+               if (seed) {
+                       struct drbg_string *input;
+
+                       list_for_each_entry(input, seed, list)
+                               hmac_sha512_update(&hmac_ctx, input->buf,
+                                                  input->len);
+               }
+               hmac_sha512_final(&hmac_ctx, drbg->C);
+               hmac_sha512_preparekey(&drbg->key, drbg->C, DRBG_STATE_LEN);
 
                /* 10.1.2.2 step 2 and 5 -- HMAC for V */
-               ret = drbg_kcapi_hash(drbg, drbg->V, &vdatalist);
-               if (ret)
-                       return ret;
+               hmac_sha512(&drbg->key, drbg->V, DRBG_STATE_LEN, drbg->V);
 
                /* 10.1.2.2 step 3 */
                if (!seed)
-                       return ret;
+                       break;
        }
-
-       return 0;
 }
 
 /* generate function of HMAC DRBG as defined in 10.1.2.5 */
-static int drbg_hmac_generate(struct drbg_state *drbg,
-                             unsigned char *buf,
-                             unsigned int buflen,
-                             struct list_head *addtl)
+static void drbg_hmac_generate(struct drbg_state *drbg,
+                              unsigned char *buf,
+                              unsigned int buflen,
+                              struct list_head *addtl)
 {
        int len = 0;
-       int ret = 0;
-       struct drbg_string data;
-       LIST_HEAD(datalist);
 
        /* 10.1.2.5 step 2 */
-       if (addtl && !list_empty(addtl)) {
-               ret = drbg_hmac_update(drbg, addtl, 1);
-               if (ret)
-                       return ret;
-       }
+       if (addtl && !list_empty(addtl))
+               drbg_hmac_update(drbg, addtl, 1);
 
-       drbg_string_fill(&data, drbg->V, DRBG_STATE_LEN);
-       list_add_tail(&data.list, &datalist);
        while (len < buflen) {
                unsigned int outlen = 0;
+
                /* 10.1.2.5 step 4.1 */
-               ret = drbg_kcapi_hash(drbg, drbg->V, &datalist);
-               if (ret)
-                       return ret;
+               hmac_sha512(&drbg->key, drbg->V, DRBG_STATE_LEN, drbg->V);
                outlen = (DRBG_STATE_LEN < (buflen - len)) ?
                          DRBG_STATE_LEN : (buflen - len);
 
@@ -284,22 +256,15 @@ static int drbg_hmac_generate(struct drbg_state *drbg,
 
        /* 10.1.2.5 step 6 */
        if (addtl && !list_empty(addtl))
-               ret = drbg_hmac_update(drbg, addtl, 1);
+               drbg_hmac_update(drbg, addtl, 1);
        else
-               ret = drbg_hmac_update(drbg, NULL, 1);
-       if (ret)
-               return ret;
-
-       return len;
+               drbg_hmac_update(drbg, NULL, 1);
 }
 
-static inline int __drbg_seed(struct drbg_state *drbg, struct list_head *seed,
-                             int reseed, enum drbg_seed_state new_seed_state)
+static inline void __drbg_seed(struct drbg_state *drbg, struct list_head *seed,
+                              int reseed, enum drbg_seed_state new_seed_state)
 {
-       int ret = drbg_hmac_update(drbg, seed, reseed);
-
-       if (ret)
-               return ret;
+       drbg_hmac_update(drbg, seed, reseed);
 
        drbg->seeded = new_seed_state;
        drbg->last_seed_time = jiffies;
@@ -325,27 +290,23 @@ static inline int __drbg_seed(struct drbg_state *drbg, struct list_head *seed,
                drbg->reseed_threshold = DRBG_MAX_REQUESTS;
                break;
        }
-
-       return ret;
 }
 
-static int drbg_seed_from_random(struct drbg_state *drbg)
+static void drbg_seed_from_random(struct drbg_state *drbg)
        __must_hold(&drbg->drbg_mutex)
 {
        struct drbg_string data;
        LIST_HEAD(seedlist);
        unsigned char entropy[DRBG_SEC_STRENGTH];
-       int ret;
 
        drbg_string_fill(&data, entropy, DRBG_SEC_STRENGTH);
        list_add_tail(&data.list, &seedlist);
 
        get_random_bytes(entropy, DRBG_SEC_STRENGTH);
 
-       ret = __drbg_seed(drbg, &seedlist, true, DRBG_SEED_STATE_FULL);
+       __drbg_seed(drbg, &seedlist, true, DRBG_SEED_STATE_FULL);
 
        memzero_explicit(entropy, DRBG_SEC_STRENGTH);
-       return ret;
 }
 
 static bool drbg_nopr_reseed_interval_elapsed(struct drbg_state *drbg)
@@ -477,8 +438,8 @@ static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers,
                memset(drbg->C, 0, DRBG_STATE_LEN);
        }
 
-       ret = __drbg_seed(drbg, &seedlist, reseed, new_seed_state);
-
+       __drbg_seed(drbg, &seedlist, reseed, new_seed_state);
+       ret = 0;
 out:
        memzero_explicit(entropy, sizeof(entropy));
 
@@ -490,30 +451,13 @@ static inline void drbg_dealloc_state(struct drbg_state *drbg)
 {
        if (!drbg)
                return;
+       memzero_explicit(&drbg->key, sizeof(drbg->key));
        memzero_explicit(drbg->V, sizeof(drbg->V));
        memzero_explicit(drbg->C, sizeof(drbg->C));
        drbg->reseed_ctr = 0;
        drbg->core = NULL;
 }
 
-/*
- * Allocate all sub-structures for a DRBG state.
- * The DRBG state structure must already be allocated.
- */
-static inline int drbg_alloc_state(struct drbg_state *drbg)
-{
-       int ret = -ENOMEM;
-
-       ret = drbg_init_hash_kernel(drbg);
-       if (ret < 0)
-               goto err;
-       return 0;
-
-err:
-       drbg_dealloc_state(drbg);
-       return ret;
-}
-
 /*
  * DRBG generate function as required by SP800-90A - this function
  * generates random numbers
@@ -590,20 +534,16 @@ static int drbg_generate(struct drbg_state *drbg,
        } else if (rng_is_initialized() &&
                   (drbg->seeded == DRBG_SEED_STATE_PARTIAL ||
                    drbg_nopr_reseed_interval_elapsed(drbg))) {
-               len = drbg_seed_from_random(drbg);
-               if (len)
-                       goto err;
+               drbg_seed_from_random(drbg);
        }
 
        if (addtl && 0 < addtl->len)
                list_add_tail(&addtl->list, &addtllist);
        /* 9.3.1 step 8 and 10 */
-       len = drbg_hmac_generate(drbg, buf, buflen, &addtllist);
+       drbg_hmac_generate(drbg, buf, buflen, &addtllist);
 
        /* 10.1.2.5 step 7 */
        drbg->reseed_ctr++;
-       if (0 >= len)
-               goto err;
 
        /*
         * Section 11.3.3 requires to re-perform self tests after some
@@ -718,10 +658,6 @@ static int drbg_instantiate(struct drbg_state *drbg, struct drbg_string *pers,
                drbg->last_seed_time = 0;
                drbg->reseed_threshold = DRBG_MAX_REQUESTS;
 
-               ret = drbg_alloc_state(drbg);
-               if (ret)
-                       goto unlock;
-
                ret = drbg_prepare_hrng(drbg);
                if (ret)
                        goto free_everything;
@@ -737,10 +673,6 @@ static int drbg_instantiate(struct drbg_state *drbg, struct drbg_string *pers,
        mutex_unlock(&drbg->drbg_mutex);
        return ret;
 
-unlock:
-       mutex_unlock(&drbg->drbg_mutex);
-       return ret;
-
 free_everything:
        mutex_unlock(&drbg->drbg_mutex);
        drbg_uninstantiate(drbg);
@@ -762,7 +694,6 @@ static int drbg_uninstantiate(struct drbg_state *drbg)
                crypto_free_rng(drbg->jent);
        drbg->jent = NULL;
 
-       drbg_fini_hash_kernel(drbg);
        drbg_dealloc_state(drbg);
        /* no scrubbing of test_data -- this shall survive an uninstantiate */
        return 0;
@@ -785,70 +716,6 @@ static void drbg_kcapi_set_entropy(struct crypto_rng *tfm,
        mutex_unlock(&drbg->drbg_mutex);
 }
 
-/***************************************************************
- * Kernel crypto API cipher invocations requested by DRBG
- ***************************************************************/
-
-struct sdesc {
-       struct shash_desc shash;
-};
-
-static int drbg_init_hash_kernel(struct drbg_state *drbg)
-{
-       struct sdesc *sdesc;
-       struct crypto_shash *tfm;
-
-       tfm = crypto_alloc_shash(drbg->core->backend_cra_name, 0, 0);
-       if (IS_ERR(tfm)) {
-               pr_info("DRBG: could not allocate digest TFM handle: %s\n",
-                               drbg->core->backend_cra_name);
-               return PTR_ERR(tfm);
-       }
-       BUG_ON(DRBG_STATE_LEN != crypto_shash_digestsize(tfm));
-       sdesc = kzalloc(sizeof(struct shash_desc) + crypto_shash_descsize(tfm),
-                       GFP_KERNEL);
-       if (!sdesc) {
-               crypto_free_shash(tfm);
-               return -ENOMEM;
-       }
-
-       sdesc->shash.tfm = tfm;
-       drbg->priv_data = sdesc;
-
-       return 0;
-}
-
-static int drbg_fini_hash_kernel(struct drbg_state *drbg)
-{
-       struct sdesc *sdesc = drbg->priv_data;
-       if (sdesc) {
-               crypto_free_shash(sdesc->shash.tfm);
-               kfree_sensitive(sdesc);
-       }
-       drbg->priv_data = NULL;
-       return 0;
-}
-
-static void drbg_kcapi_hmacsetkey(struct drbg_state *drbg,
-                                 const unsigned char *key)
-{
-       struct sdesc *sdesc = drbg->priv_data;
-
-       crypto_shash_setkey(sdesc->shash.tfm, key, DRBG_STATE_LEN);
-}
-
-static int drbg_kcapi_hash(struct drbg_state *drbg, unsigned char *outval,
-                          const struct list_head *in)
-{
-       struct sdesc *sdesc = drbg->priv_data;
-       struct drbg_string *input = NULL;
-
-       crypto_shash_init(&sdesc->shash);
-       list_for_each_entry(input, in, list)
-               crypto_shash_update(&sdesc->shash, input->buf, input->len);
-       return crypto_shash_final(&sdesc->shash, outval);
-}
-
 /***************************************************************
  * Kernel crypto API interface to register DRBG
  ***************************************************************/