[SRU][F:linux-bluefield][PATCH 1/1] UBUNTU: SAUCE: pka: Enable DRBG block in TRNG

Mahantesh Salimath mahantesh at nvidia.com
Mon May 3 16:57:22 UTC 2021


BugLink: https://bugs.launchpad.net/bugs/1926773

* DRBG should be enabled for FIPS compliance. Besides,
  it makes TRNG more robust due to the AES core present inside.

* DRBG should be reseeded often and currently it is configured to reseed
  after generating 256 blocks of 128-bit random output.
  DRBG is used in conjunction with Conditioning Functioning and without
  BC_DF block. Therefore, random output is not blocked during reseed operation.

* Before using TRNG with DRBG configuration, NIST known answer test is performed
  on the entire DRBG block to verify if DRBG is functioning as expected.

* Personalization string for DRBG is chosen so that it fits into the 12 registers (384 bits).

* 'trng_read' local variable needs to be cleared after acknowledging the random
  output everytime. Else, it is always set and old random data might be used in certain cases.

Signed-off-by: Mahantesh Salimath <mahantesh at nvidia.com>
Reviewed-by: Khalil Blaiech <kblaiech at nvidia.com>
Signed-off-by: Mahantesh Salimath <mahantesh at nvidia.com>
---
diff --git a/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_addrs.h b/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_addrs.h
index 1896612..3a30e79 100644
--- a/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_addrs.h
+++ b/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_addrs.h
@@ -117,6 +117,19 @@
 #define TRNG_POKER_B_8          0x120D0
 #define TRNG_POKER_F_C          0x120D8
 
+#define TRNG_PS_AI_0_ADDR       0x12080
+#define TRNG_PS_AI_1_ADDR       0x12088
+#define TRNG_PS_AI_2_ADDR       0x12090
+#define TRNG_PS_AI_3_ADDR       0x12098
+#define TRNG_PS_AI_4_ADDR       0x120A0
+#define TRNG_PS_AI_5_ADDR       0x120A8
+#define TRNG_PS_AI_6_ADDR       0x120B0
+#define TRNG_PS_AI_7_ADDR       0x120B8
+#define TRNG_PS_AI_8_ADDR       0x120C0
+#define TRNG_PS_AI_9_ADDR       0x120C8
+#define TRNG_PS_AI_10_ADDR      0x120D0
+#define TRNG_PS_AI_11_ADDR      0x120D8
+
 // Control register address/offset. This is accessed from the ARM using 8
 // byte reads/writes however only the bottom 32 bits are implemented.
 #define PKA_MASTER_SEQ_CTRL_ADDR    0x27F90
diff --git a/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_config.h b/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_config.h
index a562d39..2f2fd83 100644
--- a/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_config.h
+++ b/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_config.h
@@ -183,6 +183,31 @@
 // TRNG Control bit
 #define PKA_TRNG_CONTROL_TEST_MODE          0x100
 
+// TRNG Control Register Value; Set bit 10 and 12 to start the EIP-76 a.k.a TRNG
+// engine with DRBG enabled, gathering entropy from the FROs.
+#define PKA_TRNG_CONTROL_DRBG_REG_VAL       0x00001400
+
+// DRBG enabled TRNG 'request_data' value. REQ_DATA_VAL (in accordance with
+// DATA_BLOCK_MASK) requests 256 blocks of 128-bit random output.
+// 4095 blocks is the max number that can be requested for the TRNG(with DRBG)
+// configuration on Bluefield platforms.
+#define PKA_TRNG_CONTROL_REQ_DATA_VAL       0x10010000
+
+// Mask for 'Data Block' in TRNG Control Register.
+#define PKA_TRNG_DRBG_DATA_BLOCK_MASK       0xfff00000
+
+// Set bit 12 of TRNG Control Register to enable DRBG functionality.
+#define PKA_TRNG_CONTROL_DRBG_ENABLE_VAL    0x00001000
+
+// Set bit 8 a.ka 'test_sp_800_90 DRBG' bit in the TRNG Test Register.
+#define PKA_TRNG_TEST_DRBG_VAL              0x00000080
+
+// Number of Personalization String/Additional Input Registers
+#define PKA_TRNG_PS_AI_REG_COUNT            12
+
+// DRBG Reseed enable
+#define PKA_TRNG_CONTROL_DRBG_RESEED        0x00008000
+
 // TRNG Status bits
 #define PKA_TRNG_STATUS_READY               0x1
 #define PKA_TRNG_STATUS_SHUTDOWN_OFLO       0x2
