diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-01-10 00:04:40 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-01-10 00:04:40 +0000 |
commit | 62fa09ed43a813d8ff8e89d08a5ecf91dfba2a0a (patch) | |
tree | 5e06709d4eb6fb449856a106fb53f3dd1aa83c39 | |
parent | 839ec5639aceebbe83a9448433f2fdd6b698c18a (diff) | |
parent | aa6b738d994f6e44a0ec25a3dc16c74c748dc951 (diff) | |
download | boringssl-android13-qpr3-s4-release.tar.gz |
Snap for 9470583 from aa6b738d994f6e44a0ec25a3dc16c74c748dc951 to tm-qpr3-releaseandroid-13.0.0_r83android-13.0.0_r79android-13.0.0_r78android-13.0.0_r77android-13.0.0_r76android-13.0.0_r75android-13.0.0_r71android-13.0.0_r70android-13.0.0_r69android-13.0.0_r68android-13.0.0_r67android-13.0.0_r63android-13.0.0_r62android-13.0.0_r61android-13.0.0_r56android-13.0.0_r54android-13.0.0_r53android-13.0.0_r52android-13.0.0_r51android-13.0.0_r50android13-qpr3-s9-releaseandroid13-qpr3-s8-releaseandroid13-qpr3-s7-releaseandroid13-qpr3-s6-releaseandroid13-qpr3-s5-releaseandroid13-qpr3-s4-releaseandroid13-qpr3-s3-releaseandroid13-qpr3-s2-releaseandroid13-qpr3-s14-releaseandroid13-qpr3-s13-releaseandroid13-qpr3-s12-releaseandroid13-qpr3-s11-releaseandroid13-qpr3-s10-releaseandroid13-qpr3-s1-releaseandroid13-qpr3-release
Change-Id: I57e3517f2e742c01275d32cb7f0475cdf8546fa5
24 files changed, 815 insertions, 380 deletions
@@ -566,3 +566,94 @@ cc_binary { "src/util/fipstools/test_fips.c", ], } + +// Rust bindings +rust_bindgen { + name: "libbssl_sys_raw", + source_stem: "bindings", + crate_name: "bssl_sys_raw", + host_supported: true, + wrapper_src: "src/rust/wrapper.h", + vendor_available: true, + bindgen_flags: [ + // Adapted from upstream the src/rust/CMakeLists.txt file at: + // https://boringssl.googlesource.com/boringssl/+/refs/heads/master/rust/CMakeLists.txt + "--no-derive-default", + "--enable-function-attribute-detection", + "--use-core", + "--size_t-is-usize", + "--default-macro-constant-type=signed", + "--rustified-enum=point_conversion_form_t", + // These are not BoringSSL symbols, they are from glibc + // and are not relevant to the build besides throwing warnings + // about their 'long double' (aka u128) not being FFI safe. + // We block those functions so that the build doesn't + // spam warnings. + // + // https://github.com/rust-lang/rust-bindgen/issues/1549 describes the current problem + // and other folks' solutions. + "--blocklist-function=strtold", + "--blocklist-function=qecvt", + "--blocklist-function=qecvt_r", + "--blocklist-function=qgcvt", + "--blocklist-function=qfcvt", + "--blocklist-function=qfcvt_r", + ], + shared_libs: [ + "libcrypto", + "libssl", + ], +} + +// Encapsulate the bindgen-generated layout tests as a test target. +rust_test { + name: "libbssl_sys_raw_test", + srcs: [ + ":libbssl_sys_raw", + ], + crate_name: "bssl_sys_raw_test", + test_suites: ["general-tests"], + auto_gen_config: true, + clippy_lints: "none", + lints: "none", +} + +// Rust's bindgen doesn't cope with macros, so this target includes C functions that +// do the same thing as macros defined in BoringSSL header files. +cc_library_static { + name: "libbssl_rust_support", + host_supported: true, + defaults: ["boringssl_flags"], + srcs: ["src/rust/rust_wrapper.c"], + shared_libs: [ + "libcrypto", + "libssl", + ], +} + +// Replace the upstream CMake placeholder with a re-export of all of the local bindgen output. +gensrcs { + name: "libbssl_sys_src", + srcs: ["src/rust/src/lib.rs"], + cmd: "sed 's@^.{INCLUDES}@pub use bssl_sys_raw::*;@' $(in) > $(out)", +} + +rust_library { + name: "libbssl_ffi", + host_supported: true, + crate_name: "bssl_ffi", + visibility: [ + "//external/rust/crates/openssl", + "//system/keymint/boringssl", + "//system/security/prng_seeder", + ], + // Use the modified source with placeholder replaced. + srcs: [":libbssl_sys_src"], + vendor_available: true, + // Since libbssl_sys_raw is not publically visible, we can't + // accidentally force a double-link by linking statically, so do so. + rlibs: ["libbssl_sys_raw"], + static_libs: [ + "libbssl_rust_support", + ], +} diff --git a/BUILD.generated.bzl b/BUILD.generated.bzl index a1bef2ea..bac765af 100644 --- a/BUILD.generated.bzl +++ b/BUILD.generated.bzl @@ -154,6 +154,7 @@ crypto_headers = [ "src/include/openssl/conf.h", "src/include/openssl/cpu.h", "src/include/openssl/crypto.h", + "src/include/openssl/ctrdrbg.h", "src/include/openssl/curve25519.h", "src/include/openssl/des.h", "src/include/openssl/dh.h", diff --git a/TEST_MAPPING b/TEST_MAPPING new file mode 100644 index 00000000..ce976386 --- /dev/null +++ b/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "libbssl_sys_raw_test" + } + ] +} diff --git a/apple-aarch64/crypto/fipsmodule/p256-armv8-asm.S b/apple-aarch64/crypto/fipsmodule/p256-armv8-asm.S index 0b655fc1..7a5202dd 100644 --- a/apple-aarch64/crypto/fipsmodule/p256-armv8-asm.S +++ b/apple-aarch64/crypto/fipsmodule/p256-armv8-asm.S @@ -31,58 +31,6 @@ LordK: .byte 69,67,80,95,78,73,83,84,90,50,53,54,32,102,111,114,32,65,82,77,118,56,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0 .align 2 -// void ecp_nistz256_to_mont(BN_ULONG x0[4],const BN_ULONG x1[4]); -.globl _ecp_nistz256_to_mont -.private_extern _ecp_nistz256_to_mont - -.align 6 -_ecp_nistz256_to_mont: - AARCH64_SIGN_LINK_REGISTER - stp x29,x30,[sp,#-32]! - add x29,sp,#0 - stp x19,x20,[sp,#16] - - ldr x3,LRR // bp[0] - ldp x4,x5,[x1] - ldp x6,x7,[x1,#16] - ldr x12,Lpoly+8 - ldr x13,Lpoly+24 - adr x2,LRR // &bp[0] - - bl __ecp_nistz256_mul_mont - - ldp x19,x20,[sp,#16] - ldp x29,x30,[sp],#32 - AARCH64_VALIDATE_LINK_REGISTER - ret - - -// void ecp_nistz256_from_mont(BN_ULONG x0[4],const BN_ULONG x1[4]); -.globl _ecp_nistz256_from_mont -.private_extern _ecp_nistz256_from_mont - -.align 4 -_ecp_nistz256_from_mont: - AARCH64_SIGN_LINK_REGISTER - stp x29,x30,[sp,#-32]! - add x29,sp,#0 - stp x19,x20,[sp,#16] - - mov x3,#1 // bp[0] - ldp x4,x5,[x1] - ldp x6,x7,[x1,#16] - ldr x12,Lpoly+8 - ldr x13,Lpoly+24 - adr x2,Lone // &bp[0] - - bl __ecp_nistz256_mul_mont - - ldp x19,x20,[sp,#16] - ldp x29,x30,[sp],#32 - AARCH64_VALIDATE_LINK_REGISTER - ret - - // void ecp_nistz256_mul_mont(BN_ULONG x0[4],const BN_ULONG x1[4], // const BN_ULONG x2[4]); .globl _ecp_nistz256_mul_mont diff --git a/linux-aarch64/crypto/fipsmodule/p256-armv8-asm.S b/linux-aarch64/crypto/fipsmodule/p256-armv8-asm.S index 2106b851..3efcccb6 100644 --- a/linux-aarch64/crypto/fipsmodule/p256-armv8-asm.S +++ b/linux-aarch64/crypto/fipsmodule/p256-armv8-asm.S @@ -32,58 +32,6 @@ .byte 69,67,80,95,78,73,83,84,90,50,53,54,32,102,111,114,32,65,82,77,118,56,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0 .align 2 -// void ecp_nistz256_to_mont(BN_ULONG x0[4],const BN_ULONG x1[4]); -.globl ecp_nistz256_to_mont -.hidden ecp_nistz256_to_mont -.type ecp_nistz256_to_mont,%function -.align 6 -ecp_nistz256_to_mont: - AARCH64_SIGN_LINK_REGISTER - stp x29,x30,[sp,#-32]! - add x29,sp,#0 - stp x19,x20,[sp,#16] - - ldr x3,.LRR // bp[0] - ldp x4,x5,[x1] - ldp x6,x7,[x1,#16] - ldr x12,.Lpoly+8 - ldr x13,.Lpoly+24 - adr x2,.LRR // &bp[0] - - bl __ecp_nistz256_mul_mont - - ldp x19,x20,[sp,#16] - ldp x29,x30,[sp],#32 - AARCH64_VALIDATE_LINK_REGISTER - ret -.size ecp_nistz256_to_mont,.-ecp_nistz256_to_mont - -// void ecp_nistz256_from_mont(BN_ULONG x0[4],const BN_ULONG x1[4]); -.globl ecp_nistz256_from_mont -.hidden ecp_nistz256_from_mont -.type ecp_nistz256_from_mont,%function -.align 4 -ecp_nistz256_from_mont: - AARCH64_SIGN_LINK_REGISTER - stp x29,x30,[sp,#-32]! - add x29,sp,#0 - stp x19,x20,[sp,#16] - - mov x3,#1 // bp[0] - ldp x4,x5,[x1] - ldp x6,x7,[x1,#16] - ldr x12,.Lpoly+8 - ldr x13,.Lpoly+24 - adr x2,.Lone // &bp[0] - - bl __ecp_nistz256_mul_mont - - ldp x19,x20,[sp,#16] - ldp x29,x30,[sp],#32 - AARCH64_VALIDATE_LINK_REGISTER - ret -.size ecp_nistz256_from_mont,.-ecp_nistz256_from_mont - // void ecp_nistz256_mul_mont(BN_ULONG x0[4],const BN_ULONG x1[4], // const BN_ULONG x2[4]); .globl ecp_nistz256_mul_mont diff --git a/src/crypto/fipsmodule/CMakeLists.txt b/src/crypto/fipsmodule/CMakeLists.txt index b99ebc71..a04ef6aa 100644 --- a/src/crypto/fipsmodule/CMakeLists.txt +++ b/src/crypto/fipsmodule/CMakeLists.txt @@ -202,19 +202,11 @@ if(FIPS_DELOCATE) set_target_properties(bcm_hashunset PROPERTIES POSITION_INDEPENDENT_CODE ON) set_target_properties(bcm_hashunset PROPERTIES LINKER_LANGUAGE C) - set(MAYBE_INJECT_HASH_SHA256_FLAG "") - # If building with OPENSSL_NO_ASM then ARCH will be "generic", but we still - # need to use SHA-256. Since this only matters for FIPS, we only need to - # worry about the Linux spelling of AArch64. - if (ARCH STREQUAL "aarch64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") - set(MAYBE_INJECT_HASH_SHA256_FLAG "-sha256") - endif() - go_executable(inject_hash boringssl.googlesource.com/boringssl/util/fipstools/inject_hash) add_custom_command( OUTPUT bcm.o - COMMAND ./inject_hash -o bcm.o -in-archive $<TARGET_FILE:bcm_hashunset> ${MAYBE_INJECT_HASH_SHA256_FLAG} + COMMAND ./inject_hash -o bcm.o -in-archive $<TARGET_FILE:bcm_hashunset> DEPENDS bcm_hashunset inject_hash WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} ) diff --git a/src/crypto/fipsmodule/ec/asm/p256-armv8-asm.pl b/src/crypto/fipsmodule/ec/asm/p256-armv8-asm.pl index f2926b8e..0d628e73 100644 --- a/src/crypto/fipsmodule/ec/asm/p256-armv8-asm.pl +++ b/src/crypto/fipsmodule/ec/asm/p256-armv8-asm.pl @@ -72,56 +72,6 @@ $code.=<<___; .quad 0xccd1c8aaee00bc4f .asciz "ECP_NISTZ256 for ARMv8, CRYPTOGAMS by <appro\@openssl.org>" -// void ecp_nistz256_to_mont(BN_ULONG x0[4],const BN_ULONG x1[4]); -.globl ecp_nistz256_to_mont -.type ecp_nistz256_to_mont,%function -.align 6 -ecp_nistz256_to_mont: - AARCH64_SIGN_LINK_REGISTER - stp x29,x30,[sp,#-32]! - add x29,sp,#0 - stp x19,x20,[sp,#16] - - ldr $bi,.LRR // bp[0] - ldp $a0,$a1,[$ap] - ldp $a2,$a3,[$ap,#16] - ldr $poly1,.Lpoly+8 - ldr $poly3,.Lpoly+24 - adr $bp,.LRR // &bp[0] - - bl __ecp_nistz256_mul_mont - - ldp x19,x20,[sp,#16] - ldp x29,x30,[sp],#32 - AARCH64_VALIDATE_LINK_REGISTER - ret -.size ecp_nistz256_to_mont,.-ecp_nistz256_to_mont - -// void ecp_nistz256_from_mont(BN_ULONG x0[4],const BN_ULONG x1[4]); -.globl ecp_nistz256_from_mont -.type ecp_nistz256_from_mont,%function -.align 4 -ecp_nistz256_from_mont: - AARCH64_SIGN_LINK_REGISTER - stp x29,x30,[sp,#-32]! - add x29,sp,#0 - stp x19,x20,[sp,#16] - - mov $bi,#1 // bp[0] - ldp $a0,$a1,[$ap] - ldp $a2,$a3,[$ap,#16] - ldr $poly1,.Lpoly+8 - ldr $poly3,.Lpoly+24 - adr $bp,.Lone // &bp[0] - - bl __ecp_nistz256_mul_mont - - ldp x19,x20,[sp,#16] - ldp x29,x30,[sp],#32 - AARCH64_VALIDATE_LINK_REGISTER - ret -.size ecp_nistz256_from_mont,.-ecp_nistz256_from_mont - // void ecp_nistz256_mul_mont(BN_ULONG x0[4],const BN_ULONG x1[4], // const BN_ULONG x2[4]); .globl ecp_nistz256_mul_mont diff --git a/src/crypto/fipsmodule/ec/p256-nistz.h b/src/crypto/fipsmodule/ec/p256-nistz.h index 0d0a6bea..3f5ea021 100644 --- a/src/crypto/fipsmodule/ec/p256-nistz.h +++ b/src/crypto/fipsmodule/ec/p256-nistz.h @@ -64,16 +64,6 @@ static inline void ecp_nistz256_from_mont(BN_ULONG res[P256_LIMBS], ecp_nistz256_mul_mont(res, in, ONE); } -// ecp_nistz256_to_mont sets |res| to |in|, converted to Montgomery domain -// by multiplying with RR = 2^512 mod P precomputed for NIST P256 curve. -static inline void ecp_nistz256_to_mont(BN_ULONG res[P256_LIMBS], - const BN_ULONG in[P256_LIMBS]) { - static const BN_ULONG RR[P256_LIMBS] = { - TOBN(0x00000000, 0x00000003), TOBN(0xfffffffb, 0xffffffff), - TOBN(0xffffffff, 0xfffffffe), TOBN(0x00000004, 0xfffffffd)}; - ecp_nistz256_mul_mont(res, in, RR); -} - // P-256 scalar operations. // diff --git a/src/crypto/fipsmodule/rand/ctrdrbg.c b/src/crypto/fipsmodule/rand/ctrdrbg.c index 83e7f5b6..2b036828 100644 --- a/src/crypto/fipsmodule/rand/ctrdrbg.c +++ b/src/crypto/fipsmodule/rand/ctrdrbg.c @@ -12,7 +12,7 @@ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <openssl/rand.h> +#include <openssl/ctrdrbg.h> #include <openssl/type_check.h> #include <openssl/mem.h> @@ -28,6 +28,21 @@ // See table 3. static const uint64_t kMaxReseedCount = UINT64_C(1) << 48; +CTR_DRBG_STATE *CTR_DRBG_new(const uint8_t entropy[CTR_DRBG_ENTROPY_LEN], + const uint8_t *personalization, + size_t personalization_len) { + CTR_DRBG_STATE *drbg = OPENSSL_malloc(sizeof(CTR_DRBG_STATE)); + if (drbg == NULL || + !CTR_DRBG_init(drbg, entropy, personalization, personalization_len)) { + CTR_DRBG_free(drbg); + return NULL; + } + + return drbg; +} + +void CTR_DRBG_free(CTR_DRBG_STATE *state) { OPENSSL_free(state); } + int CTR_DRBG_init(CTR_DRBG_STATE *drbg, const uint8_t entropy[CTR_DRBG_ENTROPY_LEN], const uint8_t *personalization, size_t personalization_len) { diff --git a/src/crypto/fipsmodule/rand/ctrdrbg_test.cc b/src/crypto/fipsmodule/rand/ctrdrbg_test.cc index 0cc48b14..e6ebbca2 100644 --- a/src/crypto/fipsmodule/rand/ctrdrbg_test.cc +++ b/src/crypto/fipsmodule/rand/ctrdrbg_test.cc @@ -14,7 +14,7 @@ #include <gtest/gtest.h> -#include <openssl/rand.h> +#include <openssl/ctrdrbg.h> #include <openssl/sha.h> #include "internal.h" @@ -60,6 +60,18 @@ TEST(CTRDRBGTest, Basic) { CTR_DRBG_clear(&drbg); } +TEST(CTRDRBGTest, Allocated) { + const uint8_t kSeed[CTR_DRBG_ENTROPY_LEN] = {0}; + + CTR_DRBG_STATE *allocated = CTR_DRBG_new(kSeed, nullptr, 0); + ASSERT_TRUE(allocated); + CTR_DRBG_free(allocated); + + allocated = CTR_DRBG_new(kSeed, nullptr, 1<<20); + ASSERT_FALSE(allocated); + CTR_DRBG_free(allocated); +} + TEST(CTRDRBGTest, Large) { const uint8_t kSeed[CTR_DRBG_ENTROPY_LEN] = {0}; diff --git a/src/crypto/fipsmodule/rand/internal.h b/src/crypto/fipsmodule/rand/internal.h index eccf047f..a770ebc9 100644 --- a/src/crypto/fipsmodule/rand/internal.h +++ b/src/crypto/fipsmodule/rand/internal.h @@ -16,6 +16,7 @@ #define OPENSSL_HEADER_CRYPTO_RAND_INTERNAL_H #include <openssl/aes.h> +#include <openssl/ctrdrbg.h> #include "../../internal.h" #include "../modes/internal.h" @@ -49,10 +50,10 @@ void CRYPTO_get_seed_entropy(uint8_t *out_entropy, size_t out_entropy_len, int *out_used_cpu); // RAND_load_entropy supplies |entropy_len| bytes of entropy to the module. The -// |from_cpu| parameter is true iff the entropy was obtained directly from the -// CPU. +// |want_additional_input| parameter is true iff the entropy was obtained from +// a source other than the system, e.g. directly from the CPU. void RAND_load_entropy(const uint8_t *entropy, size_t entropy_len, - int from_cpu); + int want_additional_input); // RAND_need_entropy is implemented outside of the FIPS module and is called // when the module has stopped because it has run out of entropy. @@ -95,7 +96,7 @@ int rand_fork_unsafe_buffering_enabled(void); // CTR_DRBG_STATE contains the state of a CTR_DRBG based on AES-256. See SP // 800-90Ar1. -typedef struct { +struct ctr_drbg_state_st { AES_KEY ks; block128_f block; ctr128_f ctr; @@ -104,11 +105,7 @@ typedef struct { uint32_t words[4]; } counter; uint64_t reseed_counter; -} CTR_DRBG_STATE; - -// See SP 800-90Ar1, table 3. -#define CTR_DRBG_ENTROPY_LEN 48 -#define CTR_DRBG_MAX_GENERATE_LENGTH 65536 +}; // CTR_DRBG_init initialises |*drbg| given |CTR_DRBG_ENTROPY_LEN| bytes of // entropy in |entropy| and, optionally, a personalization string up to @@ -119,27 +116,6 @@ OPENSSL_EXPORT int CTR_DRBG_init(CTR_DRBG_STATE *drbg, const uint8_t *personalization, size_t personalization_len); -// CTR_DRBG_reseed reseeds |drbg| given |CTR_DRBG_ENTROPY_LEN| bytes of entropy -// in |entropy| and, optionally, up to |CTR_DRBG_ENTROPY_LEN| bytes of -// additional data. It returns one on success or zero on error. -OPENSSL_EXPORT int CTR_DRBG_reseed(CTR_DRBG_STATE *drbg, - const uint8_t entropy[CTR_DRBG_ENTROPY_LEN], - const uint8_t *additional_data, - size_t additional_data_len); - -// CTR_DRBG_generate processes to up |CTR_DRBG_ENTROPY_LEN| bytes of additional -// data (if any) and then writes |out_len| random bytes to |out|, where -// |out_len| <= |CTR_DRBG_MAX_GENERATE_LENGTH|. It returns one on success or -// zero on error. -OPENSSL_EXPORT 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); - -// CTR_DRBG_clear zeroises the state of |drbg|. -OPENSSL_EXPORT void CTR_DRBG_clear(CTR_DRBG_STATE *drbg); - - #if defined(OPENSSL_X86_64) && !defined(OPENSSL_NO_ASM) OPENSSL_INLINE int have_rdrand(void) { diff --git a/src/crypto/fipsmodule/rand/rand.c b/src/crypto/fipsmodule/rand/rand.c index 357be391..54397f9b 100644 --- a/src/crypto/fipsmodule/rand/rand.c +++ b/src/crypto/fipsmodule/rand/rand.c @@ -23,6 +23,7 @@ #endif #include <openssl/chacha.h> +#include <openssl/ctrdrbg.h> #include <openssl/mem.h> #include <openssl/type_check.h> @@ -162,19 +163,13 @@ static int rdrand(uint8_t *buf, size_t len) { #if defined(BORINGSSL_FIPS) void CRYPTO_get_seed_entropy(uint8_t *out_entropy, size_t out_entropy_len, - int *out_used_cpu) { - *out_used_cpu = 0; + int *out_want_additional_input) { + *out_want_additional_input = 0; if (have_rdrand() && rdrand(out_entropy, out_entropy_len)) { - *out_used_cpu = 1; + *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); - } } // In passive entropy mode, entropy is supplied from outside of the module via @@ -183,20 +178,22 @@ void CRYPTO_get_seed_entropy(uint8_t *out_entropy, size_t out_entropy_len, struct entropy_buffer { // bytes contains entropy suitable for seeding a DRBG. - uint8_t bytes[CTR_DRBG_ENTROPY_LEN * BORINGSSL_FIPS_OVERREAD]; + uint8_t + bytes[CRNGT_BLOCK_SIZE + CTR_DRBG_ENTROPY_LEN * BORINGSSL_FIPS_OVERREAD]; // bytes_valid indicates the number of bytes of |bytes| that contain valid // data. size_t bytes_valid; - // from_cpu is true if any of the contents of |bytes| were obtained directly - // from the CPU. - int from_cpu; + // want_additional_input is true if any of the contents of |bytes| were + // obtained via a method other than from the kernel. In these cases entropy + // from the kernel is also provided via an additional input to the DRBG. + int want_additional_input; }; DEFINE_BSS_GET(struct entropy_buffer, entropy_buffer); DEFINE_STATIC_MUTEX(entropy_buffer_lock); void RAND_load_entropy(const uint8_t *entropy, size_t entropy_len, - int from_cpu) { + int want_additional_input) { struct entropy_buffer *const buffer = entropy_buffer_bss_get(); CRYPTO_STATIC_MUTEX_lock_write(entropy_buffer_lock_bss_get()); @@ -207,14 +204,15 @@ void RAND_load_entropy(const uint8_t *entropy, size_t entropy_len, OPENSSL_memcpy(&buffer->bytes[buffer->bytes_valid], entropy, entropy_len); buffer->bytes_valid += entropy_len; - buffer->from_cpu |= from_cpu && (entropy_len != 0); + buffer->want_additional_input |= + want_additional_input && (entropy_len != 0); CRYPTO_STATIC_MUTEX_unlock_write(entropy_buffer_lock_bss_get()); } // 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_used_cpu) { + int *out_want_additional_input) { struct entropy_buffer *const buffer = entropy_buffer_bss_get(); if (out_entropy_len > sizeof(buffer->bytes)) { abort(); @@ -227,53 +225,64 @@ static void get_seed_entropy(uint8_t *out_entropy, size_t out_entropy_len, CRYPTO_STATIC_MUTEX_lock_write(entropy_buffer_lock_bss_get()); } - *out_used_cpu = buffer->from_cpu; + *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->from_cpu = 0; + buffer->want_additional_input = 0; } CRYPTO_STATIC_MUTEX_unlock_write(entropy_buffer_lock_bss_get()); } -// rand_get_seed fills |seed| with entropy and sets |*out_used_cpu| to one if -// that entropy came directly from the CPU and zero otherwise. +// rand_get_seed fills |seed| with entropy and sets +// |*out_want_additional_input| to one if that entropy came directly from the +// CPU and zero otherwise. static void rand_get_seed(struct rand_thread_state *state, uint8_t seed[CTR_DRBG_ENTROPY_LEN], - int *out_used_cpu) { - if (!state->last_block_valid) { - int unused; - get_seed_entropy(state->last_block, sizeof(state->last_block), &unused); - state->last_block_valid = 1; + int *out_want_additional_input) { + 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); } - uint8_t entropy[CTR_DRBG_ENTROPY_LEN * BORINGSSL_FIPS_OVERREAD]; - get_seed_entropy(entropy, sizeof(entropy), out_used_cpu); + get_seed_entropy(entropy, entropy_len, out_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, CRNGT_BLOCK_SIZE) == 0) { + if (CRYPTO_memcmp(state->last_block, entropy, sizeof(state->last_block)) == + 0) { fprintf(stderr, "CRNGT failed.\n"); BORINGSSL_FIPS_abort(); } - OPENSSL_STATIC_ASSERT(sizeof(entropy) % CRNGT_BLOCK_SIZE == 0, ""); - for (size_t i = CRNGT_BLOCK_SIZE; i < sizeof(entropy); - i += CRNGT_BLOCK_SIZE) { + 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 + sizeof(entropy) - CRNGT_BLOCK_SIZE, + 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++) { @@ -285,15 +294,16 @@ static void rand_get_seed(struct rand_thread_state *state, #else -// rand_get_seed fills |seed| with entropy and sets |*out_used_cpu| to one if -// that entropy came directly from the CPU and zero otherwise. +// rand_get_seed fills |seed| with entropy and sets +// |*out_want_additional_input| to one if that entropy came directly from the +// CPU and zero otherwise. static void rand_get_seed(struct rand_thread_state *state, uint8_t seed[CTR_DRBG_ENTROPY_LEN], - int *out_used_cpu) { + int *out_want_additional_input) { // If not in FIPS mode, we don't overread from the system entropy source and // we don't depend only on the hardware RDRAND. CRYPTO_sysrand_for_seed(seed, CTR_DRBG_ENTROPY_LEN); - *out_used_cpu = 0; + *out_want_additional_input = 0; } #endif @@ -352,16 +362,16 @@ void RAND_bytes_with_additional_data(uint8_t *out, size_t out_len, state->last_block_valid = 0; uint8_t seed[CTR_DRBG_ENTROPY_LEN]; - int used_cpu; - rand_get_seed(state, seed, &used_cpu); + int want_additional_input; + rand_get_seed(state, seed, &want_additional_input); uint8_t personalization[CTR_DRBG_ENTROPY_LEN] = {0}; size_t personalization_len = 0; #if defined(OPENSSL_URANDOM) - // If we used RDRAND, also opportunistically read from the system. This - // avoids solely relying on the hardware once the entropy pool has been - // initialized. - if (used_cpu && + // 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. + if (want_additional_input && CRYPTO_sysrand_if_available(personalization, sizeof(personalization))) { personalization_len = sizeof(personalization); } @@ -392,8 +402,8 @@ void RAND_bytes_with_additional_data(uint8_t *out, size_t out_len, if (state->calls >= kReseedInterval || state->fork_generation != fork_generation) { uint8_t seed[CTR_DRBG_ENTROPY_LEN]; - int used_cpu; - rand_get_seed(state, seed, &used_cpu); + int want_additional_input; + rand_get_seed(state, seed, &want_additional_input); #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 diff --git a/src/crypto/fipsmodule/rand/urandom_test.cc b/src/crypto/fipsmodule/rand/urandom_test.cc index 0a1d7539..37002f9f 100644 --- a/src/crypto/fipsmodule/rand/urandom_test.cc +++ b/src/crypto/fipsmodule/rand/urandom_test.cc @@ -15,17 +15,22 @@ #include <gtest/gtest.h> #include <stdlib.h> +#include <openssl/ctrdrbg.h> #include <openssl/rand.h> -#include "internal.h" #include "getrandom_fillin.h" +#include "internal.h" -#if defined(OPENSSL_X86_64) && !defined(BORINGSSL_SHARED_LIBRARY) && \ +#if (defined(OPENSSL_X86_64) || defined(OPENSSL_AARCH64)) && \ + !defined(BORINGSSL_SHARED_LIBRARY) && \ !defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE) && defined(USE_NR_getrandom) #include <linux/random.h> #include <sys/ptrace.h> +#include <sys/socket.h> #include <sys/syscall.h> +#include <sys/uio.h> +#include <sys/un.h> #include <sys/user.h> #include "fork_detect.h" @@ -34,6 +39,21 @@ #define PTRACE_O_EXITKILL (1 << 20) #endif +#if defined(BORINGSSL_FIPS) +static const bool kIsFIPS = true; +#if defined(OPENSSL_ANDROID) +static const bool kUsesDaemon = true; +#else +static const bool kUsesDaemon = false; +#endif +#else +static const bool kIsFIPS = false; +static const bool kUsesDaemon = false; +#endif + +// kDaemonWriteLength is the number of bytes that the entropy daemon writes. +static const size_t kDaemonWriteLength = 496; + // This test can be run with $OPENSSL_ia32cap=~0x4000000000000000 in order to // simulate the absence of RDRAND of machines that have it. @@ -45,6 +65,10 @@ struct Event { kOpen, kUrandomRead, kUrandomIoctl, + kSocket, + kConnect, + kSocketRead, + kSocketClose, kAbort, }; @@ -81,6 +105,27 @@ struct Event { return e; } + static Event Socket() { + Event e(Syscall::kSocket); + return e; + } + + static Event Connect() { + Event e(Syscall::kConnect); + return e; + } + + static Event SocketRead(size_t length) { + Event e(Syscall::kSocketRead); + e.length = length; + return e; + } + + static Event SocketClose() { + Event e(Syscall::kSocketClose); + return e; + } + static Event Abort() { Event e(Syscall::kAbort); return e; @@ -105,6 +150,19 @@ struct Event { case Syscall::kUrandomIoctl: return "ioctl(urandom_fd, RNDGETENTCNT, _)"; + case Syscall::kSocket: + return "socket(UNIX, STREAM, _)"; + + case Syscall::kConnect: + return "connect(sock, _, _)"; + + case Syscall::kSocketRead: + snprintf(buf, sizeof(buf), "read(sock_fd, _, %zu)", length); + break; + + case Syscall::kSocketClose: + return "close(sock)"; + case Syscall::kAbort: return "abort()"; } @@ -145,7 +203,193 @@ static const unsigned URANDOM_NOT_READY = 8; static const unsigned GETRANDOM_ERROR = 16; // Reading from /dev/urandom gives |EINVAL|. static const unsigned URANDOM_ERROR = 32; -static const unsigned NEXT_FLAG = 64; +static const unsigned SOCKET_ERROR = 64; +static const unsigned CONNECT_ERROR = 128; +static const unsigned SOCKET_READ_ERROR = 256; +static const unsigned SOCKET_READ_SHORT = 512; +static const unsigned NEXT_FLAG = 1024; + +// regs_read fetches the registers of |child_pid| and writes them to |out_regs|. +// That structure will contain at least the following members: +// syscall: the syscall number, if registers were read just before entering +// one. +// args[0..2]: syscall arguments, if registers were read just before +// entering one. +// ret: the syscall return value, if registers were read just after finishing +// one. +// +// This call returns true on success and false otherwise. +static bool regs_read(struct regs *out_regs, int child_pid); + +// regs_set_ret sets the return value of the system call that |child_pid| has +// just finished, to |ret|. It returns true on success and false otherwise. +static bool regs_set_ret(int child_pid, int ret); + +// regs_break_syscall causes the system call that |child_pid| is about to enter +// to fail to run. +static bool regs_break_syscall(int child_pid, const struct regs *orig_regs); + +#if defined(OPENSSL_X86_64) + +struct regs { + uintptr_t syscall; + uintptr_t args[3]; + uintptr_t ret; + struct user_regs_struct regs; +}; + +static bool regs_read(struct regs *out_regs, int child_pid) { + if (ptrace(PTRACE_GETREGS, child_pid, nullptr, &out_regs->regs) != 0) { + return false; + } + + out_regs->syscall = out_regs->regs.orig_rax; + out_regs->ret = out_regs->regs.rax; + out_regs->args[0] = out_regs->regs.rdi; + out_regs->args[1] = out_regs->regs.rsi; + out_regs->args[2] = out_regs->regs.rdx; + return true; +} + +static bool regs_set_ret(int child_pid, int ret) { + struct regs regs; + if (!regs_read(®s, child_pid)) { + return false; + } + regs.regs.rax = ret; + return ptrace(PTRACE_SETREGS, child_pid, nullptr, ®s.regs) == 0; +} + +static bool regs_break_syscall(int child_pid, const struct regs *orig_regs) { + // Replacing the syscall number with -1 doesn't work on AArch64 thus we set + // the first argument to -1, which suffices to break the syscalls that we care + // about here. + struct user_regs_struct regs; + memcpy(®s, &orig_regs->regs, sizeof(regs)); + regs.rdi = -1; + return ptrace(PTRACE_SETREGS, child_pid, nullptr, ®s) == 0; +} + +#elif defined(OPENSSL_AARCH64) + +struct regs { + uintptr_t syscall; + uintptr_t args[3]; + uintptr_t ret; + uint64_t regs[9]; +}; + +static bool regs_read(struct regs *out_regs, int child_pid) { + struct iovec io; + io.iov_base = out_regs->regs; + io.iov_len = sizeof(out_regs->regs); + if (ptrace(PTRACE_GETREGSET, child_pid, (void *)/*NT_PRSTATUS*/ 1, &io) != + 0) { + return false; + } + + out_regs->syscall = out_regs->regs[8]; + out_regs->ret = out_regs->regs[0]; + out_regs->args[0] = out_regs->regs[0]; + out_regs->args[1] = out_regs->regs[1]; + out_regs->args[2] = out_regs->regs[2]; + + return true; +} + +static bool regs_set(int child_pid, const struct regs *orig_regs, + uint64_t x0_value) { + uint64_t regs[OPENSSL_ARRAY_SIZE(orig_regs->regs)]; + memcpy(regs, orig_regs->regs, sizeof(regs)); + regs[0] = x0_value; + + struct iovec io; + io.iov_base = regs; + io.iov_len = sizeof(regs); + return ptrace(PTRACE_SETREGSET, child_pid, (void *)/*NT_PRSTATUS*/ 1, &io) == + 0; +} + +static bool regs_set_ret(int child_pid, int ret) { + struct regs regs; + return regs_read(®s, child_pid) && regs_set(child_pid, ®s, ret); +} + +static bool regs_break_syscall(int child_pid, const struct regs *orig_regs) { + // Replacing the syscall number with -1 doesn't work on AArch64 thus we set + // the first argument to -1, which suffices to break the syscalls that we care + // about here. + return regs_set(child_pid, orig_regs, -1); +} + +#endif + +// SyscallResult is like std::optional<int>. +// TODO: use std::optional when we can use C++17. +class SyscallResult { + public: + SyscallResult &operator=(int value) { + has_value_ = true; + value_ = value; + return *this; + } + + int value() const { + if (!has_value_) { + abort(); + } + return value_; + } + + bool has_value() const { return has_value_; } + + private: + bool has_value_ = false; + int value_ = 0; +}; + +// memcpy_to_remote copies |n| bytes from |in_src| in the local address space, +// to |dest| in the address space of |child_pid|. +static void memcpy_to_remote(int child_pid, uint64_t dest, const void *in_src, + size_t n) { + const uint8_t *src = reinterpret_cast<const uint8_t *>(in_src); + + // ptrace always works with ill-defined "words", which appear to be 64-bit + // on 64-bit systems. +#if !defined(OPENSSL_64_BIT) +#error "This code probably doesn't work" +#endif + + while (n) { + const uintptr_t aligned_addr = dest & ~7; + const uintptr_t offset = dest - aligned_addr; + const size_t space = 8 - offset; + size_t todo = n; + if (todo > space) { + todo = space; + } + + uint64_t word; + if (offset == 0 && todo == 8) { + word = CRYPTO_load_u64_le(src); + } else { + uint8_t bytes[8]; + CRYPTO_store_u64_le( + bytes, ptrace(PTRACE_PEEKDATA, child_pid, + reinterpret_cast<void *>(aligned_addr), nullptr)); + memcpy(&bytes[offset], src, todo); + word = CRYPTO_load_u64_le(bytes); + } + + ASSERT_EQ(0, ptrace(PTRACE_POKEDATA, child_pid, + reinterpret_cast<void *>(aligned_addr), + reinterpret_cast<void *>(word))); + + src += todo; + n -= todo; + dest += todo; + } +} // GetTrace runs |thunk| in a forked process and observes the resulting system // calls using ptrace. It simulates a variety of failures based on the contents @@ -184,6 +428,10 @@ static void GetTrace(std::vector<Event> *out_trace, unsigned flags, // process, if it opens it. int urandom_fd = -1; + // sock_fd tracks the file descriptor number for the socket to the entropy + // daemon, if one is opened. + int sock_fd = -1; + for (;;) { // Advance the child to the next system call. ASSERT_EQ(0, ptrace(PTRACE_SYSCALL, child_pid, 0, 0)); @@ -198,76 +446,130 @@ static void GetTrace(std::vector<Event> *out_trace, unsigned flags, // Otherwise the only valid ptrace event is a system call stop. ASSERT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80)); - struct user_regs_struct regs; - ASSERT_EQ(0, ptrace(PTRACE_GETREGS, child_pid, nullptr, ®s)); - const auto syscall_number = regs.orig_rax; + struct regs regs; + ASSERT_TRUE(regs_read(®s, child_pid)); bool is_opening_urandom = false; + bool is_socket_call = false; bool is_urandom_ioctl = false; uintptr_t ioctl_output_addr = 0; - // inject_error is zero to indicate that the system call should run + bool is_socket_read = false; + uint64_t socket_read_bytes = 0; + // force_result is unset to indicate that the system call should run // normally. Otherwise it's, e.g. -EINVAL, to indicate that the system call - // should not run and that error should be injected on return. - int inject_error = 0; + // should not run and that the given value should be injected on return. + SyscallResult force_result; - switch (syscall_number) { + switch (regs.syscall) { case __NR_getrandom: if (flags & NO_GETRANDOM) { - inject_error = -ENOSYS; + force_result = -ENOSYS; } else if (flags & GETRANDOM_ERROR) { - inject_error = -EINVAL; + force_result = -EINVAL; } else if (flags & GETRANDOM_NOT_READY) { - if (regs.rdx & GRND_NONBLOCK) { - inject_error = -EAGAIN; + if (regs.args[2] & GRND_NONBLOCK) { + force_result = -EAGAIN; } } out_trace->push_back( - Event::GetRandom(/*length=*/regs.rsi, /*flags=*/regs.rdx)); + Event::GetRandom(/*length=*/regs.args[1], /*flags=*/regs.args[2])); break; case __NR_openat: - case __NR_open: { +#if defined(OPENSSL_X86_64) + case __NR_open: +#endif + { // It's assumed that any arguments to open(2) are constants in read-only // memory and thus the pointer in the child's context will also be a // valid pointer in our address space. const char *filename = reinterpret_cast<const char *>( - (syscall_number == __NR_openat) ? regs.rsi : regs.rdi); + (regs.syscall == __NR_openat) ? regs.args[1] : regs.args[0]); out_trace->push_back(Event::Open(filename)); is_opening_urandom = strcmp(filename, "/dev/urandom") == 0; if (is_opening_urandom && (flags & NO_URANDOM)) { - inject_error = -ENOENT; + force_result = -ENOENT; } break; } case __NR_read: { - const int read_fd = regs.rdi; + const int read_fd = regs.args[0]; if (urandom_fd >= 0 && urandom_fd == read_fd) { - out_trace->push_back(Event::UrandomRead(/*length=*/regs.rdx)); + out_trace->push_back(Event::UrandomRead(/*length=*/regs.args[2])); if (flags & URANDOM_ERROR) { - inject_error = -EINVAL; + force_result = -EINVAL; + } + } else if (sock_fd >= 0 && sock_fd == read_fd) { + uint64_t length = regs.args[2]; + out_trace->push_back(Event::SocketRead(length)); + if (flags & SOCKET_READ_ERROR) { + force_result = -EINVAL; + } else { + is_socket_read = true; + socket_read_bytes = length; + + if (flags & SOCKET_READ_SHORT) { + ASSERT_GT(socket_read_bytes, 0u); + socket_read_bytes--; + flags &= ~SOCKET_READ_SHORT; + } } } break; } + case __NR_close: { + if (sock_fd >= 0 && static_cast<int>(regs.args[0]) == sock_fd) { + out_trace->push_back(Event::SocketClose()); + sock_fd = -1; + } + break; + } + case __NR_ioctl: { - const int ioctl_fd = regs.rdi; + const int ioctl_fd = regs.args[0]; if (urandom_fd >= 0 && ioctl_fd == urandom_fd && - regs.rsi == RNDGETENTCNT) { + regs.args[1] == RNDGETENTCNT) { out_trace->push_back(Event::UrandomIoctl()); is_urandom_ioctl = true; - ioctl_output_addr = regs.rdx; + ioctl_output_addr = regs.args[2]; + } + break; + } + + case __NR_socket: { + const int family = regs.args[0]; + const int type = regs.args[1]; + if (family == AF_UNIX && type == SOCK_STREAM) { + out_trace->push_back(Event::Socket()); + is_socket_call = true; + if (flags & SOCKET_ERROR) { + force_result = -EINVAL; + } + } + break; + } + + case __NR_connect: { + const int connect_fd = regs.args[0]; + if (sock_fd >= 0 && connect_fd == sock_fd) { + out_trace->push_back(Event::Connect()); + if (flags & CONNECT_ERROR) { + force_result = -EINVAL; + } else { + // The test system might not have an entropy daemon running so + // inject a success result. + force_result = 0; + } } + + break; } } - if (inject_error) { - // Replace the system call number with -1 to cause the kernel to ignore - // the call. The -ENOSYS will be replaced later with the value of - // |inject_error|. - regs.orig_rax = -1; - ASSERT_EQ(0, ptrace(PTRACE_SETREGS, child_pid, nullptr, ®s)); + if (force_result.has_value()) { + ASSERT_TRUE(regs_break_syscall(child_pid, ®s)); } ASSERT_EQ(0, ptrace(PTRACE_SYSCALL, child_pid, 0, 0)); @@ -284,15 +586,14 @@ static void GetTrace(std::vector<Event> *out_trace, unsigned flags, // and know that these events happen in pairs. ASSERT_TRUE(WIFSTOPPED(status) && WSTOPSIG(status) == (SIGTRAP | 0x80)); - if (inject_error) { - if (inject_error != -ENOSYS) { - ASSERT_EQ(0, ptrace(PTRACE_GETREGS, child_pid, nullptr, ®s)); - regs.rax = inject_error; - ASSERT_EQ(0, ptrace(PTRACE_SETREGS, child_pid, nullptr, ®s)); - } + if (force_result.has_value()) { + ASSERT_TRUE(regs_set_ret(child_pid, force_result.value())); } else if (is_opening_urandom) { - ASSERT_EQ(0, ptrace(PTRACE_GETREGS, child_pid, nullptr, ®s)); - urandom_fd = regs.rax; + ASSERT_TRUE(regs_read(®s, child_pid)); + urandom_fd = regs.ret; + } else if (is_socket_call) { + ASSERT_TRUE(regs_read(®s, child_pid)); + sock_fd = regs.ret; } else if (is_urandom_ioctl) { // The result is the number of bits of entropy that the kernel currently // believes that it has. urandom.c waits until 256 bits are ready. @@ -305,21 +606,19 @@ static void GetTrace(std::vector<Event> *out_trace, unsigned flags, flags &= ~URANDOM_NOT_READY; } - // ptrace always works with ill-defined "words", which appear to be 64-bit - // on x86-64. Since the ioctl result is a 32-bit int, do a - // read-modify-write to inject the answer. - const uintptr_t aligned_addr = ioctl_output_addr & ~7; - const uintptr_t offset = ioctl_output_addr - aligned_addr; - union { - uint64_t word; - uint8_t bytes[8]; - } u; - u.word = ptrace(PTRACE_PEEKDATA, child_pid, - reinterpret_cast<void *>(aligned_addr), nullptr); - memcpy(&u.bytes[offset], &result, sizeof(result)); - ASSERT_EQ(0, ptrace(PTRACE_POKEDATA, child_pid, - reinterpret_cast<void *>(aligned_addr), - reinterpret_cast<void *>(u.word))); + memcpy_to_remote(child_pid, ioctl_output_addr, &result, sizeof(result)); + } else if (is_socket_read) { + // Simulate a response from the entropy daemon since it might not be + // running on the current system. + uint8_t entropy[kDaemonWriteLength]; + ASSERT_LE(socket_read_bytes, sizeof(entropy)); + + for (size_t i = 0; i < sizeof(entropy); i++) { + entropy[i] = i & 0xff; + } + memcpy_to_remote(child_pid, regs.args[1], entropy, socket_read_bytes); + + ASSERT_TRUE(regs_set_ret(child_pid, socket_read_bytes)); } } } @@ -331,24 +630,46 @@ static void TestFunction() { RAND_bytes(&byte, sizeof(byte)); } -static bool have_fork_detection() { - return CRYPTO_get_fork_generation() != 0; +static bool have_fork_detection() { return CRYPTO_get_fork_generation() != 0; } + +static bool AppendDaemonEvents(std::vector<Event> *events, unsigned flags) { + events->push_back(Event::Socket()); + if (flags & SOCKET_ERROR) { + return false; + } + + bool ret = false; + events->push_back(Event::Connect()); + if (flags & CONNECT_ERROR) { + goto out; + } + + events->push_back(Event::SocketRead(kDaemonWriteLength)); + if (flags & SOCKET_READ_ERROR) { + goto out; + } + + if (flags & SOCKET_READ_SHORT) { + events->push_back(Event::SocketRead(1)); + } + + ret = true; + +out: + events->push_back(Event::SocketClose()); + return ret; } // TestFunctionPRNGModel is a model of how the urandom.c code will behave when // |TestFunction| is run. It should return the same trace of events that // |GetTrace| will observe the real code making. static std::vector<Event> TestFunctionPRNGModel(unsigned flags) { -#if defined(BORINGSSL_FIPS) - static const bool is_fips = true; -#else - static const bool is_fips = false; -#endif - std::vector<Event> ret; bool urandom_probed = false; bool getrandom_ready = false; + const bool used_daemon = kUsesDaemon && AppendDaemonEvents(&ret, flags); + // Probe for getrandom support ret.push_back(Event::GetRandom(1, GRND_NONBLOCK)); std::function<void()> wait_for_entropy; @@ -362,7 +683,7 @@ static std::vector<Event> TestFunctionPRNGModel(unsigned flags) { } wait_for_entropy = [&ret, &urandom_probed, flags] { - if (!is_fips || urandom_probed) { + if (!kIsFIPS || urandom_probed) { return; } @@ -413,14 +734,15 @@ static std::vector<Event> TestFunctionPRNGModel(unsigned flags) { }; } - const size_t kSeedLength = CTR_DRBG_ENTROPY_LEN * (is_fips ? 10 : 1); + const size_t kSeedLength = CTR_DRBG_ENTROPY_LEN * (kIsFIPS ? 10 : 1); const size_t kAdditionalDataLength = 32; if (!have_rdrand()) { if ((!have_fork_detection() && !sysrand(true, kAdditionalDataLength)) || // Initialise CRNGT. - (is_fips && !sysrand(true, 16)) || - !sysrand(true, kSeedLength) || + (!used_daemon && !sysrand(true, kSeedLength + (kIsFIPS ? 16 : 0))) || + // Personalisation draw if the daemon was used. + (used_daemon && !sysrand(false, CTR_DRBG_ENTROPY_LEN)) || // Second entropy draw. (!have_fork_detection() && !sysrand(true, kAdditionalDataLength))) { return ret; @@ -433,7 +755,7 @@ static std::vector<Event> TestFunctionPRNGModel(unsigned flags) { // Opportuntistic entropy draw in FIPS mode because RDRAND was used. // In non-FIPS mode it's just drawn from |CRYPTO_sysrand| in a blocking // way. - !sysrand(!is_fips, CTR_DRBG_ENTROPY_LEN) || + !sysrand(!kIsFIPS, CTR_DRBG_ENTROPY_LEN) || // Second entropy draw's additional data. (!have_fast_rdrand() && !have_fork_detection() && !sysrand(false, kAdditionalDataLength))) { @@ -480,12 +802,23 @@ TEST(URandomTest, Test) { SCOPED_TRACE(buf); for (unsigned flags = 0; flags < NEXT_FLAG; flags++) { + if (!kUsesDaemon && (flags & (SOCKET_ERROR | CONNECT_ERROR | + SOCKET_READ_ERROR | SOCKET_READ_SHORT))) { + // These cases are meaningless unless the code will try to use the entropy + // daemon. + continue; + } + TRACE_FLAG(NO_GETRANDOM); TRACE_FLAG(NO_URANDOM); TRACE_FLAG(GETRANDOM_NOT_READY); TRACE_FLAG(URANDOM_NOT_READY); TRACE_FLAG(GETRANDOM_ERROR); TRACE_FLAG(URANDOM_ERROR); + TRACE_FLAG(SOCKET_ERROR); + TRACE_FLAG(CONNECT_ERROR); + TRACE_FLAG(SOCKET_READ_ERROR); + TRACE_FLAG(SOCKET_READ_SHORT); const std::vector<Event> expected_trace = TestFunctionPRNGModel(flags); CheckInvariants(expected_trace); @@ -516,5 +849,5 @@ int main(int argc, char **argv) { return 0; } -#endif // X86_64 && !SHARED_LIBRARY && !UNSAFE_DETERMINISTIC_MODE && - // USE_NR_getrandom +#endif // (X86_64 || AARCH64) && !SHARED_LIBRARY && + // !UNSAFE_DETERMINISTIC_MODE && USE_NR_getrandom diff --git a/src/crypto/fipsmodule/self_check/self_check.c b/src/crypto/fipsmodule/self_check/self_check.c index fe9c9141..19f57434 100644 --- a/src/crypto/fipsmodule/self_check/self_check.c +++ b/src/crypto/fipsmodule/self_check/self_check.c @@ -20,6 +20,7 @@ #include <openssl/aead.h> #include <openssl/aes.h> #include <openssl/bn.h> +#include <openssl/ctrdrbg.h> #include <openssl/dh.h> #include <openssl/digest.h> #include <openssl/ec.h> diff --git a/src/crypto/fipsmodule/service_indicator/service_indicator_test.cc b/src/crypto/fipsmodule/service_indicator/service_indicator_test.cc index d0da9c91..4389b981 100644 --- a/src/crypto/fipsmodule/service_indicator/service_indicator_test.cc +++ b/src/crypto/fipsmodule/service_indicator/service_indicator_test.cc @@ -20,6 +20,7 @@ #include <openssl/cipher.h> #include <openssl/cmac.h> #include <openssl/crypto.h> +#include <openssl/ctrdrbg.h> #include <openssl/dh.h> #include <openssl/digest.h> #include <openssl/ec.h> diff --git a/src/crypto/internal.h b/src/crypto/internal.h index c9b5e8e4..2e94399c 100644 --- a/src/crypto/internal.h +++ b/src/crypto/internal.h @@ -881,6 +881,16 @@ static inline void CRYPTO_store_u32_be(void *out, uint32_t v) { OPENSSL_memcpy(out, &v, sizeof(v)); } +static inline uint64_t CRYPTO_load_u64_le(const void *in) { + uint64_t v; + OPENSSL_memcpy(&v, in, sizeof(v)); + return v; +} + +static inline void CRYPTO_store_u64_le(void *out, uint64_t v) { + OPENSSL_memcpy(out, &v, sizeof(v)); +} + static inline uint64_t CRYPTO_load_u64_be(const void *ptr) { uint64_t ret; OPENSSL_memcpy(&ret, ptr, sizeof(ret)); diff --git a/src/crypto/rand_extra/passive.c b/src/crypto/rand_extra/passive.c index a2b388f8..e091bff5 100644 --- a/src/crypto/rand_extra/passive.c +++ b/src/crypto/rand_extra/passive.c @@ -12,23 +12,149 @@ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <openssl/base.h> +#include <openssl/ctrdrbg.h> + #include "../fipsmodule/rand/internal.h" +#include "../internal.h" #if defined(BORINGSSL_FIPS) +#define ENTROPY_READ_LEN \ + (/* last_block size */ 16 + CTR_DRBG_ENTROPY_LEN * BORINGSSL_FIPS_OVERREAD) + +#if defined(OPENSSL_ANDROID) + +#include <errno.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/un.h> +#include <unistd.h> + +static struct CRYPTO_STATIC_MUTEX g_socket_history_lock = + CRYPTO_STATIC_MUTEX_INIT; + +// socket_history_t enumerates whether the entropy daemon should be contacted +// for a given entropy request. Values other than socket_not_yet_attempted are +// sticky so if the first attempt to read from the daemon fails it's assumed +// that the daemon is not present and no more attempts will be made. If the +// first attempt is successful then attempts will be made forever more. +enum socket_history_t { + // initial value, no connections to the entropy daemon have been made yet. + socket_not_yet_attempted = 0, + // reading from the entropy daemon was successful + socket_success, + // reading from the entropy daemon failed. + socket_failed, +}; + +enum socket_history_t g_socket_history = socket_not_yet_attempted; + +// DAEMON_RESPONSE_LEN is the number of bytes that the entropy daemon replies +// with. +#define DAEMON_RESPONSE_LEN 496 + +OPENSSL_STATIC_ASSERT(ENTROPY_READ_LEN == DAEMON_RESPONSE_LEN, + "entropy daemon response length mismatch"); + +static int get_seed_from_daemon(uint8_t *out_entropy, size_t out_entropy_len) { + // |RAND_need_entropy| should never call this function for more than + // |DAEMON_RESPONSE_LEN| bytes. + if (out_entropy_len > DAEMON_RESPONSE_LEN) { + abort(); + } + + CRYPTO_STATIC_MUTEX_lock_read(&g_socket_history_lock); + const enum socket_history_t socket_history = g_socket_history; + CRYPTO_STATIC_MUTEX_unlock_read(&g_socket_history_lock); + + if (socket_history == socket_failed) { + return 0; + } + + int ret = 0; + const int sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + goto out; + } + + struct sockaddr_un sun; + memset(&sun, 0, sizeof(sun)); + sun.sun_family = AF_UNIX; + static const char kSocketPath[] = "/dev/socket/prng_seeder"; + OPENSSL_memcpy(sun.sun_path, kSocketPath, sizeof(kSocketPath)); + + if (connect(sock, (struct sockaddr *)&sun, sizeof(sun))) { + goto out; + } + + uint8_t buffer[DAEMON_RESPONSE_LEN]; + size_t done = 0; + while (done < sizeof(buffer)) { + ssize_t n; + do { + n = read(sock, buffer + done, sizeof(buffer) - done); + } while (n == -1 && errno == EINTR); + + if (n < 1) { + goto out; + } + done += n; + } + + if (done != DAEMON_RESPONSE_LEN) { + // The daemon should always write |DAEMON_RESPONSE_LEN| bytes on every + // connection. + goto out; + } + + assert(out_entropy_len <= DAEMON_RESPONSE_LEN); + OPENSSL_memcpy(out_entropy, buffer, out_entropy_len); + ret = 1; + +out: + if (socket_history == socket_not_yet_attempted) { + CRYPTO_STATIC_MUTEX_lock_write(&g_socket_history_lock); + if (g_socket_history == socket_not_yet_attempted) { + g_socket_history = (ret == 0) ? socket_failed : socket_success; + } + CRYPTO_STATIC_MUTEX_unlock_write(&g_socket_history_lock); + } + + close(sock); + return ret; +} + +#else + +static int get_seed_from_daemon(uint8_t *out_entropy, size_t out_entropy_len) { + return 0; +} + +#endif // OPENSSL_ANDROID + // RAND_need_entropy is called by the FIPS module when it has blocked because of // a lack of entropy. This signal is used as an indication to feed it more. void RAND_need_entropy(size_t bytes_needed) { - uint8_t buf[CTR_DRBG_ENTROPY_LEN * BORINGSSL_FIPS_OVERREAD]; + uint8_t buf[ENTROPY_READ_LEN]; size_t todo = sizeof(buf); if (todo > bytes_needed) { todo = bytes_needed; } - int used_cpu; - CRYPTO_get_seed_entropy(buf, todo, &used_cpu); - RAND_load_entropy(buf, todo, used_cpu); + int want_additional_input; + if (get_seed_from_daemon(buf, todo)) { + want_additional_input = 1; + } else { + CRYPTO_get_seed_entropy(buf, todo, &want_additional_input); + } + + 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(buf, 0, todo); + } + + RAND_load_entropy(buf, todo, want_additional_input); } #endif // FIPS diff --git a/src/include/openssl/base.h b/src/include/openssl/base.h index 4ab9eca7..c2c953bf 100644 --- a/src/include/openssl/base.h +++ b/src/include/openssl/base.h @@ -391,6 +391,7 @@ typedef struct conf_st CONF; typedef struct conf_value_st CONF_VALUE; typedef struct crypto_buffer_pool_st CRYPTO_BUFFER_POOL; typedef struct crypto_buffer_st CRYPTO_BUFFER; +typedef struct ctr_drbg_state_st CTR_DRBG_STATE; typedef struct dh_st DH; typedef struct dsa_st DSA; typedef struct ec_group_st EC_GROUP; diff --git a/src/include/openssl/ctrdrbg.h b/src/include/openssl/ctrdrbg.h new file mode 100644 index 00000000..62afe0c1 --- /dev/null +++ b/src/include/openssl/ctrdrbg.h @@ -0,0 +1,76 @@ +/* Copyright (c) 2022, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#ifndef OPENSSL_HEADER_CTRDRBG_H +#define OPENSSL_HEADER_CTRDRBG_H + +#include <openssl/base.h> + +#if defined(__cplusplus) +extern "C" { +#endif + + +// FIPS pseudo-random number generator. + + +// CTR-DRBG state objects. +// +// CTR_DRBG_STATE contains the state of a FIPS AES-CTR-based pseudo-random +// number generator. If BoringSSL was built in FIPS mode then this is a FIPS +// Approved algorithm. + +// CTR_DRBG_ENTROPY_LEN is the number of bytes of input entropy. See SP +// 800-90Ar1, table 3. +#define CTR_DRBG_ENTROPY_LEN 48 + +// CTR_DRBG_MAX_GENERATE_LENGTH is the maximum number of bytes that can be +// generated in a single call to |CTR_DRBG_generate|. +#define CTR_DRBG_MAX_GENERATE_LENGTH 65536 + +// CTR_DRBG_new returns an initialized |CTR_DRBG_STATE|, or NULL if either +// allocation failed or if |personalization_len| is invalid. +OPENSSL_EXPORT CTR_DRBG_STATE *CTR_DRBG_new( + const uint8_t entropy[CTR_DRBG_ENTROPY_LEN], const uint8_t *personalization, + size_t personalization_len); + +// CTR_DRBG_free frees |state| if non-NULL, or else does nothing. +OPENSSL_EXPORT void CTR_DRBG_free(CTR_DRBG_STATE* state); + +// CTR_DRBG_reseed reseeds |drbg| given |CTR_DRBG_ENTROPY_LEN| bytes of entropy +// in |entropy| and, optionally, up to |CTR_DRBG_ENTROPY_LEN| bytes of +// additional data. It returns one on success or zero on error. +OPENSSL_EXPORT int CTR_DRBG_reseed(CTR_DRBG_STATE *drbg, + const uint8_t entropy[CTR_DRBG_ENTROPY_LEN], + const uint8_t *additional_data, + size_t additional_data_len); + +// CTR_DRBG_generate processes to up |CTR_DRBG_ENTROPY_LEN| bytes of additional +// data (if any) and then writes |out_len| random bytes to |out|, where +// |out_len| <= |CTR_DRBG_MAX_GENERATE_LENGTH|. It returns one on success or +// zero on error. +OPENSSL_EXPORT 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); + +// CTR_DRBG_clear zeroises the state of |drbg|. +OPENSSL_EXPORT void CTR_DRBG_clear(CTR_DRBG_STATE *drbg); + + +#if defined(__cplusplus) +} // extern C +#endif + +#endif // OPENSSL_HEADER_CTRDRBG_H diff --git a/src/rust/wrapper.h b/src/rust/wrapper.h index aa5aeedb..ff466423 100644 --- a/src/rust/wrapper.h +++ b/src/rust/wrapper.h @@ -18,6 +18,7 @@ #include "../include/openssl/conf.h" #include "../include/openssl/cpu.h" #include "../include/openssl/crypto.h" +#include "../include/openssl/ctrdrbg.h" #include "../include/openssl/curve25519.h" #include "../include/openssl/des.h" #include "../include/openssl/dh.h" diff --git a/src/util/fipstools/acvp/modulewrapper/modulewrapper.cc b/src/util/fipstools/acvp/modulewrapper/modulewrapper.cc index f3e583d5..f65492c0 100644 --- a/src/util/fipstools/acvp/modulewrapper/modulewrapper.cc +++ b/src/util/fipstools/acvp/modulewrapper/modulewrapper.cc @@ -29,6 +29,7 @@ #include <openssl/bn.h> #include <openssl/cipher.h> #include <openssl/cmac.h> +#include <openssl/ctrdrbg.h> #include <openssl/dh.h> #include <openssl/digest.h> #include <openssl/ec.h> diff --git a/src/util/fipstools/delocate/delocate.go b/src/util/fipstools/delocate/delocate.go index 55c86715..84508aa8 100644 --- a/src/util/fipstools/delocate/delocate.go +++ b/src/util/fipstools/delocate/delocate.go @@ -439,7 +439,7 @@ func (d *delocation) processAarch64Instruction(statement, instruction *node32) ( argNodes := instructionArgs(instruction.next) switch instructionName { - case "cset", "csel", "csetm", "cneg", "csinv", "cinc", "csinc", "csneg": + case "ccmn", "ccmp", "cinc", "cinv", "cneg", "csel", "cset", "csetm", "csinc", "csinv", "csneg": // These functions are special because they take a condition-code name as // an argument and that looks like a symbol reference. d.writeNode(statement) diff --git a/src/util/fipstools/test_fips.c b/src/util/fipstools/test_fips.c index e192b616..42ed96b2 100644 --- a/src/util/fipstools/test_fips.c +++ b/src/util/fipstools/test_fips.c @@ -21,6 +21,7 @@ #include <openssl/aes.h> #include <openssl/bn.h> #include <openssl/crypto.h> +#include <openssl/ctrdrbg.h> #include <openssl/des.h> #include <openssl/dh.h> #include <openssl/ecdsa.h> diff --git a/win-aarch64/crypto/fipsmodule/p256-armv8-asm.S b/win-aarch64/crypto/fipsmodule/p256-armv8-asm.S index 4ebf3abd..cfffc0d8 100644 --- a/win-aarch64/crypto/fipsmodule/p256-armv8-asm.S +++ b/win-aarch64/crypto/fipsmodule/p256-armv8-asm.S @@ -32,62 +32,6 @@ LordK: .byte 69,67,80,95,78,73,83,84,90,50,53,54,32,102,111,114,32,65,82,77,118,56,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0 .align 2 -// void ecp_nistz256_to_mont(BN_ULONG x0[4],const BN_ULONG x1[4]); -.globl ecp_nistz256_to_mont - -.def ecp_nistz256_to_mont - .type 32 -.endef -.align 6 -ecp_nistz256_to_mont: - AARCH64_SIGN_LINK_REGISTER - stp x29,x30,[sp,#-32]! - add x29,sp,#0 - stp x19,x20,[sp,#16] - - ldr x3,LRR // bp[0] - ldp x4,x5,[x1] - ldp x6,x7,[x1,#16] - ldr x12,Lpoly+8 - ldr x13,Lpoly+24 - adr x2,LRR // &bp[0] - - bl __ecp_nistz256_mul_mont - - ldp x19,x20,[sp,#16] - ldp x29,x30,[sp],#32 - AARCH64_VALIDATE_LINK_REGISTER - ret - - -// void ecp_nistz256_from_mont(BN_ULONG x0[4],const BN_ULONG x1[4]); -.globl ecp_nistz256_from_mont - -.def ecp_nistz256_from_mont - .type 32 -.endef -.align 4 -ecp_nistz256_from_mont: - AARCH64_SIGN_LINK_REGISTER - stp x29,x30,[sp,#-32]! - add x29,sp,#0 - stp x19,x20,[sp,#16] - - mov x3,#1 // bp[0] - ldp x4,x5,[x1] - ldp x6,x7,[x1,#16] - ldr x12,Lpoly+8 - ldr x13,Lpoly+24 - adr x2,Lone // &bp[0] - - bl __ecp_nistz256_mul_mont - - ldp x19,x20,[sp,#16] - ldp x29,x30,[sp],#32 - AARCH64_VALIDATE_LINK_REGISTER - ret - - // void ecp_nistz256_mul_mont(BN_ULONG x0[4],const BN_ULONG x1[4], // const BN_ULONG x2[4]); .globl ecp_nistz256_mul_mont |