Skip to content

Commit

Permalink
SLH-DSA: add support for SHA-256 prehashing.
Browse files Browse the repository at this point in the history
Turns out that we need this one too.

Change-Id: I9d9d8871f1a45576b1ef812207cb9ae44a376a2c
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/74509
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: Adam Langley <agl@google.com>
  • Loading branch information
Adam Langley authored and Boringssl LUCI CQ committed Dec 23, 2024
1 parent 9938f09 commit 8229027
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 17 deletions.
14 changes: 12 additions & 2 deletions crypto/fipsmodule/slhdsa/slhdsa.cc.inc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@


// The OBJECT IDENTIFIER header is also included in these values, per the spec.
static const uint8_t kSHA256OID[] = {0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
0x65, 0x03, 0x04, 0x02, 0x01};
static const uint8_t kSHA384OID[] = {0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
0x65, 0x03, 0x04, 0x02, 0x02};
#define MAX_OID_LENGTH 11
Expand Down Expand Up @@ -169,14 +171,22 @@ static int slhdsa_get_context_and_oid(uint8_t *out_context_and_oid,
size_t oid_len;
size_t expected_hash_len;
switch (hash_nid) {
// The SLH-DSA spec only lists SHA-256 and SHA-512. This function supports
// SHA-384, which is non-standard.
case NID_sha256:
oid = kSHA256OID;
oid_len = sizeof(kSHA256OID);
static_assert(sizeof(kSHA256OID) <= MAX_OID_LENGTH, "");
expected_hash_len = 32;
break;

// The SLH-DSA spec only lists SHA-256 and SHA-512. This function also
// supports SHA-384, which is non-standard.
case NID_sha384:
oid = kSHA384OID;
oid_len = sizeof(kSHA384OID);
static_assert(sizeof(kSHA384OID) <= MAX_OID_LENGTH, "");
expected_hash_len = 48;
break;

// If adding a hash function with a larger `oid_len`, update the size of
// `context_and_oid` in the callers.
default:
Expand Down
26 changes: 26 additions & 0 deletions crypto/slhdsa/slhdsa.cc
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,32 @@ int SLHDSA_SHA2_128S_verify(
context, context_len));
}

int SLHDSA_SHA2_128S_prehash_sign(
uint8_t out_signature[SLHDSA_SHA2_128S_SIGNATURE_BYTES],
const uint8_t private_key[SLHDSA_SHA2_128S_PRIVATE_KEY_BYTES],
const uint8_t *hashed_msg, size_t hashed_msg_len, int hash_nid,
const uint8_t *context, size_t context_len) {
if (hash_nid != NID_sha256) {
return 0;
}
return bcm_success(BCM_slhdsa_sha2_128s_prehash_sign(
out_signature, private_key, hashed_msg, hashed_msg_len, hash_nid, context,
context_len));
}

int SLHDSA_SHA2_128S_prehash_verify(
const uint8_t *signature, size_t signature_len,
const uint8_t public_key[SLHDSA_SHA2_128S_PUBLIC_KEY_BYTES],
const uint8_t *hashed_msg, size_t hashed_msg_len, int hash_nid,
const uint8_t *context, size_t context_len) {
if (hash_nid != NID_sha256) {
return 0;
}
return bcm_success(BCM_slhdsa_sha2_128s_prehash_verify(
signature, signature_len, public_key, hashed_msg, hashed_msg_len,
hash_nid, context, context_len));
}

int SLHDSA_SHA2_128S_prehash_warning_nonstandard_sign(
uint8_t out_signature[SLHDSA_SHA2_128S_SIGNATURE_BYTES],
const uint8_t private_key[SLHDSA_SHA2_128S_PRIVATE_KEY_BYTES],
Expand Down
9 changes: 8 additions & 1 deletion crypto/slhdsa/slhdsa_prehash.txt

Large diffs are not rendered by default.

33 changes: 19 additions & 14 deletions crypto/slhdsa/slhdsa_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ TEST(SLHDSATest, BasicNonstandardPrehashSignVerify) {
nullptr, 0));
}


static void NISTKeyGenerationFileTest(FileTest *t) {
std::vector<uint8_t> seed, expected_pub, expected_priv;
ASSERT_TRUE(t->GetBytes(&seed, "seed"));
Expand Down Expand Up @@ -183,11 +182,10 @@ TEST(SLHDSATest, NISTSignatureVerification) {
NISTSignatureVerificationFileTest);
}

