1 /*
2 * This file is part of the openHiTLS project.
3 *
4 * openHiTLS is licensed under the Mulan PSL v2.
5 * You can use this software according to the terms and conditions of the Mulan PSL v2.
6 * You may obtain a copy of Mulan PSL v2 at:
7 *
8 * http://license.coscl.org.cn/MulanPSL2
9 *
10 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
11 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
12 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
13 * See the Mulan PSL v2 for more details.
14 */
15
16 #include "hitls_build.h"
17 #ifdef HITLS_CRYPTO_ENTROPY
18
19 #include <stdint.h>
20 #include "securec.h"
21 #include "crypt_utils.h"
22 #include "entropy_seed_pool.h"
23
24 #ifdef HITLS_CRYPTO_ENTROPY_HARDWARE
25 #if defined(__x86_64__) || defined(__aarch64__)
26 /* For clarity */
27 #define DRNG_NO_SUPPORT 0x0
28 #define DRNG_HAS_RDRAND 0x1
29 #define DRNG_HAS_RDSEED 0x2
30
31 #define RDRAND_MAX_RETRIES 20
32
HWRandBytes(uint8_t * buf,uint32_t len,int32_t (* rand)(uint64_t *),uint32_t retries)33 static uint32_t HWRandBytes(uint8_t *buf, uint32_t len, int32_t (*rand)(uint64_t *), uint32_t retries)
34 {
35 uint32_t left = len;
36 while (left != 0) {
37 uint32_t cnt = 0;
38 uint64_t randVal = 0;
39 while (cnt < retries) {
40 if (rand(&randVal) == 1) {
41 break;
42 }
43 cnt++;
44 }
45 if (cnt == retries) {
46 // high probability that it wouldn't be here
47 return len - left;
48 }
49 uint32_t cpLen = left < sizeof(randVal) ? left : sizeof(randVal);
50 (void)memcpy_s(buf + len - left, left, (uint8_t *)&randVal, cpLen);
51 left -= cpLen;
52 }
53
54 return len;
55 }
56
57 #ifdef __x86_64__
58 #include <cpuid.h>
59
60 /**
61 * Using Intel/AMD cpu's instructions to get hardware random value.
62 *
63 * references:
64 * https://crypto.stackexchange.com/questions/42340/usage-difference-between-x86-rdrand-and-rdseed
65 *
66 * https://www.intel.com/content/www/us/en/developer/articles/guide/intel-
67 * digital-random-number-generator-drng-software-implementation-guide.html
68 */
69
70 /**
71 * If the return value is 1, the variable passed by reference will be populated with a usable random value.
72 * If the return value is 0, the caller understands that the value assigned to the variable is not usable.
73 */
Rdrand64(uint64_t * rand)74 static int32_t Rdrand64(uint64_t *rand)
75 {
76 uint8_t ok = 0;
77 asm volatile("rdrand %0; setc %1" : "=r"(*rand), "=qm"(ok));
78 return (int32_t)ok;
79 }
80
81 /**
82 * return value of "Rdseed64" is same to "Rdrand64".
83 */
Rdseed64(uint64_t * seed)84 static int32_t Rdseed64(uint64_t *seed)
85 {
86 uint8_t ok = 0;
87 asm volatile("rdseed %0; setc %1" : "=r"(*seed), "=qm"(ok));
88 return (int32_t)ok;
89 }
90
91 #define RAND_BYTES(buf, len) HWRandBytes(buf, len, Rdrand64, RDRAND_MAX_RETRIES)
92 #define SEED_BYTES(buf, len) HWRandBytes(buf, len, Rdseed64, RDRAND_MAX_RETRIES)
93
GetDrbgSupport()94 static uint32_t GetDrbgSupport()
95 {
96 static uint32_t drngCap = 0xffffffff;
97
98 if (drngCap == 0xffffffff) {
99 drngCap = DRNG_NO_SUPPORT;
100 uint32_t cpuid[CPU_ID_OUT_U32_CNT];
101 GetCpuId(0x1, 0, cpuid);
102 if (cpuid[ECX_OUT_IDX] & bit_RDRND) {
103 drngCap |= DRNG_HAS_RDRAND;
104 }
105
106 (void)memset_s(cpuid, sizeof(cpuid), 0, sizeof(cpuid));
107 GetCpuId(0x7, 0, cpuid);
108 if (cpuid[EBX_OUT_IDX] & bit_RDSEED) {
109 drngCap |= DRNG_HAS_RDSEED;
110 }
111 }
112
113 return drngCap;
114 }
115
ENTROPY_HWEntropyGet(void * ctx,uint8_t * buf,uint32_t bufLen)116 uint32_t ENTROPY_HWEntropyGet(void *ctx, uint8_t *buf, uint32_t bufLen)
117 {
118 (void)ctx;
119
120 uint32_t drngCap = GetDrbgSupport();
121 if (drngCap & DRNG_HAS_RDSEED) {
122 return SEED_BYTES(buf, bufLen);
123 } else if (drngCap & DRNG_HAS_RDRAND) {
124 return RAND_BYTES(buf, bufLen);
125 } else {
126 return 0;
127 }
128 }
129 #endif
130
131 #ifdef __aarch64__
132 #include <sys/auxv.h>
133 #include "crypt_arm.h"
GetDrbgSupport()134 static uint32_t GetDrbgSupport()
135 {
136 static uint32_t drngCap = 0xffffffff;
137
138 if (drngCap == 0xffffffff) {
139 drngCap = DRNG_NO_SUPPORT;
140 if (getauxval(CRYPT_CAP2) & CRYPT_ARM_CAP2_RNG) {
141 drngCap |= (DRNG_HAS_RDRAND | DRNG_HAS_RDSEED);
142 }
143 }
144 return drngCap;
145 }
146 // https://developer.arm.com/documentation/ddi0601/2024-12/AArch64-Registers/RNDR--Random-Number
Rndr64(uint64_t * rand)147 static int32_t Rndr64(uint64_t *rand)
148 {
149 uint8_t ok = 0;
150 asm volatile("mrs %0, s3_3_c2_c4_0; cset %w1, ne;" : "=r"(*rand), "=r"(ok));
151 return (int32_t)ok;
152 }
153
154 // https://developer.arm.com/documentation/ddi0601/2024-12/AArch64-Registers/RNDRRS--Random-Number-Full-Entropy
Rndrrs64(uint64_t * seed)155 static int32_t Rndrrs64(uint64_t *seed)
156 {
157 uint8_t ok = 0;
158 asm volatile("mrs %0, s3_3_c2_c4_1; cset %w1, ne;" : "=r"(*seed), "=r"(ok));
159 return (int32_t)ok;
160 }
161
162 #define RAND_BYTES(buf, len) HWRandBytes(buf, len, Rndr64, RDRAND_MAX_RETRIES)
163 #define SEED_BYTES(buf, len) HWRandBytes(buf, len, Rndrrs64, RDRAND_MAX_RETRIES)
164
ENTROPY_HWEntropyGet(void * ctx,uint8_t * buf,uint32_t bufLen)165 uint32_t ENTROPY_HWEntropyGet(void *ctx, uint8_t *buf, uint32_t bufLen)
166 {
167 (void)ctx;
168 uint32_t drngCap = GetDrbgSupport();
169 if (drngCap & DRNG_HAS_RDSEED) {
170 uint32_t len = SEED_BYTES(buf, bufLen);
171 if (bufLen - len > 0) {
172 len += RAND_BYTES(buf + len, bufLen - len);
173 }
174 return len;
175 } else {
176 return 0;
177 }
178 }
179
180 #endif
181
182 #else
ENTROPY_HWEntropyGet(void * ctx,uint8_t * buf,uint32_t bufLen)183 uint32_t ENTROPY_HWEntropyGet(void *ctx, uint8_t *buf, uint32_t bufLen)
184 {
185 (void)ctx;
186 (void)buf;
187 (void)bufLen;
188 return 0;
189 }
190
191 #endif
192 #else
ENTROPY_HWEntropyGet(void * ctx,uint8_t * buf,uint32_t bufLen)193 uint32_t ENTROPY_HWEntropyGet(void *ctx, uint8_t *buf, uint32_t bufLen)
194 {
195 (void)ctx;
196 (void)buf;
197 (void)bufLen;
198 return 0;
199 }
200
201 #endif
202 #endif