articles icon indicating copy to clipboard operation
articles copied to clipboard

003-chromium boringssl随机数生成分析

Open xinali opened this issue 2 years ago • 0 comments

boringssl随机数生成分析

所有代码均来源于特定boringssl开源版本,无任何定制

  • boringssl随机数生成分析
    • 调用流程
    • 具体流程分析
      • 生成additional_data
      • 生成seed
      • 生成熵值
      • 生成随机数
    • 参考

boringssl随机数据生成分析

调用流程

boringssl的随机数据生成算法的整个流程

RAND_bytes
|-> RAND_bytes_with_additional_data
      | 
      | # 1. 生成additional_data数据 
      |   // 硬件指令/compiler相关
      |-> rdrand(additional_data, sizeof(additional_data)) 
      |     ->  OPENSSL_memset(additional_data, 0, sizeof(additional_data))
      |           -> memset
      |   // 平台相关
      |-> CRYPTO_sysrand(additional_data, sizeof(additional_data)) 
      |      -> linux实现
      |      -> windows实现
      | 
      | # 2. 生成seed和entropy
      |-> rand_get_seed
      |     -> get_seed_entropy // 熵值
      |     -> CRYPTO_get_seed_entropy
      |          -> rdrand|CRYPTO_sysrand_for_seed 生成熵值
      |     -> RAND_load_entropy  // 熵值传入entropy_buffer
      | 
      | # 3. 利用seed和entropy生成随机数
      |-> CTR_DRBG_init // 初始化
      |
      |-> CTR_DRBG_generate // 结合seed,生成随机数
      |     -> ctr_drbg_update // 更新init初始化后的数据
      |
      |-> CTR_DRBG_clear

整个流程大概分为三步

  1. 利用rdrand/CRYPTO_sysrand生成additional_data数据
  2. 利用rdrand/CRYPTO_sysrand生成seedentropy
  3. 结合生成的seed/entropy/additional_data,利用AES256-CTR-DRBG算法最终生成随机数据

具体流程分析

总入口RAND_bytes

int RAND_bytes(uint8_t *out, size_t out_len) {
  static const uint8_t kZeroAdditionalData[32] = {0};
  // 调用真正的实现函数
  RAND_bytes_with_additional_data(out, out_len, kZeroAdditionalData);
  return 1;
}

将全为0的数组kZeroAdditionalData带入RAND_bytes_with_additional_data

boringssl的随机数真正实现在RAND_bytes_with_additional_data中,看一下该函数的具体使用

