You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
voidRAND_bytes_with_additional_data(uint8_t*out, size_tout_len,
constuint8_tuser_additional_data[32]) {
if (out_len==0) {
return;
}
// 是否是fork modeconstuint64_tfork_generation=CRYPTO_get_fork_generation();
// Additional data is mixed into every CTR-DRBG call to protect, as best we// can, against forks & VM clones. We do not over-read this information and// don't reseed with it so, from the point of view of FIPS, this doesn't// provide “prediction resistance”. But, in practice, it does.uint8_tadditional_data[32];
// Intel chips have fast RDRAND instructions while, in other cases, RDRAND can// be _slower_ than a system call.if (
// intel cpu,支持硬件RDRAND
!have_fast_rdrand() ||// 其他支持硬件RDRAND
!rdrand(additional_data, sizeof(additional_data))) {
// Without a hardware RNG to save us from address-space duplication, the OS// entropy is used. This can be expensive (one read per |RAND_bytes| call)// and so is disabled when we have fork detection, or if the application has// promised not to fork.if (fork_generation!=0||rand_fork_unsafe_buffering_enabled()) {
OPENSSL_memset(additional_data, 0, sizeof(additional_data));
} elseif (!have_rdrand()) {
// 没有 GCC/Clang __RAND__ 预处理,调用系统// No alternative so block for OS entropy.CRYPTO_sysrand(additional_data, sizeof(additional_data));
} elseif (!CRYPTO_sysrand_if_available(additional_data,
sizeof(additional_data)) &&
!rdrand(additional_data, sizeof(additional_data))) {
// RDRAND failed: block for OS entropy.CRYPTO_sysrand(additional_data, sizeof(additional_data));
}
}
for (size_ti=0; i<sizeof(additional_data); i++) {
additional_data[i] ^= user_additional_data[i];
}
// TLS数据设置structrand_thread_statestack_state;
structrand_thread_state*state=CRYPTO_get_thread_local(OPENSSL_THREAD_LOCAL_RAND);
// 根据状态会决定如何生成数据// 1. 状态为空if (state==NULL) {
state=OPENSSL_malloc(sizeof(structrand_thread_state));
if (state==NULL||
!CRYPTO_set_thread_local(OPENSSL_THREAD_LOCAL_RAND, state,
rand_thread_state_free)) {
// If the system is out of memory, use an ephemeral state on the// stack.state=&stack_state;
}
state->last_block_valid=0;
uint8_tseed[CTR_DRBG_ENTROPY_LEN];
uint8_tpersonalization[CTR_DRBG_ENTROPY_LEN] = {0};
size_tpersonalization_len=0;
// 获取seedrand_get_seed(state, seed, personalization, &personalization_len);
if (!CTR_DRBG_init(&state->drbg, seed, personalization,
personalization_len)) {
abort();
}
state->calls=0;
state->fork_generation=fork_generation;
#if defined(BORINGSSL_FIPS)
// 状态不空,设置状态链if (state!=&stack_state) {
CRYPTO_STATIC_MUTEX_lock_write(thread_states_list_lock_bss_get());
structrand_thread_state**states_list=thread_states_list_bss_get();
state->next=*states_list;
if (state->next!=NULL) {
state->next->prev=state;
}
state->prev=NULL;
*states_list=state;
CRYPTO_STATIC_MUTEX_unlock_write(thread_states_list_lock_bss_get());
}
#endif
}
if (state->calls >= kReseedInterval||state->fork_generation!=fork_generation) {
uint8_tseed[CTR_DRBG_ENTROPY_LEN];
uint8_treseed_additional_data[CTR_DRBG_ENTROPY_LEN] = {0};
size_treseed_additional_data_len=0;
rand_get_seed(state, seed, reseed_additional_data,
&reseed_additional_data_len);
#if defined(BORINGSSL_FIPS)
// Take a read lock around accesses to |state->drbg|. This is needed to// avoid returning bad entropy if we race with// |rand_thread_state_clear_all|.//// This lock must be taken after any calls to |CRYPTO_sysrand| to avoid a// bug on ppc64le. glibc may implement pthread locks by wrapping user code// in a hardware transaction, but, on some older versions of glibc and the// kernel, syscalls made with |syscall| did not abort the transaction.CRYPTO_STATIC_MUTEX_lock_read(state_clear_all_lock_bss_get());
#endif// 如果超过了最大调用数,需要重新生成if (!CTR_DRBG_reseed(&state->drbg, seed, reseed_additional_data,
reseed_additional_data_len)) {
abort();
}
state->calls=0;
state->fork_generation=fork_generation;
} else {
#if defined(BORINGSSL_FIPS)
CRYPTO_STATIC_MUTEX_lock_read(state_clear_all_lock_bss_get());
#endif
}
intfirst_call=1;
while (out_len>0) {
size_ttodo=out_len;
if (todo>CTR_DRBG_MAX_GENERATE_LENGTH) {
todo=CTR_DRBG_MAX_GENERATE_LENGTH;
}
// 利用CTR-DRBG伪随机数算法生成随机数据if (!CTR_DRBG_generate(&state->drbg, out, todo, additional_data,
first_call ? sizeof(additional_data) : 0)) {
abort();
}
out+=todo;
out_len-=todo;
// Though we only check before entering the loop, this cannot add enough to// overflow a |size_t|.state->calls++;
first_call=0;
}
if (state==&stack_state) {
CTR_DRBG_clear(&state->drbg);
}
#if defined(BORINGSSL_FIPS)
CRYPTO_STATIC_MUTEX_unlock_read(state_clear_all_lock_bss_get());
#endif
}
if (state==NULL) {
// 设置state状态state=OPENSSL_malloc(sizeof(structrand_thread_state));
if (state==NULL||
!CRYPTO_set_thread_local(OPENSSL_THREAD_LOCAL_RAND, state,
rand_thread_state_free)) {
// If the system is out of memory, use an ephemeral state on the// stack.state=&stack_state;
}
astate->last_block_valid=0;
uint8_tseed[CTR_DRBG_ENTROPY_LEN]; //CTR_DRBG_ENTROPY_LEN == 48uint8_tpersonalization[CTR_DRBG_ENTROPY_LEN] = {0};
size_tpersonalization_len=0;
// 设置seed数据rand_get_seed(state, seed, personalization, &personalization_len);
// 根据seed初始化if (!CTR_DRBG_init(&state->drbg, seed, personalization,
personalization_len)) {
abort();
}
// 设置调用间隔为0state->calls=0;
state->fork_generation=fork_generation;
#if defined(BORINGSSL_FIPS)
// 设置线程状态链if (state!=&stack_state) {
CRYPTO_STATIC_MUTEX_lock_write(thread_states_list_lock_bss_get());
structrand_thread_state**states_list=thread_states_list_bss_get();
state->next=*states_list;
if (state->next!=NULL) {
state->next->prev=state;
}
state->prev=NULL;
*states_list=state;
CRYPTO_STATIC_MUTEX_unlock_write(thread_states_list_lock_bss_get());
}
#endif
}
第二种情况
// 超出调用数,需要重置种子if (state->calls >= kReseedInterval||state->fork_generation!=fork_generation) {
uint8_tseed[CTR_DRBG_ENTROPY_LEN];
uint8_treseed_additional_data[CTR_DRBG_ENTROPY_LEN] = {0};
size_treseed_additional_data_len=0;
// 获取seed rand_get_seed(state, seed, reseed_additional_data,
&reseed_additional_data_len);
#if defined(BORINGSSL_FIPS)
// Take a read lock around accesses to |state->drbg|. This is needed to// avoid returning bad entropy if we race with// |rand_thread_state_clear_all|.//// This lock must be taken after any calls to |CRYPTO_sysrand| to avoid a// bug on ppc64le. glibc may implement pthread locks by wrapping user code// in a hardware transaction, but, on some older versions of glibc and the// kernel, syscalls made with |syscall| did not abort the transaction.CRYPTO_STATIC_MUTEX_lock_read(state_clear_all_lock_bss_get());
#endif// 重置种子数据if (!CTR_DRBG_reseed(&state->drbg, seed, reseed_additional_data,
reseed_additional_data_len)) {
abort();
}
state->calls=0;
state->fork_generation=fork_generation;
} else {
#if defined(BORINGSSL_FIPS)
CRYPTO_STATIC_MUTEX_lock_read(state_clear_all_lock_bss_get());
#endif
}
// rand_get_seed fills |seed| with entropy. In some cases, it will additionally// fill |additional_input| with entropy to supplement |seed|. It sets// |*out_additional_input_len| to the number of extra bytes.staticvoidrand_get_seed(structrand_thread_state*state,
uint8_tseed[CTR_DRBG_ENTROPY_LEN],
uint8_tadditional_input[CTR_DRBG_ENTROPY_LEN],
size_t*out_additional_input_len) {
uint8_tentropy_bytes[sizeof(state->last_block) +CTR_DRBG_ENTROPY_LEN*BORINGSSL_FIPS_OVERREAD];
uint8_t*entropy=entropy_bytes;
size_tentropy_len=sizeof(entropy_bytes);
if (state->last_block_valid) {
// No need to fill |state->last_block| with entropy from the read.entropy+=sizeof(state->last_block);
entropy_len-=sizeof(state->last_block);
}
intwant_additional_input;
// 获取seed的熵值get_seed_entropy(entropy, entropy_len, &want_additional_input);
if (!state->last_block_valid) {
OPENSSL_memcpy(state->last_block, entropy, sizeof(state->last_block));
entropy+=sizeof(state->last_block);
entropy_len-=sizeof(state->last_block);
}
// See FIPS 140-2, section 4.9.2. This is the “continuous random number// generator test” which causes the program to randomly abort. Hopefully the// rate of failure is small enough not to be a problem in practice.if (CRYPTO_memcmp(state->last_block, entropy, sizeof(state->last_block)) ==0) {
fprintf(stderr, "CRNGT failed.\n");
BORINGSSL_FIPS_abort();
}
assert(entropy_len % CRNGT_BLOCK_SIZE==0);
for (size_ti=CRNGT_BLOCK_SIZE; i<entropy_len; i+=CRNGT_BLOCK_SIZE) {
if (CRYPTO_memcmp(entropy+i-CRNGT_BLOCK_SIZE, entropy+i,
CRNGT_BLOCK_SIZE) ==0) {
fprintf(stderr, "CRNGT failed.\n");
BORINGSSL_FIPS_abort();
}
}
OPENSSL_memcpy(state->last_block, entropy+entropy_len-CRNGT_BLOCK_SIZE,
CRNGT_BLOCK_SIZE);
assert(entropy_len==BORINGSSL_FIPS_OVERREAD*CTR_DRBG_ENTROPY_LEN);
OPENSSL_memcpy(seed, entropy, CTR_DRBG_ENTROPY_LEN);
// 异或处理for (size_ti=1; i<BORINGSSL_FIPS_OVERREAD; i++) {
for (size_tj=0; j<CTR_DRBG_ENTROPY_LEN; j++) {
seed[j] ^= entropy[CTR_DRBG_ENTROPY_LEN*i+j];
}
}
// If we used something other than system entropy then also// opportunistically read from the system. This avoids solely relying on the// hardware once the entropy pool has been initialized.*out_additional_input_len=0;
if (want_additional_input&&CRYPTO_sysrand_if_available(additional_input, CTR_DRBG_ENTROPY_LEN)) {
*out_additional_input_len=CTR_DRBG_ENTROPY_LEN;
}
}
生成熵值
生成seed之前,需要获取熵值
// get_seed_entropy fills |out_entropy_len| bytes of |out_entropy| from the// global |entropy_buffer|.staticvoidget_seed_entropy(uint8_t*out_entropy, size_tout_entropy_len,
int*out_want_additional_input) {
// 这里获取到的是空值structentropy_buffer*constbuffer=entropy_buffer_bss_get();
if (out_entropy_len>sizeof(buffer->bytes)) {
abort();
}
CRYPTO_STATIC_MUTEX_lock_write(entropy_buffer_lock_bss_get());
while (buffer->bytes_valid<out_entropy_len) {
CRYPTO_STATIC_MUTEX_unlock_write(entropy_buffer_lock_bss_get());
// 会调用passive.c中的函数去实现RAND_need_entropy(out_entropy_len-buffer->bytes_valid);
CRYPTO_STATIC_MUTEX_lock_write(entropy_buffer_lock_bss_get());
}
// 将数据存入struct entropy_buffer中*out_want_additional_input=buffer->want_additional_input;
OPENSSL_memcpy(out_entropy, buffer->bytes, out_entropy_len);
OPENSSL_memmove(buffer->bytes, &buffer->bytes[out_entropy_len],
buffer->bytes_valid-out_entropy_len);
buffer->bytes_valid-=out_entropy_len;
if (buffer->bytes_valid==0) {
buffer->want_additional_input=0;
}
CRYPTO_STATIC_MUTEX_unlock_write(entropy_buffer_lock_bss_get());
}
voidRAND_need_entropy(size_tbytes_needed) {
uint8_tbuf[/* last_block size */16+CTR_DRBG_ENTROPY_LEN*BORINGSSL_FIPS_OVERREAD];
size_ttodo=sizeof(buf);
if (todo>bytes_needed) {
todo=bytes_needed;
}
intwant_additional_input;
// 下面两个函数都是在rand.c中实现CRYPTO_get_seed_entropy(buf, todo, &want_additional_input);
RAND_load_entropy(buf, todo, want_additional_input);
}
跟踪CRYPTO_get_seed_entropy生成熵值
voidCRYPTO_get_seed_entropy(uint8_t*out_entropy, size_tout_entropy_len,
int*out_want_additional_input) {
*out_want_additional_input=0;
// 硬件/编译器相关函数实现if (have_rdrand() &&rdrand(out_entropy, out_entropy_len)) {
*out_want_additional_input=1;
} else {
// 直接调用平台相关函数实现CRYPTO_sysrand_for_seed(out_entropy, out_entropy_len);
}
if (boringssl_fips_break_test("CRNG")) {
// This breaks the "continuous random number generator test" defined in FIPS// 140-2, section 4.9.2, and implemented in |rand_get_seed|.OPENSSL_memset(out_entropy, 0, out_entropy_len);
}
}
intCTR_DRBG_generate(CTR_DRBG_STATE*drbg, uint8_t*out, size_tout_len,
constuint8_t*additional_data,
size_tadditional_data_len) {
// See 9.3.1if (out_len>CTR_DRBG_MAX_GENERATE_LENGTH) {
return0;
}
// See 10.2.1.5.1// 是否超过最大reseed数if (drbg->reseed_counter>kMaxReseedCount) {
return0;
}
// 利用第一步生成的additional_data,更新init初始化后的数据if (additional_data_len!=0&&
!ctr_drbg_update(drbg, additional_data, additional_data_len)) {
return0;
}
// kChunkSize is used to interact better with the cache. Since the AES-CTR// code assumes that it's encrypting rather than just writing keystream, the// buffer has to be zeroed first. Without chunking, large reads would zero// the whole buffer, flushing the L1 cache, and then do another pass (missing// the cache every time) to “encrypt” it. The code can avoid this by// chunking.staticconstsize_tkChunkSize=8*1024;
// 分组处理while (out_len >= AES_BLOCK_SIZE) {
size_ttodo=kChunkSize;
if (todo>out_len) {
todo=out_len;
}
todo &= ~(AES_BLOCK_SIZE-1);
constsize_tnum_blocks=todo / AES_BLOCK_SIZE;
// 处理每组数据if (drbg->ctr) {
OPENSSL_memset(out, 0, todo);
ctr32_add(drbg, 1);
drbg->ctr(out, out, num_blocks, &drbg->ks, drbg->counter);
ctr32_add(drbg, (uint32_t)(num_blocks-1));
} else {
for (size_ti=0; i<todo; i+=AES_BLOCK_SIZE) {
ctr32_add(drbg, 1);
drbg->block(drbg->counter, out+i, &drbg->ks);
}
}
out+=todo;
out_len-=todo;
}
if (out_len>0) {
uint8_tblock[AES_BLOCK_SIZE];
ctr32_add(drbg, 1);
drbg->block(drbg->counter, block, &drbg->ks);
// 最终的out随机数据OPENSSL_memcpy(out, block, out_len);
}
// Right-padding |additional_data| in step 2.2 is handled implicitly by// |ctr_drbg_update|, to save a copy.// 再次更新if (!ctr_drbg_update(drbg, additional_data, additional_data_len)) {
return0;
}
// 生成数需要+1drbg->reseed_counter++;
FIPS_service_indicator_update_state();
return1;
}
boringssl随机数生成分析
boringssl
随机数据生成分析调用流程
boringssl
的随机数据生成算法的整个流程整个流程大概分为三步
rdrand/CRYPTO_sysrand
生成additional_data
数据rdrand/CRYPTO_sysrand
生成seed
和entropy
seed/entropy/additional_data
,利用AES256-CTR-DRBG
算法最终生成随机数据具体流程分析
总入口
RAND_bytes
将全为0的数组
kZeroAdditionalData
带入RAND_bytes_with_additional_data
boringssl
的随机数真正实现在RAND_bytes_with_additional_data
中,看一下该函数的具体使用生成additional_data
具体分析该函数
一、判读是否为
fork mode
二、 生成
additional_data
的三种方式其中调用硬件指令生成
rdrand
其中
fork mode
用的memset
其中
CRYPTO_sysrand(Linux)
实现fill_with_entropy
函数内容比较多,只截取部分做一下归纳,主要是类
linux
平台(包含macOS
)下的几种随机数产生的方式USE_NR_getrandom
调用
boringssl_getrandom
,利用系统syscall(__NR_getrandom, buf, buf_len, flags)
生成数据FREEBSD_GETRANDOM
freebsd
调用系统getrandom(out, len, getrandom_flags)
来生成OPENSSL_MACOS
macOS
调用系统etentropy(out, todo)
来生成最后一种
调用
open("/dev/urandom", O_RDONLY | O_CLOEXEC)
来获取,北大开源的国密算法实现的方式其中
CRYPTO_sysrand(Windows)
实现异或处理
additional_data
生成seed
根据
TLS
的状态会调用rand_get_seed
去生成seed
或者是重置seed
数据第一种情况
state == NULL
第二种情况
来具体看看
rand_get_seed
,该函数根据是否定义了BORINGSSL_FIPS
,会有不同的操作BORINGSSL_FIPS
具体使用
生成熵值
生成
seed
之前,需要获取熵值调用
entropy_buffer_bss_get
获取熵值,结合宏定义DEFINE_BSS_GET(struct entropy_buffer, entropy_buffer)
,真正起作用的是RAND_need_entropy(out_entropy_len - buffer->bytes_valid)
跟踪
CRYPTO_get_seed_entropy
生成熵值将对应的熵值传入结构体
entropy_buffer
中获取熵值之后,具体如何生成
seed
在循环中生成随机数
随机数生成算法可以根据是否是
hash
还是分组加密,可以分为以下几种boringssl
的随机数生成算法使用的是NIST SP 800-90A
伪随机数生成器算法AES256-CTR-DRBG
生成完
seed
和熵值,最终调用CTR_DRBG_generate
来生成具体随机数真正生成之前需要初始化,调用
CTR_DRBG_init
,设置CTR
算法部分初始化状态初始化完成后,开始生成随机数据
至此
boringssl
的随机数相关内容分析完毕参考
boringssl fipsmodule
boringssl FIPS 140-2
北京大学实现的gmssl
NIST Cryptographic Module Validation Program
NIST SP 800-90A
Five DRBG Algorithms Based on Hash Functions and Block
Pseudorandom Black Swans Cache Attacks on CTR_DRBG
Faster Randomness Testing with the NIST Statistical Test Suite
The text was updated successfully, but these errors were encountered: