1 /*
2  * SPDX-License-Identifier: BSD-3-Clause
3  * SPDX-FileCopyrightText: Copyright TF-RMM Contributors.
4  */
5 
6 #include <arch_features.h>
7 #include <assert.h>
8 #include <attestation.h>
9 #include <attestation_priv.h>
10 #include <cpuid.h>
11 #include <entropy.h>
12 #include <errno.h>
13 #include <fpu_helpers.h>
14 #include <mbedtls/entropy.h>
15 #include <mbedtls/hmac_drbg.h>
16 #include <platform_api.h>
17 #include <stdbool.h>
18 #include <utils_def.h>
19 
20 /*
21  * Allocate a PRNG object per PE in order to avoid the necessity of locking if
22  * concurrent attestation token requests are executed.
23  */
24 static mbedtls_hmac_drbg_context cpu_drbg_ctx[MAX_CPUS];
25 static bool prng_init_done;
26 
get_random_seed(unsigned char * output,size_t len)27 static int get_random_seed(unsigned char *output, size_t len)
28 {
29 	bool rc;
30 	uint64_t *random_output;
31 	uint64_t *random_end;
32 
33 	assert(!prng_init_done);
34 
35 	/* Enforce `len` is a multiple of 8 and `output` is 8-byte aligned. */
36 	assert((len & 0x7UL) == 0UL && ((uintptr_t)output & 0x7UL) == 0UL);
37 
38 	random_output = (uint64_t *)output;
39 	random_end = (uint64_t *)(output + len);
40 
41 	for (; random_output < random_end; ++random_output) {
42 		rc = arch_collect_entropy(random_output);
43 		if (!rc) {
44 			return -EINVAL;
45 		}
46 	}
47 	return 0;
48 }
49 
50 /*
51  * This function is used by Mbed TLS as a source of entropy. This means it is
52  * called during RMM operation, to add entropy to the signing process.
53  * See declaration in mbedtls/entropy_poll.h.
54  * For details see `MBEDTLS_ENTROPY_HARDWARE_ALT` in mbedtls/config.h
55  */
mbedtls_hardware_poll(void * data,unsigned char * output,size_t len,size_t * olen)56 int mbedtls_hardware_poll(void  *data, unsigned char *output,
57 			  size_t len,  size_t        *olen)
58 {
59 	int ret;
60 	unsigned int cpu_id = my_cpuid();
61 	void *rng_ctx;
62 
63 	assert(prng_init_done);
64 
65 	(void)data;
66 
67 	/* Not in RMM init, PRNGs are already initialized, use them. */
68 	rng_ctx = &cpu_drbg_ctx[cpu_id];
69 	ret = mbedtls_hmac_drbg_random(rng_ctx, output, len);
70 	if (ret != 0) {
71 		return ret;
72 	}
73 	*olen = len;
74 
75 	return 0;
76 }
77 
attest_get_cpu_rng_context(struct attest_rng_context * rng_ctx)78 void attest_get_cpu_rng_context(struct attest_rng_context *rng_ctx)
79 {
80 	unsigned int cpu_id = my_cpuid();
81 
82 	assert(prng_init_done);
83 
84 	rng_ctx->f_rng = mbedtls_hmac_drbg_random;
85 	rng_ctx->p_rng = &cpu_drbg_ctx[cpu_id];
86 }
87 
attest_rnd_prng_init(void)88 int attest_rnd_prng_init(void)
89 {
90 	const mbedtls_md_info_t *md_info;
91 	mbedtls_hmac_drbg_context drbg_ctx;
92 	uint8_t seed[128] __aligned(8) ; /* mbedtls_hardware_poll request this size */
93 	unsigned int i;
94 	int rc;
95 	int retval = 0;
96 
97 	assert(!prng_init_done);
98 	assert(IS_FPU_ALLOWED());
99 
100 	if (!is_feat_rng_present()) {
101 		return -EINVAL;
102 	}
103 
104 	/*
105 	 * Setup a temporary PRNG which seeded by real TRNG and use this
106 	 * instance to set up the per CPU PRNG objects. The temporary PRNG
107 	 * relies on the RNDR instruction to get its seed. RNDR instruction has
108 	 * an implementation defined TRNG backend. The timing of the TRNG could
109 	 * be nondeterministic therefore access to it is kept on the minimum.
110 	 */
111 	rc = get_random_seed(seed, sizeof(seed));
112 	if (rc != 0) {
113 		return -EINVAL;
114 	}
115 
116 	md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
117 	mbedtls_hmac_drbg_init(&drbg_ctx);
118 	rc = mbedtls_hmac_drbg_seed_buf(&drbg_ctx,
119 				    md_info,
120 				    seed, sizeof(seed));
121 	if (rc != 0) {
122 		retval = -EINVAL;
123 		goto free_temp_prng;
124 	}
125 
126 	/*
127 	 * Set up the per CPU PRNG objects which going to be used during
128 	 * Elliptic Curve signing to blind the private key.
129 	 */
130 	for (i = 0U; i < MAX_CPUS; ++i) {
131 		rc = mbedtls_hmac_drbg_random(&drbg_ctx, seed, sizeof(seed));
132 		if (rc != 0) {
133 			retval = -EINVAL;
134 			goto free_temp_prng;
135 		}
136 
137 		mbedtls_hmac_drbg_init(&cpu_drbg_ctx[i]);
138 		rc = mbedtls_hmac_drbg_seed_buf(&cpu_drbg_ctx[i], md_info,
139 						seed, sizeof(seed));
140 		if (rc != 0) {
141 			retval = -EINVAL;
142 			goto free_temp_prng;
143 		}
144 	}
145 
146 	prng_init_done = true;
147 
148 free_temp_prng:
149 	/* Free the memory allocated by the temporary PRNG */
150 	mbedtls_hmac_drbg_free(&drbg_ctx);
151 
152 	return retval;
153 }
154