Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions crypto/digest_extra/digest_extra.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "../asn1/internal.h"
#include "../internal.h"
#include "../fipsmodule/digest/internal.h"
#include "../fipsmodule/sha/internal.h"


struct nid_to_digest {
Expand All @@ -41,6 +42,10 @@ static const struct nid_to_digest nid_to_digest_mapping[] = {
{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},
// Keccak-256 has no NID/OID (the Ethereum-style 0x01-padding variant is
// not standardised). Registered by name only; matches OpenSSL 3.2+'s
// "KECCAK-256" provider name for cross-library lookups.
{NID_undef, EVP_keccak_256, SN_keccak_256, LN_keccak_256},
{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
Expand Down Expand Up @@ -262,6 +267,40 @@ static const EVP_MD evp_md_blake2b256 = {

const EVP_MD *EVP_blake2b256(void) { return &evp_md_blake2b256; }

// Keccak-256 (Ethereum-style, 0x01 padding). NOT FIPS-approved, no OID.
static void keccak_256_init(EVP_MD_CTX *ctx) {
AWSLC_ASSERT(KECCAK_256_Init(ctx->md_data));
}

static int keccak_256_update(EVP_MD_CTX *ctx, const void *data, size_t len) {
return KECCAK_256_Update(ctx->md_data, data, len);
}

static void keccak_256_final(EVP_MD_CTX *ctx, uint8_t *md) {
AWSLC_ASSERT(KECCAK_256_Final(md, ctx->md_data));
}

// |EVP_MD_FLAG_DIGALGID_ABSENT| matches OpenSSL 3.2+'s
// |PROV_DIGEST_FLAG_ALGID_ABSENT| on KECCAK-{224,256,384,512}. With

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Are there other algorithms that should update the flag EVP_MD_FLAG_DIGALGID_ABSENT?

// |NID_undef| this is effectively unreachable in current AWS-LC code paths
// (|EVP_marshal_digest_algorithm| rejects digests without a known OID before
// inspecting flags), but we set it for parity with OpenSSL and to remain
// correct if any downstream caller starts honouring the flag.
static const EVP_MD evp_md_keccak_256 = {
NID_undef,
KECCAK_256_DIGEST_LENGTH,
EVP_MD_FLAG_DIGALGID_ABSENT,
keccak_256_init,
keccak_256_update,
keccak_256_final,
KECCAK_256_CBLOCK,
sizeof(KECCAK1600_CTX),
/*finalXOF=*/ NULL,
/*squeezeXOF=*/ NULL
};

const EVP_MD *EVP_keccak_256(void) { return &evp_md_keccak_256; }

static void null_init(EVP_MD_CTX *ctx) {}

static int null_update(EVP_MD_CTX *ctx, const void *data, size_t count) { return 1;}
Expand Down
69 changes: 69 additions & 0 deletions crypto/digest_extra/digest_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "../fipsmodule/md5/internal.h"
#include "../fipsmodule/sha/internal.h"
#include "../internal.h"
#include "../test/file_test.h"
#include "../test/test_util.h"


Expand Down Expand Up @@ -61,6 +62,7 @@ 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 };
static const MD keccak_256 = { "KECCAK-256", &EVP_keccak_256, &KECCAK_256, nullptr };
static const MD md_null = { "NULL", &EVP_md_null, nullptr, nullptr };

struct DigestTestVector {
Expand Down Expand Up @@ -224,6 +226,16 @@ static const DigestTestVector kTestVectors[] = {
{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"},

// Keccak-256 tests (Ethereum-style, original 0x01 padding). Reproducible
// by anyone using e.g. eth_utils.keccak() or pycryptodome with
// digest_bits=256. NOT FIPS, no OID.
{keccak_256, "", 1,
"c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470"},
{keccak_256, "abc", 1,
"4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45"},
{keccak_256, "The quick brown fox jumps over the lazy dog", 1,
"4d741b6f1eb29cb2a9b9911c82f56fa8d73b04959d3d9d222895df6c0b28aa15"},

// 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.
Expand Down Expand Up @@ -383,6 +395,56 @@ TEST(DigestTest, TestVectors) {
}
}

// Keccak-256 (Ethereum-style, 0x01 padding) and FIPS SHA3-256 share rate and
// digest length but use different padding bytes, so their outputs must always
// differ. This guards against a future maintainer "fixing" the padding.
TEST(DigestTest, Keccak256DiffersFromSHA3_256) {
static_assert(KECCAK_256_DIGEST_LENGTH == SHA3_256_DIGEST_LENGTH,
"size mismatch invalidates compare");
const char *kInputs[] = {"", "abc", "keccak", "hello world"};
for (const char *in : kInputs) {
SCOPED_TRACE(in);
const size_t len = strlen(in);
const uint8_t *data = reinterpret_cast<const uint8_t *>(in);
uint8_t k[KECCAK_256_DIGEST_LENGTH];
uint8_t s[SHA3_256_DIGEST_LENGTH];
ASSERT_EQ(k, KECCAK_256(data, len, k));
ASSERT_EQ(s, SHA3_256(data, len, s));
EXPECT_NE(0, OPENSSL_memcmp(k, s, KECCAK_256_DIGEST_LENGTH))
<< "Keccak-256 and SHA3-256 must produce different digests";
}
}

// File-driven Keccak-256 KAT vectors. Format mirrors NIST SHA-3 KATs
// (Len in bits, Msg/MD in lowercase hex). See sha3_test.cc:391 for the same
// pattern applied to SHA-3.
TEST(DigestTest, Keccak256KAT) {
auto run = [](FileTest *t) {
std::string len_str;
ASSERT_TRUE(t->GetAttribute(&len_str, "Len"));
int bit_len = 0;
ASSERT_EQ(1, sscanf(len_str.c_str(), "%d", &bit_len));
ASSERT_GE(bit_len, 0);
// The current KAT covers byte-aligned messages only; bit-level inputs
// need a separate API.
ASSERT_EQ(0, bit_len % 8) << "Non-byte-aligned KAT vectors are unsupported";
const size_t byte_len = static_cast<size_t>(bit_len) / 8;

std::vector<uint8_t> msg, md;
ASSERT_TRUE(t->GetBytes(&msg, "Msg"));
ASSERT_TRUE(t->GetBytes(&md, "MD"));
ASSERT_LE(byte_len, msg.size());
ASSERT_EQ(static_cast<size_t>(KECCAK_256_DIGEST_LENGTH), md.size());

uint8_t out[KECCAK_256_DIGEST_LENGTH];
ASSERT_EQ(out, KECCAK_256(msg.data(), byte_len, out));
EXPECT_EQ(EncodeHex(bssl::MakeConstSpan(md)),
EncodeHex(bssl::MakeConstSpan(out, KECCAK_256_DIGEST_LENGTH)));
};
FileTestGTest("crypto/keccak/testvectors/KECCAK_256ShortMsg.txt", run);
FileTestGTest("crypto/keccak/testvectors/KECCAK_256LongMsg.txt", run);
}

TEST(DigestTest, Getters) {
EXPECT_EQ(EVP_sha512(), EVP_get_digestbyname("RSA-SHA512"));
EXPECT_EQ(EVP_sha512(), EVP_get_digestbyname("sha512WithRSAEncryption"));
Expand All @@ -392,6 +454,13 @@ TEST(DigestTest, Getters) {
EXPECT_EQ(EVP_md4(), EVP_get_digestbyname("md4"));
EXPECT_EQ(EVP_md4(), EVP_get_digestbyname("MD4"));

// Keccak-256 is registered by name only (no OID), matching OpenSSL 3.x's
// "KECCAK-256" provider name. Lookup via either casing must work; lookup
// by NID does not (the digest's type is NID_undef).
EXPECT_EQ(EVP_keccak_256(), EVP_get_digestbyname("KECCAK-256"));
EXPECT_EQ(EVP_keccak_256(), EVP_get_digestbyname("keccak-256"));
EXPECT_EQ(NID_undef, EVP_MD_type(EVP_keccak_256()));

EXPECT_EQ(EVP_sha512(), EVP_get_digestbynid(NID_sha512));
EXPECT_EQ(nullptr, EVP_get_digestbynid(NID_sha512WithRSAEncryption));
EXPECT_EQ(nullptr, EVP_get_digestbynid(NID_undef));
Expand Down
18 changes: 18 additions & 0 deletions crypto/fipsmodule/service_indicator/service_indicator_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,12 @@ static const uint8_t kOutput_sha3_512[SHA3_512_DIGEST_LENGTH] = {
0x85, 0x3c, 0x64, 0xa1, 0x56, 0x6f, 0xeb, 0x76, 0x25, 0x9a, 0x4a, 0x44,
0x23, 0xf7, 0xcf, 0x46};

// Keccak-256 of |kPlaintext|, computed by util/gen_keccak256_kat.py.
static const uint8_t kOutput_keccak_256[KECCAK_256_DIGEST_LENGTH] = {
0xaa, 0x2f, 0x0b, 0xbd, 0x30, 0xd5, 0xf5, 0xd1, 0x2e, 0xf9, 0xca, 0xe0,
0xbe, 0xea, 0x24, 0xb1, 0x99, 0x9c, 0x95, 0xea, 0x50, 0xe9, 0x03, 0xaf,
0xaf, 0xea, 0x7d, 0xcb, 0x36, 0xf4, 0x59, 0xfc};

// NOTE: SHAKE is a variable-length XOF; this number is chosen somewhat
// arbitrarily for testing.
static const size_t SHAKE_OUTPUT_LENGTH = 64;
Expand Down Expand Up @@ -1167,6 +1173,18 @@ static const struct DigestTestVector {
kOutput_sha3_512,
AWSLC_APPROVED,
},
{
// Keccak-256 (Ethereum-style, 0x01 padding) shares the SHA-3
// implementation but is NOT FIPS-approved: Init/Update/Final/one-shot
// intentionally do not call FIPS_service_indicator_update_state, so
// the indicator must remain unset.
"KECCAK-256",
KECCAK_256_DIGEST_LENGTH,
&EVP_keccak_256,
&KECCAK_256,
kOutput_keccak_256,
AWSLC_NOT_APPROVED,
},
};

class EVPMDServiceIndicatorTest : public TestWithNoErrors<DigestTestVector> {};
Expand Down
29 changes: 29 additions & 0 deletions crypto/fipsmodule/sha/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ extern "C" {
#define SHA3_BLOCKSIZE(bitlen) (KECCAK1600_WIDTH - bitlen * 2) / 8
#define SHA3_PAD_CHAR 0x06

// KECCAK_PAD_CHAR is the original Keccak submission's padding byte, as used
// by Ethereum's keccak256. It is NOT FIPS-approved.
#define KECCAK_PAD_CHAR 0x01

#define KECCAK_256_DIGEST_BITLENGTH 256
#define KECCAK_256_DIGEST_LENGTH 32
#define KECCAK_256_CBLOCK SHA3_BLOCKSIZE(KECCAK_256_DIGEST_BITLENGTH)

// SHAKE constants, from NIST FIPS202.
// https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf
#define SHAKE_PAD_CHAR 0x1F
Expand Down Expand Up @@ -506,6 +514,27 @@ int SHA3_512_Update(KECCAK1600_CTX *sha, const void *data, size_t len);
// digest to |out|. It returns one on success and zero on programmer error.
int SHA3_512_Final(uint8_t out[SHA3_512_DIGEST_LENGTH], KECCAK1600_CTX *sha);

// Keccak-256 (Ethereum-style, padding 0x01). Reuses the same context type and
// permutation as the SHA-3 family but is NOT FIPS-approved: it does not
// update the FIPS service indicator. Streaming functions are exposed only
// internally; public consumers reach Keccak-256 via |EVP_keccak_256|.
// KECCAK_256_Init initialises |ctx| and returns 1 on success or 0 on failure.
int KECCAK_256_Init(KECCAK1600_CTX *ctx);

// KECCAK_256_Update absorbs |len| bytes from |data| into |ctx|.
int KECCAK_256_Update(KECCAK1600_CTX *ctx, const void *data, size_t len);

// KECCAK_256_Final pads the last block and writes |KECCAK_256_DIGEST_LENGTH|
// bytes to |out|. It returns one on success and zero on programmer error.
int KECCAK_256_Final(uint8_t out[KECCAK_256_DIGEST_LENGTH], KECCAK1600_CTX *ctx);

// KECCAK_256 writes the Keccak-256 digest of |len| bytes from |data| to |out|
// and returns |out|. Like the SHA-3 one-shots, this is OPENSSL_EXPORTed for
// in-tree consumers; external users should access Keccak-256 via
// |EVP_keccak_256|.
OPENSSL_EXPORT uint8_t *KECCAK_256(const uint8_t *data, size_t len,
uint8_t out[KECCAK_256_DIGEST_LENGTH]);

/*
* SHAKE APIs implement SHAKE functionalities on top of FIPS202 API layer
*
Expand Down
66 changes: 64 additions & 2 deletions crypto/fipsmodule/sha/sha3.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,9 @@ static void FIPS202_Reset(KECCAK1600_CTX *ctx) {
// the internal buffer. It initialises the |ctx| fields and returns 1 on
// success and 0 on failure.
static int FIPS202_Init(KECCAK1600_CTX *ctx, uint8_t pad, size_t block_size, size_t bit_len) {
if (pad != SHA3_PAD_CHAR &&
pad != SHAKE_PAD_CHAR) {
if (pad != SHA3_PAD_CHAR &&
pad != SHAKE_PAD_CHAR &&
pad != KECCAK_PAD_CHAR) {
return 0;
}

Expand Down Expand Up @@ -325,6 +326,67 @@ int SHA3_512_Final(uint8_t out[SHA3_512_DIGEST_LENGTH],
return SHA3_Final(&out[0], ctx);
}

/*
* Keccak-256 (Ethereum-style) APIs. Reuse the FIPS202 framework with the
* original Keccak padding byte (0x01). NOT FIPS-approved: these never call
* FIPS_service_indicator_update_state, so consumers always see
* AWSLC_NOT_APPROVED for the service indicator.
*/
int KECCAK_256_Init(KECCAK1600_CTX *ctx) {
if (ctx == NULL) {
return 0;
}
return FIPS202_Init(ctx, KECCAK_PAD_CHAR,
SHA3_BLOCKSIZE(KECCAK_256_DIGEST_BITLENGTH),
KECCAK_256_DIGEST_BITLENGTH);
}

int KECCAK_256_Update(KECCAK1600_CTX *ctx, const void *data, size_t len) {
if (ctx == NULL) {
return 0;
}
if (data == NULL && len != 0) {
return 0;
}
if (len == 0) {
return 1;
}
return FIPS202_Update(ctx, data, len);
}

int KECCAK_256_Final(uint8_t out[KECCAK_256_DIGEST_LENGTH],
KECCAK1600_CTX *ctx) {
if (out == NULL || ctx == NULL) {
return 0;
}
if (ctx->md_size == 0) {
return 1;
}
if (FIPS202_Finalize(out, ctx) == 0) {
return 0;
}
Keccak1600_Squeeze(ctx->A, out, ctx->md_size, ctx->block_size, ctx->state);
ctx->state = KECCAK1600_STATE_FINAL;
// Intentionally no FIPS_service_indicator_update_state(): Keccak-256 with
// 0x01 padding is not an approved service.
return 1;
}

uint8_t *KECCAK_256(const uint8_t *data, size_t len,
uint8_t out[KECCAK_256_DIGEST_LENGTH]) {
KECCAK1600_CTX ctx;
int ok = (KECCAK_256_Init(&ctx) &&
KECCAK_256_Update(&ctx, data, len) &&
KECCAK_256_Final(out, &ctx));

OPENSSL_cleanse(&ctx, sizeof(ctx));
if (ok == 0) {
return NULL;
}
// Intentionally no FIPS_service_indicator_update_state().
return out;
}

/*
* SHAKE APIs implement SHAKE functionalities on top of FIPS202 API layer
*/
Expand Down
Loading
Loading