• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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