void RAND_bytes_with_additional_data(uint8_t *out, size_t out_len,
                                     const uint8_t user_additional_data[32]) {
  if (out_len == 0) {
    return;
  }
  // 是否是fork mode
  const uint64_t fork_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_t additional_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));
    } else if (!have_rdrand()) {
      // 没有 GCC/Clang __RAND__ 预处理,调用系统
      // No alternative so block for OS entropy.
      CRYPTO_sysrand(additional_data, sizeof(additional_data));
    } else if (!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_t i = 0; i < sizeof(additional_data); i++) {
    additional_data[i] ^= user_additional_data[i];
  }
  // TLS数据设置
  struct rand_thread_state stack_state;
  struct rand_thread_state *state =
      CRYPTO_get_thread_local(OPENSSL_THREAD_LOCAL_RAND);

  // 根据状态会决定如何生成数据
  // 1. 状态为空
  if (state == NULL) {
    state = OPENSSL_malloc(sizeof(struct rand_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_t seed[CTR_DRBG_ENTROPY_LEN];
    uint8_t personalization[CTR_DRBG_ENTROPY_LEN] = {0};
    size_t personalization_len = 0;
    // 获取seed
    rand_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());
      struct rand_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_t seed[CTR_DRBG_ENTROPY_LEN];
    uint8_t reseed_additional_data[CTR_DRBG_ENTROPY_LEN] = {0};
    size_t reseed_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
  }

  int first_call = 1;
  while (out_len > 0) {
    size_t todo = 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
}

生成additional_data

具体分析该函数

一、判读是否为fork mode

const uint64_t fork_generation = CRYPTO_get_fork_generation();

二、 生成additional_data的三种方式

// 通过cpu硬件指令生成
rdrand(additional_data, sizeof(additional_data))
// 调用memset全设置为0,fork mode
OPENSSL_memset(additional_data, 0, sizeof(additional_data))
// 调用系统system call
CRYPTO_sysrand(additional_data, sizeof(additional_data))

其中调用硬件指令生成rdrand

// 将生成的数据分为两部分
// 1. CRYPTO_rdrand_multiple8_buf,生成8的倍数数据
// 2. rand_buf,生成剩余数据
static int rdrand(uint8_t *buf, const size_t len) {
  const size_t len_multiple8 = len & ~7;
  if (!CRYPTO_rdrand_multiple8_buf(buf, len_multiple8)) {
    return 0;
  }
  const size_t remainder = len - len_multiple8;

  if (remainder != 0) {
    assert(remainder < 8);

    uint8_t rand_buf[8];
    if (!CRYPTO_rdrand(rand_buf)) {
      return 0;
    }
    OPENSSL_memcpy(buf + len_multiple8, rand_buf, remainder);
  }

  return 1;
}

其中fork mode用的memset

static inline void *OPENSSL_memset(void *dst, int c, size_t n) {
  if (n == 0) {
    return dst;
  }
  return memset(dst, c, n);
}

其中CRYPTO_sysrand(Linux)实现

// CRYPTO_sysrand puts |requested| random bytes into |out|.
void CRYPTO_sysrand(uint8_t *out, size_t requested) {
  if (!fill_with_entropy(out, requested, /*block=*/1, /*seed=*/0)) {
    perror("entropy fill failed");
    abort();
  }
}

fill_with_entropy函数内容比较多,只截取部分

if defined(USE_NR_getrandom)
      // 普通类linux系统实现
      r = boringssl_getrandom(out, len, getrandom_flags);
#elif defined(FREEBSD_GETRANDOM)
      // freebsd系统实现
      r = getrandom(out, len, getrandom_flags);
#elif defined(OPENSSL_MACOS)
      // macOS系统
      if (__builtin_available(macos 10.12, *)) {
        // |getentropy| can only request 256 bytes at a time.
        size_t todo = len <= 256 ? len : 256;
        if (getentropy(out, todo) != 0) {
          r = -1;
        } else {
          r = (ssize_t)todo;
        }
      } else {
        fprintf(stderr, "urandom fd corrupt.\n");
        abort();
      }
#else  // USE_NR_getrandom
      fprintf(stderr, "urandom fd corrupt.\n");
      abort();
#endif
    } else {
      do {
        // 比较简单的一种方式直接取/dev/random的数据
        r = read(*urandom_fd_bss_get(), out, len);
      } while (r == -1 && errno == EINTR);
    }

    if (r <= 0) {
      return 0;
    }
    out += r;
    len -= r;
  }

做一下归纳,主要是类linux平台(包含macOS)下的几种随机数产生的方式

  1. USE_NR_getrandom

    调用boringssl_getrandom,利用系统syscall(__NR_getrandom, buf, buf_len, flags)生成数据

  2. FREEBSD_GETRANDOM

    freebsd调用系统getrandom(out, len, getrandom_flags)来生成

  3. OPENSSL_MACOS

    macOS调用系统etentropy(out, todo)来生成

  4. 最后一种

    调用open("/dev/urandom", O_RDONLY | O_CLOEXEC)来获取,北大开源的国密算法实现的方式

其中CRYPTO_sysrand(Windows)实现

