Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

003-chromium boringssl随机数生成分析 #84

Open
xinali opened this issue Dec 5, 2022 · 0 comments
Open

003-chromium boringssl随机数生成分析 #84

xinali opened this issue Dec 5, 2022 · 0 comments

Comments

@xinali
Copy link
Owner

xinali commented Dec 5, 2022

boringssl随机数生成分析

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

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 xinali added the Chromium label Dec 5, 2022
@xinali xinali changed the title boringssl随机数生成分析 003-chromium boringssl随机数生成分析 Apr 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant