[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