diff --git a/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_dev.c b/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_dev.c
index 358989f..4ee4462a 100644
--- a/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_dev.c
+++ b/drivers/platform/mellanox/mlxbf_pka/mlxbf_pka_dev.c
@@ -43,6 +43,44 @@
 #define BYTES_PER_WORD 4
 #define BYTES_PER_DOUBLE_WORD 8
 
+// Personalization string "NVIDIA-MELLANOX-BLUEFIELD-TRUE_RANDOM_NUMBER_GEN"
+uint32_t pka_trng_drbg_ps_str[] =
+{
+    0x4e564944, 0x49412d4d, 0x454c4c41, 0x4e4f582d,
+    0x424c5545, 0x4649454c, 0x442d5452, 0x55455f52,
+    0x414e444f, 0x4d5f4e55, 0x4d424552, 0x5f47454e
+};
+
+// Personalization string for DRBG test
+uint32_t pka_trng_drbg_test_ps_str[] =
+{
+    0x64299d83, 0xc34d7098, 0x5bd1f51d, 0xddccfdc1,
+    0xdd0455b7, 0x166279e5, 0x0974cb1b, 0x2f2cd100,
+    0x59a5060a, 0xca79940d, 0xd4e29a40, 0x56b7b779
+};
+
+// First Entropy string for DRBG test
+uint32_t pka_trng_drbg_test_etpy_str1[] =
+{
+    0xaa6bbcab, 0xef45e339, 0x136ca1e7, 0xbce1c881,
+    0x9fa37b09, 0x63b53667, 0xb36e0053, 0xa202ed81,
+    0x4650d90d, 0x8eed6127, 0x666f2402, 0x0dfd3af9
+};
+
+// Second Entropy string for DRBG test
+uint32_t pka_trng_drbg_test_etpy_str2[] =
+{
+    0x35c1b7a1, 0x0154c52b, 0xd5777390, 0x226a4fdb,
+    0x5f16080d, 0x06b68369, 0xd0c93d00, 0x3336e27f,
+    0x1abf2c37, 0xe6ab006c, 0xa4adc6e1, 0x8e1907a2
+};
+
+// Known answer for DRBG test
+uint32_t pka_trng_drbg_test_output[] =
+{
+    0xb663b9f1, 0x24943e13, 0x80f7dce5, 0xaba1a16f
+};
+
 pka_dev_gbl_config_t pka_gbl_config;
 
 // Global PKA shim resource info table
@@ -1100,6 +1138,29 @@ static int pka_dev_config_trng_clk(pka_dev_res_t *aic_csr_ptr)
     return ret;
 }
 
