Add DILITHIUM_public_from_private function.
Change-Id: I9b9c53d4f994bd0fc731c9fff3119f0d388900e9
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/69667
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
diff --git a/crypto/dilithium/dilithium.c b/crypto/dilithium/dilithium.c
index 4f209bf..9630715 100644
--- a/crypto/dilithium/dilithium.c
+++ b/crypto/dilithium/dilithium.c
@@ -1214,6 +1214,48 @@
return ret;
}
+int DILITHIUM_public_from_private(
+ struct DILITHIUM_public_key *out_public_key,
+ const struct DILITHIUM_private_key *private_key) {
+ int ret = 0;
+
+ // Intermediate values, allocated on the heap to allow use when there is a
+ // limited amount of stack.
+ struct values_st {
+ matrix a_ntt;
+ vectorl s1_ntt;
+ vectork t;
+ vectork t0;
+ };
+ struct values_st *values = OPENSSL_malloc(sizeof(*values));
+ if (values == NULL) {
+ goto err;
+ }
+
+ const struct private_key *priv = private_key_from_external(private_key);
+ struct public_key *pub = public_key_from_external(out_public_key);
+
+ OPENSSL_memcpy(pub->rho, priv->rho, sizeof(pub->rho));
+ OPENSSL_memcpy(pub->public_key_hash, priv->public_key_hash,
+ sizeof(pub->public_key_hash));
+
+ matrix_expand(&values->a_ntt, priv->rho);
+
+ OPENSSL_memcpy(&values->s1_ntt, &priv->s1, sizeof(values->s1_ntt));
+ vectorl_ntt(&values->s1_ntt);
+
+ matrix_mult(&values->t, &values->a_ntt, &values->s1_ntt);
+ vectork_inverse_ntt(&values->t);
+ vectork_add(&values->t, &values->t, &priv->s2);
+
+ vectork_power2_round(&pub->t1, &values->t0, &values->t);
+
+ ret = 1;
+err:
+ OPENSSL_free(values);
+ return ret;
+}
+
// FIPS 204, Algorithm 2 (`ML-DSA.Sign`). Returns 1 on success and 0 on failure.
static int dilithium_sign_with_randomizer(
uint8_t out_encoded_signature[DILITHIUM_SIGNATURE_BYTES],
diff --git a/crypto/dilithium/dilithium_test.cc b/crypto/dilithium/dilithium_test.cc
index dcd2e8b..51cb295 100644
--- a/crypto/dilithium/dilithium_test.cc
+++ b/crypto/dilithium/dilithium_test.cc
@@ -92,6 +92,23 @@
1);
}
+TEST(DilithiumTest, PublicFromPrivateIsConsistent) {
+ std::vector<uint8_t> encoded_public_key(DILITHIUM_PUBLIC_KEY_BYTES);
+ auto priv = std::make_unique<DILITHIUM_private_key>();
+ EXPECT_TRUE(DILITHIUM_generate_key(encoded_public_key.data(), priv.get()));
+
+ auto pub = std::make_unique<DILITHIUM_public_key>();
+ EXPECT_TRUE(DILITHIUM_public_from_private(pub.get(), priv.get()));
+
+ std::vector<uint8_t> encoded_public_key2(DILITHIUM_PUBLIC_KEY_BYTES);
+
+ CBB cbb;
+ CBB_init_fixed(&cbb, encoded_public_key2.data(), encoded_public_key2.size());
+ ASSERT_TRUE(DILITHIUM_marshal_public_key(&cbb, pub.get()));
+
+ EXPECT_EQ(Bytes(encoded_public_key2), Bytes(encoded_public_key));
+}
+
TEST(DilithiumTest, InvalidPublicKeyEncodingLength) {
// Encode a public key with a trailing 0 at the end.
std::vector<uint8_t> encoded_public_key(DILITHIUM_PUBLIC_KEY_BYTES + 1);
@@ -100,13 +117,13 @@
// Public key is 1 byte too short.
CBS cbs = bssl::MakeConstSpan(encoded_public_key)
- .first(DILITHIUM_PUBLIC_KEY_BYTES - 1);
+ .first(DILITHIUM_PUBLIC_KEY_BYTES - 1);
auto parsed_pub = std::make_unique<DILITHIUM_public_key>();
EXPECT_FALSE(DILITHIUM_parse_public_key(parsed_pub.get(), &cbs));
// Public key has the correct length.
- cbs = bssl::MakeConstSpan(encoded_public_key)
- .first(DILITHIUM_PUBLIC_KEY_BYTES);
+ cbs =
+ bssl::MakeConstSpan(encoded_public_key).first(DILITHIUM_PUBLIC_KEY_BYTES);
EXPECT_TRUE(DILITHIUM_parse_public_key(parsed_pub.get(), &cbs));
// Public key is 1 byte too long.
@@ -142,10 +159,6 @@
EXPECT_FALSE(DILITHIUM_parse_private_key(parsed_priv.get(), &cbs));
}
-// TODO: Add more parsing tests:
-// - signed values out of range (private key's s1/s2 beyond ETA)
-// - signature hints not in the canonical order (signature malleability)
-
static void DilithiumFileTest(FileTest *t) {
std::vector<uint8_t> seed, message, public_key_expected, private_key_expected,
signed_message_expected;
diff --git a/include/openssl/experimental/dilithium.h b/include/openssl/experimental/dilithium.h
index 72e79bd..2273421 100644
--- a/include/openssl/experimental/dilithium.h
+++ b/include/openssl/experimental/dilithium.h
@@ -70,6 +70,12 @@
uint8_t out_encoded_public_key[DILITHIUM_PUBLIC_KEY_BYTES],
struct DILITHIUM_private_key *out_private_key);
+// DILITHIUM_public_from_private sets |*out_public_key| to the public key that
+// corresponds to |private_key|. Returns 1 on success and 0 on failure.
+OPENSSL_EXPORT int DILITHIUM_public_from_private(
+ struct DILITHIUM_public_key *out_public_key,
+ const struct DILITHIUM_private_key *private_key);
+
// DILITHIUM_sign generates a signature for the message |msg| of length
// |msg_len| using |private_key| following the randomized algorithm, and writes
// the encoded signature to |out_encoded_signature|. Returns 1 on success and 0