]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
crypto: xilinx-trng - Replace crypto_drbg_ctr_df() with HMAC-SHA512
authorEric Biggers <ebiggers@kernel.org>
Sun, 31 May 2026 19:17:37 +0000 (12:17 -0700)
committerHerbert Xu <herbert@gondor.apana.org.au>
Thu, 11 Jun 2026 06:03:13 +0000 (14:03 +0800)
This code is just trying to condition 48 bytes of random data.  This can
be done easily using HKDF-SHA512-Extract, saving 300 lines of code.

This commit also fixes forward security (in this particular case) by
clearing the entropy from memory after it's used.

Signed-off-by: Eric Biggers <ebiggers@kernel.org>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
crypto/Kconfig
crypto/Makefile
crypto/df_sp80090a.c [deleted file]
drivers/crypto/Kconfig
drivers/crypto/xilinx/xilinx-trng.c
include/crypto/df_sp80090a.h [deleted file]

index b5c5a1e0443539931532e46fdb6fa02e7da88f8b..c3d7a20d5cb1b80ee37ed03079538e0227a4fbbd 100644 (file)
@@ -1246,11 +1246,6 @@ config CRYPTO_KDF800108_CTR
        select CRYPTO_HMAC
        select CRYPTO_SHA256
 
-config CRYPTO_DF80090A
-       tristate
-       select CRYPTO_AES
-       select CRYPTO_CTR
-
 endmenu
 menu "Userspace interface (deprecated)"
 
index c73f4d51d0368d9cb110f4f1799098174f1e773a..f98f57c7a49f32dcf0f82ff75210762a91ef30bf 100644 (file)
@@ -208,6 +208,4 @@ obj-$(CONFIG_CRYPTO_SIMD) += crypto_simd.o
 #
 obj-$(CONFIG_CRYPTO_KDF800108_CTR) += kdf_sp800108.o
 
-obj-$(CONFIG_CRYPTO_DF80090A) += df_sp80090a.o
-
 obj-$(CONFIG_CRYPTO_KRB5) += krb5/
