diff --git a/.github/workflows/abidiff.yml b/.github/workflows/abidiff.yml index f4f8ea595c..5710961bb7 100644 --- a/.github/workflows/abidiff.yml +++ b/.github/workflows/abidiff.yml @@ -2,6 +2,9 @@ name: AWS-LC ABI Diff on: pull_request: branches: [ '*' ] +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true env: DOCKER_BUILDKIT: 1 GOPROXY: https://proxy.golang.org,direct diff --git a/.github/workflows/aws-lc-rs.yml b/.github/workflows/aws-lc-rs.yml index bb98bdb7e8..c5febc739c 100644 --- a/.github/workflows/aws-lc-rs.yml +++ b/.github/workflows/aws-lc-rs.yml @@ -4,6 +4,9 @@ on: branches: [ '*' ] pull_request: branches: [ '*' ] +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true env: GOPROXY: https://proxy.golang.org,direct jobs: diff --git a/.github/workflows/integrations.yml b/.github/workflows/integrations.yml index f3453e7761..83a54d92c2 100644 --- a/.github/workflows/integrations.yml +++ b/.github/workflows/integrations.yml @@ -4,6 +4,9 @@ on: branches: [ '*' ] pull_request: branches: [ '*' ] +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true env: CC: gcc jobs: diff --git a/.github/workflows/osx.yml b/.github/workflows/osx.yml index f263acb82f..d3dee44d2a 100644 --- a/.github/workflows/osx.yml +++ b/.github/workflows/osx.yml @@ -7,6 +7,10 @@ on: branches: - '*' +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + env: PACKAGE_NAME: aws-lc # Used to enable ASAN test dimension. diff --git a/.gitignore b/.gitignore index f346187861..79b436fa08 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,6 @@ cmake-build-debug/ symbols.txt .DS_Store -.idea +.idea/ +.fleet/ +.cache/ diff --git a/crypto/bio/bio_test.cc b/crypto/bio/bio_test.cc index ee35dc3bb3..9266581381 100644 --- a/crypto/bio/bio_test.cc +++ b/crypto/bio/bio_test.cc @@ -696,4 +696,31 @@ TEST_P(BIOPairTest, TestCallbacks) { ASSERT_EQ(param_len_ex[0], 0u); } +namespace { + static int callback_invoked = 0; + + static long callback(BIO *b, int state, int res) { + callback_invoked = 1; + EXPECT_EQ(state, 0); + EXPECT_EQ(res, -1); + return 0; + } + + TEST(BIOTest, InvokeConnectCallback) { + + ASSERT_EQ(callback_invoked, 0); + BIO *bio = BIO_new(BIO_s_connect()); + ASSERT_NE(bio, nullptr); + + ASSERT_TRUE(BIO_set_conn_hostname(bio, "localhost")); + ASSERT_TRUE(BIO_set_conn_port(bio, "8080")); + ASSERT_TRUE(BIO_callback_ctrl(bio, BIO_CTRL_SET_CALLBACK, callback)); + + ASSERT_EQ(BIO_do_connect(bio), 0); + ASSERT_EQ(callback_invoked, 1); + + ASSERT_TRUE(BIO_free(bio)); + } +} + INSTANTIATE_TEST_SUITE_P(All, BIOPairTest, testing::Values(false, true)); diff --git a/crypto/bio/connect.c b/crypto/bio/connect.c index c19f1c4e65..ae730b8795 100644 --- a/crypto/bio/connect.c +++ b/crypto/bio/connect.c @@ -105,7 +105,7 @@ typedef struct bio_connect_st { // info_callback is called when the connection is initially made // callback(BIO,state,ret); The callback should return 'ret', state is for // compatibility with the SSL info_callback. - int (*info_callback)(const BIO *bio, int state, int ret); + bio_info_cb info_callback; } BIO_CONNECT; #if !defined(OPENSSL_WINDOWS) @@ -168,7 +168,7 @@ static int split_host_and_port(char **out_host, char **out_port, static int conn_state(BIO *bio, BIO_CONNECT *c) { int ret = -1, i; - int (*cb)(const BIO *, int, int) = NULL; + bio_info_cb cb = NULL; if (c->info_callback != NULL) { cb = c->info_callback; @@ -467,7 +467,7 @@ static long conn_ctrl(BIO *bio, int cmd, long num, void *ptr) { case BIO_CTRL_FLUSH: break; case BIO_CTRL_GET_CALLBACK: { - int (**fptr)(const BIO *bio, int state, int xret) = ptr; + bio_info_cb *fptr = ptr; *fptr = data->info_callback; } break; default: @@ -485,13 +485,7 @@ static long conn_callback_ctrl(BIO *bio, int cmd, bio_info_cb fp) { switch (cmd) { case BIO_CTRL_SET_CALLBACK: - // This is the actual type signature of |fp|. The caller is expected to - // cast it to |bio_info_cb| due to the |BIO_callback_ctrl| calling - // convention. - OPENSSL_MSVC_PRAGMA(warning(push)) - OPENSSL_MSVC_PRAGMA(warning(disable : 4191)) - data->info_callback = (int (*)(const struct bio_st *, int, int))fp; - OPENSSL_MSVC_PRAGMA(warning(pop)) + data->info_callback = fp; break; default: ret = 0; diff --git a/crypto/blake2/blake2.c b/crypto/blake2/blake2.c index 38a320d2ce..96f65eb95d 100644 --- a/crypto/blake2/blake2.c +++ b/crypto/blake2/blake2.c @@ -59,6 +59,16 @@ static uint64_t blake2b_load(const uint8_t block[BLAKE2B_CBLOCK], size_t i) { return CRYPTO_load_u64_le(block + 8 * i); } +static void copy_digest_words_to_dest(uint8_t* dest, uint64_t* src, size_t word_count) { +#ifdef OPENSSL_BIG_ENDIAN + for(size_t i = 0; i < word_count; i++) { + CRYPTO_store_u64_le(&dest[i * sizeof(uint64_t)], src[i]); + } +#else + OPENSSL_memcpy(dest, src, word_count * sizeof(uint64_t)); +#endif +} + static void blake2b_transform(BLAKE2B_CTX *b2b, const uint8_t block[BLAKE2B_CBLOCK], size_t num_bytes, int is_final_block) { @@ -158,7 +168,7 @@ void BLAKE2B256_Final(uint8_t out[BLAKE2B256_DIGEST_LENGTH], BLAKE2B_CTX *b2b) { blake2b_transform(b2b, b2b->block, b2b->block_used, /*is_final_block=*/1); OPENSSL_STATIC_ASSERT(BLAKE2B256_DIGEST_LENGTH <= sizeof(b2b->h), _) - memcpy(out, b2b->h, BLAKE2B256_DIGEST_LENGTH); + copy_digest_words_to_dest(out, b2b->h, BLAKE2B256_DIGEST_LENGTH / 8); } void BLAKE2B256(const uint8_t *data, size_t len, diff --git a/crypto/chacha/chacha.c b/crypto/chacha/chacha.c index 1092b7aa28..a4d88c0a40 100644 --- a/crypto/chacha/chacha.c +++ b/crypto/chacha/chacha.c @@ -91,7 +91,25 @@ void CRYPTO_chacha_20(uint8_t *out, const uint8_t *in, size_t in_len, } #endif - ChaCha20_ctr32(out, in, in_len, key_ptr, counter_nonce); + while (in_len > 0) { + // The assembly functions do not have defined overflow behavior. While + // overflow is almost always a bug in the caller, we prefer our functions to + // behave the same across platforms, so divide into multiple calls to avoid + // this case. + uint64_t todo = 64 * ((UINT64_C(1) << 32) - counter_nonce[0]); + if (todo > in_len) { + todo = in_len; + } + + ChaCha20_ctr32(out, in, (size_t)todo, key_ptr, counter_nonce); + in += todo; + out += todo; + in_len -= todo; + + // We're either done and will next break out of the loop, or we stopped at + // the wraparound point and the counter should continue at zero. + counter_nonce[0] = 0; + } } #else diff --git a/crypto/chacha/chacha_test.cc b/crypto/chacha/chacha_test.cc index 25313cad6d..00683ce51e 100644 --- a/crypto/chacha/chacha_test.cc +++ b/crypto/chacha/chacha_test.cc @@ -218,8 +218,102 @@ static const uint8_t kOutput[] = { 0x8f, 0x40, 0xcf, 0x4a, }; +static uint32_t kOverflowCounter = 0xffffffff; + +static const uint8_t kOverflowOutput[] = { + 0x37, 0x64, 0x38, 0xcb, 0x25, 0x69, 0x2c, 0xf5, 0x88, 0x8a, 0xfe, 0x6d, + 0x3b, 0x10, 0x07, 0x3c, 0x77, 0xac, 0xcd, 0x1c, 0x0c, 0xa7, 0x17, 0x31, + 0x1d, 0xc3, 0x81, 0xd1, 0xa5, 0x20, 0x55, 0xea, 0xd3, 0x00, 0xc9, 0x84, + 0xde, 0xe2, 0xe5, 0x5e, 0x7b, 0x28, 0x28, 0x59, 0x73, 0x3a, 0x8e, 0x57, + 0x62, 0x18, 0x50, 0x55, 0x97, 0xca, 0x50, 0x3e, 0x8a, 0x84, 0x61, 0x28, + 0x4c, 0x22, 0x93, 0x50, 0x48, 0x7e, 0x65, 0x78, 0x06, 0x5a, 0xcd, 0x2b, + 0x11, 0xf7, 0x10, 0xfd, 0x6f, 0x41, 0x92, 0x82, 0x7c, 0x3a, 0x71, 0x07, + 0x67, 0xd0, 0x7e, 0xb7, 0xdf, 0xdc, 0xfc, 0xee, 0xe6, 0x55, 0xdd, 0x6f, + 0x79, 0x23, 0xf3, 0xae, 0xb1, 0x21, 0x96, 0xbe, 0xea, 0x0e, 0x1b, 0x58, + 0x0b, 0x3f, 0x63, 0x51, 0xd4, 0xce, 0x98, 0xfe, 0x1a, 0xc7, 0xa7, 0x43, + 0x7f, 0x0c, 0xe8, 0x62, 0xcf, 0x78, 0x3f, 0x4e, 0x31, 0xbf, 0x2b, 0x76, + 0x91, 0xcd, 0x19, 0x80, 0x0d, 0x7f, 0x11, 0x8b, 0x76, 0xef, 0x43, 0x3c, + 0x4f, 0x61, 0x86, 0xc5, 0x64, 0xa8, 0xc2, 0x73, 0xc2, 0x64, 0x39, 0xa0, + 0x8b, 0xe6, 0x7f, 0xf6, 0x26, 0xd4, 0x47, 0x4f, 0xe4, 0x46, 0xe2, 0xf5, + 0x9e, 0xe6, 0xc7, 0x76, 0x6c, 0xa9, 0x0f, 0x1d, 0x1b, 0x22, 0xa5, 0x62, + 0x0a, 0x88, 0x3e, 0x8c, 0xf0, 0xbc, 0x4c, 0x11, 0x3f, 0x0d, 0xf7, 0x85, + 0x67, 0x0b, 0x4c, 0xa3, 0x3f, 0xa8, 0xf1, 0x2a, 0x65, 0x2e, 0x00, 0x03, + 0xc9, 0x49, 0x91, 0x48, 0xb7, 0xc8, 0x29, 0x28, 0x2f, 0x46, 0x8e, 0x8b, + 0xd6, 0x73, 0x19, 0x06, 0x3e, 0x6f, 0x92, 0xc8, 0x3d, 0x3f, 0x4d, 0x68, + 0xbc, 0x02, 0xc0, 0x8f, 0x71, 0x46, 0x0d, 0x28, 0x63, 0xfe, 0xad, 0x14, + 0x81, 0x04, 0xb7, 0x23, 0xfd, 0x21, 0x0a, 0xf0, 0x6f, 0xcd, 0x47, 0x0b, + 0x0e, 0x93, 0xa3, 0xa8, 0x44, 0x15, 0xd6, 0xae, 0x06, 0x44, 0x6b, 0xbc, + 0xff, 0x8a, 0x56, 0x60, 0x3c, 0x38, 0xd6, 0xed, 0x03, 0x2d, 0x79, 0x2a, + 0xe9, 0x15, 0xef, 0xfc, 0x92, 0x1f, 0x83, 0xa4, 0x60, 0x8f, 0xc9, 0x29, + 0xb2, 0xb4, 0x9e, 0x3f, 0xa9, 0xe8, 0xfb, 0xa2, 0x62, 0x20, 0x2e, 0xc9, + 0x43, 0xb2, 0xd1, 0x36, 0x85, 0x1e, 0xa4, 0xb3, 0x4f, 0x8c, 0x9e, 0x81, + 0x75, 0x68, 0xbc, 0xf1, 0x52, 0xd5, 0x03, 0x22, 0xcf, 0xdf, 0x64, 0xb0, + 0x28, 0xd2, 0x45, 0x18, 0x38, 0x8c, 0xd0, 0xf6, 0x30, 0x3c, 0x04, 0xd9, + 0x8d, 0xb6, 0xb2, 0x57, 0x2a, 0xee, 0x28, 0xeb, 0x5f, 0x1a, 0x10, 0x6e, + 0x88, 0x79, 0x08, 0x23, 0x19, 0x84, 0xf8, 0x80, 0x1a, 0x7d, 0x6f, 0x8b, + 0xc1, 0x8e, 0x5f, 0x5f, 0x54, 0x14, 0x2a, 0xdc, 0x41, 0x5d, 0xeb, 0x00, + 0xf2, 0x50, 0xae, 0xd3, 0x55, 0x32, 0xf6, 0xd9, 0x34, 0xf4, 0xb2, 0xf2, + 0xf5, 0x90, 0x05, 0x8a, 0x9c, 0xc7, 0x94, 0x5d, 0x2d, 0x5a, 0x0f, 0xdd, + 0x03, 0xde, 0xbe, 0x18, 0xb3, 0xe3, 0x07, 0x6b, 0x57, 0xfa, 0x1b, 0x7b, + 0x75, 0xcb, 0xc2, 0x4d, 0xf7, 0x88, 0xfe, 0xf9, 0xc0, 0x6c, 0xdb, 0x5f, + 0xf6, 0x48, 0x00, 0x4a, 0x5d, 0x75, 0xfa, 0x6b, 0x45, 0x43, 0xc4, 0x7f, + 0x97, 0x31, 0x22, 0xb4, 0x9c, 0xa3, 0xee, 0x2f, 0x27, 0xa9, 0x9f, 0x0e, + 0xdc, 0x40, 0x67, 0x17, 0x2e, 0xcb, 0xfd, 0x9e, 0xe7, 0xb2, 0x85, 0xcd, + 0x49, 0x24, 0xc8, 0x8a, 0x59, 0x6b, 0x1f, 0xec, 0x72, 0x89, 0xf8, 0x30, + 0xdf, 0x82, 0x61, 0x3b, 0x8b, 0xc9, 0x80, 0xe4, 0x27, 0x0d, 0xfe, 0x42, + 0x27, 0x6c, 0xaf, 0x62, 0x3e, 0x2f, 0x1d, 0x38, 0xb6, 0x88, 0x8f, 0x71, + 0x5a, 0x54, 0x6c, 0x68, 0x57, 0x40, 0x49, 0x7a, 0xb2, 0xe8, 0xb6, 0x97, + 0xab, 0xd6, 0x3c, 0x35, 0xf3, 0x95, 0x12, 0xde, 0xa2, 0x39, 0x54, 0x52, + 0x8c, 0x38, 0x2a, 0x2b, 0xe7, 0x21, 0x38, 0x63, 0xb0, 0xd6, 0xad, 0x94, + 0x44, 0xaf, 0x49, 0x5d, 0xfc, 0x49, 0x6b, 0x30, 0xdf, 0xe9, 0x19, 0x1e, + 0xed, 0x98, 0x0d, 0x4a, 0x3d, 0x56, 0x5e, 0x74, 0xad, 0x13, 0x8b, 0x68, + 0x45, 0x08, 0xbe, 0x0e, 0x6c, 0xb4, 0x62, 0x93, 0x27, 0x8b, 0x4f, 0xab, + 0x3e, 0xba, 0xe1, 0xe5, 0xff, 0xa8, 0x5d, 0x33, 0x32, 0xff, 0x34, 0xf9, + 0x8d, 0x67, 0x24, 0x4a, 0xbb, 0x2c, 0x60, 0xb5, 0x88, 0x96, 0x1b, 0xcc, + 0x53, 0xfb, 0x2e, 0x05, 0x1d, 0x8b, 0xc2, 0xa0, 0xde, 0x21, 0x41, 0x5e, + 0x11, 0x1b, 0x96, 0xd9, 0xa6, 0xae, 0xbd, 0xf0, 0x91, 0xad, 0x69, 0x2b, + 0xd2, 0x3f, 0xe4, 0x3d, 0x16, 0x69, 0xa6, 0xb2, 0x9c, 0xbe, 0x59, 0x7b, + 0x87, 0x79, 0xf5, 0xc2, 0x5a, 0xcc, 0xdf, 0xfe, 0x7f, 0xf9, 0xa6, 0x52, + 0xde, 0x5f, 0x46, 0x91, 0x21, 0x2c, 0x2c, 0x49, 0x25, 0x00, 0xd5, 0xe4, + 0x81, 0x6b, 0x85, 0xad, 0x98, 0xaf, 0x06, 0x4a, 0x83, 0xb2, 0xe3, 0x42, + 0x39, 0x31, 0x50, 0xe1, 0x2d, 0x22, 0xe6, 0x07, 0x24, 0x65, 0x29, 0x3f, + 0x4c, 0xbd, 0x14, 0x8d, 0xfa, 0x31, 0xfa, 0xa4, 0xb5, 0x99, 0x04, 0xa2, + 0xa5, 0xcc, 0x3b, 0x12, 0xb1, 0xaa, 0x6a, 0x17, 0x78, 0x8b, 0xb3, 0xe4, + 0x3c, 0x4c, 0xc5, 0xaa, 0x79, 0x12, 0x17, 0xe0, 0x22, 0x4d, 0xf4, 0xa9, + 0xd5, 0xd0, 0xed, 0xf8, 0xfe, 0x0a, 0x45, 0x80, 0x9f, 0x3b, 0x74, 0xa0, + 0xb1, 0xda, 0x18, 0xfa, 0xc2, 0x7d, 0xf6, 0x18, 0x2e, 0xa9, 0x2b, 0x7e, + 0x69, 0x06, 0x43, 0x2d, 0x62, 0x09, 0x42, 0x10, 0x9f, 0x83, 0xad, 0xd9, + 0xdd, 0xcd, 0xcb, 0x1b, 0x33, 0x32, 0x3e, 0x1f, 0xf6, 0xac, 0x3b, 0xa3, + 0x29, 0xd7, 0xc0, 0x88, 0xf9, 0xb7, 0x4c, 0xcd, 0x0a, 0x1f, 0xb8, 0x0f, + 0xe6, 0xf7, 0xd7, 0x4d, 0x5f, 0x06, 0x12, 0x8a, 0x12, 0xa6, 0x2d, 0xbe, + 0x5c, 0x57, 0xf8, 0x7f, 0x54, 0x3f, 0x90, 0x83, 0x2c, 0x0a, 0xc5, 0x3d, + 0x03, 0x78, 0x8a, 0x68, 0xf0, 0xbd, 0xa5, 0x3e, 0xe7, 0x07, 0xab, 0xc8, + 0x58, 0x2f, 0x5c, 0xfd, 0xb5, 0x39, 0xe3, 0xc6, 0x1c, 0x27, 0xf9, 0x0b, + 0xc7, 0x4c, 0xcc, 0x67, 0x62, 0xe6, 0x79, 0xe8, 0xc1, 0x0a, 0x86, 0x8a, + 0xb2, 0x32, 0x7b, 0x90, 0x36, 0x50, 0x92, 0x1f, 0x3e, 0x68, 0x39, 0x1c, + 0x4d, 0x5d, 0xf8, 0x2b, 0xe8, 0x7d, 0xe2, 0x34, 0x61, 0x9e, 0xc3, 0x77, + 0xb9, 0x4c, 0x34, 0x08, 0xda, 0x31, 0xc9, 0x1d, 0xbd, 0x3b, 0x7b, 0xf1, + 0x14, 0xba, 0x3a, 0x34, 0x13, 0xaa, 0x5e, 0xa8, 0x36, 0xf6, 0xfe, 0xed, + 0x5b, 0xef, 0xaf, 0x24, 0x42, 0xba, 0xfc, 0xc9, 0x30, 0x84, 0xec, 0x49, + 0x14, 0xab, 0x58, 0x71, 0xfe, 0x4b, 0x6d, 0x7b, 0x9f, 0xbb, 0x3c, 0x83, + 0xdf, 0x3a, 0xfb, 0x54, 0xff, 0x36, 0xaa, 0x6c, 0x47, 0x94, 0xc0, 0xde, + 0x89, 0x2e, 0xac, 0x68, 0xee, 0xe8, 0xf4, 0xae, 0xa3, 0xe0, 0x91, 0x55, + 0x0b, 0x0c, 0xd7, 0xf4, 0x33, 0xb5, 0xf9, 0xf2, 0x9e, 0xda, 0x78, 0xe5, + 0x75, 0xec, 0xdb, 0xf6, 0xed, 0x27, 0x9f, 0x44, 0x19, 0x9f, 0xb7, 0xf0, + 0xac, 0x1b, 0x3a, 0xf5, 0x77, 0xc7, 0x76, 0x1e, 0x3f, 0x78, 0x12, 0x48, + 0x1d, 0xb8, 0xe0, 0x30, 0x29, 0x9a, 0x8c, 0x8f, 0x21, 0x44, 0x9c, 0x89, + 0xec, 0x8e, 0xd0, 0x81, 0xf5, 0x6a, 0xd0, 0xac, 0x5e, 0xf0, 0x0f, 0x88, + 0x86, 0x31, 0x2e, 0x15, 0x1e, 0x0d, 0x2d, 0xeb, 0x56, 0x30, 0x27, 0x02, + 0x93, 0xf4, 0x07, 0x07, 0xba, 0xf7, 0xbd, 0xe8, 0x27, 0x4f, 0xc6, 0xd9, + 0x57, 0x10, 0x3b, 0xf0, 0xff, 0x2f, 0x2d, 0x6b, 0xd0, 0x17, 0xb3, 0x49, + 0xeb, 0xc2, 0x49, 0xdb, +}; + + static_assert(sizeof(kInput) == sizeof(kOutput), "Input and output lengths don't match."); +static_assert(sizeof(kInput) == sizeof(kOverflowOutput), + "Input and output lengths don't match."); TEST(ChaChaTest, TestVector) { // Run the test with the test vector at all lengths. @@ -237,6 +331,22 @@ TEST(ChaChaTest, TestVector) { } } +TEST(ChaChaTest, CounterOverflow) { + // Run the test with the test vector at all lengths. + for (size_t len = 0; len <= sizeof(kInput); len++) { + SCOPED_TRACE(len); + + std::unique_ptr buf(new uint8_t[len]); + CRYPTO_chacha_20(buf.get(), kInput, len, kKey, kNonce, kOverflowCounter); + EXPECT_EQ(Bytes(kOverflowOutput, len), Bytes(buf.get(), len)); + + // Test the in-place version. + OPENSSL_memcpy(buf.get(), kInput, len); + CRYPTO_chacha_20(buf.get(), buf.get(), len, kKey, kNonce, kOverflowCounter); + EXPECT_EQ(Bytes(kOverflowOutput, len), Bytes(buf.get(), len)); + } +} + #if defined(CHACHA20_ASM) && defined(SUPPORTS_ABI_TEST) TEST(ChaChaTest, ABI) { uint32_t key[8]; diff --git a/crypto/chacha/internal.h b/crypto/chacha/internal.h index 1435e3b01e..5f442ec461 100644 --- a/crypto/chacha/internal.h +++ b/crypto/chacha/internal.h @@ -32,7 +32,14 @@ void CRYPTO_hchacha20(uint8_t out[32], const uint8_t key[32], defined(OPENSSL_ARM) || defined(OPENSSL_AARCH64)) #define CHACHA20_ASM -// ChaCha20_ctr32 is defined in asm/chacha-*.pl. +// ChaCha20_ctr32 encrypts |in_len| bytes from |in| and writes the result to +// |out|. If |in| and |out| alias, they must be equal. +// +// |counter[0]| is the initial 32-bit block counter, and the remainder is the +// 96-bit nonce. If the counter overflows, the output is undefined. The function +// will produce output, but the output may vary by machine and may not be +// self-consistent. (On some architectures, the assembly implements a mix of +// 64-bit and 32-bit counters.) void ChaCha20_ctr32(uint8_t *out, const uint8_t *in, size_t in_len, const uint32_t key[8], const uint32_t counter[4]); #endif diff --git a/crypto/curve25519/curve25519.c b/crypto/curve25519/curve25519.c index 7dea771e12..4e4e33aeb2 100644 --- a/crypto/curve25519/curve25519.c +++ b/crypto/curve25519/curve25519.c @@ -406,6 +406,6 @@ int X25519(uint8_t out_shared_key[32], const uint8_t private_key[32], } // The all-zero output results when the input is a point of small order. - // See https://www.rfc-editor.org/rfc/rfc7748#section-6.1. - return CRYPTO_memcmp(kZeros, out_shared_key, 32) != 0; + return constant_time_declassify_int( + CRYPTO_memcmp(kZeros, out_shared_key, 32)) != 0; } diff --git a/crypto/curve25519/curve25519_nohw.c b/crypto/curve25519/curve25519_nohw.c index c2adbaba90..3776c49c51 100644 --- a/crypto/curve25519/curve25519_nohw.c +++ b/crypto/curve25519/curve25519_nohw.c @@ -1946,7 +1946,7 @@ void x25519_scalar_mult_generic_nohw(uint8_t out[32], } void x25519_public_from_private_nohw(uint8_t out_public_value[32], - const uint8_t private_key[32]) { + const uint8_t private_key[32]) { uint8_t e[32]; OPENSSL_memcpy(e, private_key, 32); @@ -1966,4 +1966,5 @@ void x25519_public_from_private_nohw(uint8_t out_public_value[32], fe_loose_invert(&zminusy_inv, &zminusy); fe_mul_tlt(&zminusy_inv, &zplusy, &zminusy_inv); fe_tobytes(out_public_value, &zminusy_inv); + CONSTTIME_DECLASSIFY(out_public_value, 32); } diff --git a/crypto/curve25519/ed25519_test.cc b/crypto/curve25519/ed25519_test.cc index d56abe686c..0b7c585cf6 100644 --- a/crypto/curve25519/ed25519_test.cc +++ b/crypto/curve25519/ed25519_test.cc @@ -35,9 +35,15 @@ TEST(Ed25519Test, TestVectors) { ASSERT_TRUE(t->GetBytes(&expected_signature, "SIG")); ASSERT_EQ(64u, expected_signature.size()); + // Signing should not leak the private key or the message. + CONSTTIME_SECRET(private_key.data(), private_key.size()); + CONSTTIME_SECRET(message.data(), message.size()); uint8_t signature[64]; ASSERT_TRUE(ED25519_sign(signature, message.data(), message.size(), private_key.data())); + CONSTTIME_DECLASSIFY(signature, sizeof(signature)); + CONSTTIME_DECLASSIFY(message.data(), message.size()); + EXPECT_EQ(Bytes(expected_signature), Bytes(signature)); EXPECT_TRUE(ED25519_verify(message.data(), message.size(), signature, public_key.data())); @@ -114,9 +120,12 @@ TEST(Ed25519Test, KeypairFromSeed) { uint8_t seed[32]; OPENSSL_memcpy(seed, private_key1, sizeof(seed)); + CONSTTIME_SECRET(seed, sizeof(seed)); uint8_t public_key2[32], private_key2[64]; ED25519_keypair_from_seed(public_key2, private_key2, seed); + CONSTTIME_DECLASSIFY(public_key2, sizeof(public_key2)); + CONSTTIME_DECLASSIFY(private_key2, sizeof(private_key2)); EXPECT_EQ(Bytes(public_key1), Bytes(public_key2)); EXPECT_EQ(Bytes(private_key1), Bytes(private_key2)); diff --git a/crypto/curve25519/x25519_test.cc b/crypto/curve25519/x25519_test.cc index 3cea9fbb9d..f512d01ab1 100644 --- a/crypto/curve25519/x25519_test.cc +++ b/crypto/curve25519/x25519_test.cc @@ -27,9 +27,31 @@ #include "../test/wycheproof_util.h" #include "internal.h" +static inline int ctwrapX25519(uint8_t out_shared_key[32], + const uint8_t private_key[32], + const uint8_t peer_public_value[32]) { + uint8_t scalar[32], point[32]; + // Copy all the secrets into a temporary buffer, so we can run constant-time + // validation on them. + OPENSSL_memcpy(scalar, private_key, sizeof(scalar)); + OPENSSL_memcpy(point, peer_public_value, sizeof(point)); + + // X25519 should not leak the private key. + CONSTTIME_SECRET(scalar, sizeof(scalar)); + // All other inputs are also marked as secret. This is not to support any + // particular use case for calling X25519 with a secret *point*, but + // rather to ensure that the choice of the point cannot influence whether + // the scalar is leaked or not. Same for the initial contents of the + // output buffer. This conservative choice may be revised in the future. + CONSTTIME_SECRET(point, sizeof(point)); + CONSTTIME_SECRET(out_shared_key, 32); + int r = X25519(out_shared_key, scalar, point); + CONSTTIME_DECLASSIFY(out_shared_key, 32); + return r; +} TEST(X25519Test, TestVector) { - // Taken from https://tools.ietf.org/html/rfc7748#section-5.2 + // Taken from https://www.rfc-editor.org/rfc/rfc7748#section-5.2 static const uint8_t kScalar1[32] = { 0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d, 0x3b, 0x16, 0x15, 0x4b, 0x82, 0x46, 0x5e, 0xdd, 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, @@ -41,9 +63,8 @@ TEST(X25519Test, TestVector) { 0x35, 0x3b, 0x10, 0xa9, 0x03, 0xa6, 0xd0, 0xab, 0x1c, 0x4c, }; - uint8_t out[32]; - EXPECT_TRUE(X25519(out, kScalar1, kPoint1)); - + uint8_t out[32], secret[32]; + EXPECT_TRUE(ctwrapX25519(out, kScalar1, kPoint1)); static const uint8_t kExpected1[32] = { 0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90, 0x8e, 0x94, 0xea, 0x4d, 0xf2, 0x8d, 0x08, 0x4f, 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, @@ -61,15 +82,58 @@ TEST(X25519Test, TestVector) { 0x9d, 0x05, 0x38, 0xae, 0x2c, 0x31, 0xdb, 0xe7, 0x10, 0x6f, 0xc0, 0x3c, 0x3e, 0xfc, 0x4c, 0xd5, 0x49, 0xc7, 0x15, 0xa4, 0x93, }; - - EXPECT_TRUE(X25519(out, kScalar2, kPoint2)); - + EXPECT_TRUE(ctwrapX25519(out, kScalar2, kPoint2)); static const uint8_t kExpected2[32] = { 0x95, 0xcb, 0xde, 0x94, 0x76, 0xe8, 0x90, 0x7d, 0x7a, 0xad, 0xe4, 0x5c, 0xb4, 0xb8, 0x73, 0xf8, 0x8b, 0x59, 0x5a, 0x68, 0x79, 0x9f, 0xa1, 0x52, 0xe6, 0xf8, 0xf7, 0x64, 0x7a, 0xac, 0x79, 0x57, }; EXPECT_EQ(Bytes(kExpected2), Bytes(out)); + + // Taken from https://www.rfc-editor.org/rfc/rfc7748.html#section-6.1 + static const uint8_t kPrivateA[32] = { + 0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d, 0x3c, 0x16, 0xc1, + 0x72, 0x51, 0xb2, 0x66, 0x45, 0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0, + 0x99, 0x2a, 0xb1, 0x77, 0xfb, 0xa5, 0x1d, 0xb9, 0x2c, 0x2a, + }; + static const uint8_t kPublicA[32] = { + 0x85, 0x20, 0xf0, 0x09, 0x89, 0x30, 0xa7, 0x54, 0x74, 0x8b, 0x7d, + 0xdc, 0xb4, 0x3e, 0xf7, 0x5a, 0x0d, 0xbf, 0x3a, 0x0d, 0x26, 0x38, + 0x1a, 0xf4, 0xeb, 0xa4, 0xa9, 0x8e, 0xaa, 0x9b, 0x4e, 0x6a, + }; + static const uint8_t kPrivateB[32] = { + 0x5d, 0xab, 0x08, 0x7e, 0x62, 0x4a, 0x8a, 0x4b, 0x79, 0xe1, 0x7f, + 0x8b, 0x83, 0x80, 0x0e, 0xe6, 0x6f, 0x3b, 0xb1, 0x29, 0x26, 0x18, + 0xb6, 0xfd, 0x1c, 0x2f, 0x8b, 0x27, 0xff, 0x88, 0xe0, 0xeb, + }; + static const uint8_t kPublicB[32] = { + 0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4, 0xd3, 0x5b, 0x61, + 0xc2, 0xec, 0xe4, 0x35, 0x37, 0x3f, 0x83, 0x43, 0xc8, 0x5b, 0x78, + 0x67, 0x4d, 0xad, 0xfc, 0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f, + }; + static const uint8_t kSecret[32] = { + 0x4a, 0x5d, 0x9d, 0x5b, 0xa4, 0xce, 0x2d, 0xe1, 0x72, 0x8e, 0x3b, + 0xf4, 0x80, 0x35, 0x0f, 0x25, 0xe0, 0x7e, 0x21, 0xc9, 0x47, 0xd1, + 0x9e, 0x33, 0x76, 0xf0, 0x9b, 0x3c, 0x1e, 0x16, 0x17, 0x42, + }; + + OPENSSL_memcpy(secret, kPrivateA, sizeof(secret)); + CONSTTIME_SECRET(secret, sizeof(secret)); + X25519_public_from_private(out, secret); + CONSTTIME_DECLASSIFY(out, sizeof(out)); + EXPECT_EQ(Bytes(out), Bytes(kPublicA)); + + OPENSSL_memcpy(secret, kPrivateB, sizeof(secret)); + CONSTTIME_SECRET(secret, sizeof(secret)); + X25519_public_from_private(out, secret); + CONSTTIME_DECLASSIFY(out, sizeof(out)); + EXPECT_EQ(Bytes(out), Bytes(kPublicB)); + + ctwrapX25519(out, kPrivateA, kPublicB); + EXPECT_EQ(Bytes(out), Bytes(kSecret)); + + ctwrapX25519(out, kPrivateB, kPublicA); + EXPECT_EQ(Bytes(out), Bytes(kSecret)); } TEST(X25519Test, SmallOrder) { @@ -83,7 +147,7 @@ TEST(X25519Test, SmallOrder) { OPENSSL_memset(private_key, 0x11, sizeof(private_key)); OPENSSL_memset(out, 0xff, sizeof(out)); - EXPECT_FALSE(X25519(out, private_key, kSmallOrderPoint)) + EXPECT_FALSE(ctwrapX25519(out, private_key, kSmallOrderPoint)) << "X25519 returned success with a small-order input."; // For callers which don't check, |out| should still be filled with zeros. @@ -96,7 +160,7 @@ TEST(X25519Test, Iterated) { uint8_t scalar[32] = {9}, point[32] = {9}, out[32]; for (unsigned i = 0; i < 1000; i++) { - EXPECT_TRUE(X25519(out, scalar, point)); + EXPECT_TRUE(ctwrapX25519(out, scalar, point)); OPENSSL_memcpy(point, scalar, sizeof(point)); OPENSSL_memcpy(scalar, out, sizeof(scalar)); } @@ -115,7 +179,7 @@ TEST(X25519Test, DISABLED_IteratedLarge) { uint8_t scalar[32] = {9}, point[32] = {9}, out[32]; for (unsigned i = 0; i < 1000000; i++) { - EXPECT_TRUE(X25519(out, scalar, point)); + EXPECT_TRUE(ctwrapX25519(out, scalar, point)); OPENSSL_memcpy(point, scalar, sizeof(point)); OPENSSL_memcpy(scalar, out, sizeof(scalar)); } @@ -143,8 +207,9 @@ TEST(X25519Test, Wycheproof) { ASSERT_TRUE(t->GetBytes(&shared, "shared")); ASSERT_EQ(32u, priv.size()); ASSERT_EQ(32u, pub.size()); + uint8_t secret[32]; - int ret = X25519(secret, priv.data(), pub.data()); + int ret = ctwrapX25519(secret, priv.data(), pub.data()); EXPECT_EQ(ret, result.IsValid({"NonCanonicalPublic", "Twist"}) ? 1 : 0); EXPECT_EQ(Bytes(secret), Bytes(shared)); }); diff --git a/crypto/digest_extra/digest_extra.c b/crypto/digest_extra/digest_extra.c index f6f3e9ca7d..7f2a1b36da 100644 --- a/crypto/digest_extra/digest_extra.c +++ b/crypto/digest_extra/digest_extra.c @@ -86,6 +86,12 @@ static const struct nid_to_digest nid_to_digest_mapping[] = { {NID_sha512, EVP_sha512, SN_sha512, LN_sha512}, {NID_sha512_224, EVP_sha512_224, SN_sha512_224, LN_sha512_224}, {NID_sha512_256, EVP_sha512_256, SN_sha512_256, LN_sha512_256}, + {NID_sha3_224, EVP_sha3_224, SN_sha3_224, LN_sha3_224}, + {NID_sha3_256, EVP_sha3_256, SN_sha3_256, LN_sha3_256}, + {NID_sha3_384, EVP_sha3_384, SN_sha3_384, LN_sha3_384}, + {NID_sha3_512, EVP_sha3_512, SN_sha3_512, LN_sha3_512}, + {NID_shake128, EVP_shake128, SN_shake128, LN_shake128}, + {NID_shake256, EVP_shake256, SN_shake256, LN_shake256}, {NID_md5_sha1, EVP_md5_sha1, SN_md5_sha1, LN_md5_sha1}, // As a remnant of signing |EVP_MD|s, OpenSSL returned the corresponding // hash function when given a signature OID. To avoid unintended lax parsing @@ -264,6 +270,7 @@ static const EVP_MD evp_md_blake2b256 = { blake2b256_final, BLAKE2B_CBLOCK, sizeof(BLAKE2B_CTX), + /*finalXOf*/ NULL, }; const EVP_MD *EVP_blake2b256(void) { return &evp_md_blake2b256; } diff --git a/crypto/digest_extra/digest_test.cc b/crypto/digest_extra/digest_test.cc index b13599a9ca..56d980cf5f 100644 --- a/crypto/digest_extra/digest_test.cc +++ b/crypto/digest_extra/digest_test.cc @@ -46,31 +46,37 @@ struct MD { // one_shot_func is the convenience one-shot version of the // digest. uint8_t *(*one_shot_func)(const uint8_t *, size_t, uint8_t *); + // one_shot_xof_func is the convenience one-shot version of the + // digest. + uint8_t *(*one_shot_xof_func)(const uint8_t *, const size_t in_len, uint8_t *, size_t); }; -static const MD md4 = { "MD4", &EVP_md4, nullptr }; -static const MD md5 = { "MD5", &EVP_md5, &MD5 }; -static const MD ripemd160 = { "RIPEMD160", &EVP_ripemd160, &RIPEMD160 }; -static const MD sha1 = { "SHA1", &EVP_sha1, &SHA1 }; -static const MD sha224 = { "SHA224", &EVP_sha224, &SHA224 }; -static const MD sha256 = { "SHA256", &EVP_sha256, &SHA256 }; -static const MD sha384 = { "SHA384", &EVP_sha384, &SHA384 }; -static const MD sha512 = { "SHA512", &EVP_sha512, &SHA512 }; -static const MD sha512_224 = { "SHA512-224", &EVP_sha512_224, &SHA512_224 }; -static const MD sha512_256 = { "SHA512-256", &EVP_sha512_256, &SHA512_256 }; -static const MD sha3_224 = { "SHA3-224", &EVP_sha3_224, &SHA3_224 }; -static const MD sha3_256 = { "SHA3-256", &EVP_sha3_256, &SHA3_256 }; -static const MD sha3_384 = { "SHA3-384", &EVP_sha3_384, &SHA3_384 }; -static const MD sha3_512 = { "SHA3-512", &EVP_sha3_512, &SHA3_512 }; -static const MD md5_sha1 = { "MD5-SHA1", &EVP_md5_sha1, nullptr }; -static const MD blake2b256 = { "BLAKE2b-256", &EVP_blake2b256, nullptr }; +static const MD md4 = { "MD4", &EVP_md4, nullptr, nullptr }; +static const MD md5 = { "MD5", &EVP_md5, &MD5, nullptr }; +static const MD ripemd160 = { "RIPEMD160", &EVP_ripemd160, &RIPEMD160, nullptr }; +static const MD sha1 = { "SHA1", &EVP_sha1, &SHA1, nullptr }; +static const MD sha224 = { "SHA224", &EVP_sha224, &SHA224, nullptr }; +static const MD sha256 = { "SHA256", &EVP_sha256, &SHA256, nullptr }; +static const MD sha384 = { "SHA384", &EVP_sha384, &SHA384, nullptr }; +static const MD sha512 = { "SHA512", &EVP_sha512, &SHA512, nullptr }; +static const MD sha512_224 = { "SHA512-224", &EVP_sha512_224, &SHA512_224, nullptr }; +static const MD sha512_256 = { "SHA512-256", &EVP_sha512_256, &SHA512_256, nullptr }; +static const MD sha3_224 = { "SHA3-224", &EVP_sha3_224, &SHA3_224, nullptr }; +static const MD sha3_256 = { "SHA3-256", &EVP_sha3_256, &SHA3_256, nullptr }; +static const MD sha3_384 = { "SHA3-384", &EVP_sha3_384, &SHA3_384, nullptr }; +static const MD sha3_512 = { "SHA3-512", &EVP_sha3_512, &SHA3_512, nullptr }; +static const MD shake128 = { "shake128", &EVP_shake128, nullptr, &SHAKE128}; +static const MD shake256 = { "shake256", &EVP_shake256, nullptr, &SHAKE256}; +static const MD md5_sha1 = { "MD5-SHA1", &EVP_md5_sha1, nullptr, nullptr }; +static const MD blake2b256 = { "BLAKE2b-256", &EVP_blake2b256, nullptr, nullptr }; struct DigestTestVector { // md is the digest to test. const MD &md; // input is a NUL-terminated string to hash. const char *input; - // repeat is the number of times to repeat input. + // for regular digest, repeat is the number of times to repeat input. for + // XOF, it is the requested output size. size_t repeat; // expected_hex is the expected digest in hexadecimal. const char *expected_hex; @@ -224,7 +230,26 @@ static const DigestTestVector kTestVectors[] = { {sha3_512, "\xec\x83\xd7\x07\xa1\x41\x4a", 1, "84fd3775bac5b87e550d03ec6fe4905cc60e851a4c33a61858d4e7d8a34d471f05008b9a1d63044445df5a9fce958cb012a6ac778ecf45104b0fcb979aa4692d"}, {sha3_512, "\x0c\xe9\xf8\xc3\xa9\x90\xc2\x68\xf3\x4e\xfd\x9b\xef\xdb\x0f\x7c\x4e\xf8\x46\x6c\xfd\xb0\x11\x71\xf8\xde\x70\xdc\x5f\xef\xa9\x2a\xcb\xe9\x3d\x29\xe2\xac\x1a\x5c\x29\x79\x12\x9f\x1a\xb0\x8c\x0e\x77\xde\x79\x24\xdd\xf6\x8a\x20\x9c\xdf\xa0\xad\xc6\x2f\x85\xc1\x86\x37\xd9\xc6\xb3\x3f\x4f\xf8", 1, "b018a20fcf831dde290e4fb18c56342efe138472cbe142da6b77eea4fce52588c04c808eb32912faa345245a850346faec46c3a16d39bd2e1ddb1816bc57d2da"}, - + + // SHAKE128 XOF tests, from NIST. + // http://csrc.nist.gov/groups/STM/cavp/secure-hashing.html + // NOTE: the |repeat| field in this struct denotes output length for XOF digests. + {shake128, "\x84\xe9\x50\x05\x18\x76\x05\x0d\xc8\x51\xfb\xd9\x9e\x62\x47\xb8", 16, "8599bd89f63a848c49ca593ec37a12c6"}, + {shake128, "\xf1\x67\x51\x1e\xc8\x86\x49\x79\x30\x22\x37\xab\xea\x4c\xf7\xef", 17, "20f8938daa54b260860a104f8556278bac"}, + {shake128, "\x96\xdb\xe1\x83\xec\x72\x90\x57\x0b\x82\x54\x6a\xf7\x92\xeb\x90", 18, "762b421dc6374055a061caeddcf50f5dfbb6"}, + {shake128, "\x9b\xd2\xbd\x3a\x38\x4b\x9e\xf1\x41\xea\xd2\x63\x04\x96\x35\x49", 36, "3cdecb09f1673d8c823da2e02a2eeb28f32095e7c0ce8ab391811c626c472511a433845b"}, + {shake128, "\x5b\x2f\x2f\x2a\xf8\x3e\x86\xd4\x2c\x4e\x98\x15\x3f\xce\x27\x79", 37, "b6e0361dbce6d4a809a2e982f1dcffa4a49781c989402bf9c603cdacbc15484261a47b050d"}, + + + // SHAKE256 XOF tests, from NIST. + // http://csrc.nist.gov/groups/STM/cavp/secure-hashing.html + // NOTE: the |repeat| field in this struct denotes output length for XOF digests. + {shake256, "\xdc\x88\x6d\xf3\xf6\x9c\x49\x51\x3d\xe3\x62\x7e\x94\x81\xdb\x58\x71\xe8\xee\x88\xeb\x9f\x99\x61\x15\x41\x93\x0a\x8b\xc8\x85\xe0", 16, "00648afbc5e651649db1fd82936b00db"}, + {shake256, "\x8d\x80\x01\xe2\xc0\x96\xf1\xb8\x8e\x7c\x92\x24\xa0\x86\xef\xd4\x79\x7f\xbf\x74\xa8\x03\x3a\x2d\x42\x2a\x2b\x6b\x8f\x67\x47\xe4", 17, "2e975f6a8a14f0704d51b13667d8195c21"}, + {shake256, "\xe3\xef\x12\x7e\xad\xfa\xfa\xf4\x04\x08\xce\xbb\x28\x70\x5d\xf3\x0b\x68\xd9\x9d\xfa\x18\x93\x50\x7e\xf3\x06\x2d\x85\x46\x17\x15", 18, "7314002948c057006d4fc21e3e19c258fb5b"}, + {shake256, "\xdc\x88\x6d\xf3\xf6\x9c\x49\x51\x3d\xe3\x62\x7e\x94\x81\xdb\x58\x71\xe8\xee\x88\xeb\x9f\x99\x61\x15\x41\x93\x0a\x8b\xc8\x85\xe0", 36, "00648afbc5e651649db1fd82936b00dbbc122fb4c877860d385c4950d56de7e096d613d7"}, + {shake256, "\x79\x35\xb6\x8b\xb3\x34\xf3\x5d\xdc\x15\x7a\x8c\x47\x33\x49\xeb\x03\xad\x0e\x41\x53\x0d\x3c\x04\x5e\x2c\x5f\x64\x28\x50\xad\x8c", 37, "b44d25998e5cf77a83a4c0b2aae3061785adc7507d76fe07f4dcf299e04c991c922b51570f"}, + // MD5-SHA1 tests. {md5_sha1, "abc", 1, "900150983cd24fb0d6963f7d28e17f72a9993e364706816aba3e25717850c26c9cd0d89d"}, @@ -241,29 +266,44 @@ static void CompareDigest(const DigestTestVector *test, EncodeHex(bssl::MakeConstSpan(digest, digest_len))); } +static bool DoFinal(const DigestTestVector *test, EVP_MD_CTX *ctx, uint8_t *md_out, unsigned int *out_size) { + if (ctx->digest && (EVP_MD_flags(ctx->digest) & EVP_MD_FLAG_XOF)) { + // For XOF digests, DigestTestVector.repeat is the desired output length + *out_size = test->repeat; + return EVP_DigestFinalXOF(ctx, md_out, *out_size); + } + return EVP_DigestFinal(ctx, md_out, out_size); +} + static void TestDigest(const DigestTestVector *test) { SCOPED_TRACE(test->md.name); + const bool is_xof = EVP_MD_flags(test->md.func()) & EVP_MD_FLAG_XOF; + const size_t repeat = is_xof ? 1 : test->repeat; + const size_t expected_output_size = is_xof + ? test->repeat + : EVP_MD_size(test->md.func()); + bssl::ScopedEVP_MD_CTX ctx; // Test the input provided. ASSERT_TRUE(EVP_DigestInit_ex(ctx.get(), test->md.func(), nullptr)); - for (size_t i = 0; i < test->repeat; i++) { + for (size_t i = 0; i < repeat; i++) { ASSERT_TRUE(EVP_DigestUpdate(ctx.get(), test->input, strlen(test->input))); } - std::unique_ptr digest(new uint8_t[EVP_MD_size(test->md.func())]); + std::unique_ptr digest(new uint8_t[expected_output_size]); unsigned digest_len; - ASSERT_TRUE(EVP_DigestFinal_ex(ctx.get(), digest.get(), &digest_len)); + ASSERT_TRUE(DoFinal(test, ctx.get(), digest.get(), &digest_len)); CompareDigest(test, digest.get(), digest_len); // Test the input one character at a time. ASSERT_TRUE(EVP_DigestInit_ex(ctx.get(), test->md.func(), nullptr)); ASSERT_TRUE(EVP_DigestUpdate(ctx.get(), nullptr, 0)); - for (size_t i = 0; i < test->repeat; i++) { + for (size_t i = 0; i < repeat; i++) { for (const char *p = test->input; *p; p++) { ASSERT_TRUE(EVP_DigestUpdate(ctx.get(), p, 1)); } } - ASSERT_TRUE(EVP_DigestFinal_ex(ctx.get(), digest.get(), &digest_len)); - EXPECT_EQ(EVP_MD_size(test->md.func()), digest_len); + ASSERT_TRUE(DoFinal(test, ctx.get(), digest.get(), &digest_len)); + EXPECT_EQ(expected_output_size, digest_len); CompareDigest(test, digest.get(), digest_len); // Test with unaligned input. @@ -274,20 +314,20 @@ static void TestDigest(const DigestTestVector *test) { ptr++; } OPENSSL_memcpy(ptr, test->input, strlen(test->input)); - for (size_t i = 0; i < test->repeat; i++) { + for (size_t i = 0; i < repeat; i++) { ASSERT_TRUE(EVP_DigestUpdate(ctx.get(), ptr, strlen(test->input))); } - ASSERT_TRUE(EVP_DigestFinal_ex(ctx.get(), digest.get(), &digest_len)); + ASSERT_TRUE(DoFinal(test, ctx.get(), digest.get(), &digest_len)); CompareDigest(test, digest.get(), digest_len); // Make a copy of the digest in the initial state. ASSERT_TRUE(EVP_DigestInit_ex(ctx.get(), test->md.func(), nullptr)); bssl::ScopedEVP_MD_CTX copy; ASSERT_TRUE(EVP_MD_CTX_copy_ex(copy.get(), ctx.get())); - for (size_t i = 0; i < test->repeat; i++) { + for (size_t i = 0; i < repeat; i++) { ASSERT_TRUE(EVP_DigestUpdate(copy.get(), test->input, strlen(test->input))); } - ASSERT_TRUE(EVP_DigestFinal_ex(copy.get(), digest.get(), &digest_len)); + ASSERT_TRUE(DoFinal(test, copy.get(), digest.get(), &digest_len)); CompareDigest(test, digest.get(), digest_len); // Make a copy of the digest with half the input provided. @@ -296,19 +336,19 @@ static void TestDigest(const DigestTestVector *test) { ASSERT_TRUE(EVP_MD_CTX_copy_ex(copy.get(), ctx.get())); ASSERT_TRUE(EVP_DigestUpdate(copy.get(), test->input + half, strlen(test->input) - half)); - for (size_t i = 1; i < test->repeat; i++) { + for (size_t i = 1; i < repeat; i++) { ASSERT_TRUE(EVP_DigestUpdate(copy.get(), test->input, strlen(test->input))); } - ASSERT_TRUE(EVP_DigestFinal_ex(copy.get(), digest.get(), &digest_len)); + ASSERT_TRUE(DoFinal(test, copy.get(), digest.get(), &digest_len)); CompareDigest(test, digest.get(), digest_len); // Move the digest from the initial state. ASSERT_TRUE(EVP_DigestInit_ex(ctx.get(), test->md.func(), nullptr)); copy = std::move(ctx); - for (size_t i = 0; i < test->repeat; i++) { + for (size_t i = 0; i < repeat; i++) { ASSERT_TRUE(EVP_DigestUpdate(copy.get(), test->input, strlen(test->input))); } - ASSERT_TRUE(EVP_DigestFinal_ex(copy.get(), digest.get(), &digest_len)); + ASSERT_TRUE(DoFinal(test, copy.get(), digest.get(), &digest_len)); CompareDigest(test, digest.get(), digest_len); // Move the digest with half the input provided. @@ -317,20 +357,25 @@ static void TestDigest(const DigestTestVector *test) { copy = std::move(ctx); ASSERT_TRUE(EVP_DigestUpdate(copy.get(), test->input + half, strlen(test->input) - half)); - for (size_t i = 1; i < test->repeat; i++) { + for (size_t i = 1; i < repeat; i++) { ASSERT_TRUE(EVP_DigestUpdate(copy.get(), test->input, strlen(test->input))); } - ASSERT_TRUE(EVP_DigestFinal_ex(copy.get(), digest.get(), &digest_len)); + ASSERT_TRUE(DoFinal(test, copy.get(), digest.get(), &digest_len)); CompareDigest(test, digest.get(), digest_len); + // Digest context should be cleared by finalization + EXPECT_FALSE(DoFinal(test, copy.get(), digest.get(), &digest_len)); + // Test the one-shot function. - if (test->md.one_shot_func && test->repeat == 1) { - uint8_t *out = test->md.one_shot_func((const uint8_t *)test->input, - strlen(test->input), digest.get()); - // One-shot functions return their supplied buffers. - EXPECT_EQ(digest.get(), out); - CompareDigest(test, digest.get(), EVP_MD_size(test->md.func())); - } + if (is_xof || (test->md.one_shot_func && test->repeat == 1)) { + uint8_t *out = is_xof + ? test->md.one_shot_xof_func((const uint8_t *)test->input, strlen(test->input), + digest.get(), expected_output_size) + : test->md.one_shot_func((const uint8_t *)test->input, strlen(test->input), digest.get()); + // One-shot functions return their supplied buffers. + EXPECT_EQ(digest.get(), out); + CompareDigest(test, digest.get(), expected_output_size); + } } TEST(DigestTest, TestVectors) { @@ -358,6 +403,17 @@ TEST(DigestTest, Getters) { EXPECT_EQ(EVP_sha1(), EVP_get_digestbyobj(OBJ_nid2obj(NID_sha1))); } +TEST(DigestTest, TestXOF) { + // Assert that passing null outsize pointer for EVP XOF results in error. + // Use same buffer for input/output; contents don't matter. + const size_t out_size = 16; + std::unique_ptr digest(new uint8_t[out_size]); + EXPECT_FALSE(EVP_Digest(digest.get(), out_size, digest.get(), + /*out_len*/nullptr, EVP_shake128(), /*engine*/nullptr)); + EXPECT_EQ(ERR_R_PASSED_NULL_PARAMETER, ERR_GET_REASON(ERR_peek_last_error())); + ERR_clear_error(); +} + TEST(DigestTest, ASN1) { bssl::ScopedCBB cbb; ASSERT_TRUE(CBB_init(cbb.get(), 0)); diff --git a/crypto/ec_extra/hash_to_curve.c b/crypto/ec_extra/hash_to_curve.c index 5b3828c86c..7ddd41ec42 100644 --- a/crypto/ec_extra/hash_to_curve.c +++ b/crypto/ec_extra/hash_to_curve.c @@ -158,19 +158,6 @@ static int num_bytes_to_derive(size_t *out, const BIGNUM *modulus, unsigned k) { return 1; } -// big_endian_to_words decodes |in| as a big-endian integer and writes the -// result to |out|. |num_words| must be large enough to contain the output. -static void big_endian_to_words(BN_ULONG *out, size_t num_words, - const uint8_t *in, size_t len) { - assert(len <= num_words * sizeof(BN_ULONG)); - // Ensure any excess bytes are zeroed. - OPENSSL_memset(out, 0, num_words * sizeof(BN_ULONG)); - uint8_t *out_u8 = (uint8_t *)out; - for (size_t i = 0; i < len; i++) { - out_u8[len - 1 - i] = in[i]; - } -} - // hash_to_field implements the operation described in section 5.2 // of draft-irtf-cfrg-hash-to-curve-16, with count = 2. |k| is the security // factor. @@ -186,9 +173,9 @@ static int hash_to_field2(const EC_GROUP *group, const EVP_MD *md, } BN_ULONG words[2 * EC_MAX_WORDS]; size_t num_words = 2 * group->field.width; - big_endian_to_words(words, num_words, buf, L); + bn_big_endian_to_words(words, num_words, buf, L); group->meth->felem_reduce(group, out1, words, num_words); - big_endian_to_words(words, num_words, buf + L, L); + bn_big_endian_to_words(words, num_words, buf + L, L); group->meth->felem_reduce(group, out2, words, num_words); return 1; } @@ -207,7 +194,7 @@ static int hash_to_scalar(const EC_GROUP *group, const EVP_MD *md, BN_ULONG words[2 * EC_MAX_WORDS]; size_t num_words = 2 * group->order.width; - big_endian_to_words(words, num_words, buf, L); + bn_big_endian_to_words(words, num_words, buf, L); ec_scalar_reduce(group, out, words, num_words); return 1; } diff --git a/crypto/endian_test.cc b/crypto/endian_test.cc index 0f5cacdcb8..ac7a7732dd 100644 --- a/crypto/endian_test.cc +++ b/crypto/endian_test.cc @@ -159,6 +159,26 @@ TEST(EndianTest, BN_le2bn) { EXPECT_EQ((uint64_t)0x0201 << (BN_BITS2-16), x.get()->d[(256*8/BN_BITS2)-1]); } +// This test creates a BIGNUM, where 255 bytes are significant. +// Notice that 255 = 7 (mod 8) and 255 = 3 (mod 4), so the most significant +// bytes do not fill an entire word in the output BIGNUM, requiring special +// handling in the underlying logic. +TEST(EndianTest, BN_le2bn_255) { + bssl::UniquePtr x(BN_new()); + uint8_t input[255]; + OPENSSL_memset(input, 0, sizeof(input)); + input[0] = 0xaa; + input[1] = 0x01; + input[254] = 0x01; + ASSERT_TRUE(BN_le2bn(input, sizeof(input), x.get())); + EXPECT_FALSE(BN_is_zero(x.get())); + for (size_t i = 1; i <= (255/sizeof(BN_ULONG)) - 1; i++) { + EXPECT_EQ((BN_ULONG)0, x.get()->d[i]); + } + EXPECT_EQ((BN_ULONG)0x01aa, x.get()->d[0]); + EXPECT_EQ((BN_ULONG)0x01 << (BN_BITS2-16), x.get()->d[255/sizeof(BN_ULONG)]); +} + TEST(EndianTest, BN_bn2bin) { bssl::UniquePtr x(BN_new()); uint8_t input[256]; @@ -191,6 +211,46 @@ TEST(EndianTest, BN_bn2le_padded) { EXPECT_EQ(Bytes(input), Bytes(out)); } +// This test creates a BIGNUM, where 255 bytes are significant. +// It then calls |BN_bn2le_padded| to write the number into a 255-byte array +// in little-endian byte-order. +// Notice that 255 = 7 (mod 8) and 255 = 3 (mod 4), so the output array does not +// have room to hold every word of the input, requiring special handling +// in the underlying logic. +TEST(EndianTest, BN_bn2le_padded_255) { + bssl::UniquePtr x(BN_new()); + uint8_t input[255]; + OPENSSL_memset(input, 0, sizeof(input)); + input[0] = 0xaa; + input[1] = 0x01; + input[253] = 0x01; + input[254] = 0x01; + ASSERT_TRUE(BN_le2bn(input, sizeof(input), x.get())); + + uint8_t out[255]; + OPENSSL_memset(out, 0, sizeof(out)); + EXPECT_EQ(1, BN_bn2le_padded(out, sizeof(out), x.get())); + EXPECT_EQ(Bytes(input), Bytes(out)); +} + +// This test creates a 256-byte BIGNUM, where only 2 bytes are significant. +// It then calls |BN_bn2le_padded| to write the number into a 2-byte array +// in little-endian byte-order. +TEST(EndianTest, BN_bn2le_padded_much) { + bssl::UniquePtr x(BN_new()); + uint8_t input[256]; + OPENSSL_memset(input, 0, sizeof(input)); + input[0] = 0xaa; + input[1] = 0x01; + ASSERT_TRUE(BN_le2bn(input, sizeof(input), x.get())); + + uint8_t out[2]; + OPENSSL_memset(out, 0, sizeof(out)); + EXPECT_EQ(1, BN_bn2le_padded(out, sizeof(out), x.get())); + EXPECT_EQ(Bytes(input, 2), Bytes(out, 2)); +} + + TEST(EndianTest, BN_bn2bin_padded) { bssl::UniquePtr x(BN_new()); uint8_t input[256]; diff --git a/crypto/evp_extra/evp_test.cc b/crypto/evp_extra/evp_test.cc index 880278f836..c1dc70501f 100644 --- a/crypto/evp_extra/evp_test.cc +++ b/crypto/evp_extra/evp_test.cc @@ -115,6 +115,10 @@ static const EVP_MD *GetDigest(FileTest *t, const std::string &name) { return EVP_sha3_384(); } else if (name == "SHA3-512") { return EVP_sha3_512(); + } else if (name == "SHAKE128") { + return EVP_shake128(); + } else if (name == "SHAKE256") { + return EVP_shake256(); } ADD_FAILURE() << "Unknown digest: " << name; return nullptr; diff --git a/crypto/fipsmodule/CMakeLists.txt b/crypto/fipsmodule/CMakeLists.txt index 45ba034b44..db325bc60c 100644 --- a/crypto/fipsmodule/CMakeLists.txt +++ b/crypto/fipsmodule/CMakeLists.txt @@ -249,6 +249,11 @@ if((((ARCH STREQUAL "x86_64") AND NOT MY_ASSEMBLER_IS_TOO_OLD_FOR_AVX) OR fastmul/bignum_ksqr_16_32_neon.S fastmul/bignum_ksqr_32_64_neon.S fastmul/bignum_emontredc_8n_neon.S + + generic/bignum_copy_row_from_table.S + generic/bignum_copy_row_from_table_8n_neon.S + generic/bignum_copy_row_from_table_16_neon.S + generic/bignum_copy_row_from_table_32_neon.S ) endif() endif() @@ -455,12 +460,12 @@ elseif(FIPS_SHARED) # respective start and end markers. add_custom_command( OUTPUT fips_apple_start.o - COMMAND ${CMAKE_C_COMPILER} -arch ${CMAKE_SYSTEM_PROCESSOR} -isysroot ${CMAKE_OSX_SYSROOT} -c ${CMAKE_CURRENT_SOURCE_DIR}/fips_shared_library_marker.c -DAWSLC_FIPS_SHARED_START -o fips_apple_start.o + COMMAND ${CMAKE_C_COMPILER} -arch ${CMAKE_SYSTEM_PROCESSOR} -isysroot ${CMAKE_OSX_SYSROOT} -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET} -c ${CMAKE_CURRENT_SOURCE_DIR}/fips_shared_library_marker.c -DAWSLC_FIPS_SHARED_START -o fips_apple_start.o DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/fips_shared_library_marker.c ) add_custom_command( OUTPUT fips_apple_end.o - COMMAND ${CMAKE_C_COMPILER} -arch ${CMAKE_SYSTEM_PROCESSOR} -isysroot ${CMAKE_OSX_SYSROOT} -c ${CMAKE_CURRENT_SOURCE_DIR}/fips_shared_library_marker.c -DAWSLC_FIPS_SHARED_END -o fips_apple_end.o + COMMAND ${CMAKE_C_COMPILER} -arch ${CMAKE_SYSTEM_PROCESSOR} -isysroot ${CMAKE_OSX_SYSROOT} -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET} -c ${CMAKE_CURRENT_SOURCE_DIR}/fips_shared_library_marker.c -DAWSLC_FIPS_SHARED_END -o fips_apple_end.o DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/fips_shared_library_marker.c ) diff --git a/crypto/fipsmodule/bn/bn_test.cc b/crypto/fipsmodule/bn/bn_test.cc index a11d04de91..51d46921ec 100644 --- a/crypto/fipsmodule/bn/bn_test.cc +++ b/crypto/fipsmodule/bn/bn_test.cc @@ -74,6 +74,7 @@ #include #include +#include #include #include @@ -908,6 +909,14 @@ static void TestModInv(BIGNUMFileTest *t, BN_CTX *ctx) { bn_mod_inverse_consttime(ret.get(), &no_inverse, a.get(), m.get(), ctx)); EXPECT_BIGNUMS_EQUAL("inv(A) (mod M) (constant-time)", mod_inv.get(), ret.get()); + + ASSERT_TRUE(BN_copy(ret.get(), m.get())); + ASSERT_TRUE(BN_mod_inverse(ret.get(), a.get(), ret.get(), ctx)); + EXPECT_BIGNUMS_EQUAL("inv(A) (mod M) (ret == m)", mod_inv.get(), ret.get()); + + ASSERT_TRUE(BN_copy(ret.get(), a.get())); + ASSERT_TRUE(BN_mod_inverse(ret.get(), ret.get(), m.get(), ctx)); + EXPECT_BIGNUMS_EQUAL("inv(A) (mod M) (ret == a)", mod_inv.get(), ret.get()); } static void TestGCD(BIGNUMFileTest *t, BN_CTX *ctx) { @@ -2796,6 +2805,27 @@ TEST_F(BNTest, MontgomeryLarge) { ctx(), nullptr)); } +TEST_F(BNTest, FormatWord) { + char buf[32]; + snprintf(buf, sizeof(buf), BN_DEC_FMT1, BN_ULONG{1234}); + EXPECT_STREQ(buf, "1234"); + snprintf(buf, sizeof(buf), BN_HEX_FMT1, BN_ULONG{1234}); + EXPECT_STREQ(buf, "4d2"); + + // |BN_HEX_FMT2| is zero-padded up to the maximum value. +#if defined(OPENSSL_64_BIT) + snprintf(buf, sizeof(buf), BN_HEX_FMT2, BN_ULONG{1234}); + EXPECT_STREQ(buf, "00000000000004d2"); + snprintf(buf, sizeof(buf), BN_HEX_FMT2, std::numeric_limits::max()); + EXPECT_STREQ(buf, "ffffffffffffffff"); +#else + snprintf(buf, sizeof(buf), BN_HEX_FMT2, BN_ULONG{1234}); + EXPECT_STREQ(buf, "000004d2"); + snprintf(buf, sizeof(buf), BN_HEX_FMT2, std::numeric_limits::max()); + EXPECT_STREQ(buf, "ffffffff"); +#endif +} + #if defined(OPENSSL_BN_ASM_MONT) && defined(SUPPORTS_ABI_TEST) TEST_F(BNTest, BNMulMontABI) { for (size_t words : {4, 5, 6, 7, 8, 16, 32}) { diff --git a/crypto/fipsmodule/bn/bytes.c b/crypto/fipsmodule/bn/bytes.c index 76e8ddb282..97b0d3f958 100644 --- a/crypto/fipsmodule/bn/bytes.c +++ b/crypto/fipsmodule/bn/bytes.c @@ -140,27 +140,42 @@ BIGNUM *BN_le2bn(const uint8_t *in, size_t len, BIGNUM *ret) { } ret->width = (int)num_words; - // Make sure the top bytes will be zeroed. - ret->d[num_words - 1] = 0; + bn_little_endian_to_words(ret->d, ret->width, in, len); + return ret; +} + +void bn_little_endian_to_words(BN_ULONG *out, size_t out_len, const uint8_t *in, const size_t in_len) { + assert(out_len > 0); #ifdef OPENSSL_BIG_ENDIAN - BN_ULONG word = 0; - unsigned m; + size_t in_index = 0; + for (size_t i = 0; i < out_len; i++) { + if ((in_len-in_index) < sizeof(BN_ULONG)) { + // Load the last partial word. + BN_ULONG word = 0; + // size_t is unsigned, so j >= 0 is always true. + for (size_t j = in_len-1; j >= in_index && j < in_len; j--) { + word = (word << 8) | in[j]; + } + in_index = in_len; + out[i] = word; - m = (len - 1) % BN_BYTES; - // size_t is unsigned so i >= 0 is always true - for (size_t i = len - 1; i < len; i--) { - word = (word << 8) | in[i]; - if (m-- == 0) { - ret->d[--num_words] = word; - word = 0; - m = BN_BYTES - 1; + // Fill the remainder with zeros. + OPENSSL_memset(out + i + 1, 0, (out_len - i - 1) * sizeof(BN_ULONG)); + break; } + + out[i] = CRYPTO_load_word_le(in + in_index); + in_index += sizeof(BN_ULONG); } + + // The caller should have sized the output to avoid truncation. + assert(in_index == in_len); #else - OPENSSL_memcpy(ret->d, in, len); + OPENSSL_memcpy(out, in, in_len); + // Fill the remainder with zeros. + OPENSSL_memset( ((uint8_t*)out) + in_len, 0, sizeof(BN_ULONG)*out_len - in_len); #endif - return ret; } // fits_in_bytes returns one if the |num_words| words in |words| can be @@ -190,6 +205,18 @@ static int fits_in_bytes(const BN_ULONG *words, size_t num_words, return mask == 0; } +void bn_assert_fits_in_bytes(const BIGNUM *bn, size_t num) { + const uint8_t *bytes = (const uint8_t *)bn->d; + size_t tot_bytes = bn->width * sizeof(BN_ULONG); + if (tot_bytes > num) { + CONSTTIME_DECLASSIFY(bytes + num, tot_bytes - num); + for (size_t i = num; i < tot_bytes; i++) { + assert(bytes[i] == 0); + } + (void)bytes; + } +} + void bn_words_to_big_endian(uint8_t *out, size_t out_len, const BN_ULONG *in, size_t in_len) { // The caller should have selected an output length without truncation. @@ -220,27 +247,36 @@ size_t BN_bn2bin(const BIGNUM *in, uint8_t *out) { return n; } -int BN_bn2le_padded(uint8_t *out, size_t len, const BIGNUM *in) { - if (!fits_in_bytes(in->d, in->width, len)) { - return 0; - } - - size_t num_bytes = in->width * BN_BYTES; - if (len < num_bytes) { - num_bytes = len; +void bn_words_to_little_endian(uint8_t *out, size_t out_len, const BN_ULONG *in, const size_t in_len) { + // The caller should have selected an output length without truncation. + assert(fits_in_bytes(in, in_len, out_len)); + size_t num_bytes = in_len * sizeof(BN_ULONG); + if (out_len < num_bytes) { + num_bytes = out_len; } #ifdef OPENSSL_BIG_ENDIAN - BN_ULONG l; - for (size_t i = 0; i < num_bytes; i++) { - l = in->d[i / BN_BYTES]; - out[i] = (uint8_t)(l >> (8 * (i % BN_BYTES))) & 0xff; + size_t byte_idx = 0; + for (size_t word_idx = 0; word_idx < in_len; word_idx++) { + BN_ULONG l = in[word_idx]; + for(size_t j = 0; j < BN_BYTES && byte_idx < num_bytes; j++) { + out[byte_idx] = (uint8_t)(l & 0xff); + l >>= 8; + byte_idx++; + } } #else - const uint8_t *bytes = (const uint8_t *)in->d; + const uint8_t *bytes = (const uint8_t *)in; OPENSSL_memcpy(out, bytes, num_bytes); #endif - // Pad out the rest of the buffer with zeroes. - OPENSSL_memset(out + num_bytes, 0, len - num_bytes); + // Fill the remainder with zeros. + OPENSSL_memset(out + num_bytes, 0, out_len - num_bytes); +} + +int BN_bn2le_padded(uint8_t *out, size_t len, const BIGNUM *in) { + if (!fits_in_bytes(in->d, in->width, len)) { + return 0; + } + bn_words_to_little_endian(out, len, in->d, in->width); return 1; } diff --git a/crypto/fipsmodule/bn/exponentiation.c b/crypto/fipsmodule/bn/exponentiation.c index 802b498325..da4152e4cd 100644 --- a/crypto/fipsmodule/bn/exponentiation.c +++ b/crypto/fipsmodule/bn/exponentiation.c @@ -119,6 +119,52 @@ #include "internal.h" #include "rsaz_exp.h" +#if !defined(OPENSSL_NO_ASM) && \ + (defined(OPENSSL_LINUX) || defined(OPENSSL_APPLE)) && \ + defined(OPENSSL_AARCH64) + +#include "../../../third_party/s2n-bignum/include/s2n-bignum_aws-lc.h" + +#define BN_EXPONENTIATION_S2N_BIGNUM_CAPABLE 1 + +OPENSSL_INLINE int exponentiation_use_s2n_bignum(void) { return 1; } + +#else + +OPENSSL_INLINE int exponentiation_use_s2n_bignum(void) { return 0; } + +#endif + +static void exponentiation_s2n_bignum_copy_from_prebuf(BN_ULONG *dest, int width, + const BN_ULONG *table, int rowidx, + int window) { + +#if defined(BN_EXPONENTIATION_S2N_BIGNUM_CAPABLE) + + int table_height = 1 << window; + if (CRYPTO_is_NEON_capable()) { + if (width == 32) { + bignum_copy_row_from_table_32_neon(dest, table, table_height, rowidx); + } else if (width == 16) { + bignum_copy_row_from_table_16_neon(dest, table, table_height, rowidx); + } else if (width % 8 == 0) { + bignum_copy_row_from_table_8n_neon(dest, table, table_height, width, + rowidx); + } else { + bignum_copy_row_from_table(dest, table, table_height, width, rowidx); + } + } else { + bignum_copy_row_from_table(dest, table, table_height, width, rowidx); + } + +#else + + // Should not call this function unless s2n-bignum is supported. + abort(); + +#endif +} + int BN_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx) { int i, bits, ret = 0; @@ -594,7 +640,8 @@ int BN_mod_exp_mont(BIGNUM *rr, const BIGNUM *a, const BIGNUM *p, OPENSSL_PUT_ERROR(BN, BN_R_NEGATIVE_NUMBER); return 0; } - if (a->neg || BN_ucmp(a, m) >= 0) { + // |a| is secret, but |a < m| is not. + if (a->neg || constant_time_declassify_int(BN_ucmp(a, m)) >= 0) { OPENSSL_PUT_ERROR(BN, BN_R_INPUT_NOT_REDUCED); return 0; } @@ -848,6 +895,12 @@ static int copy_from_prebuf(BIGNUM *b, int top, const BN_ULONG *table, int idx, return 0; } + if (exponentiation_use_s2n_bignum()) { + exponentiation_s2n_bignum_copy_from_prebuf(b->d, top, table, idx, window); + b->width = top; + return 1; + } + OPENSSL_memset(b->d, 0, sizeof(BN_ULONG) * top); const int width = 1 << window; for (int i = 0; i < width; i++, table += top) { diff --git a/crypto/fipsmodule/bn/gcd.c b/crypto/fipsmodule/bn/gcd.c index e8cc764cf8..df12569a71 100644 --- a/crypto/fipsmodule/bn/gcd.c +++ b/crypto/fipsmodule/bn/gcd.c @@ -263,15 +263,14 @@ int BN_mod_inverse_odd(BIGNUM *out, int *out_no_inverse, const BIGNUM *a, // Now Y*a == A (mod |n|). // Y*a == 1 (mod |n|) - if (!Y->neg && BN_ucmp(Y, n) < 0) { - if (!BN_copy(R, Y)) { - goto err; - } - } else { - if (!BN_nnmod(R, Y, n, ctx)) { + if (Y->neg || BN_ucmp(Y, n) >= 0) { + if (!BN_nnmod(Y, Y, n, ctx)) { goto err; } } + if (!BN_copy(R, Y)) { + goto err; + } ret = 1; diff --git a/crypto/fipsmodule/bn/internal.h b/crypto/fipsmodule/bn/internal.h index 67a6321183..417a3eac6c 100644 --- a/crypto/fipsmodule/bn/internal.h +++ b/crypto/fipsmodule/bn/internal.h @@ -221,8 +221,8 @@ extern "C" { #define BN_GENCB_NEW_STYLE 1 #define BN_GENCB_OLD_STYLE 2 -// bn_minimal_width returns the minimal value of |bn->top| which fits the -// value of |bn|. +// bn_minimal_width returns the minimal number of words needed to represent +// |bn|. int bn_minimal_width(const BIGNUM *bn); // bn_set_minimal_width sets |bn->width| to |bn_minimal_width(bn)|. If |bn| is @@ -238,7 +238,7 @@ int bn_wexpand(BIGNUM *bn, size_t words); // than a number of words. int bn_expand(BIGNUM *bn, size_t bits); -// bn_resize_words adjusts |bn->top| to be |words|. It returns one on success +// bn_resize_words adjusts |bn->width| to be |words|. It returns one on success // and zero on allocation error or if |bn|'s value is too large. OPENSSL_EXPORT int bn_resize_words(BIGNUM *bn, size_t words); @@ -267,6 +267,12 @@ int bn_fits_in_words(const BIGNUM *bn, size_t num); // is representable in |num| words. Otherwise, it returns zero. int bn_copy_words(BN_ULONG *out, size_t num, const BIGNUM *bn); +// bn_assert_fits_in_bytes asserts that |bn| fits in |num| bytes. This is a +// no-op in release builds, but triggers an assert in debug builds, and +// declassifies all bytes which are therefore known to be zero in constant-time +// validation. +void bn_assert_fits_in_bytes(const BIGNUM *bn, size_t num); + // bn_mul_add_words multiples |ap| by |w|, adds the result to |rp|, and places // the result in |rp|. |ap| and |rp| must both be |num| words long. It returns // the carry word of the operation. |ap| and |rp| may be equal but otherwise may @@ -745,21 +751,38 @@ void bn_mod_inverse0_prime_mont_small(BN_ULONG *r, const BN_ULONG *a, // Word-based byte conversion functions. // bn_big_endian_to_words interprets |in_len| bytes from |in| as a big-endian, -// unsigned integer and writes the result to |out_len| words in |out|. |out_len| -// must be large enough to represent any |in_len|-byte value. That is, |out_len| -// must be at least |BN_BYTES * in_len|. +// unsigned integer and writes the result to |out_len| words in |out|. The output +// is in little-endian word order with |out[0]| being the least-significant word. +// |out_len| must be large enough to represent any |in_len|-byte value. That is, +// |out_len| must be at least |BN_BYTES * in_len|. void bn_big_endian_to_words(BN_ULONG *out, size_t out_len, const uint8_t *in, size_t in_len); -// bn_words_to_big_endian represents |in_len| words from |in| as a big-endian, -// unsigned integer in |out_len| bytes. It writes the result to |out|. |out_len| -// must be large enough to represent |in| without truncation. +// bn_words_to_big_endian represents |in_len| words from |in| (in little-endian +// word order) as a big-endian, unsigned integer in |out_len| bytes. It writes +// the result to |out|. |out_len| must be large enough to represent |in| without +// truncation. // // Note |out_len| may be less than |BN_BYTES * in_len| if |in| is known to have // leading zeros. void bn_words_to_big_endian(uint8_t *out, size_t out_len, const BN_ULONG *in, size_t in_len); +// bn_little_endian_to_words interprets |in_len| bytes from |in| as a little-endian, +// unsigned integer and writes the result to |out_len| words in |out|. The output +// is in little-endian word order with |out[0]| being the least-significant word. +// |out_len| must be large enough to represent any |in_len|-byte value. That is, +// |out_len| must be at least |BN_BYTES * in_len|. +void bn_little_endian_to_words(BN_ULONG *out, size_t out_len, const uint8_t *in, const size_t in_len); + +// bn_words_to_little_endian represents |in_len| words from |in| (in little-endian +// word order) as a little-endian, unsigned integer in |out_len| bytes. It +// writes the result to |out|. |out_len| must be large enough to represent |in| +// without truncation. +// +// Note |out_len| may be less than |BN_BYTES * in_len| if |in| is known to have +// leading zeros. +void bn_words_to_little_endian(uint8_t *out, size_t out_len, const BN_ULONG *in, const size_t in_len); #if defined(__cplusplus) } // extern C diff --git a/crypto/fipsmodule/digest/digest.c b/crypto/fipsmodule/digest/digest.c index 4799f2f304..433ec00f25 100644 --- a/crypto/fipsmodule/digest/digest.c +++ b/crypto/fipsmodule/digest/digest.c @@ -131,8 +131,16 @@ void EVP_MD_CTX_free(EVP_MD_CTX *ctx) { void EVP_MD_CTX_destroy(EVP_MD_CTX *ctx) { EVP_MD_CTX_free(ctx); } int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, uint8_t *out, size_t len) { - OPENSSL_PUT_ERROR(DIGEST, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); - return 0; + if (ctx->digest == NULL) { + return 0; + } + if ((EVP_MD_flags(ctx->digest) & EVP_MD_FLAG_XOF) == 0) { + OPENSSL_PUT_ERROR(DIGEST, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; + } + ctx->digest->finalXOF(ctx, out, len); + EVP_MD_CTX_cleanse(ctx); + return 1; } uint32_t EVP_MD_meth_get_flags(const EVP_MD *md) { return EVP_MD_flags(md); } @@ -245,6 +253,10 @@ int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, uint8_t *md_out, unsigned int *size) { if (ctx->digest == NULL) { return 0; } + if (EVP_MD_flags(ctx->digest) & EVP_MD_FLAG_XOF) { + OPENSSL_PUT_ERROR(DIGEST, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; + } assert(ctx->digest->md_size <= EVP_MAX_MD_SIZE); ctx->digest->final(ctx, md_out); @@ -256,7 +268,6 @@ int EVP_DigestFinal_ex(EVP_MD_CTX *ctx, uint8_t *md_out, unsigned int *size) { } int EVP_DigestFinal(EVP_MD_CTX *ctx, uint8_t *md, unsigned int *size) { - int ok = EVP_DigestFinal_ex(ctx, md, size); EVP_MD_CTX_cleanup(ctx); return ok; @@ -267,11 +278,23 @@ int EVP_Digest(const void *data, size_t count, uint8_t *out_md, EVP_MD_CTX ctx; int ret; + if ((EVP_MD_flags(type) & EVP_MD_FLAG_XOF) && out_size == NULL) { + OPENSSL_PUT_ERROR(DIGEST, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + EVP_MD_CTX_init(&ctx); ret = EVP_DigestInit_ex(&ctx, type, impl) && - EVP_DigestUpdate(&ctx, data, count) && - EVP_DigestFinal_ex(&ctx, out_md, out_size); - EVP_MD_CTX_cleanup(&ctx); + EVP_DigestUpdate(&ctx, data, count); + if (ret == 0) { + return 0; + } + + if (EVP_MD_flags(type) & EVP_MD_FLAG_XOF) { + ret &= EVP_DigestFinalXOF(&ctx, out_md, *out_size); + } else { + ret &= EVP_DigestFinal(&ctx, out_md, out_size); + } return ret; } diff --git a/crypto/fipsmodule/digest/digests.c b/crypto/fipsmodule/digest/digests.c index 95e71a22e6..648278c6d0 100644 --- a/crypto/fipsmodule/digest/digests.c +++ b/crypto/fipsmodule/digest/digests.c @@ -95,6 +95,7 @@ DEFINE_METHOD_FUNCTION(EVP_MD, EVP_md4) { out->init = md4_init; out->update = md4_update; out->final = md4_final; + out->finalXOF = NULL; out->block_size = 64; out->ctx_size = sizeof(MD4_CTX); } @@ -119,6 +120,7 @@ DEFINE_METHOD_FUNCTION(EVP_MD, EVP_md5) { out->init = md5_init; out->update = md5_update; out->final = md5_final; + out->finalXOF = NULL; out->block_size = 64; out->ctx_size = sizeof(MD5_CTX); } @@ -143,6 +145,7 @@ DEFINE_METHOD_FUNCTION(EVP_MD, EVP_ripemd160) { out->init = ripemd160_init; out->update = ripemd160_update; out->final = ripemd160_final; + out->finalXOF = NULL; out->block_size = 64; out->ctx_size = sizeof(RIPEMD160_CTX); } @@ -167,6 +170,7 @@ DEFINE_METHOD_FUNCTION(EVP_MD, EVP_sha1) { out->init = sha1_init; out->update = sha1_update; out->final = sha1_final; + out->finalXOF = NULL; out->block_size = 64; out->ctx_size = sizeof(SHA_CTX); } @@ -191,6 +195,7 @@ DEFINE_METHOD_FUNCTION(EVP_MD, EVP_sha224) { out->init = sha224_init; out->update = sha224_update; out->final = sha224_final; + out->finalXOF = NULL; out->block_size = 64; out->ctx_size = sizeof(SHA256_CTX); } @@ -215,6 +220,7 @@ DEFINE_METHOD_FUNCTION(EVP_MD, EVP_sha256) { out->init = sha256_init; out->update = sha256_update; out->final = sha256_final; + out->finalXOF = NULL; out->block_size = 64; out->ctx_size = sizeof(SHA256_CTX); } @@ -239,6 +245,7 @@ DEFINE_METHOD_FUNCTION(EVP_MD, EVP_sha384) { out->init = sha384_init; out->update = sha384_update; out->final = sha384_final; + out->finalXOF = NULL; out->block_size = 128; out->ctx_size = sizeof(SHA512_CTX); } @@ -263,6 +270,7 @@ DEFINE_METHOD_FUNCTION(EVP_MD, EVP_sha512) { out->init = sha512_init; out->update = sha512_update; out->final = sha512_final; + out->finalXOF = NULL; out->block_size = 128; out->ctx_size = sizeof(SHA512_CTX); } @@ -311,6 +319,7 @@ DEFINE_METHOD_FUNCTION(EVP_MD, EVP_sha512_256) { out->init = sha512_256_init; out->update = sha512_256_update; out->final = sha512_256_final; + out->finalXOF = NULL; out->block_size = 128; out->ctx_size = sizeof(SHA512_CTX); } @@ -335,6 +344,7 @@ DEFINE_METHOD_FUNCTION(EVP_MD, EVP_sha3_224) { out->init = sha3_224_init; out->update = sha3_224_update; out->final = sha3_224_final; + out->finalXOF = NULL; out->block_size = SHA3_BLOCKSIZE(SHA3_224_DIGEST_BITLENGTH); out->ctx_size = sizeof(KECCAK1600_CTX); } @@ -359,6 +369,7 @@ DEFINE_METHOD_FUNCTION(EVP_MD, EVP_sha3_256) { out->init = sha3_256_init; out->update = sha3_256_update; out->final = sha3_256_final; + out->finalXOF = NULL; out->block_size = SHA3_BLOCKSIZE(SHA3_256_DIGEST_BITLENGTH); out->ctx_size = sizeof(KECCAK1600_CTX); } @@ -383,6 +394,7 @@ DEFINE_METHOD_FUNCTION(EVP_MD, EVP_sha3_384) { out->init = sha3_384_init; out->update = sha3_384_update; out->final = sha3_384_final; + out->finalXOF = NULL; out->block_size = SHA3_BLOCKSIZE(SHA3_384_DIGEST_BITLENGTH); out->ctx_size = sizeof(KECCAK1600_CTX); } @@ -407,10 +419,62 @@ DEFINE_METHOD_FUNCTION(EVP_MD, EVP_sha3_512) { out->init = sha3_512_init; out->update = sha3_512_update; out->final = sha3_512_final; + out->finalXOF = NULL; out->block_size = SHA3_BLOCKSIZE(SHA3_512_DIGEST_BITLENGTH); out->ctx_size = sizeof(KECCAK1600_CTX); } + +static void shake128_init(EVP_MD_CTX *ctx) { + CHECK(SHAKE_Init(ctx->md_data, SHAKE128_BLOCKSIZE)); +} + +static void shake128_update(EVP_MD_CTX *ctx, const void *data, size_t count) { + CHECK(SHA3_Update(ctx->md_data, data, count)); +} + +static void shake128_final(EVP_MD_CTX *ctx, uint8_t *md, size_t len) { + CHECK(SHAKE_Final(md, ctx->md_data, len)); +} + +DEFINE_METHOD_FUNCTION(EVP_MD, EVP_shake128) { + out->type = NID_shake128; + out->md_size = 0; + out->flags = EVP_MD_FLAG_XOF; + out->init = shake128_init; + out->update = shake128_update; + out->final = NULL; + out->finalXOF = shake128_final; + out->block_size = SHAKE128_BLOCKSIZE; + out->ctx_size = sizeof(KECCAK1600_CTX); +} + + +static void shake256_init(EVP_MD_CTX *ctx) { + CHECK(SHAKE_Init(ctx->md_data, SHAKE256_BLOCKSIZE)); +} + +static void shake256_update(EVP_MD_CTX *ctx, const void *data, size_t count) { + CHECK(SHA3_Update(ctx->md_data, data, count)); +} + +static void shake256_finalXOF(EVP_MD_CTX *ctx, uint8_t *md, size_t len) { + CHECK(SHAKE_Final(md, ctx->md_data, len)); +} + +DEFINE_METHOD_FUNCTION(EVP_MD, EVP_shake256) { + out->type = NID_shake256; + out->md_size = 0; + out->flags = EVP_MD_FLAG_XOF; + out->init = shake256_init; + out->update = shake256_update; + out->final = NULL; + out->finalXOF = shake256_finalXOF; + out->block_size = SHAKE256_BLOCKSIZE; + out->ctx_size = sizeof(KECCAK1600_CTX); +} + + typedef struct { MD5_CTX md5; SHA_CTX sha1; @@ -441,6 +505,7 @@ DEFINE_METHOD_FUNCTION(EVP_MD, EVP_md5_sha1) { out->init = md5_sha1_init; out->update = md5_sha1_update; out->final = md5_sha1_final; + out->finalXOF = NULL; out->block_size = 64; out->ctx_size = sizeof(MD5_SHA1_CTX); } diff --git a/crypto/fipsmodule/digest/internal.h b/crypto/fipsmodule/digest/internal.h index ac3c77d1d4..7b93754f5a 100644 --- a/crypto/fipsmodule/digest/internal.h +++ b/crypto/fipsmodule/digest/internal.h @@ -90,6 +90,10 @@ struct env_md_st { // ctx_size contains the size, in bytes, of the state of the hash function. unsigned ctx_size; + + // finalXOF completes the hash and writes |len| bytes of digest extended output + // to |out|. + void (*finalXOF)(EVP_MD_CTX *ctx, uint8_t *out, size_t len); }; // evp_md_pctx_ops contains function pointers to allow the |pctx| member of diff --git a/crypto/fipsmodule/ec/ec.c b/crypto/fipsmodule/ec/ec.c index 112933f126..ea96c33741 100644 --- a/crypto/fipsmodule/ec/ec.c +++ b/crypto/fipsmodule/ec/ec.c @@ -1122,8 +1122,11 @@ int ec_point_mul_scalar_base(const EC_GROUP *group, EC_JACOBIAN *r, group->meth->mul_base(group, r, scalar); // Check the result is on the curve to defend against fault attacks or bugs. - // This has negligible cost compared to the multiplication. - if (!ec_GFp_simple_is_on_curve(group, r)) { + // This has negligible cost compared to the multiplication. This can only + // happen on bug or CPU fault, so it is okay to leak this information (if the + // computed point is on the curve or not). The alternative would be to + // proceed with bad data. + if (!constant_time_declassify_int(ec_GFp_simple_is_on_curve(group, r))) { OPENSSL_PUT_ERROR(EC, ERR_R_INTERNAL_ERROR); return 0; } diff --git a/crypto/fipsmodule/ec/ec_montgomery.c b/crypto/fipsmodule/ec/ec_montgomery.c index eeaee64ca9..78e0507699 100644 --- a/crypto/fipsmodule/ec/ec_montgomery.c +++ b/crypto/fipsmodule/ec/ec_montgomery.c @@ -177,7 +177,8 @@ void ec_GFp_mont_felem_exp(const EC_GROUP *group, EC_FELEM *out, static int ec_GFp_mont_point_get_affine_coordinates(const EC_GROUP *group, const EC_JACOBIAN *point, EC_FELEM *x, EC_FELEM *y) { - if (ec_GFp_simple_is_at_infinity(group, point)) { + if (constant_time_declassify_int( + ec_GFp_simple_is_at_infinity(group, point))) { OPENSSL_PUT_ERROR(EC, EC_R_POINT_AT_INFINITY); return 0; } @@ -317,7 +318,7 @@ void ec_GFp_mont_add(const EC_GROUP *group, EC_JACOBIAN *out, // This case will never occur in the constant-time |ec_GFp_mont_mul|. BN_ULONG is_nontrivial_double = ~xneq & ~yneq & z1nz & z2nz; - if (is_nontrivial_double) { + if (constant_time_declassify_w(is_nontrivial_double)) { ec_GFp_mont_dbl(group, out, a); return; } diff --git a/crypto/fipsmodule/ec/ec_test.cc b/crypto/fipsmodule/ec/ec_test.cc index 2ecb455d30..617488eec1 100644 --- a/crypto/fipsmodule/ec/ec_test.cc +++ b/crypto/fipsmodule/ec/ec_test.cc @@ -1886,7 +1886,6 @@ TEST(ECTest, LargeXCoordinateVectors) { bssl::UniquePtr pub_key(EC_POINT_new(group.get())); ASSERT_TRUE(pub_key); - size_t len = BN_num_bytes(&group.get()->field); // Modulus byte-length ASSERT_TRUE(EC_KEY_set_group(key.get(), group.get())); // |EC_POINT_set_affine_coordinates_GFp| sets given (x, y) according to the @@ -1900,10 +1899,10 @@ TEST(ECTest, LargeXCoordinateVectors) { // Set the raw point directly with the BIGNUM coordinates. // Note that both are in little-endian byte order. OPENSSL_memcpy(key.get()->pub_key->raw.X.words, - x.get()->d, len); + x.get()->d, BN_BYTES * group->field.width); OPENSSL_memcpy(key.get()->pub_key->raw.Y.words, - y.get()->d, len); - OPENSSL_memset(key.get()->pub_key->raw.Z.words, 0, len); + y.get()->d, BN_BYTES * group->field.width); + OPENSSL_memset(key.get()->pub_key->raw.Z.words, 0, BN_BYTES * group->field.width); key.get()->pub_key->raw.Z.words[0] = 1; // |EC_KEY_check_fips| first calls the |EC_KEY_check_key| function that @@ -1922,7 +1921,7 @@ TEST(ECTest, LargeXCoordinateVectors) { // Now replace the x-coordinate with the larger one, x+p. OPENSSL_memcpy(key.get()->pub_key->raw.X.words, - xpp.get()->d, len); + xpp.get()->d, BN_BYTES * group->field.width); // We expect |EC_KEY_check_fips| to always fail when given key with x > p. ASSERT_FALSE(EC_KEY_check_fips(key.get())); diff --git a/crypto/fipsmodule/ec/internal.h b/crypto/fipsmodule/ec/internal.h index 1a1b84dfe8..4d693f678c 100644 --- a/crypto/fipsmodule/ec/internal.h +++ b/crypto/fipsmodule/ec/internal.h @@ -491,7 +491,8 @@ struct ec_method_st { // point_get_affine_coordinates sets |*x| and |*y| to the affine coordinates // of |p|. Either |x| or |y| may be NULL to omit it. It returns one on success - // and zero if |p| is the point at infinity. + // and zero if |p| is the point at infinity. It leaks whether |p| was the + // point at infinity, but otherwise treats |p| as secret. int (*point_get_affine_coordinates)(const EC_GROUP *, const EC_JACOBIAN *p, EC_FELEM *x, EC_FELEM *y); diff --git a/crypto/fipsmodule/ec/p224-64.c b/crypto/fipsmodule/ec/p224-64.c index 9aaf2154b6..720d1bcde5 100644 --- a/crypto/fipsmodule/ec/p224-64.c +++ b/crypto/fipsmodule/ec/p224-64.c @@ -734,8 +734,8 @@ static void p224_point_add(p224_felem x3, p224_felem y3, p224_felem z3, // tmp[i] < 2^116 + 2^64 + 8 < 2^117 p224_felem_reduce(ftmp, tmp); - // the formulae are incorrect if the points are equal - // so we check for this and do doubling if this happens + // The formulae are incorrect if the points are equal, so we check for this + // and do doubling if this happens. x_equal = p224_felem_is_zero(ftmp); y_equal = p224_felem_is_zero(ftmp3); z1_is_zero = p224_felem_is_zero(z1); @@ -743,7 +743,7 @@ static void p224_point_add(p224_felem x3, p224_felem y3, p224_felem z3, // In affine coordinates, (X_1, Y_1) == (X_2, Y_2) p224_limb is_nontrivial_double = x_equal & y_equal & (1 - z1_is_zero) & (1 - z2_is_zero); - if (is_nontrivial_double) { + if (constant_time_declassify_w(is_nontrivial_double)) { p224_point_double(x3, y3, z3, x1, y1, z1); return; } @@ -862,7 +862,8 @@ static crypto_word_t p224_get_bit(const EC_SCALAR *in, size_t i) { static int ec_GFp_nistp224_point_get_affine_coordinates( const EC_GROUP *group, const EC_JACOBIAN *point, EC_FELEM *x, EC_FELEM *y) { - if (ec_GFp_simple_is_at_infinity(group, point)) { + if (constant_time_declassify_int( + ec_GFp_simple_is_at_infinity(group, point))) { OPENSSL_PUT_ERROR(EC, EC_R_POINT_AT_INFINITY); return 0; } diff --git a/crypto/fipsmodule/ec/p256-nistz.c b/crypto/fipsmodule/ec/p256-nistz.c index 0c499842ef..0229a1aaea 100644 --- a/crypto/fipsmodule/ec/p256-nistz.c +++ b/crypto/fipsmodule/ec/p256-nistz.c @@ -437,7 +437,8 @@ static void ecp_nistz256_points_mul_public(const EC_GROUP *group, static int ecp_nistz256_get_affine(const EC_GROUP *group, const EC_JACOBIAN *point, EC_FELEM *x, EC_FELEM *y) { - if (ec_GFp_simple_is_at_infinity(group, point)) { + if (constant_time_declassify_int( + ec_GFp_simple_is_at_infinity(group, point))) { OPENSSL_PUT_ERROR(EC, EC_R_POINT_AT_INFINITY); return 0; } diff --git a/crypto/fipsmodule/ec/p256.c b/crypto/fipsmodule/ec/p256.c index cd4563a419..8684d1e1b0 100644 --- a/crypto/fipsmodule/ec/p256.c +++ b/crypto/fipsmodule/ec/p256.c @@ -324,7 +324,7 @@ static void fiat_p256_point_add(fiat_p256_felem x3, fiat_p256_felem y3, fiat_p256_limb_t is_nontrivial_double = constant_time_is_zero_w(xneq | yneq) & ~constant_time_is_zero_w(z1nz) & ~constant_time_is_zero_w(z2nz); - if (is_nontrivial_double) { + if (constant_time_declassify_w(is_nontrivial_double)) { fiat_p256_point_double(x3, y3, z3, x1, y1, z1); return; } @@ -416,7 +416,8 @@ static crypto_word_t fiat_p256_get_bit(const EC_SCALAR *in, int i) { static int ec_GFp_nistp256_point_get_affine_coordinates( const EC_GROUP *group, const EC_JACOBIAN *point, EC_FELEM *x_out, EC_FELEM *y_out) { - if (ec_GFp_simple_is_at_infinity(group, point)) { + if (constant_time_declassify_int( + ec_GFp_simple_is_at_infinity(group, point))) { OPENSSL_PUT_ERROR(EC, EC_R_POINT_AT_INFINITY); return 0; } diff --git a/crypto/fipsmodule/ec/p384.c b/crypto/fipsmodule/ec/p384.c index 3ac946d8e9..42f1ee4399 100644 --- a/crypto/fipsmodule/ec/p384.c +++ b/crypto/fipsmodule/ec/p384.c @@ -98,9 +98,7 @@ static inline uint8_t p384_use_s2n_bignum_alt(void) { #define p384_felem_add(out, in0, in1) bignum_add_p384(out, in0, in1) #define p384_felem_sub(out, in0, in1) bignum_sub_p384(out, in0, in1) #define p384_felem_opp(out, in0) bignum_neg_p384(out, in0) -// TODO: convert to p384_felem_to_words #define p384_felem_to_bytes(out, in0) bignum_tolebytes_6(out, in0) -// TODO: convert to p384_felem_from_words #define p384_felem_from_bytes(out, in0) bignum_fromlebytes_6(out, in0) // The following four functions need bmi2 and adx support. @@ -134,9 +132,7 @@ static p384_limb_t p384_felem_nz(const p384_limb_t in1[P384_NLIMBS]) { #define p384_felem_sqr(out, in0) fiat_p384_square(out, in0) #define p384_felem_to_mont(out, in0) fiat_p384_to_montgomery(out, in0) #define p384_felem_from_mont(out, in0) fiat_p384_from_montgomery(out, in0) -// TODO: convert to p384_felem_to_words #define p384_felem_to_bytes(out, in0) fiat_p384_to_bytes(out, in0) -// TODO: convert to p384_felem_from_words #define p384_felem_from_bytes(out, in0) fiat_p384_from_bytes(out, in0) static p384_limb_t p384_felem_nz(const p384_limb_t in1[P384_NLIMBS]) { @@ -147,6 +143,8 @@ static p384_limb_t p384_felem_nz(const p384_limb_t in1[P384_NLIMBS]) { #endif // P384_USE_S2N_BIGNUM_FIELD_ARITH +#define P384_FELEM_BYTES (48) + static void p384_felem_copy(p384_limb_t out[P384_NLIMBS], const p384_limb_t in1[P384_NLIMBS]) { for (size_t i = 0; i < P384_NLIMBS; i++) { @@ -164,19 +162,40 @@ static void p384_felem_cmovznz(p384_limb_t out[P384_NLIMBS], } } -// NOTE: the input and output are in little-endian representation. static void p384_from_generic(p384_felem out, const EC_FELEM *in) { +#ifdef OPENSSL_BIG_ENDIAN + uint8_t tmp[P384_FELEM_BYTES]; + bn_words_to_little_endian(tmp, P384_FELEM_BYTES, in->words, P384_NLIMBS); + p384_felem_from_bytes(out, tmp); +#else p384_felem_from_bytes(out, (const uint8_t *)in->words); +#endif } -// NOTE: the input and output are in little-endian representation. static void p384_to_generic(EC_FELEM *out, const p384_felem in) { // This works because 384 is a multiple of 64, so there are no excess bytes to // zero when rounding up to |BN_ULONG|s. OPENSSL_STATIC_ASSERT( 384 / 8 == sizeof(BN_ULONG) * ((384 + BN_BITS2 - 1) / BN_BITS2), p384_felem_to_bytes_leaves_bytes_uninitialized); + +#ifdef OPENSSL_BIG_ENDIAN + uint8_t tmp[P384_FELEM_BYTES]; + p384_felem_to_bytes(tmp, in); + bn_little_endian_to_words(out->words, P384_NLIMBS, tmp, P384_FELEM_BYTES); +#else p384_felem_to_bytes((uint8_t *)out->words, in); +#endif +} + +static void p384_from_scalar(p384_felem out, const EC_SCALAR *in) { +#ifdef OPENSSL_BIG_ENDIAN + uint8_t tmp[P384_FELEM_BYTES]; + bn_words_to_little_endian(tmp, P384_FELEM_BYTES, in->words, P384_NLIMBS); + p384_felem_from_bytes(out, tmp); +#else + p384_felem_from_bytes(out, (const uint8_t *)in->words); +#endif } // p384_inv_square calculates |out| = |in|^{-2} @@ -435,7 +454,7 @@ static void p384_point_add(p384_felem x3, p384_felem y3, p384_felem z3, p384_limb_t is_nontrivial_double = constant_time_is_zero_w(xneq | yneq) & ~constant_time_is_zero_w(z1nz) & ~constant_time_is_zero_w(z2nz); - if (is_nontrivial_double) { + if (constant_time_declassify_w(is_nontrivial_double)) { p384_point_double(x3, y3, z3, x1, y1, z1); return; } @@ -483,7 +502,7 @@ static int ec_GFp_nistp384_point_get_affine_coordinates( const EC_GROUP *group, const EC_JACOBIAN *point, EC_FELEM *x_out, EC_FELEM *y_out) { - if (ec_GFp_simple_is_at_infinity(group, point)) { + if (constant_time_declassify_w(ec_GFp_simple_is_at_infinity(group, point))) { OPENSSL_PUT_ERROR(EC, EC_R_POINT_AT_INFINITY); return 0; } @@ -586,7 +605,7 @@ static int ec_GFp_nistp384_cmp_x_coordinate(const EC_GROUP *group, p384_felem_mul(Z2_mont, Z2_mont, Z2_mont); p384_felem r_Z2; - p384_felem_from_bytes(r_Z2, (const uint8_t*)r->words); // r < order < p, so this is valid. + p384_from_scalar(r_Z2, r); // r < order < p, so this is valid. p384_felem_mul(r_Z2, r_Z2, Z2_mont); p384_felem X; diff --git a/crypto/fipsmodule/ec/p521.c b/crypto/fipsmodule/ec/p521.c index e840acd922..ea5982c26e 100644 --- a/crypto/fipsmodule/ec/p521.c +++ b/crypto/fipsmodule/ec/p521.c @@ -100,9 +100,7 @@ static inline uint8_t p521_use_s2n_bignum_alt(void) { #define p521_felem_add(out, in0, in1) bignum_add_p521(out, in0, in1) #define p521_felem_sub(out, in0, in1) bignum_sub_p521(out, in0, in1) #define p521_felem_opp(out, in0) bignum_neg_p521(out, in0) -// TODO: Convert to p521_felem_to_words #define p521_felem_to_bytes(out, in0) bignum_tolebytes_p521(out, in0) -// TODO: Convert to p521_felem_from_words #define p521_felem_from_bytes(out, in0) bignum_fromlebytes_p521(out, in0) // The following two functions need bmi2 and adx support. @@ -173,13 +171,13 @@ static const p521_limb_t p521_felem_p[P521_NLIMBS] = { #define p521_felem_opp(out, in0) fiat_secp521r1_carry_opp(out, in0) #define p521_felem_mul(out, in0, in1) fiat_secp521r1_carry_mul(out, in0, in1) #define p521_felem_sqr(out, in0) fiat_secp521r1_carry_square(out, in0) -// TODO: Convert to p521_felem_to_words #define p521_felem_to_bytes(out, in0) fiat_secp521r1_to_bytes(out, in0) -// TODO: Convert to p521_felem_from_words #define p521_felem_from_bytes(out, in0) fiat_secp521r1_from_bytes(out, in0) #endif // P521_USE_S2N_BIGNUM_FIELD_ARITH +#define P521_FELEM_BYTES (66) + static p521_limb_t p521_felem_nz(const p521_limb_t in1[P521_NLIMBS]) { p521_limb_t is_not_zero = 0; for (int i = 0; i < P521_NLIMBS; i++) { @@ -220,7 +218,13 @@ static void p521_felem_cmovznz(p521_limb_t out[P521_NLIMBS], // NOTE: the input and output are in little-endian representation. static void p521_from_generic(p521_felem out, const EC_FELEM *in) { +#ifdef OPENSSL_BIG_ENDIAN + uint8_t tmp[P521_FELEM_BYTES]; + bn_words_to_little_endian(tmp, P521_FELEM_BYTES, in->words, P521_NLIMBS); + p521_felem_from_bytes(out, tmp); +#else p521_felem_from_bytes(out, (const uint8_t *)in->words); +#endif } // NOTE: the input and output are in little-endian representation. @@ -233,9 +237,15 @@ static void p521_to_generic(EC_FELEM *out, const p521_felem in) { // translate to 72 bytes, which means that we have to make sure that the // extra 6 bytes are zeroed out. To avoid confusion over 32 vs. 64 bit // systems and Fiat's vs. ours representation we zero out the whole element. +#ifdef OPENSSL_BIG_ENDIAN + uint8_t tmp[P521_FELEM_BYTES]; + p521_felem_to_bytes(tmp, in); + bn_little_endian_to_words(out->words, P521_NLIMBS, tmp, P521_FELEM_BYTES); +#else OPENSSL_memset((uint8_t*)out->words, 0, sizeof(out->words)); // Convert the element to bytes. p521_felem_to_bytes((uint8_t *)out->words, in); +#endif } // Finite field inversion using Fermat Little Theorem. @@ -451,7 +461,7 @@ static void p521_point_add(p521_felem x3, p521_felem y3, p521_felem z3, p521_limb_t is_nontrivial_double = constant_time_is_zero_w(xneq | yneq) & ~constant_time_is_zero_w(z1nz) & ~constant_time_is_zero_w(z2nz); - if (is_nontrivial_double) { + if (constant_time_declassify_w(is_nontrivial_double)) { p521_point_double(x3, y3, z3, x1, y1, z1); return; } @@ -499,7 +509,7 @@ static int ec_GFp_nistp521_point_get_affine_coordinates( const EC_GROUP *group, const EC_JACOBIAN *point, EC_FELEM *x_out, EC_FELEM *y_out) { - if (ec_GFp_simple_is_at_infinity(group, point)) { + if (constant_time_declassify_w(ec_GFp_simple_is_at_infinity(group, point))) { OPENSSL_PUT_ERROR(EC, EC_R_POINT_AT_INFINITY); return 0; } diff --git a/crypto/fipsmodule/ec/simple_mul.c b/crypto/fipsmodule/ec/simple_mul.c index d774d14162..b7ce6f1358 100644 --- a/crypto/fipsmodule/ec/simple_mul.c +++ b/crypto/fipsmodule/ec/simple_mul.c @@ -120,6 +120,9 @@ static void ec_GFp_mont_batch_get_window(const EC_GROUP *group, // Negate if necessary. EC_FELEM neg_Y; + // Initialize |out| to avoid "may be used uninitialized" warning below. + // https://github.com/aws/aws-lc/issues/1185 + OPENSSL_memset(&neg_Y, 0, sizeof(EC_FELEM)); ec_felem_neg(group, &neg_Y, &out->Y); crypto_word_t sign_mask = sign; sign_mask = 0u - sign_mask; diff --git a/crypto/fipsmodule/ecdsa/ecdsa.c b/crypto/fipsmodule/ecdsa/ecdsa.c index 9f7a4ab222..3d38ef3472 100644 --- a/crypto/fipsmodule/ecdsa/ecdsa.c +++ b/crypto/fipsmodule/ecdsa/ecdsa.c @@ -224,7 +224,7 @@ static ECDSA_SIG *ecdsa_sign_impl(const EC_GROUP *group, int *out_retry, return NULL; } - if (ec_scalar_is_zero(group, &r)) { + if (constant_time_declassify_int(ec_scalar_is_zero(group, &r))) { *out_retry = 1; return NULL; } @@ -251,11 +251,13 @@ static ECDSA_SIG *ecdsa_sign_impl(const EC_GROUP *group, int *out_retry, ec_scalar_inv0_montgomery(group, &tmp, k); // tmp = k^-1 R^2 ec_scalar_from_montgomery(group, &tmp, &tmp); // tmp = k^-1 R ec_scalar_mul_montgomery(group, &s, &s, &tmp); - if (ec_scalar_is_zero(group, &s)) { + if (constant_time_declassify_int(ec_scalar_is_zero(group, &s))) { *out_retry = 1; return NULL; } + CONSTTIME_DECLASSIFY(r.words, sizeof(r.words)); + CONSTTIME_DECLASSIFY(s.words, sizeof(r.words)); ECDSA_SIG *ret = ECDSA_SIG_new(); if (ret == NULL || // !bn_set_words(ret->r, r.words, order->width) || @@ -348,6 +350,10 @@ ECDSA_SIG *ECDSA_do_sign(const uint8_t *digest, size_t digest_len, return NULL; } + // TODO(davidben): Move this inside |ec_random_nonzero_scalar| or lower, so + // that all scalars we generate are, by default, secret. + CONSTTIME_SECRET(k.words, sizeof(k.words)); + int retry; ECDSA_SIG *sig = ecdsa_sign_impl(group, &retry, priv_key, &k, digest, digest_len); diff --git a/crypto/fipsmodule/rand/internal.h b/crypto/fipsmodule/rand/internal.h index 96b4982e96..50d3130f12 100644 --- a/crypto/fipsmodule/rand/internal.h +++ b/crypto/fipsmodule/rand/internal.h @@ -129,13 +129,10 @@ OPENSSL_INLINE int have_fast_rdrand(void) { #define MAX_BACKOFF_RETRIES 9 OPENSSL_EXPORT void HAZMAT_set_urandom_test_mode_for_testing(void); -// Whiten factor. -// Uses XOR as a mixing function to concentrate entropy. Logically, considers -// the total sourced bytes as chunks of |CTR_DRBG_ENTROPY_LEN| bytes and XORs -// them together. The mixing function is implemented in |RAND_load_entropy|. -#define PASSIVE_ENTROPY_WHITEN_FACTOR 10 -// Total number of bytes of entropy to load into FIPS module -#define PASSIVE_ENTROPY_LOAD_LENGTH (CTR_DRBG_ENTROPY_LEN * PASSIVE_ENTROPY_WHITEN_FACTOR) +// Total number of bytes of entropy to load into FIPS module. Separate constants +// that separate the logically distinct operations (1) loading entropy, and (2) +// invoking DRBG. +#define PASSIVE_ENTROPY_LOAD_LENGTH CTR_DRBG_ENTROPY_LEN #if defined(BORINGSSL_FIPS) diff --git a/crypto/fipsmodule/rand/rand.c b/crypto/fipsmodule/rand/rand.c index f93b28cfa4..0e390dc764 100644 --- a/crypto/fipsmodule/rand/rand.c +++ b/crypto/fipsmodule/rand/rand.c @@ -318,16 +318,7 @@ static int rdrand(uint8_t *buf, size_t len) { #if defined(FIPS_ENTROPY_SOURCE_PASSIVE) void RAND_load_entropy(uint8_t out_entropy[CTR_DRBG_ENTROPY_LEN], uint8_t entropy[PASSIVE_ENTROPY_LOAD_LENGTH]) { - OPENSSL_memcpy(out_entropy, entropy, CTR_DRBG_ENTROPY_LEN); - - // Whiten the resulting entropy - OPENSSL_STATIC_ASSERT(PASSIVE_ENTROPY_LOAD_LENGTH == (PASSIVE_ENTROPY_WHITEN_FACTOR * CTR_DRBG_ENTROPY_LEN), PASSIVE_ENTROPY_LOAD_LENGTH_wrong_size); - for (size_t i = 1; i < PASSIVE_ENTROPY_WHITEN_FACTOR; i++) { - for (size_t j = 0; j < CTR_DRBG_ENTROPY_LEN; j++) { - out_entropy[j] ^= entropy[CTR_DRBG_ENTROPY_LEN * i + j]; - } - } } void CRYPTO_get_seed_entropy(uint8_t entropy[PASSIVE_ENTROPY_LOAD_LENGTH], diff --git a/crypto/fipsmodule/rsa/padding.c b/crypto/fipsmodule/rsa/padding.c index 36e068ec2b..932d0f03d4 100644 --- a/crypto/fipsmodule/rsa/padding.c +++ b/crypto/fipsmodule/rsa/padding.c @@ -462,10 +462,16 @@ int RSA_padding_check_PKCS1_OAEP_mgf1(uint8_t *out, size_t *out_len, bad |= looking_for_one_byte; - if (bad) { + // Whether the overall padding was valid or not in OAEP is public. + if (constant_time_declassify_w(bad)) { goto decoding_err; } + // Once the padding is known to be valid, the output length is also public. + OPENSSL_STATIC_ASSERT(sizeof(size_t) <= sizeof(crypto_word_t), + size_t_does_not_fit_in_crypto_word_t); + one_index = constant_time_declassify_w(one_index); + one_index++; size_t mlen = dblen - one_index; if (max_out < mlen) { @@ -480,8 +486,8 @@ int RSA_padding_check_PKCS1_OAEP_mgf1(uint8_t *out, size_t *out_len, return 1; decoding_err: - // to avoid chosen ciphertext attacks, the error message should not reveal - // which kind of decoding error happened + // To avoid chosen ciphertext attacks, the error message should not reveal + // which kind of decoding error happened. OPENSSL_PUT_ERROR(RSA, RSA_R_OAEP_DECODING_ERROR); err: OPENSSL_free(db); diff --git a/crypto/fipsmodule/rsa/rsa_impl.c b/crypto/fipsmodule/rsa/rsa_impl.c index 29529d3efd..79466f16f4 100644 --- a/crypto/fipsmodule/rsa/rsa_impl.c +++ b/crypto/fipsmodule/rsa/rsa_impl.c @@ -763,6 +763,8 @@ int rsa_default_private_transform(RSA *rsa, uint8_t *out, const uint8_t *in, goto err; } + // The caller should have ensured this. + assert(len == BN_num_bytes(rsa->n)); if (BN_bin2bn(in, len, f) == NULL) { goto err; } @@ -824,16 +826,16 @@ int rsa_default_private_transform(RSA *rsa, uint8_t *out, const uint8_t *in, // works when the CRT isn't used. That attack is much less likely to succeed // than the CRT attack, but there have likely been improvements since 1997. // - // This check is cheap assuming |e| is small; it almost always is. + // This check is cheap assuming |e| is small, which we require in + // |rsa_check_public_key|. if (rsa->e != NULL) { BIGNUM *vrfy = BN_CTX_get(ctx); if (vrfy == NULL || !BN_mod_exp_mont(vrfy, result, rsa->e, rsa->n, ctx, rsa->mont_n) || - !BN_equal_consttime(vrfy, f)) { + !constant_time_declassify_int(BN_equal_consttime(vrfy, f))) { OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR); goto err; } - } if (do_blinding && @@ -847,6 +849,7 @@ int rsa_default_private_transform(RSA *rsa, uint8_t *out, const uint8_t *in, // // See Falko Strenzke, "Manger's Attack revisited", ICICS 2010. assert(result->width == rsa->mont_n->N.width); + bn_assert_fits_in_bytes(result, len); if (!BN_bn2bin_padded(out, len, result)) { OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR); goto err; @@ -965,11 +968,18 @@ static int mod_exp(BIGNUM *r0, const BIGNUM *I, RSA *rsa, BN_CTX *ctx) { // so it is correct mod q. Finally, the result is bounded by [m1, n + m1), // and the result is at least |m1|, so this must be the unique answer in // [0, n). - !bn_mul_consttime(r0, r0, q, ctx) || - !bn_uadd_consttime(r0, r0, m1) || - // The result should be bounded by |n|, but fixed-width operations may - // bound the width slightly higher, so fix it. - !bn_resize_words(r0, n->width)) { + !bn_mul_consttime(r0, r0, q, ctx) || // + !bn_uadd_consttime(r0, r0, m1)) { + goto err; + } + + // The result should be bounded by |n|, but fixed-width operations may + // bound the width slightly higher, so fix it. This trips constant-time checks + // because a naive data flow analysis does not realize the excess words are + // publicly zero. + assert(BN_cmp(r0, n) < 0); + bn_assert_fits_in_bytes(r0, BN_num_bytes(n)); + if (!bn_resize_words(r0, n->width)) { goto err; } diff --git a/crypto/fipsmodule/service_indicator/service_indicator_test.cc b/crypto/fipsmodule/service_indicator/service_indicator_test.cc index aaa41fde20..3642d5c69d 100644 --- a/crypto/fipsmodule/service_indicator/service_indicator_test.cc +++ b/crypto/fipsmodule/service_indicator/service_indicator_test.cc @@ -31,6 +31,7 @@ #include "../../test/test_util.h" #include "../bn/internal.h" #include "../rand/internal.h" +#include "../sha/internal.h" static const uint8_t kAESKey[16] = { 'A','W','S','-','L','C','C','r','y','p','t','o',' ','K', 'e','y'}; @@ -371,6 +372,50 @@ static const uint8_t kOutput_sha512_256[SHA512_256_DIGEST_LENGTH] = { 0x2d, 0xfb, 0x72, 0x35, 0xfa, 0xc1, 0xc4, 0x5f, 0x5c, 0x49, 0x91, 0x08, 0x95, 0x0b, 0x0f, 0xc9, 0x88, 0x44, 0x12, 0x01, 0x6a}; +static const uint8_t kOutput_sha3_224[SHA3_224_DIGEST_LENGTH] = { + 0xd4, 0x7e, 0x2d, 0xca, 0xf9, 0x36, 0x7a, 0x73, 0x2f, 0x9b, 0x42, 0x46, + 0x25, 0x49, 0x29, 0x68, 0xfa, 0x2c, 0xc7, 0xd0, 0xb0, 0x11, 0x1c, 0x86, + 0xa6, 0xc0, 0xa1, 0x29}; + +static const uint8_t kOutput_sha3_256[SHA3_256_DIGEST_LENGTH] = { + 0x4a, 0x95, 0x1c, 0x1e, 0xd1, 0x58, 0x5f, 0xa3, 0xcf, 0x77, 0x24, 0x73, + 0x7b, 0xd2, 0x28, 0x55, 0x9f, 0xa5, 0xe8, 0xc6, 0x58, 0x99, 0xe3, 0xb1, + 0x88, 0x17, 0xd6, 0xc4, 0x1d, 0x3e, 0xa8, 0x4c}; + +static const uint8_t kOutput_sha3_384[SHA3_384_DIGEST_LENGTH] = { + 0x19, 0x97, 0xad, 0xa6, 0x45, 0x40, 0x3d, 0x10, 0xda, 0xe6, 0xd4, 0xfd, + 0xe1, 0xd3, 0x2b, 0x1b, 0xd6, 0xdb, 0x0c, 0xdb, 0xca, 0x6f, 0xae, 0x58, + 0xbf, 0x75, 0x9a, 0xf6, 0x97, 0xc6, 0xb4, 0xb4, 0xbf, 0xef, 0x3c, 0x2d, + 0xb1, 0xb3, 0x4a, 0x1d, 0xd9, 0x69, 0x58, 0x25, 0x5b, 0xd0, 0xb6, 0xad}; + +static const uint8_t kOutput_sha3_512[SHA3_512_DIGEST_LENGTH] = { + 0x36, 0xe5, 0xa2, 0x70, 0xa4, 0xd1, 0xc3, 0x76, 0xc6, 0x44, 0xe6, 0x00, + 0x49, 0xae, 0x7d, 0x83, 0x21, 0xdc, 0xab, 0x2e, 0xa2, 0xe3, 0x96, 0xc2, + 0xeb, 0xe6, 0x61, 0x14, 0x95, 0xd6, 0x6a, 0xf2, 0xf0, 0xa0, 0x4e, 0x93, + 0x14, 0x2f, 0x02, 0x6a, 0xdb, 0xae, 0xbd, 0x76, 0x4e, 0xb9, 0x52, 0x88, + 0x85, 0x3c, 0x64, 0xa1, 0x56, 0x6f, 0xeb, 0x76, 0x25, 0x9a, 0x4a, 0x44, + 0x23, 0xf7, 0xcf, 0x46}; + +// NOTE: SHAKE is a variable-length XOF; this number is chosen somewhat +// arbitrarily for testing. +static const size_t SHAKE_OUTPUT_LENGTH = 64; + +static const uint8_t kOutput_shake128[SHAKE_OUTPUT_LENGTH] = { + 0x22, 0xfe, 0x51, 0xb7, 0x9c, 0x28, 0x1c, 0x0e, 0xfc, 0x66, 0x58, 0x6a, + 0xa1, 0x60, 0x85, 0x0b, 0xe6, 0xeb, 0x20, 0x0b, 0xdb, 0x0c, 0xe7, 0xfe, + 0x49, 0x51, 0xcd, 0xc2, 0x92, 0x3f, 0xfc, 0xf8, 0xcb, 0x4b, 0x19, 0xce, + 0x80, 0x9f, 0x1f, 0xbf, 0x10, 0xf1, 0x74, 0x38, 0x7a, 0x19, 0xd0, 0xca, + 0x52, 0xf2, 0xf3, 0xd0, 0x77, 0x08, 0xe2, 0x1e, 0x20, 0x2d, 0x57, 0x25, + 0x8b, 0xd5, 0xca, 0x66}; + +static const uint8_t kOutput_shake256[SHAKE_OUTPUT_LENGTH] = { + 0xfc, 0xd1, 0x32, 0xd0, 0x02, 0x43, 0x7c, 0x31, 0xb2, 0x78, 0xdf, 0x34, + 0x74, 0xc8, 0x9b, 0x77, 0x08, 0x14, 0x9d, 0xde, 0x69, 0x79, 0xb5, 0x58, + 0x98, 0x01, 0x69, 0xaa, 0x64, 0x11, 0x04, 0xbe, 0xa2, 0x5f, 0xf1, 0x29, + 0x9b, 0x94, 0x03, 0x4a, 0x1e, 0x82, 0xf0, 0x9e, 0xee, 0x9b, 0xa0, 0xe3, + 0xe1, 0x5f, 0x9c, 0x13, 0xb7, 0x52, 0xef, 0x3c, 0x96, 0xf3, 0xf8, 0xf3, + 0x1f, 0x59, 0x7e, 0x41}; + static const uint8_t kHMACOutput_sha1[SHA_DIGEST_LENGTH] = { 0x34, 0xac, 0x50, 0x9b, 0xa9, 0x4c, 0x39, 0xef, 0x45, 0xa0, 0x6b, 0xdc, 0xfc, 0xbd, 0x3d, 0x42, 0xe8, 0x0a, 0x97, 0x86}; @@ -1082,6 +1127,38 @@ static const struct DigestTestVector { kOutput_sha512_256, AWSLC_APPROVED, }, + { + "SHA3-224", + SHA3_224_DIGEST_LENGTH, + &EVP_sha3_224, + &SHA3_224, + kOutput_sha3_224, + AWSLC_APPROVED, + }, + { + "SHA3-256", + SHA3_256_DIGEST_LENGTH, + &EVP_sha3_256, + &SHA3_256, + kOutput_sha3_256, + AWSLC_APPROVED, + }, + { + "SHA3-384", + SHA3_384_DIGEST_LENGTH, + &EVP_sha3_384, + &SHA3_384, + kOutput_sha3_384, + AWSLC_APPROVED, + }, + { + "SHA3-512", + SHA3_512_DIGEST_LENGTH, + &EVP_sha3_512, + &SHA3_512, + kOutput_sha3_512, + AWSLC_APPROVED, + }, }; class EVPMDServiceIndicatorTest : public TestWithNoErrors {}; @@ -1128,6 +1205,75 @@ TEST_P(EVPMDServiceIndicatorTest, EVP_Digests) { EXPECT_EQ(Bytes(test.expected_digest, test.length), Bytes(digest)); } +static const struct XofTestVector { + // name is the name of the digest test. + const char *name; + // output length to specify in XOF finalization + const int length; + // func is the digest to test. + const EVP_MD *(*func)(); + // one_shot_func is the convenience one-shot version of the digest. + uint8_t *(*one_shot_func)(const uint8_t *, size_t, uint8_t *, size_t); + // expected_digest is the expected digest. + const uint8_t *expected_digest; + // expected to be approved or not. + const FIPSStatus expect_approved; +} kXofTestVectors[] = { + { + "SHAKE128", + SHAKE_OUTPUT_LENGTH, + &EVP_shake128, + &SHAKE128, + kOutput_shake128, + AWSLC_APPROVED, + }, + { + "SHAKE256", + SHAKE_OUTPUT_LENGTH, + &EVP_shake256, + &SHAKE256, + kOutput_shake256, + AWSLC_APPROVED, + }, +}; + +class EVPXOFServiceIndicatorTest : public TestWithNoErrors {}; + +INSTANTIATE_TEST_SUITE_P(All, EVPXOFServiceIndicatorTest, + testing::ValuesIn(kXofTestVectors)); + +TEST_P(EVPXOFServiceIndicatorTest, EVP_Xofs) { + const XofTestVector &test = GetParam(); + SCOPED_TRACE(test.name); + + FIPSStatus approved = AWSLC_NOT_APPROVED; + bssl::ScopedEVP_MD_CTX ctx; + std::vector digest(test.length); + + // Test running the EVP_Digest interfaces one by one directly, and check + // |EVP_DigestFinalXOF| for approval at the end. |EVP_DigestInit_ex| and + // |EVP_DigestUpdate| should not be approved, because the functions do not + // indicate that a service has been fully completed yet. + CALL_SERVICE_AND_CHECK_APPROVED(approved, + ASSERT_TRUE(EVP_DigestInit_ex(ctx.get(), test.func(), nullptr))); + EXPECT_EQ(approved, AWSLC_NOT_APPROVED); + CALL_SERVICE_AND_CHECK_APPROVED(approved, + ASSERT_TRUE(EVP_DigestUpdate(ctx.get(), kPlaintext, sizeof(kPlaintext)))); + EXPECT_EQ(approved, AWSLC_NOT_APPROVED); + EXPECT_TRUE(EVP_MD_flags(ctx->digest) & EVP_MD_FLAG_XOF); + CALL_SERVICE_AND_CHECK_APPROVED(approved, + ASSERT_TRUE(EVP_DigestFinalXOF(ctx.get(), digest.data(), test.length))); + EXPECT_EQ(approved, test.expect_approved); + EXPECT_EQ(Bytes(test.expected_digest, test.length), Bytes(digest)); + + // Test using the one-shot API for approval. + CALL_SERVICE_AND_CHECK_APPROVED( + approved, + test.one_shot_func(kPlaintext, sizeof(kPlaintext), digest.data(), test.length)); + EXPECT_EQ(approved, test.expect_approved); + EXPECT_EQ(Bytes(test.expected_digest, test.length), Bytes(digest)); +} + static const struct HMACTestVector { // func is the hash function for HMAC to test. const EVP_MD *(*func)(void); @@ -1910,12 +2056,17 @@ struct RSATestVector kRSATestVectors[] = { // RSA test cases that are approved. { 1024, &EVP_sha1, false, AWSLC_NOT_APPROVED, AWSLC_APPROVED }, + { 1024, &EVP_sha224, false, AWSLC_NOT_APPROVED, AWSLC_APPROVED }, { 1024, &EVP_sha256, false, AWSLC_NOT_APPROVED, AWSLC_APPROVED }, + { 1024, &EVP_sha384, false, AWSLC_NOT_APPROVED, AWSLC_APPROVED }, { 1024, &EVP_sha512, false, AWSLC_NOT_APPROVED, AWSLC_APPROVED }, { 1024, &EVP_sha1, true, AWSLC_NOT_APPROVED, AWSLC_APPROVED }, + { 1024, &EVP_sha224, true, AWSLC_NOT_APPROVED, AWSLC_APPROVED }, { 1024, &EVP_sha256, true, AWSLC_NOT_APPROVED, AWSLC_APPROVED }, + { 1024, &EVP_sha384, true, AWSLC_NOT_APPROVED, AWSLC_APPROVED }, + { 1024, &EVP_sha512_256, true, AWSLC_NOT_APPROVED, AWSLC_APPROVED }, // PSS with hashLen == saltLen is not possible for 1024-bit modulus and - // SHA-512. + // SHA-512. This means we can't test it here because the API won't work. { 2048, &EVP_sha1, false, AWSLC_NOT_APPROVED, AWSLC_APPROVED }, { 2048, &EVP_sha224, false, AWSLC_APPROVED, AWSLC_APPROVED }, diff --git a/crypto/fipsmodule/sha/internal.h b/crypto/fipsmodule/sha/internal.h index 014143d14b..caeac4a976 100644 --- a/crypto/fipsmodule/sha/internal.h +++ b/crypto/fipsmodule/sha/internal.h @@ -126,6 +126,14 @@ OPENSSL_EXPORT uint8_t *SHAKE128(const uint8_t *data, const size_t in_len, OPENSSL_EXPORT uint8_t *SHAKE256(const uint8_t *data, const size_t in_len, uint8_t *out, size_t out_len); +// SHAKE_Init initializes |ctx| with specified |block_size|, returns 1 on +// success and 0 on failure. Calls SHA3_Init under the hood. +OPENSSL_EXPORT int SHAKE_Init(KECCAK1600_CTX *ctx, size_t block_size); + +// SHAKE_Final writes |len| bytes of finalized digest to |md|, returns 1 on +// success and 0 on failure. Calls SHA3_Final under the hood. +OPENSSL_EXPORT int SHAKE_Final(uint8_t *md, KECCAK1600_CTX *ctx, size_t len); + // SHA3_Reset zeros the bitstate and the amount of processed input. OPENSSL_EXPORT void SHA3_Reset(KECCAK1600_CTX *ctx); diff --git a/crypto/fipsmodule/sha/sha3.c b/crypto/fipsmodule/sha/sha3.c index 7bab5ebd3b..bd2076a415 100644 --- a/crypto/fipsmodule/sha/sha3.c +++ b/crypto/fipsmodule/sha/sha3.c @@ -13,96 +13,116 @@ uint8_t *SHA3_224(const uint8_t *data, size_t len, uint8_t out[SHA3_224_DIGEST_LENGTH]) { + FIPS_service_indicator_lock_state(); KECCAK1600_CTX ctx; int ok = (SHA3_Init(&ctx, SHA3_PAD_CHAR, SHA3_224_DIGEST_BITLENGTH) && SHA3_Update(&ctx, data, len) && SHA3_Final(out, &ctx)); OPENSSL_cleanse(&ctx, sizeof(ctx)); + FIPS_service_indicator_unlock_state(); if (ok == 0) { return NULL; } + FIPS_service_indicator_update_state(); return out; } uint8_t *SHA3_256(const uint8_t *data, size_t len, uint8_t out[SHA3_256_DIGEST_LENGTH]) { + FIPS_service_indicator_lock_state(); KECCAK1600_CTX ctx; int ok = (SHA3_Init(&ctx, SHA3_PAD_CHAR, SHA3_256_DIGEST_BITLENGTH) && SHA3_Update(&ctx, data, len) && SHA3_Final(out, &ctx)); OPENSSL_cleanse(&ctx, sizeof(ctx)); + FIPS_service_indicator_unlock_state(); if (ok == 0) { return NULL; } + FIPS_service_indicator_update_state(); return out; } uint8_t *SHA3_384(const uint8_t *data, size_t len, uint8_t out[SHA3_384_DIGEST_LENGTH]) { + FIPS_service_indicator_lock_state(); KECCAK1600_CTX ctx; int ok = (SHA3_Init(&ctx, SHA3_PAD_CHAR, SHA3_384_DIGEST_BITLENGTH) && SHA3_Update(&ctx, data, len) && SHA3_Final(out, &ctx)); OPENSSL_cleanse(&ctx, sizeof(ctx)); + FIPS_service_indicator_unlock_state(); if (ok == 0) { return NULL; } + FIPS_service_indicator_update_state(); return out; } uint8_t *SHA3_512(const uint8_t *data, size_t len, uint8_t out[SHA3_512_DIGEST_LENGTH]) { + FIPS_service_indicator_lock_state(); KECCAK1600_CTX ctx; int ok = (SHA3_Init(&ctx, SHA3_PAD_CHAR, SHA3_512_DIGEST_BITLENGTH) && SHA3_Update(&ctx, data, len) && SHA3_Final(out, &ctx)); OPENSSL_cleanse(&ctx, sizeof(ctx)); + FIPS_service_indicator_unlock_state(); if (ok == 0) { return NULL; } + FIPS_service_indicator_update_state(); return out; } uint8_t *SHAKE128(const uint8_t *data, const size_t in_len, uint8_t *out, size_t out_len) { + FIPS_service_indicator_lock_state(); KECCAK1600_CTX ctx; - - // The SHAKE block size depends on the security level of the algorithm only - // It is independent of the output size - ctx.block_size = SHAKE128_BLOCKSIZE; - - int ok = (SHA3_Init(&ctx, SHAKE_PAD_CHAR, out_len) && + int ok = (SHAKE_Init(&ctx, SHAKE128_BLOCKSIZE) && SHA3_Update(&ctx, data, in_len) && - SHA3_Final(out, &ctx)); + SHAKE_Final(out, &ctx, out_len)); OPENSSL_cleanse(&ctx, sizeof(ctx)); + FIPS_service_indicator_unlock_state(); if (ok == 0) { return NULL; } + FIPS_service_indicator_update_state(); return out; } uint8_t *SHAKE256(const uint8_t *data, const size_t in_len, uint8_t *out, size_t out_len) { + FIPS_service_indicator_lock_state(); KECCAK1600_CTX ctx; - - // The SHAKE block size depends on the security level of the algorithm only - // It is independent of the output size - ctx.block_size = SHAKE256_BLOCKSIZE; - - int ok = (SHA3_Init(&ctx, SHAKE_PAD_CHAR, out_len) && + int ok = (SHAKE_Init(&ctx, SHAKE256_BLOCKSIZE) && SHA3_Update(&ctx, data, in_len) && - SHA3_Final(out, &ctx)); - + SHAKE_Final(out, &ctx, out_len)); OPENSSL_cleanse(&ctx, sizeof(ctx)); + FIPS_service_indicator_unlock_state(); if (ok == 0) { return NULL; } + FIPS_service_indicator_update_state(); return out; } +int SHAKE_Init(KECCAK1600_CTX *ctx, size_t block_size) { + // The SHAKE block size depends on the security level of the algorithm only + // It is independent of the output size + ctx->block_size = block_size; + return SHA3_Init(ctx, SHAKE_PAD_CHAR, 0); +} + + +int SHAKE_Final(uint8_t *md, KECCAK1600_CTX *ctx, size_t len) { + ctx->md_size = len; + return SHA3_Final(md, ctx); +} + void SHA3_Reset(KECCAK1600_CTX *ctx) { memset(ctx->A, 0, sizeof(ctx->A)); ctx->buf_load = 0; @@ -202,5 +222,7 @@ int SHA3_Final(uint8_t *md, KECCAK1600_CTX *ctx) { SHA3_Squeeze(ctx->A, md, ctx->md_size, block_size); + FIPS_service_indicator_update_state(); + return 1; } diff --git a/crypto/fipsmodule/sha/sha3_test.cc b/crypto/fipsmodule/sha/sha3_test.cc index 0d1be686a3..cb39fc2f66 100644 --- a/crypto/fipsmodule/sha/sha3_test.cc +++ b/crypto/fipsmodule/sha/sha3_test.cc @@ -52,24 +52,24 @@ class SHA3TestVector { OPENSSL_free(ctx); } - void NISTTestVectors_SHAKE128() const { + void NISTTestVectors_SHAKE(const EVP_MD *algorithm) const { uint32_t digest_length = out_len_ / 8; std::unique_ptr digest(new uint8_t[digest_length]); + EVP_MD_CTX* ctx = EVP_MD_CTX_new(); - ASSERT_TRUE(SHAKE128(msg_.data(), msg_.size() , digest.get(), out_len_)); - - ASSERT_EQ(Bytes(digest.get(), out_len_ / 8), - Bytes(digest_.data(), out_len_ / 8)); - } - - void NISTTestVectors_SHAKE256() const { - uint32_t digest_length = out_len_ / 8; - std::unique_ptr digest(new uint8_t[digest_length]); + // Test the incremental EVP API + ASSERT_TRUE(EVP_DigestInit(ctx, algorithm)); + ASSERT_TRUE(EVP_DigestUpdate(ctx, msg_.data(), msg_.size())); + ASSERT_TRUE(EVP_DigestFinalXOF(ctx, digest.get(), digest_length)); + EXPECT_EQ(Bytes(digest.get(), digest_length), + Bytes(digest_.data(), digest_length)); - ASSERT_TRUE(SHAKE256(msg_.data(), msg_.size() , digest.get(), out_len_)); + // Test the one-shot + ASSERT_TRUE(EVP_Digest(msg_.data(), msg_.size(), digest.get(), &digest_length, algorithm, NULL)); + EXPECT_EQ(Bytes(digest.get(), digest_length), + Bytes(digest_.data(), digest_length)); - ASSERT_EQ(Bytes(digest.get(), out_len_ / 8), - Bytes(digest_.data(), out_len_ / 8)); + OPENSSL_free(ctx); } private: @@ -183,18 +183,15 @@ TEST(KeccakInternalTest, SqueezeOutputBufferOverflow) { EVP_MD_unstable_sha3_enable(false); } -TEST(SHAKE128Test, NISTTestVectors) { +TEST(SHAKETest, NISTTestVectors) { FileTestGTest("crypto/fipsmodule/sha/testvectors/SHAKE128VariableOut.txt", [](FileTest *t) { SHA3TestVector test_vec; EXPECT_TRUE(test_vec.ReadFromFileTest(t)); - test_vec.NISTTestVectors_SHAKE128(); + test_vec.NISTTestVectors_SHAKE(EVP_shake128()); }); -} - -TEST(SHAKE256Test, NISTTestVectors) { FileTestGTest("crypto/fipsmodule/sha/testvectors/SHAKE256VariableOut.txt", [](FileTest *t) { SHA3TestVector test_vec; EXPECT_TRUE(test_vec.ReadFromFileTest(t)); - test_vec.NISTTestVectors_SHAKE256(); + test_vec.NISTTestVectors_SHAKE(EVP_shake256()); }); } diff --git a/crypto/fipsmodule/sshkdf/sshkdf.c b/crypto/fipsmodule/sshkdf/sshkdf.c index 90e9580c47..b95cd9c6b5 100644 --- a/crypto/fipsmodule/sshkdf/sshkdf.c +++ b/crypto/fipsmodule/sshkdf/sshkdf.c @@ -23,7 +23,7 @@ int SSHKDF(const EVP_MD *evp_md, { EVP_MD_CTX *md = NULL; uint8_t digest[EVP_MAX_MD_SIZE]; - size_t digest_size = 0; + unsigned int digest_size = 0; size_t cursize = 0; int ret = 0; @@ -72,7 +72,7 @@ int SSHKDF(const EVP_MD *evp_md, goto out; } - if (!EVP_DigestFinal_ex(md, digest, (unsigned int *)&digest_size)) { + if (!EVP_DigestFinal_ex(md, digest, &digest_size)) { goto out; } diff --git a/crypto/internal.h b/crypto/internal.h index a0b958d5a6..295f1dd971 100644 --- a/crypto/internal.h +++ b/crypto/internal.h @@ -445,20 +445,44 @@ static inline int constant_time_select_int(crypto_word_t mask, int a, int b) { // of memory as secret. Secret data is tracked as it flows to registers and // other parts of a memory. If secret data is used as a condition for a branch, // or as a memory index, it will trigger warnings in valgrind. -#define CONSTTIME_SECRET(x, y) VALGRIND_MAKE_MEM_UNDEFINED(x, y) +#define CONSTTIME_SECRET(ptr, len) VALGRIND_MAKE_MEM_UNDEFINED(ptr, len) // CONSTTIME_DECLASSIFY takes a pointer and a number of bytes and marks that // region of memory as public. Public data is not subject to constant-time // rules. -#define CONSTTIME_DECLASSIFY(x, y) VALGRIND_MAKE_MEM_DEFINED(x, y) +#define CONSTTIME_DECLASSIFY(ptr, len) VALGRIND_MAKE_MEM_DEFINED(ptr, len) #else -#define CONSTTIME_SECRET(x, y) -#define CONSTTIME_DECLASSIFY(x, y) +#define CONSTTIME_SECRET(ptr, len) +#define CONSTTIME_DECLASSIFY(ptr, len) #endif // BORINGSSL_CONSTANT_TIME_VALIDATION +static inline crypto_word_t constant_time_declassify_w(crypto_word_t v) { + // Return |v| through a value barrier to be safe. Valgrind-based constant-time + // validation is partly to check the compiler has not undone any constant-time + // work. Any place |BORINGSSL_CONSTANT_TIME_VALIDATION| influences + // optimizations, this validation is inaccurate. + // + // However, by sending pointers through valgrind, we likely inhibit escape + // analysis. On local variables, particularly booleans, we likely + // significantly impact optimizations. + // + // Thus, to be safe, stick a value barrier, in hopes of comparably inhibiting + // compiler analysis. + CONSTTIME_DECLASSIFY(&v, sizeof(v)); + return value_barrier_w(v); +} + +static inline int constant_time_declassify_int(int v) { + OPENSSL_STATIC_ASSERT(sizeof(uint32_t) == sizeof(int), + int_is_not_the_same_size_as_uint32_t); + // See comment above. + CONSTTIME_DECLASSIFY(&v, sizeof(v)); + return value_barrier_u32(v); +} + // Thread-safe initialisation. diff --git a/crypto/obj/obj_dat.h b/crypto/obj/obj_dat.h index 9765fd9462..982564af50 100644 --- a/crypto/obj/obj_dat.h +++ b/crypto/obj/obj_dat.h @@ -56,7 +56,7 @@ /* This file is generated by crypto/obj/objects.go. */ -#define NUM_NID 980 +#define NUM_NID 981 static const uint8_t kObjectData[] = { /* NID_rsadsi */ @@ -7198,6 +7198,26 @@ static const uint8_t kObjectData[] = { 0x04, 0x02, 0x05, + /* NID_shake128 */ + 0x60, + 0x86, + 0x48, + 0x01, + 0x65, + 0x03, + 0x04, + 0x02, + 0x0b, + /* NID_shake256 */ + 0x60, + 0x86, + 0x48, + 0x01, + 0x65, + 0x03, + 0x04, + 0x02, + 0x0c, }; static const ASN1_OBJECT kObjects[NUM_NID] = { @@ -8860,8 +8880,9 @@ static const ASN1_OBJECT kObjects[NUM_NID] = { &kObjectData[6232], 0}, {"ffdhe2048", "ffdhe2048", NID_ffdhe2048, 0, NULL, 0}, {"ffdhe4096", "ffdhe4096", NID_ffdhe4096, 0, NULL, 0}, - {NULL, NULL, NID_undef, 0, NULL, 0}, {"SHA512-224", "sha512-224", NID_sha512_224, 9, &kObjectData[6243], 0}, + {"SHAKE128", "shake128", NID_shake128, 9, &kObjectData[6252], 0}, + {"SHAKE256", "shake256", NID_shake256, 9, &kObjectData[6261], 0}, }; static const uint16_t kNIDsInShortNameOrder[] = { @@ -9063,8 +9084,10 @@ static const uint16_t kNIDsInShortNameOrder[] = { 968 /* SHA3-512 */, 673 /* SHA384 */, 674 /* SHA512 */, - 979 /* SHA512-224 */, + 978 /* SHA512-224 */, 962 /* SHA512-256 */, + 979 /* SHAKE128 */, + 980 /* SHAKE256 */, 188 /* SMIME */, 167 /* SMIME-CAPS */, 100 /* SN */, @@ -10754,10 +10777,12 @@ static const uint16_t kNIDsInLongNameOrder[] = { 673 /* sha384 */, 669 /* sha384WithRSAEncryption */, 674 /* sha512 */, - 979 /* sha512-224 */, + 978 /* sha512-224 */, 962 /* sha512-256 */, 670 /* sha512WithRSAEncryption */, 42 /* shaWithRSAEncryption */, + 979 /* shake128 */, + 980 /* shake256 */, 52 /* signingTime */, 454 /* simpleSecurityObject */, 496 /* singleLevelQuality */, @@ -10809,8 +10834,8 @@ static const uint16_t kNIDsInLongNameOrder[] = { static const uint16_t kNIDsInOIDOrder[] = { 434 /* 0.9 (OBJ_data) */, 182 /* 1.2 (OBJ_member_body) */, - 379 /* 1.3 (OBJ_org) */, 676 /* 1.3 (OBJ_identified_organization) */, + 379 /* 1.3 (OBJ_org) */, 11 /* 2.5 (OBJ_X500) */, 647 /* 2.23 (OBJ_international_organizations) */, 380 /* 1.3.6 (OBJ_dod) */, @@ -11515,12 +11540,14 @@ static const uint16_t kNIDsInOIDOrder[] = { 673 /* 2.16.840.1.101.3.4.2.2 (OBJ_sha384) */, 674 /* 2.16.840.1.101.3.4.2.3 (OBJ_sha512) */, 675 /* 2.16.840.1.101.3.4.2.4 (OBJ_sha224) */, - 979 /* 2.16.840.1.101.3.4.2.5 (OBJ_sha512_224) */, + 978 /* 2.16.840.1.101.3.4.2.5 (OBJ_sha512_224) */, 962 /* 2.16.840.1.101.3.4.2.6 (OBJ_sha512_256) */, 965 /* 2.16.840.1.101.3.4.2.7 (OBJ_sha3_224) */, 966 /* 2.16.840.1.101.3.4.2.8 (OBJ_sha3_256) */, 967 /* 2.16.840.1.101.3.4.2.9 (OBJ_sha3_384) */, 968 /* 2.16.840.1.101.3.4.2.10 (OBJ_sha3_512) */, + 979 /* 2.16.840.1.101.3.4.2.11 (OBJ_shake128) */, + 980 /* 2.16.840.1.101.3.4.2.12 (OBJ_shake256) */, 802 /* 2.16.840.1.101.3.4.3.1 (OBJ_dsa_with_SHA224) */, 803 /* 2.16.840.1.101.3.4.3.2 (OBJ_dsa_with_SHA256) */, 71 /* 2.16.840.1.113730.1.1 (OBJ_netscape_cert_type) */, diff --git a/crypto/obj/obj_mac.num b/crypto/obj/obj_mac.num index 1de1519e0b..25d96d4084 100644 --- a/crypto/obj/obj_mac.num +++ b/crypto/obj/obj_mac.num @@ -965,4 +965,6 @@ KYBER1024_R3 974 DILITHIUM3_R3 975 ffdhe2048 976 ffdhe4096 977 -sha512_224 979 +sha512_224 978 +shake128 979 +shake256 980 diff --git a/crypto/obj/objects.txt b/crypto/obj/objects.txt index 812c9969b7..a0d2bf794d 100644 --- a/crypto/obj/objects.txt +++ b/crypto/obj/objects.txt @@ -1369,6 +1369,8 @@ nist_sha3hashalgs 7 : SHA3-224 : sha3-224 nist_sha3hashalgs 8 : SHA3-256 : sha3-256 nist_sha3hashalgs 9 : SHA3-384 : sha3-384 nist_sha3hashalgs 10 : SHA3-512 : sha3-512 +nist_sha3hashalgs 11 : SHAKE128 : shake128 +nist_sha3hashalgs 12 : SHAKE256 : shake256 : HKDF : hkdf diff --git a/crypto/rand_extra/rand_test.cc b/crypto/rand_extra/rand_test.cc index 03360f37b6..8c3246218d 100644 --- a/crypto/rand_extra/rand_test.cc +++ b/crypto/rand_extra/rand_test.cc @@ -236,75 +236,12 @@ TEST(RandTest, RdrandABI) { TEST(RandTest, PassiveEntropyLoad) { uint8_t out_entropy[CTR_DRBG_ENTROPY_LEN] = {0}; uint8_t entropy[PASSIVE_ENTROPY_LOAD_LENGTH] = { - 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, - 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, - 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, - 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, - 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, - 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, - - 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, - 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, - 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, - 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, - 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, - 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, - - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, - 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, - 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, - 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, - 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, - 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, - - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, - - 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, - 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, - 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, - 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, - 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, - 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, }; uint8_t expected_out_entropy[CTR_DRBG_ENTROPY_LEN] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, diff --git a/include/openssl/bio.h b/include/openssl/bio.h index cb8902edf2..b0e8694737 100644 --- a/include/openssl/bio.h +++ b/include/openssl/bio.h @@ -244,13 +244,12 @@ OPENSSL_EXPORT int BIO_method_type(const BIO *bio); // The BIO_CB_RETURN flag indicates if it is after the call #define BIO_CB_RETURN 0x80 -// bio_info_cb is the type of a callback function that can be called for most -// BIO operations. The |event| argument is one of |BIO_CB_*| and can be ORed -// with |BIO_CB_RETURN| if the callback is being made after the operation in -// question. In that case, |return_value| will contain the return value from -// the operation. -typedef long (*bio_info_cb)(BIO *bio, int event, const char *parg, int cmd, - long larg, long return_value); +// |bio_info_cb| is a type of callback function providing information about a +// BIO operation. |state| identifies the current state of the BIO +// object, such as |BIO_CONN_S_BEFORE|. |res| represent the result of the +// operation that triggered the callback. This can be a positive value, zero, +// or a negative value depending on the operation and its outcome. +typedef long (*bio_info_cb)(BIO *b, int state, int res); // |BIO_callback_fn_ex| parameters have the following meaning: // |bio| the bio that made the call diff --git a/include/openssl/bn.h b/include/openssl/bn.h index faec6694f6..a761317513 100644 --- a/include/openssl/bn.h +++ b/include/openssl/bn.h @@ -164,14 +164,12 @@ extern "C" { typedef uint64_t BN_ULONG; #define BN_BITS2 64 #define BN_DEC_FMT1 "%" PRIu64 -#define BN_DEC_FMT2 "%019" PRIu64 #define BN_HEX_FMT1 "%" PRIx64 #define BN_HEX_FMT2 "%016" PRIx64 #elif defined(OPENSSL_32_BIT) typedef uint32_t BN_ULONG; #define BN_BITS2 32 #define BN_DEC_FMT1 "%" PRIu32 -#define BN_DEC_FMT2 "%09" PRIu32 #define BN_HEX_FMT1 "%" PRIx32 #define BN_HEX_FMT2 "%08" PRIx32 #else diff --git a/include/openssl/chacha.h b/include/openssl/chacha.h index cfbaa75680..2868c29062 100644 --- a/include/openssl/chacha.h +++ b/include/openssl/chacha.h @@ -29,6 +29,12 @@ extern "C" { // CRYPTO_chacha_20 encrypts |in_len| bytes from |in| with the given key and // nonce and writes the result to |out|. If |in| and |out| alias, they must be // equal. The initial block counter is specified by |counter|. +// +// This function implements a 32-bit block counter as in RFC 8439. On overflow, +// the counter wraps. Reusing a key, nonce, and block counter combination is not +// secure, so wrapping is usually a bug in the caller. While it is possible to +// wrap without reuse with a large initial block counter, this is not +// recommended and may not be portable to other ChaCha20 implementations. OPENSSL_EXPORT void CRYPTO_chacha_20(uint8_t *out, const uint8_t *in, size_t in_len, const uint8_t key[32], const uint8_t nonce[12], uint32_t counter); diff --git a/include/openssl/digest.h b/include/openssl/digest.h index e940ffe635..3d3d359e42 100644 --- a/include/openssl/digest.h +++ b/include/openssl/digest.h @@ -91,6 +91,8 @@ OPENSSL_EXPORT const EVP_MD *EVP_sha3_224(void); OPENSSL_EXPORT const EVP_MD *EVP_sha3_256(void); OPENSSL_EXPORT const EVP_MD *EVP_sha3_384(void); OPENSSL_EXPORT const EVP_MD *EVP_sha3_512(void); +OPENSSL_EXPORT const EVP_MD *EVP_shake128(void); +OPENSSL_EXPORT const EVP_MD *EVP_shake256(void); OPENSSL_EXPORT const EVP_MD *EVP_blake2b256(void); // EVP_md5_sha1 is a TLS-specific |EVP_MD| which computes the concatenation of @@ -191,9 +193,10 @@ OPENSSL_EXPORT int EVP_DigestFinal(EVP_MD_CTX *ctx, uint8_t *md_out, // bytes from |data| and writes the digest to |md_out|. |EVP_MD_CTX_size| bytes // are written, which is at most |EVP_MAX_MD_SIZE|. If |out_size| is not NULL // then |*out_size| is set to the number of bytes written. It returns one on -// success and zero otherwise. +// success and zero otherwise. If |type| is an XOF, |out_size| must be set to +// the desired output length. OPENSSL_EXPORT int EVP_Digest(const void *data, size_t len, uint8_t *md_out, - unsigned int *md_out_size, const EVP_MD *type, + unsigned int *out_size, const EVP_MD *type, ENGINE *impl); @@ -221,8 +224,7 @@ OPENSSL_EXPORT size_t EVP_MD_block_size(const EVP_MD *md); #define EVP_MD_FLAG_DIGALGID_ABSENT 2 // EVP_MD_FLAG_XOF indicates that the digest is an extensible-output function -// (XOF). This flag is defined for compatibility and will never be set in any -// |EVP_MD| in BoringSSL. +// (XOF). #define EVP_MD_FLAG_XOF 4 @@ -290,8 +292,8 @@ OPENSSL_EXPORT EVP_MD_CTX *EVP_MD_CTX_create(void); // EVP_MD_CTX_destroy calls |EVP_MD_CTX_free|. OPENSSL_EXPORT void EVP_MD_CTX_destroy(EVP_MD_CTX *ctx); -// EVP_DigestFinalXOF returns zero and adds an error to the error queue. -// BoringSSL does not support any XOF digests. +// EVP_DigestFinalXOF behaves like |EVP_DigestFinal| for XOF digests, writing +// |len| bytes of extended output to |out|. OPENSSL_EXPORT int EVP_DigestFinalXOF(EVP_MD_CTX *ctx, uint8_t *out, size_t len); diff --git a/include/openssl/nid.h b/include/openssl/nid.h index 3382ef679a..0eb236bf03 100644 --- a/include/openssl/nid.h +++ b/include/openssl/nid.h @@ -4306,9 +4306,19 @@ extern "C" { #define SN_sha512_224 "SHA512-224" #define LN_sha512_224 "sha512-224" -#define NID_sha512_224 979 +#define NID_sha512_224 978 #define OBJ_sha512_224 2L, 16L, 840L, 1L, 101L, 3L, 4L, 2L, 5L +#define SN_shake128 "SHAKE128" +#define LN_shake128 "shake128" +#define NID_shake128 979 +#define OBJ_shake128 2L, 16L, 840L, 1L, 101L, 3L, 4L, 2L, 11L + +#define SN_shake256 "SHAKE256" +#define LN_shake256 "shake256" +#define NID_shake256 980 +#define OBJ_shake256 2L, 16L, 840L, 1L, 101L, 3L, 4L, 2L, 12L + #if defined(__cplusplus) } /* extern C */ #endif diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 79ff07e6c5..475d360e60 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -2641,6 +2641,14 @@ OPENSSL_EXPORT const char *SSL_get_group_name(uint16_t group_id); // ownership of the buffer and must call |OPENSSL_free| when done. It returns // one on success and zero on error. // +// CAUTION: This function will serialize an established TLS 1.2/1.3 +// connection, which includes sensitive security parameters established during +// the connection handshake, and memory buffers that may contain +// sensitive in-flight application data. It is the callers responsibility for +// ensuring the confidentiality and data integrity of the serialized encoding. +// Minimally a caller must encrypt the returned bytes using an AEAD cipher, such +// as AES-128-GCM before persisting the bytes to storage. +// // WARNING: Currently only works with TLS 1.2 or TLS 1.3 after handshake has // finished. // WARNING: Currently only supports |SSL| as server. @@ -2659,6 +2667,13 @@ OPENSSL_EXPORT int SSL_to_bytes(const SSL *in, uint8_t **out_data, // come from |out_data| and |out_len| of |SSL_to_bytes|. In other words, // |SSL_from_bytes| should be called after |SSL_to_bytes|. // +// CAUTION: This function deserializes an encoded TLS 1.2/1.3 established +// connection so that the communication may continue on the previously +// established channel. It is the callers responsibility for maintaining +// confidentiality and integrity of serialized bytes between the time of +// serialization and invoking this function. +// See |SSL_to_bytes| for more details. +// // WARNING: Do not decode the same bytes |in| for different connections. // Otherwise, the connections use the same key material. // WARNING: Remember set |ssl->rbio| and |ssl->wbio| before using |ssl|. diff --git a/tests/ci/cdk/app.py b/tests/ci/cdk/app.py index 01322e69d3..7931a6f4af 100644 --- a/tests/ci/cdk/app.py +++ b/tests/ci/cdk/app.py @@ -6,11 +6,11 @@ from aws_cdk import Environment, App # from cdk.bm_framework_stack import BmFrameworkStack -from cdk.aws_lc_mac_arm_ci_stack import AwsLcMacArmCIStack from cdk.aws_lc_analytics_stack import AwsLcGitHubAnalyticsStack +from cdk.aws_lc_android_ci_stack import AwsLcAndroidCIStack from cdk.aws_lc_github_ci_stack import AwsLcGitHubCIStack from cdk.aws_lc_github_fuzz_ci_stack import AwsLcGitHubFuzzCIStack -from cdk.aws_lc_android_ci_stack import AwsLcAndroidCIStack +from cdk.aws_lc_ec2_test_framework_ci_stack import AwsLcEC2TestingCIStack from cdk.linux_docker_image_batch_build_stack import LinuxDockerImageBatchBuildStack from cdk.windows_docker_image_build_stack import WindowsDockerImageBuildStack from cdk.ecr_stack import EcrStack @@ -50,8 +50,8 @@ AwsLcGitHubAnalyticsStack(app, "aws-lc-ci-analytics", analytics_build_spec_file, env=env) # bm_framework_build_spec_file = "cdk/codebuild/bm_framework_omnibus.yaml" # BmFrameworkStack(app, "aws-lc-ci-bm-framework", bm_framework_build_spec_file, env=env) -mac_arm_build_spec_file = "cdk/codebuild/github_ci_macos_m1_omnibus.yaml" -AwsLcMacArmCIStack(app, "aws-lc-ci-macos-arm", mac_arm_build_spec_file, env=env) +ec2_test_framework_build_spec_file = "cdk/codebuild/ec2_test_framework_omnibus.yaml" +AwsLcEC2TestingCIStack(app, "aws-lc-ci-ec2-test-framework", ec2_test_framework_build_spec_file, env=env) android_build_spec_file = "cdk/codebuild/github_ci_android_omnibus.yaml" AwsLcAndroidCIStack(app, "aws-lc-ci-devicefarm-android", android_build_spec_file, env=env) diff --git a/tests/ci/cdk/cdk/aws_lc_mac_arm_ci_stack.py b/tests/ci/cdk/cdk/aws_lc_ec2_test_framework_ci_stack.py similarity index 87% rename from tests/ci/cdk/cdk/aws_lc_mac_arm_ci_stack.py rename to tests/ci/cdk/cdk/aws_lc_ec2_test_framework_ci_stack.py index 4719e98159..bdf52129ed 100644 --- a/tests/ci/cdk/cdk/aws_lc_mac_arm_ci_stack.py +++ b/tests/ci/cdk/cdk/aws_lc_ec2_test_framework_ci_stack.py @@ -9,13 +9,14 @@ from constructs import Construct from cdk.components import PruneStaleGitHubBuilds -from util.metadata import AWS_ACCOUNT, AWS_REGION, GITHUB_PUSH_CI_BRANCH_TARGETS, GITHUB_REPO_OWNER, GITHUB_REPO_NAME -from util.iam_policies import code_build_batch_policy_in_json, ec2_policies_in_json, ssm_policies_in_json, s3_read_write_policy_in_json +from util.metadata import AWS_ACCOUNT, AWS_REGION, GITHUB_PUSH_CI_BRANCH_TARGETS, GITHUB_REPO_OWNER, GITHUB_REPO_NAME, LINUX_AARCH_ECR_REPO, \ + LINUX_X86_ECR_REPO +from util.iam_policies import code_build_batch_policy_in_json, ec2_policies_in_json, ssm_policies_in_json, s3_read_write_policy_in_json, ecr_power_user_policy_in_json from util.build_spec_loader import BuildSpecLoader # detailed documentation can be found here: https://docs.aws.amazon.com/cdk/api/latest/docs/aws-ec2-readme.html -class AwsLcMacArmCIStack(Stack): +class AwsLcEC2TestingCIStack(Stack): """Define a stack used to create a CodeBuild instance on which to execute the AWS-LC m1 ci ec2 instance""" def __init__(self, @@ -25,9 +26,6 @@ def __init__(self, **kwargs) -> None: super().__init__(scope, id, **kwargs) - # Define some variables that will be commonly used - CLOUDWATCH_LOGS = "{}-cw-logs".format(id) - # Define CodeBuild resource. git_hub_source = codebuild.Source.git_hub( owner=GITHUB_REPO_OWNER, @@ -76,7 +74,8 @@ def __init__(self, # S3 bucket for testing internal fixes. s3_read_write_policy = iam.PolicyDocument.from_json(s3_read_write_policy_in_json("aws-lc-codebuild")) - ec2_inline_policies = {"s3_read_write_policy": s3_read_write_policy} + ecr_power_user_policy = iam.PolicyDocument.from_json(ecr_power_user_policy_in_json([LINUX_X86_ECR_REPO, LINUX_AARCH_ECR_REPO])) + ec2_inline_policies = {"s3_read_write_policy": s3_read_write_policy, "ecr_power_user_policy": ecr_power_user_policy} ec2_role = iam.Role(scope=self, id="{}-ec2-role".format(id), role_name="{}-ec2-role".format(id), assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"), @@ -97,18 +96,18 @@ def __init__(self, security_group = ec2.SecurityGroup(self, id="{}-ec2-sg".format(id), allow_all_outbound=True, vpc=vpc, - security_group_name='macos_arm_ec2_sg') + security_group_name='codebuild_ec2_sg') + # MacOS EC2 tag names must be specific for use in general tests/ci/run_m1_ec2_instance.sh script. # Dedicated Hosts are required for Mac ec2 instances. cfn_host = ec2.CfnHost(self, id="{}-dedicated-host".format(id), availability_zone="us-west-2a", auto_placement="off", instance_type="mac2.metal") Tags.of(cfn_host).add("Name", "{}-dedicated-host".format(id)) - # AMI is for M1 MacOS Monterey. ami_id="ami-084c6ab9d03ad4d46" - cfn_instance = ec2.CfnInstance(self, "{}-ec2-instance".format(id), + macos_arm_instance = ec2.CfnInstance(self, "aws-lc-ci-macos-arm-ec2-instance", availability_zone="us-west-2a", tenancy="host", host_id=cfn_host.attr_host_id, @@ -120,4 +119,9 @@ def __init__(self, tags=[CfnTag(key="Name",value="aws-lc-ci-macos-arm-ec2-instance")]) # Define logs for SSM. - logs.LogGroup(self, "{}-cw-logs".format(id), log_group_name=CLOUDWATCH_LOGS) + log_group_name = "{}-cw-logs".format(id) + log_group = logs.CfnLogGroup(self, log_group_name, + log_group_name=log_group_name, + retention_in_days=365, + ) + diff --git a/tests/ci/cdk/cdk/codebuild/ec2_test_framework_omnibus.yaml b/tests/ci/cdk/cdk/codebuild/ec2_test_framework_omnibus.yaml new file mode 100644 index 0000000000..53e498b434 --- /dev/null +++ b/tests/ci/cdk/cdk/codebuild/ec2_test_framework_omnibus.yaml @@ -0,0 +1,31 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 OR ISC + +version: 0.2 + +# Doc for batch https://docs.aws.amazon.com/codebuild/latest/userguide/batch-build-buildspec.html#build-spec.batch.build-list +batch: + build-list: + # Actual tests are ran on an m1 ec2 instance via SSM Commands. + # MacOS is special since the ec2 instance can't be spun up and closed on the fly. + # TODO: Migrate this to Github Actions when https://github.com/actions/runner-images/issues/2187 is resolved. + - identifier: macos_arm_tests + buildspec: ./tests/ci/codebuild/macos-aarch/run_m1_tests.yml + env: + type: LINUX_CONTAINER + privileged-mode: true + compute-type: BUILD_GENERAL1_LARGE + image: 620771051181.dkr.ecr.us-west-2.amazonaws.com/aws-lc-docker-images-linux-x86:ubuntu-20.04_clang-7x-bm-framework_latest + + # Actual tests are ran on an Graviton3 ec2 instance via SSM Commands. + - identifier: graviton3_tests + buildspec: ./tests/ci/codebuild/common/run_ec2_target.yml + env: + type: LINUX_CONTAINER + privileged-mode: true + compute-type: BUILD_GENERAL1_LARGE + image: 620771051181.dkr.ecr.us-west-2.amazonaws.com/aws-lc-docker-images-linux-x86:ubuntu-20.04_clang-7x-bm-framework_latest + variables: + EC2_AMI: "ami-0c79a55dda52434da" + EC2_INSTANCE_TYPE: "c7g.2xlarge" + ECR_DOCKER_TAG: "amazonlinux-2023_clang-15x_sanitizer" \ No newline at end of file diff --git a/tests/ci/cdk/cdk/codebuild/github_ci_macos_m1_omnibus.yaml b/tests/ci/cdk/cdk/codebuild/github_ci_macos_m1_omnibus.yaml deleted file mode 100644 index 7fa5a78be7..0000000000 --- a/tests/ci/cdk/cdk/codebuild/github_ci_macos_m1_omnibus.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0 OR ISC - -version: 0.2 - -# Doc for batch https://docs.aws.amazon.com/codebuild/latest/userguide/batch-build-buildspec.html#build-spec.batch.build-list -batch: - build-list: - # Actual tests are ran on an m1 ec2 instance via SSM Commands. - - identifier: macos_arm_tests - buildspec: ./tests/ci/codebuild/macos-aarch/run_m1_tests.yml - env: - type: LINUX_CONTAINER - privileged-mode: true - compute-type: BUILD_GENERAL1_LARGE - image: 620771051181.dkr.ecr.us-west-2.amazonaws.com/aws-lc-docker-images-linux-x86:ubuntu-20.04_clang-7x-bm-framework_latest diff --git a/tests/ci/cdk/cdk/ssm/general_test_run_ssm_document.yaml b/tests/ci/cdk/cdk/ssm/general_test_run_ssm_document.yaml new file mode 100644 index 0000000000..7bede07f2a --- /dev/null +++ b/tests/ci/cdk/cdk/ssm/general_test_run_ssm_document.yaml @@ -0,0 +1,43 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 OR ISC + +--- +schemaVersion: '2.2' +description: aws-lc:bmFrameworkEc2Benchmark +mainSteps: + - action: aws:runShellScript + name: runShellScript + inputs: + timeoutSeconds: '7200' + runCommand: + # Fallback plan to shut down the ec2 instance in 60 minutes in case it's not terminated. + # Codebuild just "stops" the instance calling the script, so "trap cleanup" is not executed. + - shutdown -P +60 + - sudo -i + - export DEBIAN_FRONTEND=noninteractive + - export CPU_TYPE=$(dpkg --print-architecture) + # if we have a cpu type of x86, we want linux-x86 + - if [ "${CPU_TYPE}" = amd64 ]; then export CPU_ARCH=linux-x86; export AWS_CLI_PREFIX=x86_; sudo sh -c "echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo"; fi + # if we have a cpu type of arm, we want linux-aarch + - if [ "${CPU_TYPE}" = arm64 ]; then export CPU_ARCH=linux-aarch; export AWS_CLI_PREFIX=aarch; fi + # install aws-cli + - apt-get -y install unzip + - curl "https://awscli.amazonaws.com/awscli-exe-linux-${AWS_CLI_PREFIX}64.zip" -o "awscliv2.zip" + - unzip awscliv2.zip + - ./aws/install + - git clone {GITHUB_REPO} aws-lc-pr + - cd aws-lc-pr + - git checkout {COMMIT_ID} + # install docker if its not already installed + - chmod +x ./tests/ci/benchmark_framework/install_docker.sh + - ./tests/ci/benchmark_framework/install_docker.sh + # log into docker and get needed docker image from ecr + - export ECR_REPO="{AWS_ACCOUNT_ID}.dkr.ecr.us-west-2.amazonaws.com/aws-lc-docker-images-${CPU_ARCH}" + - aws ecr get-login-password --region us-west-2 | docker login -u AWS --password-stdin "${ECR_REPO}" + - docker pull "${ECR_REPO}:{ECR_DOCKER_TAG}" + - # start the container and run the bm script + - exec_docker="docker run --env AWS_ACCOUNT_ID={AWS_ACCOUNT_ID} --env PR_NUM={PR_NUM} --env COMMIT_ID={COMMIT_ID} --env CPU_TYPE=${CPU_TYPE} -v `pwd`:`pwd` -w `pwd` ${ECR_REPO}:{ECR_DOCKER_TAG}_latest" + - chmod +x ./tests/ci/run_posix_sanitizers.sh + - $exec_docker ./tests/ci/run_posix_sanitizers.sh + - chmod +x ./tests/ci/run_fips_tests.sh + - $exec_docker ./tests/ci/run_fips_tests.sh diff --git a/tests/ci/cdk/cdk/ssm/m1_tests_ssm_document.yaml b/tests/ci/cdk/cdk/ssm/m1_tests_ssm_document.yaml index bc5e3637c0..5003889a7f 100644 --- a/tests/ci/cdk/cdk/ssm/m1_tests_ssm_document.yaml +++ b/tests/ci/cdk/cdk/ssm/m1_tests_ssm_document.yaml @@ -10,12 +10,12 @@ mainSteps: inputs: timeoutSeconds: '7200' runCommand: + - export HOME="/var/root" + - export SOURCE={SOURCE} - eval $(/opt/homebrew/bin/brew shellenv) - sudo su - BUILD_ROOT=$(mktemp -d) - cd ${BUILD_ROOT} - - export HOME="/var/root" - - export SOURCE={SOURCE} - trap "rm -rf ${BUILD_ROOT}" EXIT # Check if the source code is on S3, otherwise treat the source as a PR. - > diff --git a/tests/ci/codebuild/common/run_ec2_target.yml b/tests/ci/codebuild/common/run_ec2_target.yml new file mode 100755 index 0000000000..bf2b6b9525 --- /dev/null +++ b/tests/ci/codebuild/common/run_ec2_target.yml @@ -0,0 +1,13 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 OR ISC + +version: 0.2 + +env: + variables: + GOPROXY: https://proxy.golang.org,direct + +phases: + build: + commands: + - ./tests/ci/run_ec2_test_framework.sh "${EC2_AMI}" "${EC2_INSTANCE_TYPE}" "${ECR_DOCKER_TAG}" diff --git a/tests/ci/run_ec2_test_framework.sh b/tests/ci/run_ec2_test_framework.sh new file mode 100755 index 0000000000..b2cfcfce25 --- /dev/null +++ b/tests/ci/run_ec2_test_framework.sh @@ -0,0 +1,135 @@ +#!/usr/bin/env bash +set -exo pipefail +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 OR ISC + +# Please run from project root folder! +# You'll want to set the codebuild env variables set if running locally +source tests/ci/common_ssm_setup.sh + +AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) + +# Cleanup AWS resources. +cleanup() { + set +e + aws ec2 terminate-instances --instance-ids "${instance_id}" + aws ssm delete-document --name "${ssm_doc_name}" +} + +generate_ssm_document_file() { + # use sed to replace placeholder values inside preexisting document + sed -e "s,{AWS_ACCOUNT_ID},${AWS_ACCOUNT_ID},g" \ + -e "s,{PR_NUM},${CODEBUILD_WEBHOOK_TRIGGER},g" \ + -e "s,{COMMIT_ID},${CODEBUILD_SOURCE_VERSION},g" \ + -e "s,{GITHUB_REPO},${CODEBUILD_SOURCE_REPO_URL},g" \ + -e "s,{ECR_DOCKER_TAG},${ecr_docker_tag},g" \ + tests/ci/cdk/cdk/ssm/general_test_run_ssm_document.yaml \ + > "tests/ci/cdk/cdk/ssm/${ec2_ami_id}_ssm_document.yaml" +} + +#$1 for ami, $2 for instance-type, echos the instance id so we can capture the output +create_ec2_instances() { + local instance_id + instance_id="$(aws ec2 run-instances --image-id "$1" --count 1 \ + --instance-type "$2" --security-group-ids "${sg_id}" --subnet-id "${subnet_id}" \ + --block-device-mappings 'DeviceName="/dev/sda1",Ebs={DeleteOnTermination=True,VolumeSize=200}' \ + --tag-specifications 'ResourceType="instance",Tags=[{Key="aws-lc",Value="aws-lc-ci-ec2-test-framework-ec2-x86-instance"}]' \ + --iam-instance-profile Name=aws-lc-ci-ec2-test-framework-ec2-profile \ + --placement 'AvailabilityZone=us-west-2a' \ + --instance-initiated-shutdown-behavior terminate \ + --query Instances[*].InstanceId --output text)" + echo "${instance_id}" +} + +trap cleanup EXIT + +# print some information for reference +echo GitHub PR Number: "${CODEBUILD_WEBHOOK_TRIGGER}" +echo GitHub Commit Version: "${CODEBUILD_SOURCE_VERSION}" +echo AWS Account ID: "${AWS_ACCOUNT_ID}" +echo GitHub Repo Link: "${CODEBUILD_SOURCE_REPO_URL}" +export ec2_ami_id="$1" +export ec2_instance_type="$2" +export ecr_docker_tag="$3" +export s3_bucket_name="aws-lc-codebuild" + +# Get resources for ec2 instances. These were created with the cdk script. +vpc_id="$(aws ec2 describe-vpcs --filter Name=tag:Name,Values=aws-lc-ci-ec2-test-framework/aws-lc-ci-ec2-test-framework-ec2-vpc --query Vpcs[*].VpcId --output text)" +sg_id="$(aws ec2 describe-security-groups --filter Name=vpc-id,Values="${vpc_id}" --filter Name=group-name,Values=codebuild_ec2_sg --query SecurityGroups[*].GroupId --output text)" +subnet_id="$(aws ec2 describe-subnets --filter Name=vpc-id,Values="${vpc_id}" --filter Name=state,Values=available --filter Name=tag:Name,Values=aws-lc-ci-ec2-test-framework/aws-lc-ci-ec2-test-framework-ec2-vpc/PrivateSubnet1 --query Subnets[*].SubnetId --output text)" + +# create the ssm documents that will be used for the various ssm commands +generate_ssm_document_file + +# create ec2 instances +instance_id=$(create_ec2_instances "${ec2_ami_id}" "${ec2_instance_type}") +if [[ -z "${instance_id}" ]]; then + exit 1 +fi + +# Give a few minutes for the ec2 instance to be ready +sleep 60 +for i in {1..30}; do + status=$(aws ssm describe-instance-information --filter Key="InstanceIds",Values="${instance_id}" \ + --query InstanceInformationList[*].PingStatus --output text) + if [ "${status}" == Online ]; then + break + fi + echo "Wait for instances to be able to run the SSM commands" + + # if we've hit the 30 minute mark and still aren't ready, then something has gone wrong + if [ "${i}" = 30 ]; then exit 1; fi + sleep 60 +done + + +# Create, and run ssm command. +ssm_doc_name=$(create_ssm_document "${ec2_ami_id}") + +cloudwatch_group_name="aws-lc-ci-ec2-test-framework-cw-logs" +ec2_test_ssm_command_id=$(run_ssm_command "${ssm_doc_name}" "${instance_id}" ${cloudwatch_group_name}) + +run_url="https://${AWS_REGION}.console.aws.amazon.com/cloudwatch/home?region=${AWS_REGION}\ +#logsV2:log-groups/log-group/${cloudwatch_group_name}/log-events/\ +${ec2_test_ssm_command_id}\$252F${instance_id}\$252FrunShellScript\$252Fstdout" + +echo "Actual Run in EC2 can be observered at CloudWatch URL: ${run_url}" + +# Give some time for the commands to run +done=false +success=false +for i in {1..45}; do + echo "${i}: Continue to wait 2 min for SSM commands to finish." + sleep 120 + + ssm_command_status="$(aws ssm list-commands --command-id "${ec2_test_ssm_command_id}" --query Commands[*].Status --output text)" + ssm_target_count="$(aws ssm list-commands --command-id "${ec2_test_ssm_command_id}" --query Commands[*].TargetCount --output text)" + ssm_completed_count="$(aws ssm list-commands --command-id "${ec2_test_ssm_command_id}" --query Commands[*].CompletedCount --output text)" + if [[ ${ssm_command_status} == 'Success' && ${ssm_completed_count} == "${ssm_target_count}" ]]; then + echo "SSM command ${ec2_test_ssm_command_id} finished successfully." + success=true + done=true + elif [[ ${ssm_command_status} == 'Failed' && ${ssm_completed_count} == "${ssm_target_count}" ]]; then + echo "SSM command ${ec2_test_ssm_command_id} failed." + done=true + else + # Still running. + done=false + fi + + # if after the loop finish and done is still true, then we're done + if [ "${done}" = true ]; then + echo "EC2 SSM command has finished." + + # if success is still true here, then none of the commands failed + if [ "${success}" == true ]; then + echo "EC2 SSM command succeeded!" + exit 0 + else + echo "EC2 SSM command failed!" + exit 1 + fi + break + fi +done +exit 1 diff --git a/tests/ci/run_m1_ec2_instance.sh b/tests/ci/run_m1_ec2_instance.sh index 7bac0d6764..ce77e3e0d0 100755 --- a/tests/ci/run_m1_ec2_instance.sh +++ b/tests/ci/run_m1_ec2_instance.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -exo pipefail # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 OR ISC @@ -25,7 +25,7 @@ echo GitHub Branch Name: "${CODEBUILD_WEBHOOK_HEAD_REF}" AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) echo AWS Account ID: "${AWS_ACCOUNT_ID}" echo GitHub Repo Link: "${CODEBUILD_SOURCE_REPO_URL}" -export cloudwatch_group_name="aws-lc-ci-macos-arm-cw-logs" +export cloudwatch_group_name="aws-lc-ci-ec2-test-framework-cw-logs" export s3_bucket_name="aws-lc-codebuild" # get information for ec2 instances diff --git a/third_party/s2n-bignum/include/s2n-bignum_aws-lc.h b/third_party/s2n-bignum/include/s2n-bignum_aws-lc.h index 9a202cf3a0..b007db3849 100644 --- a/third_party/s2n-bignum/include/s2n-bignum_aws-lc.h +++ b/third_party/s2n-bignum/include/s2n-bignum_aws-lc.h @@ -212,3 +212,37 @@ extern void bignum_mul(uint64_t k, uint64_t *z, uint64_t m, const uint64_t *x, // General big-integer squaring (z := x^2). // Inputs: x[m]; output: z[k]. If k < 2m, the result is truncated. extern void bignum_sqr(uint64_t k, uint64_t *z, uint64_t m, const uint64_t *x); + +// Given table: uint64_t[height*width], copy table[idx*width...(idx+1)*width-1] +// into z[0..row-1]. +// This function is constant-time with respect to the value of `idx`. This is +// achieved by reading the whole table and using the bit-masking to get the +// `idx`-th row. +// Input table[height*width]; output z[width] +extern void bignum_copy_row_from_table (uint64_t *z, const uint64_t *table, + uint64_t height, uint64_t width, uint64_t idx); + +// Given table: uint64_t[height*width], copy table[idx*width...(idx+1)*width-1] +// into z[0..row-1]. width must be a multiple of 8. +// This function is constant-time with respect to the value of `idx`. This is +// achieved by reading the whole table and using the bit-masking to get the +// `idx`-th row. +// Input table[height*width]; output z[width] +extern void bignum_copy_row_from_table_8n_neon (uint64_t *z, const uint64_t *table, + uint64_t height, uint64_t width, uint64_t idx); + +// Given table: uint64_t[height*16], copy table[idx*16...(idx+1)*16-1] into z[0..row-1]. +// This function is constant-time with respect to the value of `idx`. This is +// achieved by reading the whole table and using the bit-masking to get the +// `idx`-th row. +// Input table[height*16]; output z[16] +extern void bignum_copy_row_from_table_16_neon (uint64_t *z, const uint64_t *table, + uint64_t height, uint64_t idx); + +// Given table: uint64_t[height*32], copy table[idx*32...(idx+1)*32-1] into z[0..row-1]. +// This function is constant-time with respect to the value of `idx`. This is +// achieved by reading the whole table and using the bit-masking to get the +// `idx`-th row. +// Input table[height*32]; output z[32] +extern void bignum_copy_row_from_table_32_neon (uint64_t *z, const uint64_t *table, + uint64_t height, uint64_t idx); diff --git a/tool/speed.cc b/tool/speed.cc index 68a437747f..0f4fd01a59 100644 --- a/tool/speed.cc +++ b/tool/speed.cc @@ -1635,7 +1635,8 @@ static bool SpeedECMUL(const std::string &selected) { #endif // !defined(OPENSSL_1_0_BENCHMARK) -#if (!defined(OPENSSL_1_0_BENCHMARK) && !defined(BORINGSSL_BENCHMARK)) || AWSLC_API_VERSION >= 22 +// Only new AWS-LC (>= 22) and new OpenSSL (>= 1.1.1) support FFDH +#if (!defined(OPENSSL_1_0_BENCHMARK) && !defined(BORINGSSL_BENCHMARK) && !defined(OPENSSL_IS_AWSLC)) || AWSLC_API_VERSION >= 22 static bool SpeedFFDHGroup(const std::string &name, int nid, const std::string &selected) { if (!selected.empty() && name.find(selected) == std::string::npos) { @@ -1668,7 +1669,7 @@ static bool SpeedFFDH(const std::string &selected) { return SpeedFFDHGroup("FFDH 2048", NID_ffdhe2048, selected) && SpeedFFDHGroup("FFDH 4096", NID_ffdhe4096, selected); } -#endif //(!defined(OPENSSL_1_0_BENCHMARK) && !defined(BORINGSSL_BENCHMARK)) || AWSLC_API_VERSION >= 22 +#endif //(!defined(OPENSSL_1_0_BENCHMARK) && !defined(BORINGSSL_BENCHMARK) && !defined(OPENSSL_IS_AWSLC)) || AWSLC_API_VERSION >= 22 #if !defined(OPENSSL_BENCHMARK) static bool Speed25519(const std::string &selected) { @@ -2625,7 +2626,7 @@ bool Speed(const std::vector &args) { // OpenSSL 1.0 doesn't support Scrypt !SpeedScrypt(selected) || #endif -#if (!defined(OPENSSL_1_0_BENCHMARK) && !defined(BORINGSSL_BENCHMARK)) || AWSLC_API_VERSION >= 22 +#if (!defined(OPENSSL_1_0_BENCHMARK) && !defined(BORINGSSL_BENCHMARK) && !defined(OPENSSL_IS_AWSLC)) || AWSLC_API_VERSION >= 22 // OpenSSL 1.0 and BoringSSL don't support DH_new_by_nid, NID_ffdhe2048, or NID_ffdhe4096 !SpeedFFDH(selected) || #endif diff --git a/util/fipstools/acvp/ACVP.md b/util/fipstools/acvp/ACVP.md index 9a6ba86c75..60673bd6de 100644 --- a/util/fipstools/acvp/ACVP.md +++ b/util/fipstools/acvp/ACVP.md @@ -100,6 +100,8 @@ The other commands are as follows. (Note that you only need to implement the com | SHA2-512 | Value to hash | Digest | | SHA2-512/224 | Value to hash | Digest | | SHA2-512/256 | Value to hash | Digest | +| SHAKE-128 | Value to hash, output len | Digest | +| SHAKE-256 | Value to hash, output len | Digest | | SHA-1/MCT | Initial seed¹ | Digest | | SHA2-224/MCT | Initial seed¹ | Digest | | SHA2-256/MCT | Initial seed¹ | Digest | @@ -107,6 +109,8 @@ The other commands are as follows. (Note that you only need to implement the com | SHA2-512/MCT | Initial seed¹ | Digest | | SHA2-512/224/MCT | Initial seed¹ | Digest | | SHA2-512/256/MCT | Initial seed¹ | Digest | +| SHAKE-128/MCT | Initial seed_, output len | Digest | +| SHAKE-256/MCT | Initial seed_, output len | Digest | | TLSKDF/<1.0\|1.2>/<HASH> | Number output bytes, secret, label, seed1, seed2 | Output | ¹ The iterated tests would result in excessive numbers of round trips if the module wrapper handled only basic operations. Thus some ACVP logic is pushed down for these tests so that the inner loop can be handled locally. Either read the NIST documentation ([block-ciphers](https://pages.nist.gov/ACVP/draft-celi-acvp-symmetric.html#name-monte-carlo-tests-for-block) [hashes](https://pages.nist.gov/ACVP/draft-celi-acvp-sha.html#name-monte-carlo-tests-for-sha-1)) to understand the iteration count and return values or, probably more fruitfully, see how these functions are handled in the `modulewrapper` directory. diff --git a/util/fipstools/acvp/acvptool/subprocess/hash.go b/util/fipstools/acvp/acvptool/subprocess/hash.go index b5a6e8952e..f9caf85e17 100644 --- a/util/fipstools/acvp/acvptool/subprocess/hash.go +++ b/util/fipstools/acvp/acvptool/subprocess/hash.go @@ -19,6 +19,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "strings" ) // The following structures reflect the JSON of ACVP hash tests. See @@ -29,13 +30,16 @@ type hashTestVectorSet struct { } type hashTestGroup struct { - ID uint64 `json:"tgId"` - Type string `json:"testType"` - Tests []struct { - ID uint64 `json:"tcId"` - BitLength uint64 `json:"len"` - MsgHex string `json:"msg"` - LargeMsg hashLargeMsg `json:"largeMsg"` + ID uint64 `json:"tgId"` + Type string `json:"testType"` + MaxOutLen *uint64 `json:"maxOutLen,omitempty"` + MinOutLen *uint64 `json:"minxOutLen,omitempty"` + Tests []struct { + ID uint64 `json:"tcId"` + BitLength uint64 `json:"len"` + MsgHex string `json:"msg"` + OutputLength *uint64 `json:"outLen,omitempty"` + LargeMsg hashLargeMsg `json:"largeMsg"` } `json:"tests"` } @@ -97,8 +101,16 @@ func (h *hashPrimitive) Process(vectorSet []byte, m Transactable) (any, error) { // http://usnistgov.github.io/ACVP/artifacts/draft-celi-acvp-sha-00.html#rfc.section.3 switch group.Type { + case "VOT": + fallthrough case "AFT": - result, err := m.Transact(h.algo, 1, msg) + args := [][]byte{} + args = append(args, msg) + if test.OutputLength != nil { + outLenBytes := *test.OutputLength / 8 + args = append(args, uint32le(uint32(outLenBytes))) + } + result, err := m.Transact(h.algo, 1, args...) if err != nil { panic(h.algo + " hash operation failed: " + err.Error()) } @@ -109,7 +121,10 @@ func (h *hashPrimitive) Process(vectorSet []byte, m Transactable) (any, error) { }) case "MCT": - if len(msg) != h.size { + // MCT tests for conventional digest functions expect message + // and digest output lengths to be equivalent, however SHAKE + // does not have a predefined output length. + if len(msg) != h.size && !strings.HasPrefix(h.algo, "SHAKE") { return nil, fmt.Errorf("MCT test case %d/%d contains message of length %d but the digest length is %d", group.ID, test.ID, len(msg), h.size) } diff --git a/util/fipstools/acvp/acvptool/subprocess/rsa.go b/util/fipstools/acvp/acvptool/subprocess/rsa.go index 2d5a46f119..4140c7f560 100644 --- a/util/fipstools/acvp/acvptool/subprocess/rsa.go +++ b/util/fipstools/acvp/acvptool/subprocess/rsa.go @@ -126,10 +126,9 @@ func processKeyGen(vectorSet []byte, m Transactable) (any, error) { var ret []rsaKeyGenTestGroupResponse for _, group := range parsed.Groups { - // GDT means "Generated data test", i.e. "please generate an RSA key". - const expectedType = "GDT" - if group.Type != expectedType { - return nil, fmt.Errorf("RSA KeyGen test group has type %q, but only generation tests (%q) are supported", group.Type, expectedType) + // We support both GDT and AFT tests, which are formatted the same and expect the same output. + if !(group.Type == "GDT" || group.Type == "AFT") { + return nil, fmt.Errorf("RSA KeyGen test group has type %q, but only GDT and AFT tests are supported", group.Type) } response := rsaKeyGenTestGroupResponse{ diff --git a/util/fipstools/acvp/acvptool/subprocess/subprocess.go b/util/fipstools/acvp/acvptool/subprocess/subprocess.go index f6726c5447..26e1d2fab0 100644 --- a/util/fipstools/acvp/acvptool/subprocess/subprocess.go +++ b/util/fipstools/acvp/acvptool/subprocess/subprocess.go @@ -97,17 +97,20 @@ func NewWithIO(cmd *exec.Cmd, in io.WriteCloser, out io.ReadCloser) *Subprocess } m.primitives = map[string]primitive{ - "SHA-1": &hashPrimitive{"SHA-1", 20}, - "SHA2-224": &hashPrimitive{"SHA2-224", 28}, - "SHA2-256": &hashPrimitive{"SHA2-256", 32}, - "SHA2-384": &hashPrimitive{"SHA2-384", 48}, - "SHA2-512": &hashPrimitive{"SHA2-512", 64}, - "SHA2-512/224": &hashPrimitive{"SHA2-512/224", 28}, - "SHA2-512/256": &hashPrimitive{"SHA2-512/256", 32}, - "SHA3-224": &hashPrimitive{"SHA3-224", 28}, - "SHA3-256": &hashPrimitive{"SHA3-256", 32}, - "SHA3-384": &hashPrimitive{"SHA3-384", 48}, - "SHA3-512": &hashPrimitive{"SHA3-512", 64}, + "SHA-1": &hashPrimitive{"SHA-1", 20}, + "SHA2-224": &hashPrimitive{"SHA2-224", 28}, + "SHA2-256": &hashPrimitive{"SHA2-256", 32}, + "SHA2-384": &hashPrimitive{"SHA2-384", 48}, + "SHA2-512": &hashPrimitive{"SHA2-512", 64}, + "SHA2-512/224": &hashPrimitive{"SHA2-512/224", 28}, + "SHA2-512/256": &hashPrimitive{"SHA2-512/256", 32}, + "SHA3-224": &hashPrimitive{"SHA3-224", 28}, + "SHA3-256": &hashPrimitive{"SHA3-256", 32}, + "SHA3-384": &hashPrimitive{"SHA3-384", 48}, + "SHA3-512": &hashPrimitive{"SHA3-512", 64}, + // NOTE: SHAKE does not have a predifined digest output size + "SHAKE-128": &hashPrimitive{"SHAKE-128", -1}, + "SHAKE-256": &hashPrimitive{"SHAKE-256", -1}, "ACVP-AES-ECB": &blockCipher{"AES", 16, 2, true, false, iterateAES}, "ACVP-AES-CBC": &blockCipher{"AES-CBC", 16, 2, true, true, iterateAESCBC}, "ACVP-AES-CBC-CS3": &blockCipher{"AES-CBC-CS3", 16, 1, false, true, iterateAESCBC}, diff --git a/util/fipstools/acvp/acvptool/test/expected/SHAKE-128.bz2 b/util/fipstools/acvp/acvptool/test/expected/SHAKE-128.bz2 new file mode 100644 index 0000000000..c7895f91e9 Binary files /dev/null and b/util/fipstools/acvp/acvptool/test/expected/SHAKE-128.bz2 differ diff --git a/util/fipstools/acvp/acvptool/test/expected/SHAKE-256.bz2 b/util/fipstools/acvp/acvptool/test/expected/SHAKE-256.bz2 new file mode 100644 index 0000000000..8cc895f0de Binary files /dev/null and b/util/fipstools/acvp/acvptool/test/expected/SHAKE-256.bz2 differ diff --git a/util/fipstools/acvp/acvptool/test/sha-tests/shake-128-tests.json b/util/fipstools/acvp/acvptool/test/sha-tests/shake-128-tests.json new file mode 100644 index 0000000000..b5b0720ace --- /dev/null +++ b/util/fipstools/acvp/acvptool/test/sha-tests/shake-128-tests.json @@ -0,0 +1 @@ +[{"Wrapper": "modulewrapper", "In": "vectors/SHAKE-128.bz2", "Out": "expected/SHAKE-128.bz2"}] diff --git a/util/fipstools/acvp/acvptool/test/sha-tests/shake-256-tests.json b/util/fipstools/acvp/acvptool/test/sha-tests/shake-256-tests.json new file mode 100644 index 0000000000..dd10960bfd --- /dev/null +++ b/util/fipstools/acvp/acvptool/test/sha-tests/shake-256-tests.json @@ -0,0 +1 @@ +[{"Wrapper": "modulewrapper", "In": "vectors/SHAKE-256.bz2", "Out": "expected/SHAKE-256.bz2"}] diff --git a/util/fipstools/acvp/acvptool/test/vectors/SHAKE-128.bz2 b/util/fipstools/acvp/acvptool/test/vectors/SHAKE-128.bz2 new file mode 100644 index 0000000000..235f4dbac2 Binary files /dev/null and b/util/fipstools/acvp/acvptool/test/vectors/SHAKE-128.bz2 differ diff --git a/util/fipstools/acvp/acvptool/test/vectors/SHAKE-256.bz2 b/util/fipstools/acvp/acvptool/test/vectors/SHAKE-256.bz2 new file mode 100644 index 0000000000..10ba367bbb Binary files /dev/null and b/util/fipstools/acvp/acvptool/test/vectors/SHAKE-256.bz2 differ diff --git a/util/fipstools/acvp/modulewrapper/modulewrapper.cc b/util/fipstools/acvp/modulewrapper/modulewrapper.cc index 4c36c5ec11..48bc917b70 100644 --- a/util/fipstools/acvp/modulewrapper/modulewrapper.cc +++ b/util/fipstools/acvp/modulewrapper/modulewrapper.cc @@ -266,6 +266,22 @@ static bool GetConfig(const Span args[], ReplyCallback write_repl }], "performLargeDataTest": [1, 2, 4, 8] }, + { + "algorithm": "SHAKE-128", + "revision": "2.0", + "messageLength": [{ + "min": 0, "max": 65536, "increment": 8 + }], + "performLargeDataTest": [1, 2, 4, 8] + }, + { + "algorithm": "SHAKE-256", + "revision": "2.0", + "messageLength": [{ + "min": 0, "max": 65536, "increment": 8 + }], + "performLargeDataTest": [1, 2, 4, 8] + }, { "algorithm": "SHA-1", "revision": "1.0", @@ -1118,6 +1134,26 @@ static bool HashSha3(const Span args[], ReplyCallback write_reply return write_reply({Span(digest)}); } +template +static bool HashXof(const Span args[], ReplyCallback write_reply) { + // NOTE: Max outLen in the test vectors is 1024 bits (128 bytes). If that + // changes, we'll need to use a bigger stack-allocated array size here. + uint8_t digest[128]; + const EVP_MD *md = MDFunc(); + const uint8_t *outlen_bytes = args[1].data(); + // MD outLen is passed to modulewrapper as a length-4 byte array representing + // a little-endian unsigned 32-bit integer. + uint32_t md_out_size = 0; + md_out_size |= outlen_bytes[3] << 24; + md_out_size |= outlen_bytes[2] << 16; + md_out_size |= outlen_bytes[1] << 8; + md_out_size |= outlen_bytes[0] << 0; + + EVP_Digest(args[0].data(), args[0].size(), digest, &md_out_size, md, NULL); + + return write_reply({Span(digest, md_out_size)}); +} + template static bool HashMCT(const Span args[], @@ -1151,7 +1187,6 @@ static bool HashMCTSha3(const Span args[], const EVP_MD *evp_md = MDFunc(); unsigned int md_out_size = DigestLength; - // The following logic conforms to the Monte Carlo tests described in // https://pages.nist.gov/ACVP/draft-celi-acvp-sha3.html#name-monte-carlo-tests-for-sha3- unsigned char md[1001][DigestLength]; @@ -1168,6 +1203,39 @@ static bool HashMCTSha3(const Span args[], {Span(md[1000])}); } +template +static bool HashMCTXof(const Span args[], ReplyCallback write_reply) { + // The spec for SHAKE monte carlo tests is written to be generic between a + // minimum and maximum output length, but the vectors obtained from ACVP + // allow only 1024 bits. Supporting dynamically sized MCTs would require + // passing the min/max output lengths to the modulewrapper, parsing ibid., + // and dynamically allocating and freeing appropriately sized bufffers. To + // keep things simple, we defer that complexity until/if needed. + // + // https://pages.nist.gov/ACVP/draft-celi-acvp-sha3.html#name-shake-monte-carlo-test + const unsigned output_len = 1024/8; + const unsigned msg_size = 128/8; + const size_t array_len = 1001; + unsigned char md[array_len][output_len]; + unsigned char msg[array_len][msg_size]; + + // Zero out |md| and |msg| to clear any residual stack garbage before XOF computation + for (size_t i = 0; i < array_len; i++) { + OPENSSL_cleanse(md[i], sizeof(md[0]) * sizeof(unsigned char)); + OPENSSL_cleanse(msg[i], sizeof(msg[0]) * sizeof(unsigned char)); + } + + memcpy(md[0], args[0].data(), msg_size); + + unsigned output_len_mut = output_len; + for (size_t i = 1; i < array_len; i++) { + memcpy(msg[i], md[i-1], msg_size); + EVP_Digest(msg[i], sizeof(msg[i]), md[i], &output_len_mut, MDFunc(), NULL); + } + + return write_reply({Span(md[1000])}); +} + // The following logic conforms to the Large Data Tests described in // https://pages.nist.gov/ACVP/draft-celi-acvp-sha.html#name-large-data-tests-for-sha-1- // Which are the same for SHA-1, SHA2, and SHA3 @@ -1953,6 +2021,10 @@ static const EVP_MD *HashFromName(Span name) { return EVP_sha512_224(); } else if (StringEq(name, "SHA2-512/256")) { return EVP_sha512_256(); + } else if (StringEq(name, "SHAKE-128")) { + return EVP_shake128(); + } else if (StringEq(name, "SHAKE-256")) { + return EVP_shake256(); } else { return nullptr; } @@ -2454,6 +2526,8 @@ static struct { {"SHA3-256", 1, HashSha3}, {"SHA3-384", 1, HashSha3}, {"SHA3-512", 1, HashSha3}, + {"SHAKE-128", 2, HashXof}, + {"SHAKE-256", 2, HashXof}, {"SHA-1/MCT", 1, HashMCT}, {"SHA2-224/MCT", 1, HashMCT}, {"SHA2-256/MCT", 1, HashMCT}, @@ -2465,6 +2539,8 @@ static struct { {"SHA3-256/MCT", 1, HashMCTSha3}, {"SHA3-384/MCT", 1, HashMCTSha3}, {"SHA3-512/MCT", 1, HashMCTSha3}, + {"SHAKE-128/MCT", 1, HashMCTXof}, + {"SHAKE-256/MCT", 1, HashMCTXof}, {"SHA-1/LDT", 2, HashLDT}, {"SHA2-224/LDT", 2, HashLDT}, {"SHA2-256/LDT", 2, HashLDT}, diff --git a/util/fipstools/inject_hash/inject_hash.go b/util/fipstools/inject_hash/inject_hash.go index 85e201f53b..0d1e15732d 100644 --- a/util/fipstools/inject_hash/inject_hash.go +++ b/util/fipstools/inject_hash/inject_hash.go @@ -168,7 +168,6 @@ func doLinux(objectBytes []byte, isStatic bool) ([]byte, []byte, error) { return moduleText, moduleROData, nil } - func doAppleOS(objectBytes []byte) ([]byte, []byte, error) { object, err := macho.NewFile(bytes.NewReader(objectBytes)) @@ -221,6 +220,19 @@ func doAppleOS(objectBytes []byte) ([]byte, []byte, error) { return nil, nil, fmt.Errorf("symbol %q at %x, which is below base of %x\n", symbol.Name, symbol.Value, base) } + // Skip debugging symbols + // + // #define N_STAB 0xe0 /* if any of these bits set, a symbolic debugging entry */ + // + // "Only symbolic debugging entries have some of the N_STAB bits set and if any of these bits are set then it is + // a symbolic debugging entry (a stab). In which case then the values of the n_type field (the entire field) + // are given in " + // + // https://github.com/apple-oss-distributions/xnu/blob/main/EXTERNAL_HEADERS/mach-o/nlist.h + if symbol.Type&0xe0 != 0 { + continue + } + value := symbol.Value - base switch symbol.Name { case "_BORINGSSL_bcm_text_start": @@ -296,8 +308,6 @@ func doAppleOS(objectBytes []byte) ([]byte, []byte, error) { return moduleText, moduleROData, nil } - - func do(outPath, oInput string, arInput string, appleOS bool) error { var objectBytes []byte var isStatic bool @@ -365,7 +375,6 @@ func do(outPath, oInput string, arInput string, appleOS bool) error { return err } - var zeroKey [64]byte mac := hmac.New(sha256.New, zeroKey[:])