// 直接调用windows系统实现的算法
void CRYPTO_sysrand(uint8_t *out, size_t requested) {
  while (requested > 0) {
    ULONG output_bytes_this_pass = ULONG_MAX;
    if (requested < output_bytes_this_pass) {
      output_bytes_this_pass = (ULONG)requested;
    }
    // On non-UWP configurations, use RtlGenRandom instead of BCryptGenRandom
    // to avoid accessing resources that may be unavailable inside the
    // Chromium sandbox. See https://crbug.com/boringssl/307
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && \
    !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
    if (!BCRYPT_SUCCESS(BCryptGenRandom(
            /*hAlgorithm=*/NULL, out, output_bytes_this_pass,
            BCRYPT_USE_SYSTEM_PREFERRED_RNG))) {
#else
    // 调用windows系统随机数
    if (RtlGenRandom(out, output_bytes_this_pass) == FALSE) {
#endif  // WINAPI_PARTITION_APP && !WINAPI_PARTITION_DESKTOP
      abort();
    }
    requested -= output_bytes_this_pass;
    out += output_bytes_this_pass;
  }
  return;
}

异或处理additional_data

for (size_t i = 0; i < sizeof(additional_data); i++) {
    additional_data[i] ^= user_additional_data[i];
  }

生成seed

根据TLS的状态会调用rand_get_seed去生成seed或者是重置seed数据

struct rand_thread_state *state =
      CRYPTO_get_thread_local(OPENSSL_THREAD_LOCAL_RAND);

第一种情况state == NULL

  if (state == NULL) {
    // 设置state状态
    state = OPENSSL_malloc(sizeof(struct rand_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;
    }
a
    state->last_block_valid = 0;
    uint8_t seed[CTR_DRBG_ENTROPY_LEN]; //CTR_DRBG_ENTROPY_LEN == 48
    uint8_t personalization[CTR_DRBG_ENTROPY_LEN] = {0};
    size_t personalization_len = 0;
    // 设置seed数据
    rand_get_seed(state, seed, personalization, &personalization_len);
    
    // 根据seed初始化
    if (!CTR_DRBG_init(&state->drbg, seed, personalization,
                       personalization_len)) {
      abort();
    }
    // 设置调用间隔为0
    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());
      struct rand_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_t seed[CTR_DRBG_ENTROPY_LEN];
    uint8_t reseed_additional_data[CTR_DRBG_ENTROPY_LEN] = {0};
    size_t reseed_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,该函数根据是否定义了BORINGSSL_FIPS,会有不同的操作

  1. 已定义BORINGSSL_FIPS

具体使用


// 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.
static void rand_get_seed(struct rand_thread_state *state,
                          uint8_t seed[CTR_DRBG_ENTROPY_LEN],
                          uint8_t additional_input[CTR_DRBG_ENTROPY_LEN],
                          size_t *out_additional_input_len) {
  uint8_t entropy_bytes[sizeof(state->last_block) +
                        CTR_DRBG_ENTROPY_LEN * BORINGSSL_FIPS_OVERREAD];
  uint8_t *entropy = entropy_bytes;
  size_t entropy_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);
  }

  int want_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_t i = 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_t i = 1; i < BORINGSSL_FIPS_OVERREAD; i++) {
    for (size_t j = 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|.
static void get_seed_entropy(uint8_t *out_entropy, size_t out_entropy_len,
                             int *out_want_additional_input) {
  // 这里获取到的是空值
  struct entropy_buffer *const buffer = 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());
}

调用entropy_buffer_bss_get获取熵值,结合宏定义DEFINE_BSS_GET(struct entropy_buffer, entropy_buffer),真正起作用的是RAND_need_entropy(out_entropy_len - buffer->bytes_valid)

void RAND_need_entropy(size_t bytes_needed) {
  uint8_t buf[/* last_block size */ 16 +
              CTR_DRBG_ENTROPY_LEN * BORINGSSL_FIPS_OVERREAD];
  size_t todo = sizeof(buf);
  if (todo > bytes_needed) {
    todo = bytes_needed;
  }

  int want_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生成熵值

void CRYPTO_get_seed_entropy(uint8_t *out_entropy, size_t out_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);
  }
}

将对应的熵值传入结构体entropy_buffer

void RAND_load_entropy(const uint8_t *entropy, size_t entropy_len,
                       int want_additional_input) {
  // 获取entropy_buffer
  struct entropy_buffer *const buffer = entropy_buffer_bss_get();

  CRYPTO_STATIC_MUTEX_lock_write(entropy_buffer_lock_bss_get());
  const size_t space = sizeof(buffer->bytes) - buffer->bytes_valid;
  if (entropy_len > space) {
    entropy_len = space;
  }

  // entropy存入buffer中
  OPENSSL_memcpy(&buffer->bytes[buffer->bytes_valid], entropy, entropy_len);
  buffer->bytes_valid += entropy_len;
  buffer->want_additional_input |=
      want_additional_input && (entropy_len != 0);
  CRYPTO_STATIC_MUTEX_unlock_write(entropy_buffer_lock_bss_get());
}

获取熵值之后,具体如何生成seed在循环中

 OPENSSL_memcpy(seed, entropy, CTR_DRBG_ENTROPY_LEN);
 // 异或处理
  for (size_t i = 1; i < BORINGSSL_FIPS_OVERREAD; i++) {
    for (size_t j = 0; j < CTR_DRBG_ENTROPY_LEN; j++) {
      seed[j] ^= entropy[CTR_DRBG_ENTROPY_LEN * i + j];
    }
  }