diff --git a/crypto/df_sp80090a.c b/crypto/df_sp80090a.c
deleted file mode 100644 (file)
index 90e1973..0000000
+++ /dev/null
@@ -1,222 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-/*
- * NIST SP800-90A DRBG derivation function
- *
- * Copyright (C) 2014, Stephan Mueller <smueller@chronox.de>
- */
-
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/string.h>
-#include <linux/unaligned.h>
-#include <crypto/aes.h>
-#include <crypto/df_sp80090a.h>
-
-static void drbg_kcapi_sym(struct aes_enckey *aeskey, unsigned char *outval,
-                          const struct drbg_string *in, u8 blocklen_bytes)
-{
-       /* there is only component in *in */
-       BUG_ON(in->len < blocklen_bytes);
-       aes_encrypt(aeskey, outval, in->buf);
-}
-
-/* BCC function for CTR DRBG as defined in 10.4.3 */
-
-static void drbg_ctr_bcc(struct aes_enckey *aeskey,
-                        unsigned char *out, const unsigned char *key,
-                        struct list_head *in,
-                        u8 blocklen_bytes,
-                        u8 keylen)
-{
-       struct drbg_string *curr = NULL;
-       struct drbg_string data;
-       short cnt = 0;
-
-       drbg_string_fill(&data, out, blocklen_bytes);
-
-       /* 10.4.3 step 2 / 4 */
-       aes_prepareenckey(aeskey, key, keylen);
-       list_for_each_entry(curr, in, list) {
-               const unsigned char *pos = curr->buf;
-               size_t len = curr->len;
-               /* 10.4.3 step 4.1 */
-               while (len) {
-                       /* 10.4.3 step 4.2 */
-                       if (blocklen_bytes == cnt) {
-                               cnt = 0;
-                               drbg_kcapi_sym(aeskey, out, &data, blocklen_bytes);
-                       }
-                       out[cnt] ^= *pos;
-                       pos++;
-                       cnt++;
-                       len--;
-               }
-       }
-       /* 10.4.3 step 4.2 for last block */
-       if (cnt)
-               drbg_kcapi_sym(aeskey, out, &data, blocklen_bytes);
-}
-
-/*
- * scratchpad usage: drbg_ctr_update is interlinked with crypto_drbg_ctr_df
- * (and drbg_ctr_bcc, but this function does not need any temporary buffers),
- * the scratchpad is used as follows:
- * drbg_ctr_update:
- *     temp
- *             start: drbg->scratchpad
- *             length: drbg_statelen(drbg) + drbg_blocklen(drbg)
- *                     note: the cipher writing into this variable works
- *                     blocklen-wise. Now, when the statelen is not a multiple
- *                     of blocklen, the generateion loop below "spills over"
- *                     by at most blocklen. Thus, we need to give sufficient
- *                     memory.
- *     df_data
- *             start: drbg->scratchpad +
- *                             drbg_statelen(drbg) + drbg_blocklen(drbg)
- *             length: drbg_statelen(drbg)
- *
- * crypto_drbg_ctr_df:
- *     pad
- *             start: df_data + drbg_statelen(drbg)
- *             length: drbg_blocklen(drbg)
- *     iv
- *             start: pad + drbg_blocklen(drbg)
- *             length: drbg_blocklen(drbg)
- *     temp
- *             start: iv + drbg_blocklen(drbg)
- *             length: drbg_satelen(drbg) + drbg_blocklen(drbg)
- *                     note: temp is the buffer that the BCC function operates
- *                     on. BCC operates blockwise. drbg_statelen(drbg)
- *                     is sufficient when the DRBG state length is a multiple
- *                     of the block size. For AES192 (and maybe other ciphers)
- *                     this is not correct and the length for temp is
- *                     insufficient (yes, that also means for such ciphers,
- *                     the final output of all BCC rounds are truncated).
- *                     Therefore, add drbg_blocklen(drbg) to cover all
- *                     possibilities.
- * refer to crypto_drbg_ctr_df_datalen() to get required length
- */
-
-/* Derivation Function for CTR DRBG as defined in 10.4.2 */
-int crypto_drbg_ctr_df(struct aes_enckey *aeskey,
-                      unsigned char *df_data, size_t bytes_to_return,
-                      struct list_head *seedlist,
-                      u8 blocklen_bytes,
-                      u8 statelen)
-{
-       unsigned char L_N[8];
-       /* S3 is input */
-       struct drbg_string S1, S2, S4, cipherin;
-       LIST_HEAD(bcc_list);
-       unsigned char *pad = df_data + statelen;
-       unsigned char *iv = pad + blocklen_bytes;
-       unsigned char *temp = iv + blocklen_bytes;
-       size_t padlen = 0;
-       unsigned int templen = 0;
-       /* 10.4.2 step 7 */
-       unsigned int i = 0;
-       /* 10.4.2 step 8 */
-       const unsigned char *K = (unsigned char *)
-                          "\x00\x01\x02\x03\x04\x05\x06\x07"
-                          "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
-                          "\x10\x11\x12\x13\x14\x15\x16\x17"
-                          "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f";
-       unsigned char *X;
-       size_t generated_len = 0;
-       size_t inputlen = 0;
-       struct drbg_string *seed = NULL;
-       u8 keylen;
-
-       memset(pad, 0, blocklen_bytes);
-       memset(iv, 0, blocklen_bytes);
-       keylen = statelen - blocklen_bytes;
-       /* 10.4.2 step 1 is implicit as we work byte-wise */
-
-       /* 10.4.2 step 2 */
-       if ((512 / 8) < bytes_to_return)
-               return -EINVAL;
-
-       /* 10.4.2 step 2 -- calculate the entire length of all input data */
-       list_for_each_entry(seed, seedlist, list)
-               inputlen += seed->len;
-       put_unaligned_be32(inputlen, &L_N[0]);
-
-       /* 10.4.2 step 3 */
-       put_unaligned_be32(bytes_to_return, &L_N[4]);
-
-       /* 10.4.2 step 5: length is L_N, input_string, one byte, padding */
-       padlen = (inputlen + sizeof(L_N) + 1) % (blocklen_bytes);
-       /* wrap the padlen appropriately */
-       if (padlen)
-               padlen = blocklen_bytes - padlen;
-       /*
-        * pad / padlen contains the 0x80 byte and the following zero bytes.
-        * As the calculated padlen value only covers the number of zero
-        * bytes, this value has to be incremented by one for the 0x80 byte.
-        */
-       padlen++;
-       pad[0] = 0x80;
-
-       /* 10.4.2 step 4 -- first fill the linked list and then order it */
-       drbg_string_fill(&S1, iv, blocklen_bytes);
-       list_add_tail(&S1.list, &bcc_list);
-       drbg_string_fill(&S2, L_N, sizeof(L_N));
-       list_add_tail(&S2.list, &bcc_list);
-       list_splice_tail(seedlist, &bcc_list);
-       drbg_string_fill(&S4, pad, padlen);
-       list_add_tail(&S4.list, &bcc_list);
-
-       /* 10.4.2 step 9 */
-       while (templen < (keylen + (blocklen_bytes))) {
-               /*
-                * 10.4.2 step 9.1 - the padding is implicit as the buffer
-                * holds zeros after allocation -- even the increment of i
-                * is irrelevant as the increment remains within length of i
-                */
-               put_unaligned_be32(i, iv);
-               /* 10.4.2 step 9.2 -- BCC and concatenation with temp */
-               drbg_ctr_bcc(aeskey, temp + templen, K, &bcc_list,
-                            blocklen_bytes, keylen);
-               /* 10.4.2 step 9.3 */
-               i++;
-               templen += blocklen_bytes;
-       }
-
-       /* 10.4.2 step 11 */
-       X = temp + (keylen);
-       drbg_string_fill(&cipherin, X, blocklen_bytes);
-
-       /* 10.4.2 step 12: overwriting of outval is implemented in next step */
-
-       /* 10.4.2 step 13 */
-       aes_prepareenckey(aeskey, temp, keylen);
-       while (generated_len < bytes_to_return) {
-               short blocklen = 0;
-               /*
-                * 10.4.2 step 13.1: the truncation of the key length is
-                * implicit as the key is only drbg_blocklen in size based on
-                * the implementation of the cipher function callback
-                */
-               drbg_kcapi_sym(aeskey, X, &cipherin, blocklen_bytes);
-               blocklen = (blocklen_bytes <
-                               (bytes_to_return - generated_len)) ?
-                           blocklen_bytes :
-                               (bytes_to_return - generated_len);
-               /* 10.4.2 step 13.2 and 14 */
-               memcpy(df_data + generated_len, X, blocklen);
-               generated_len += blocklen;
-       }
-
-       memset(iv, 0, blocklen_bytes);
-       memset(temp, 0, statelen + blocklen_bytes);
-       memset(pad, 0, blocklen_bytes);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(crypto_drbg_ctr_df);
-
-MODULE_IMPORT_NS("CRYPTO_INTERNAL");
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>");
-MODULE_DESCRIPTION("Derivation Function conformant to SP800-90A");
index 851904fb9ba49739868b53d0f406f6506251caa2..b94ba934e2d8448b1d4eb64b523ad56ff1f90163 100644 (file)
@@ -702,7 +702,7 @@ config CRYPTO_DEV_TEGRA
 config CRYPTO_DEV_XILINX_TRNG
        tristate "Support for Xilinx True Random Generator"
        depends on ZYNQMP_FIRMWARE || COMPILE_TEST
-       select CRYPTO_DF80090A
+       select CRYPTO_LIB_SHA512
        select HW_RANDOM
        help
          Xilinx Versal SoC driver provides kernel-side support for True Random Number
index a30b0b3b3685f2d9360879882bd8bde449e74fed..f615d5adddde8930ba4cfd793b2a9aca2653912e 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright (c) 2024 - 2025 Advanced Micro Devices, Inc.
  */
 
+#include <crypto/sha2.h>
 #include <linux/bitfield.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
@@ -15,9 +16,6 @@
 #include <linux/module.h>
 #include <linux/mod_devicetable.h>
 #include <linux/platform_device.h>
-#include <crypto/aes.h>
-#include <crypto/df_sp80090a.h>
-#include <crypto/internal/cipher.h>
 
 /* TRNG Registers Offsets */
 #define TRNG_STATUS_OFFSET                     0x4U
@@ -43,7 +41,6 @@
 
 /* Sizes in bytes */
 #define TRNG_SEED_LEN_BYTES                    48U
-#define TRNG_ENTROPY_SEED_LEN_BYTES            64U
 #define TRNG_SEC_STRENGTH_SHIFT                        5U
 #define TRNG_SEC_STRENGTH_BYTES                        BIT(TRNG_SEC_STRENGTH_SHIFT)
 #define TRNG_BYTES_PER_REG                     4U
@@ -55,8 +52,6 @@
 struct xilinx_rng {
        void __iomem *rng_base;
        struct device *dev;
-       unsigned char *scratchpadbuf;
-       struct aes_enckey *aeskey;
        struct hwrng trng;
 };
 
@@ -172,29 +167,30 @@ static void xtrng_enable_entropy(struct xilinx_rng *rng)
 
 static int xtrng_reseed_internal(struct xilinx_rng *rng)
 {
-       u8 entropy[TRNG_ENTROPY_SEED_LEN_BYTES];
-       struct drbg_string data;
-       LIST_HEAD(seedlist);
+       static const u8 default_salt[SHA512_DIGEST_SIZE];
+       u8 entropy[SHA512_DIGEST_SIZE] __aligned(4);
        u32 val;
        int ret;
 
-       drbg_string_fill(&data, entropy, TRNG_SEED_LEN_BYTES);
-       list_add_tail(&data.list, &seedlist);
-       memset(entropy, 0, sizeof(entropy));
        xtrng_enable_entropy(rng);
 
-       /* collect random data to use it as entropy (input for DF) */
+       /* Collect some output from the TRNG. */
+       static_assert(sizeof(entropy) >= TRNG_SEED_LEN_BYTES);
        ret = xtrng_collect_random_data(rng, entropy, TRNG_SEED_LEN_BYTES, true);
        if (ret != TRNG_SEED_LEN_BYTES)
                return -EINVAL;
-       ret = crypto_drbg_ctr_df(rng->aeskey, rng->scratchpadbuf,
-                                TRNG_SEED_LEN_BYTES, &seedlist, AES_BLOCK_SIZE,
-                                TRNG_SEED_LEN_BYTES);
-       if (ret)
-               return ret;
 
+       /* Extract entropy from the TRNG output using HKDF-SHA512-Extract. */
+       hmac_sha512_usingrawkey(default_salt, sizeof(default_salt), entropy,
+                               TRNG_SEED_LEN_BYTES, entropy);
+
+       /* Write the extracted entropy to the hardware. */
        xtrng_write_multiple_registers(rng->rng_base + TRNG_EXT_SEED_OFFSET,
-                                      (u32 *)rng->scratchpadbuf, TRNG_NUM_INIT_REGS);
+                                      (u32 *)entropy, TRNG_NUM_INIT_REGS);
+
+       /* Clear the entropy from the stack. */
+       memzero_explicit(entropy, sizeof(entropy));
+
        /* select reseed operation */
        iowrite32(TRNG_CTRL_PRNGXS_MASK, rng->rng_base + TRNG_CTRL_OFFSET);
 
@@ -278,7 +274,6 @@ static void xtrng_hwrng_unregister(struct hwrng *trng)
 static int xtrng_probe(struct platform_device *pdev)
 {
        struct xilinx_rng *rng;
-       size_t sb_size;
        int ret;
 
        rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
@@ -292,15 +287,6 @@ static int xtrng_probe(struct platform_device *pdev)
                return PTR_ERR(rng->rng_base);
        }
 
-       rng->aeskey = devm_kzalloc(&pdev->dev, sizeof(*rng->aeskey), GFP_KERNEL);
-       if (!rng->aeskey)
-               return -ENOMEM;
-
-       sb_size = crypto_drbg_ctr_df_datalen(TRNG_SEED_LEN_BYTES, AES_BLOCK_SIZE);
-       rng->scratchpadbuf = devm_kzalloc(&pdev->dev, sb_size, GFP_KERNEL);
-       if (!rng->scratchpadbuf)
-               return -ENOMEM;
-
        xtrng_trng_reset(rng->rng_base);
        ret = xtrng_reseed_internal(rng);
        if (ret) {
diff --git a/include/crypto/df_sp80090a.h b/include/crypto/df_sp80090a.h
deleted file mode 100644 (file)
index e594fb7..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-
-/*
- * Copyright Stephan Mueller <smueller@chronox.de>, 2014
- */
-
-#ifndef _CRYPTO_DF80090A_H
-#define _CRYPTO_DF80090A_H
-
-#include <crypto/internal/cipher.h>
-#include <crypto/aes.h>
-#include <linux/list.h>
-
-/*
- * Concatenation Helper and string operation helper
- *
- * SP800-90A requires the concatenation of different data. To avoid copying
- * buffers around or allocate additional memory, the following data structure
- * is used to point to the original memory with its size. In addition, it
- * is used to build a linked list. The linked list defines the concatenation
- * of individual buffers. The order of memory block referenced in that
- * linked list determines the order of concatenation.
- */
-struct drbg_string {
-       const unsigned char *buf;
-       size_t len;
-       struct list_head list;
-};
-
-static inline void drbg_string_fill(struct drbg_string *string,
-                                   const unsigned char *buf, size_t len)
-{
-       string->buf = buf;
-       string->len = len;
-       INIT_LIST_HEAD(&string->list);
-}
-
-static inline int crypto_drbg_ctr_df_datalen(u8 statelen, u8 blocklen)
-{
-       return statelen +       /* df_data */
-               blocklen +      /* pad */
-               blocklen +      /* iv */
-               statelen + blocklen;  /* temp */
-}
-
-int crypto_drbg_ctr_df(struct aes_enckey *aes,
-                      unsigned char *df_data,
-                      size_t bytes_to_return,
-                      struct list_head *seedlist,
-                      u8 blocklen_bytes,
-                      u8 statelen);
-
-#endif /* _CRYPTO_DF80090A_H */