+static int pka_dev_trng_wait_test_ready(void *csr_reg_ptr, uint64_t csr_reg_base)
+{
+    uint64_t csr_reg_off, timer, test_ready, csr_reg_val;
+
+    test_ready  = 0;
+    csr_reg_off = pka_dev_get_register_offset(csr_reg_base, TRNG_STATUS_ADDR);
+    timer       = pka_dev_timer_start(1000000); // 1000 ms
+
+    while (!test_ready)
+    {
+        csr_reg_val = pka_dev_io_read(csr_reg_ptr, csr_reg_off);
+        test_ready  = csr_reg_val & PKA_TRNG_STATUS_TEST_READY;
+
+        if (pka_dev_timer_done(timer))
+        {
+            PKA_DEBUG(PKA_DEV, "TRNG: TEST ready timer done, 0x%llx\n", csr_reg_val);
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
 static int pka_dev_trng_enable_test(void *csr_reg_ptr, uint64_t csr_reg_base,
                                     uint32_t test)
 {
@@ -1434,9 +1495,106 @@ static int pka_dev_test_trng(void *csr_reg_ptr, uint64_t csr_reg_base)
     return ret;
 }
 
+static void pka_dev_trng_write_ps_ai_str(void *csr_reg_ptr,
+                                         uint64_t csr_reg_base,
+                                         uint32_t input_str[])
+{
+    uint64_t csr_reg_off;
+    int i;
+
+    for (i = 0; i < PKA_TRNG_PS_AI_REG_COUNT; i++)
+    {
+        csr_reg_off = pka_dev_get_register_offset(csr_reg_base,
+                          TRNG_PS_AI_0_ADDR + (i * 0x8));
+
+        pka_dev_io_write(csr_reg_ptr, csr_reg_off, input_str[i]);
+    }
+}
+
+static void pka_dev_trng_drbg_generate(void *csr_reg_ptr, uint64_t csr_reg_base)
+{
+    uint64_t csr_reg_off;
+
+    csr_reg_off = pka_dev_get_register_offset(csr_reg_base, TRNG_CONTROL_ADDR);
+    pka_dev_io_write(csr_reg_ptr, csr_reg_off, PKA_TRNG_CONTROL_REQ_DATA_VAL);
+}
+
+static int pka_dev_test_trng_drbg(void *csr_reg_ptr, uint64_t csr_reg_base)
+{
+    uint64_t csr_reg_off, csr_reg_val;
+    int i, ret;
+
+    ret = 0;
+
+    // Make sure the engine is idle.
+    csr_reg_off = pka_dev_get_register_offset(csr_reg_base, TRNG_CONTROL_ADDR);
+    pka_dev_io_write(csr_reg_ptr, csr_reg_off, 0);
+
+    // Enable DRBG, TRNG need not be enabled for this test.
+    csr_reg_off = pka_dev_get_register_offset(csr_reg_base, TRNG_CONTROL_ADDR);
+    pka_dev_io_write(csr_reg_ptr, csr_reg_off, PKA_TRNG_CONTROL_DRBG_ENABLE_VAL);
+
+    // Set 'test_sp_800_90' bit in the TRNG_TEST register
+    csr_reg_off = pka_dev_get_register_offset(csr_reg_base, TRNG_TEST_ADDR);
+    pka_dev_io_write(csr_reg_ptr, csr_reg_off, PKA_TRNG_TEST_DRBG_VAL);
+
+    // Wait for 'test_ready' bit to be set.
+    ret = pka_dev_trng_wait_test_ready(csr_reg_ptr, csr_reg_base);
+    if (ret)
+        goto exit;
+
+    // Instantiate
+    pka_dev_trng_write_ps_ai_str(csr_reg_ptr, csr_reg_base, pka_trng_drbg_test_ps_str);
+    ret = pka_dev_trng_wait_test_ready(csr_reg_ptr, csr_reg_base);
+    if (ret)
+        goto exit;
+
+    // Generate
+    pka_dev_trng_write_ps_ai_str(csr_reg_ptr, csr_reg_base, pka_trng_drbg_test_etpy_str1);
+    ret = pka_dev_trng_wait_test_ready(csr_reg_ptr, csr_reg_base);
+    if (ret)
+        goto exit;
+
+    // A standard NIST SP 800-90A DRBG known-answer test discards
+    // the result of the first 'Generate' function and only checks
+    // the result of the second 'Generate' function. Hence 'Generate'
+    // is performed again.
+
+    // Generate
+    pka_dev_trng_write_ps_ai_str(csr_reg_ptr, csr_reg_base, pka_trng_drbg_test_etpy_str2);
+    ret = pka_dev_trng_wait_test_ready(csr_reg_ptr, csr_reg_base);
+    if (ret)
+        goto exit;
+
+    // Check output registers
+    for (i = 0; i < PKA_TRNG_OUTPUT_CNT; i++)
+    {
+        csr_reg_off = pka_dev_get_register_offset(csr_reg_base,
+                          TRNG_OUTPUT_0_ADDR + (i * 0x8));
+
+        csr_reg_val = pka_dev_io_read(csr_reg_ptr, csr_reg_off);
+
+        if ((uint32_t)csr_reg_val != pka_trng_drbg_test_output[i])
+        {
+            PKA_DEBUG(PKA_DEV,
+                "DRBG known answer test failed for output register:%d, 0x%x\n",
+                i, (uint32_t)csr_reg_val);
+            ret = 1;
+            goto exit;
+        }
+    }
+
+    // Clear 'test_sp_800_90' bit in the TRNG_TEST register.
+    csr_reg_off = pka_dev_get_register_offset(csr_reg_base, TRNG_TEST_ADDR);
+    pka_dev_io_write(csr_reg_ptr, csr_reg_off, 0);
+
+exit:
+    return ret;
+}
+
 // Configure the TRNG.
-static int pka_dev_config_trng(pka_dev_res_t *aic_csr_ptr,
-                               pka_dev_res_t *trng_csr_ptr)
+static int pka_dev_config_trng_drbg(pka_dev_res_t *aic_csr_ptr,
+                                    pka_dev_res_t *trng_csr_ptr)
 {
     int ret = 0;
 
@@ -1456,10 +1614,13 @@ static int pka_dev_config_trng(pka_dev_res_t *aic_csr_ptr,
     csr_reg_base = trng_csr_ptr->base;
     csr_reg_ptr  = trng_csr_ptr->ioaddr;
 
-    // Starting up the TRNG without a DRBG (default configuration);
-    // When not using the AES-256 DRBG, the startup sequence is relatively
-    // straightforward and the engine will generate data automatically to
-    // keep the output register and buffer RAM filled.
+    // Perform NIST known-answer tests on the complete SP 800-90A DRBG
+    // without BC_DF functionality.
+    ret = pka_dev_test_trng_drbg(csr_reg_ptr, csr_reg_base);
+    if (ret)
+        return ret;
+
+    // Starting up the TRNG with a DRBG
 
     // Make sure the engine is idle.
     csr_reg_off =
@@ -1496,16 +1657,31 @@ static int pka_dev_config_trng(pka_dev_res_t *aic_csr_ptr,
             pka_dev_get_register_offset(csr_reg_base, TRNG_FROENABLE_ADDR);
     pka_dev_io_write(csr_reg_ptr, csr_reg_off, PKA_TRNG_FROENABLE_REG_VAL);
 
+    // Optionally, write 'Personalization string' of upto 384 bits in
+    // TRNG_PS_AI_... registers. The contents of these registers will be
+    // XOR-ed into the output of the SHA-256 'Conditioning Function' to be
+    // used as seed value for the actual DRBG.
+    pka_dev_trng_write_ps_ai_str(csr_reg_ptr, csr_reg_base, pka_trng_drbg_ps_str);
+
+
+    // Run TRNG tests after configuring TRNG.
+    // NOTE: TRNG need not be enabled to carry out these tests.
     ret = pka_dev_test_trng(csr_reg_ptr, csr_reg_base);
     if (ret)
         return ret;
 
-    // Start the actual engine by setting the 'enable_trng' bit in the
-    // TRNG_CONTROL register (also a nice point to set the interrupt mask
+    // Start the actual engine by setting the 'enable_trng' and 'drbg_en' bit
+    // in the TRNG_CONTROL register (also a nice point to set the interrupt mask
     // bits).
     csr_reg_off =
             pka_dev_get_register_offset(csr_reg_base, TRNG_CONTROL_ADDR);
-    pka_dev_io_write(csr_reg_ptr, csr_reg_off, PKA_TRNG_CONTROL_REG_VAL);
+    pka_dev_io_write(csr_reg_ptr, csr_reg_off, PKA_TRNG_CONTROL_DRBG_REG_VAL);
+
+    // The engine is now ready to handle the first 'Generate' request using
+    // the 'request_data' bit of the TRNG_CONTROL register. The first output
+    // for these requests will take a while, as Noise Source and Conditioning
+    // Function must first generate seed entropy for the DRBG.
+
 
     // Optionally, when buffer RAM is configured: Set a data available
     // interrupt threshold using the 'load_thresh' and 'blocks_thresh'
@@ -1513,6 +1689,11 @@ static int pka_dev_config_trng(pka_dev_res_t *aic_csr_ptr,
     // available interrupt until the indicated number of 128-bit words are
     // available in the buffer RAM.
 
+    // Start the actual 'Generate' operation using the 'request_data' and 'data_blocks'
+    // fields of the TRNG_CONTROL register.
+
+    pka_dev_trng_drbg_generate(csr_reg_ptr, csr_reg_base);
+
     mdelay(200);
 
     return ret;
@@ -1637,8 +1818,8 @@ static int pka_dev_init_shim(pka_dev_shim_t *shim)
     shim->trng_err_cycle = 0;
 
     // Configure the TRNG
-    ret = pka_dev_config_trng(&shim->resources.aic_csr,
-                              &shim->resources.trng_csr);
+    ret = pka_dev_config_trng_drbg(&shim->resources.aic_csr,
+                                   &shim->resources.trng_csr);
 
     // Pull out data from the content of the TRNG buffer RAM and
     // start the re-generation of new numbers; read and drop 512
@@ -1948,6 +2129,27 @@ static bool pka_dev_trng_shutdown_oflo(pka_dev_res_t *trng_csr_ptr,
     return true;
 }
 
+static int pka_dev_trng_drbg_reseed(void *csr_reg_ptr, uint64_t csr_reg_base)
+{
+    uint64_t csr_reg_off;
+    int ret;
+
+    ret = 0;
+
+    csr_reg_off = pka_dev_get_register_offset(csr_reg_base, TRNG_CONTROL_ADDR);
+    pka_dev_io_write(csr_reg_ptr, csr_reg_off, PKA_TRNG_CONTROL_DRBG_RESEED);
+
+    ret = pka_dev_trng_wait_test_ready(csr_reg_ptr, csr_reg_base);
+    if (ret)
+        return ret;
+
+    // Write personalization string
+    pka_dev_trng_write_ps_ai_str(csr_reg_ptr, csr_reg_base, pka_trng_drbg_ps_str);
+
+    return ret;
+}
+
+// Read from DRBG enabled TRNG
 int pka_dev_trng_read(pka_dev_shim_t *shim, uint32_t *data, uint32_t cnt)
 {
     int ret = 0;
@@ -1959,7 +2161,7 @@ int pka_dev_trng_read(pka_dev_shim_t *shim, uint32_t *data, uint32_t cnt)
     uint8_t        output_idx, trng_ready = 0;
     void          *csr_reg_ptr;
 
-    if (!shim || ! data || (cnt % PKA_TRNG_OUTPUT_CNT != 0))
+    if (!shim || !data || (cnt % PKA_TRNG_OUTPUT_CNT != 0))
         return -EINVAL;
 
     if (!cnt)
@@ -1972,8 +2174,8 @@ int pka_dev_trng_read(pka_dev_shim_t *shim, uint32_t *data, uint32_t cnt)
     if (trng_csr_ptr->status != PKA_DEV_RES_STATUS_MAPPED ||
             trng_csr_ptr->type != PKA_DEV_RES_TYPE_REG)
     {
-        mutex_unlock(&shim->mutex);
-        return -EPERM;
+        ret = -EPERM;
+        goto exit;
     }
 
     csr_reg_base = trng_csr_ptr->base;
@@ -1982,8 +2184,8 @@ int pka_dev_trng_read(pka_dev_shim_t *shim, uint32_t *data, uint32_t cnt)
     if (!pka_dev_trng_shutdown_oflo(trng_csr_ptr,
                                     &shim->trng_err_cycle))
     {
-        mutex_unlock(&shim->mutex);
-        return -EWOULDBLOCK;
+        ret = -EWOULDBLOCK;
+        goto exit;
     }
 
     // Determine the number of 32-bit words.
@@ -1992,12 +2194,36 @@ int pka_dev_trng_read(pka_dev_shim_t *shim, uint32_t *data, uint32_t cnt)
     for (data_idx = 0; data_idx < word_cnt; data_idx++)
     {
         output_idx = data_idx % PKA_TRNG_OUTPUT_CNT;
+
         // Tell the hardware to advance
         if (output_idx == 0)
         {
             csr_reg_off = pka_dev_get_register_offset(csr_reg_base,
                                                         TRNG_INTACK_ADDR);
             pka_dev_io_write(csr_reg_ptr, csr_reg_off, PKA_TRNG_STATUS_READY);
+            trng_ready = 0;
+
+            // Check if 'data_blocks' field is zero in TRNG_CONTROL register,
+            // if it is then we have to issue a 'Reseed' and Generate' request
+            // for DRBG enabled TRNG.
+            csr_reg_off = pka_dev_get_register_offset(csr_reg_base,
+                                                      TRNG_CONTROL_ADDR);
+            csr_reg_value = pka_dev_io_read(csr_reg_ptr, csr_reg_off);
+
+            if (!((uint32_t)csr_reg_value & PKA_TRNG_DRBG_DATA_BLOCK_MASK))
+            {
+                // Issue reseed
+                ret = pka_dev_trng_drbg_reseed(csr_reg_ptr, csr_reg_base);
+                if (ret)
+                {
+                    ret = -EBUSY;
+                    goto exit;
+                }
+
+                // Issue generate request
+                pka_dev_trng_drbg_generate(csr_reg_ptr, csr_reg_base);
+            }
+
         }
 
         // Wait until a data word is available in the TRNG_OUTPUT_X
@@ -2018,8 +2244,8 @@ int pka_dev_trng_read(pka_dev_shim_t *shim, uint32_t *data, uint32_t cnt)
                 PKA_DEBUG(PKA_DEV,
                     "Shim %u got error obtaining random number\n",
                                 shim->shim_id);
-                mutex_unlock(&shim->mutex);
-                return -EBUSY;
+                ret = -EBUSY;
+                goto exit;
             }
         }
 
@@ -2030,6 +2256,7 @@ int pka_dev_trng_read(pka_dev_shim_t *shim, uint32_t *data, uint32_t cnt)
         data[data_idx] = (uint32_t) csr_reg_value;
     }
 
+exit:
     mutex_unlock(&shim->mutex);
     return ret;
 }
-- 
2.1.2




More information about the kernel-team mailing list