生成随机数

随机数生成算法可以根据是否是hash还是分组加密,可以分为以下几种

// 基于hash算法
HMAC-DRBG
KHF-DRBG
Hash-DRBG

// 基于分组加密算法
AES-OFB
AES-CTR
TDEA-OFB
TDEA-CTR

boringssl的随机数生成算法使用的是NIST SP 800-90A伪随机数生成器算法AES256-CTR-DRBG

生成完seed和熵值,最终调用CTR_DRBG_generate来生成具体随机数

真正生成之前需要初始化,调用CTR_DRBG_init,设置CTR算法部分初始化状态

int CTR_DRBG_init(CTR_DRBG_STATE *drbg,
                  const uint8_t entropy[CTR_DRBG_ENTROPY_LEN],
                  const uint8_t *personalization, size_t personalization_len) {
  // Section 10.2.1.3.1
  if (personalization_len > CTR_DRBG_ENTROPY_LEN) {
    return 0;
  }

  uint8_t seed_material[CTR_DRBG_ENTROPY_LEN];
  // 将上一个步骤生成的entropy熵值复制
  OPENSSL_memcpy(seed_material, entropy, CTR_DRBG_ENTROPY_LEN);

  // 结合entropy和前一步生成的personalization做异或计算
  for (size_t i = 0; i < personalization_len; i++) {
    seed_material[i] ^= personalization[i];
  }

  // Section 10.2.1.2

  // kInitMask is the result of encrypting blocks with big-endian value 1, 2
  // and 3 with the all-zero AES-256 key.
  // 异或的初始化table
  static const uint8_t kInitMask[CTR_DRBG_ENTROPY_LEN] = {
      0x53, 0x0f, 0x8a, 0xfb, 0xc7, 0x45, 0x36, 0xb9, 0xa9, 0x63, 0xb4, 0xf1,
      0xc4, 0xcb, 0x73, 0x8b, 0xce, 0xa7, 0x40, 0x3d, 0x4d, 0x60, 0x6b, 0x6e,
      0x07, 0x4e, 0xc5, 0xd3, 0xba, 0xf3, 0x9d, 0x18, 0x72, 0x60, 0x03, 0xca,
      0x37, 0xa6, 0x2a, 0x74, 0xd1, 0xa2, 0xf5, 0x8e, 0x75, 0x06, 0x35, 0x8e,
  };

  // 再次跟table做异或计算
  for (size_t i = 0; i < sizeof(kInitMask); i++) {
    seed_material[i] ^= kInitMask[i];
  }

  // 根据seed_material初始化ctr
  drbg->ctr = aes_ctr_set_key(&drbg->ks, NULL, &drbg->block, seed_material, 32);
  OPENSSL_memcpy(drbg->counter, seed_material + 32, 16);
  drbg->reseed_counter = 1;

  return 1;
}

初始化完成后,开始生成随机数据

int CTR_DRBG_generate(CTR_DRBG_STATE *drbg, uint8_t *out, size_t out_len,
                      const uint8_t *additional_data,
                      size_t additional_data_len) {
  // See 9.3.1
  if (out_len > CTR_DRBG_MAX_GENERATE_LENGTH) {
    return 0;
  }

  // See 10.2.1.5.1
  // 是否超过最大reseed数
  if (drbg->reseed_counter > kMaxReseedCount) {
    return 0;
  }

  // 利用第一步生成的additional_data,更新init初始化后的数据
  if (additional_data_len != 0 &&
      !ctr_drbg_update(drbg, additional_data, additional_data_len)) {
    return 0;
  }

  // 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.
  static const size_t kChunkSize = 8 * 1024;

  // 分组处理
  while (out_len >= AES_BLOCK_SIZE) {
    size_t todo = kChunkSize;
    if (todo > out_len) {
      todo = out_len;
    }

    todo &= ~(AES_BLOCK_SIZE-1);
    const size_t num_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_t i = 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_t block[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)) {
    return 0;
  }

  // 生成数需要+1
  drbg->reseed_counter++;
  FIPS_service_indicator_update_state();
  return 1;
}

至此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

xinali avatar Dec 05 '22 06:12 xinali