static void NISTPrehashSignatureGenerationFileTest(FileTest *t) {
std::vector<uint8_t> priv, msg, sig, context;
ASSERT_TRUE(t->GetBytes(&priv, "priv"));
ASSERT_EQ(priv.size(),
static_cast<size_t>(SLHDSA_SHA2_128S_PRIVATE_KEY_BYTES));
static void NISTPrehashSignatureVerificationFileTest(FileTest *t) {
std::vector<uint8_t> pub, msg, sig, context;
ASSERT_TRUE(t->GetBytes(&pub, "pub"));
ASSERT_EQ(pub.size(), static_cast<size_t>(SLHDSA_SHA2_128S_PUBLIC_KEY_BYTES));
ASSERT_TRUE(t->GetBytes(&msg, "msg"));
ASSERT_TRUE(t->GetBytes(&sig, "sig"));
ASSERT_EQ(sig.size(), size_t{SLHDSA_SHA2_128S_SIGNATURE_BYTES});
Expand All @@ -196,23 +194,30 @@ static void NISTPrehashSignatureGenerationFileTest(FileTest *t) {
std::string hash_func;
ASSERT_TRUE(t->GetAttribute(&hash_func, "hash"));
int nid = 0;
if (hash_func == "SHA-384") {
bool nonstandard = false;
if (hash_func == "SHA-256") {
nid = NID_sha256;
} else if (hash_func == "SHA-384") {
nid = NID_sha384;
nonstandard = true;
} else {
abort();
}

uint8_t pub[SLHDSA_SHA2_128S_PUBLIC_KEY_BYTES];
SLHDSA_SHA2_128S_public_from_private(pub, priv.data());
EXPECT_TRUE(SLHDSA_SHA2_128S_prehash_warning_nonstandard_verify(
sig.data(), sig.size(), pub, msg.data(), msg.size(), nid, context.data(),
context.size()));
if (nonstandard) {
EXPECT_TRUE(SLHDSA_SHA2_128S_prehash_warning_nonstandard_verify(
sig.data(), sig.size(), pub.data(), msg.data(), msg.size(), nid,
context.data(), context.size()));
} else {
EXPECT_TRUE(SLHDSA_SHA2_128S_prehash_verify(
sig.data(), sig.size(), pub.data(), msg.data(), msg.size(), nid,
context.data(), context.size()));
}
}


TEST(SLHDSATest, NISTPrehashSignatureVerification) {
FileTestGTest("crypto/slhdsa/slhdsa_prehash.txt",
NISTPrehashSignatureGenerationFileTest);
NISTPrehashSignatureVerificationFileTest);
}

} // namespace
41 changes: 41 additions & 0 deletions include/openssl/slhdsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,47 @@ OPENSSL_EXPORT int SLHDSA_SHA2_128S_verify(
// b) A single private key is used to sign both prehashed and raw messages,
// and there's no other way to prevent ambiguity.

// SLHDSA_SHA2_128S_prehash_sign slowly generates a SLH-DSA-SHA2-128s signature
// of the prehashed |hashed_msg| using |private_key| and writes it to
// |out_signature|. The |context| argument is also signed over and can be used
// to include implicit contextual information that isn't included in
// |hashed_msg|. The same value of |context| must be presented to
// |SLHDSA_SHA2_128S_prehash_verify| in order for the generated signature to be
// considered valid. |context| and |context_len| may be |NULL| and 0 to use an
// empty context (this is common).
//
// The |hash_nid| argument must specify the hash function that was used to
// generate |hashed_msg|. This function only accepts hash functions listed in
// FIPS 205.
//
// This function returns 1 on success and 0 if |context_len| is larger than 255,
// if the hash function is not supported, or if |hashed_msg| is the wrong
// length.
OPENSSL_EXPORT int SLHDSA_SHA2_128S_prehash_sign(
uint8_t out_signature[SLHDSA_SHA2_128S_SIGNATURE_BYTES],
const uint8_t private_key[SLHDSA_SHA2_128S_PRIVATE_KEY_BYTES],
const uint8_t *hashed_msg, size_t hashed_msg_len, int hash_nid,
const uint8_t *context, size_t context_len);

// SLHDSA_SHA2_128S_prehash_verify verifies that |signature| is a valid
// SLH-DSA-SHA2-128s signature of the prehashed |hashed_msg| by |public_key|,
// using the hash algorithm identified by |hash_nid|. The value of |context|
// must equal the value that was passed to |SLHDSA_SHA2_128S_prehash_sign| when
// the signature was generated.
//
// The |hash_nid| argument must specify the hash function that was used to
// generate |hashed_msg|. This function only accepts hash functions that are
// listed in FIPS 205.
//
// This function returns 1 if the signature is valid and 0 if the signature is
// invalid, the hash function is not supported, or if |hashed_msg| is the wrong
// length.
OPENSSL_EXPORT int SLHDSA_SHA2_128S_prehash_verify(
const uint8_t *signature, size_t signature_len,
const uint8_t public_key[SLHDSA_SHA2_128S_PUBLIC_KEY_BYTES],
const uint8_t *hashed_msg, size_t hashed_msg_len, int hash_nid,
const uint8_t *context, size_t context_len);

// SLHDSA_SHA2_128S_prehash_warning_nonstandard_sign slowly generates a
// SLH-DSA-SHA2-128s signature of the prehashed |hashed_msg| using |private_key|
// and writes it to |out_signature|. The |context| argument is also signed over
Expand Down

0 comments on commit 8229027

Please sign in to comment.