From bb1aabad676ccb61e195917452d8d90c86d73217 Mon Sep 17 00:00:00 2001 From: Will Childs-Klein Date: Mon, 9 Oct 2023 18:17:30 -0400 Subject: [PATCH] Expose SHAKE through the EVP API (#1199) * chkpnt: wired to EVP, compiles, need to add tests * Tests running, all failing * Fix tests, now only some (but not all!) SHAKE128 tests are failing * Fix pre-existing bug in SHAKE one-shot tests * Remove SHA128 digest test cases with \x00 in msg due to strlen usage * Add SHAKE256 digest_text.cc NIST vectors * Add digest and evp _extra, doc comments * Move finalXOF to end of evp_md_st to preserve memory layout * Add SHA3 and SHAKE to the service indicator * SQUASHME * Add SHAKE ACVP vectors * Support SHAKE in EVP_Digest, add XOF functionality to modulewrapper * Fix digest_test.cc after merge * Add SHAKE ACVP modulewrapper JSON configs * Clarify and add comments to digest_test.cc * Add SHAKE monte carlo tests * Fix SHA2 tests, account for weird golang case/switch impl * Fix SHAKE MCTs * Update formatting for SHAKE128 expected ACVP vectors * Replace SHAKE256 ACVP vectors with byte-aligned vectors * Reformat expected SHAKE-256 ACVP vectors * Clean up TODOs * Support sha3 in EVP_getdigestbyname * Update crypto/fipsmodule/sha/internal.h Co-authored-by: dkostic <25055813+dkostic@users.noreply.github.com> * Trim ACVP vectors, adjust error code in EVP_Digest * Fix merge typo * Add test case for successive digest finalization * Add self check for SHA3/SHAKE * Add SHA3-512 to self_check.c and test_fips.c * Add SHA3 to break-kat.go, use unique input in self-check.c * Revert "Add SHA3 to break-kat.go, use unique input in self-check.c" This reverts commit 090e2ba2ed3b5b57fc25b38df50af2a676b6ab03. * Revert "Add SHA3-512 to self_check.c and test_fips.c" This reverts commit 5d23a695dce2a8c2477ded974755c2dbf845de13. * Revert "Add self check for SHA3/SHAKE" This reverts commit 172603960eb283106016488d0d1e98888864f80e. * Update crypto/fipsmodule/digest/digest.c Co-authored-by: dkostic <25055813+dkostic@users.noreply.github.com> * Simplify ctx cleansing * Refactor EVP_digest for better failure perf, add test --------- Co-authored-by: dkostic <25055813+dkostic@users.noreply.github.com> --- crypto/digest_extra/digest_extra.c | 7 + crypto/digest_extra/digest_test.cc | 138 ++++++++++++----- crypto/evp_extra/evp_test.cc | 4 + crypto/fipsmodule/digest/digest.c | 35 ++++- crypto/fipsmodule/digest/digests.c | 65 ++++++++ crypto/fipsmodule/digest/internal.h | 4 + .../service_indicator_test.cc | 146 ++++++++++++++++++ crypto/fipsmodule/sha/internal.h | 8 + crypto/fipsmodule/sha/sha3.c | 52 +++++-- crypto/fipsmodule/sha/sha3_test.cc | 35 ++--- crypto/obj/obj_dat.h | 39 ++++- crypto/obj/obj_mac.num | 4 +- crypto/obj/objects.txt | 2 + include/openssl/digest.h | 14 +- include/openssl/nid.h | 12 +- util/fipstools/acvp/ACVP.md | 4 + .../acvp/acvptool/subprocess/hash.go | 33 ++-- .../acvp/acvptool/subprocess/subprocess.go | 25 +-- .../acvp/acvptool/test/expected/SHAKE-128.bz2 | Bin 0 -> 13774 bytes .../acvp/acvptool/test/expected/SHAKE-256.bz2 | Bin 0 -> 13800 bytes .../test/sha-tests/shake-128-tests.json | 1 + .../test/sha-tests/shake-256-tests.json | 1 + .../acvp/acvptool/test/vectors/SHAKE-128.bz2 | Bin 0 -> 438 bytes .../acvp/acvptool/test/vectors/SHAKE-256.bz2 | Bin 0 -> 459 bytes .../acvp/modulewrapper/modulewrapper.cc | 78 +++++++++- 25 files changed, 591 insertions(+), 116 deletions(-) create mode 100644 util/fipstools/acvp/acvptool/test/expected/SHAKE-128.bz2 create mode 100644 util/fipstools/acvp/acvptool/test/expected/SHAKE-256.bz2 create mode 100644 util/fipstools/acvp/acvptool/test/sha-tests/shake-128-tests.json create mode 100644 util/fipstools/acvp/acvptool/test/sha-tests/shake-256-tests.json create mode 100644 util/fipstools/acvp/acvptool/test/vectors/SHAKE-128.bz2 create mode 100644 util/fipstools/acvp/acvptool/test/vectors/SHAKE-256.bz2 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/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/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/service_indicator/service_indicator_test.cc b/crypto/fipsmodule/service_indicator/service_indicator_test.cc index be9e11eaa0..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); 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/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/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/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/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 0000000000000000000000000000000000000000..c7895f91e9bcabf9f0b45fc39b38dbb540c1d58d GIT binary patch literal 13774 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 EfEtpPF#rGn literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..8cc895f0de8d4c3b72a9b7dcc21f219b6482260d GIT binary patch literal 13800 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..235f4dbac21a8a2bdb32ffd3b8a867234f683db2 GIT binary patch literal 438 zcmV;n0ZINsT4*^jL0KkKSrg1KNdN(5Uw~CmPzV3;KkknTF6ZBBFaem2U^O(GK~Did zm;y9COoojHCYc6$Mv?j{nx2}56A)x%G-;+mf&xVmse(*uHl`y%pc())G&ZD=3w0^A zP5R>9xh=sk+hUTEpDof-K|S5&79qt9A|apj>Nn<#+Ik7VBMHkuyP zO@SOu@+~Q%4LBfSrgpBl)fUuO0!SjUS@c_n1kluuL0a)MD8 zMyQD3SCwZKoS>JIz_=G66Ejn5MaVMOT{&NRCs5&7+@V0m&9W6+2~ZRfIyQ@^gJjgG z!$>Xy0_&`u=f(kY7)W7200GurCCJ)DdYQq`p~l%lkXo1?>1AdVL!S#d@zj)pGXnn( zyDVvrgN}UI%mx;O4}GLwXgy>vOWDXTLA3fxX}lM%?-Oc>+_Tt012}Uiso7LR6*4P| z!x#G#`NgfqM5dHT1!pAEDlb@Vq@BY0?{(zI0JyUiiSJrckn9GCYQ(L>48uW7oa3QU gRkG$FQbo@}8vYeW;=MMZ0so7+BAh5l3Fa6ifLHjzeE+0GR>I0000000000kwj@d5i=>KCYk^SfDHiJ zl0Yr9O{w3mE!}O^P&U}4q^ZljB_tHl+*M*gg47{tNhk#~%xLn~vKn&D9(yW)&5vas z%{IY}{i2GIrM?xZG(o2X3^h{LeC9Z|qO;sJ2aOjTTJ$N!RpI1c(S|lY4?SX73KFB% zbiQeNvls9o45ul={ku$3*mh=s2Ou|_1QJx*h{>CPI%Sh?v_jz|8%(4O!oV;H1(69< z6*A=w7YhkUrA>_lMGG7P_NrUB6ipRWqH1ho7;%K$+NDYS$uvc`r$P*&zz(&t?S@^F_s6c<>?ntK!5(d}!0icw< B!SetB literal 0 HcmV?d00001 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},