blob: a32cb304f5adb9f2c82f14dc6fbf882342af3320 [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/webcrypto/algorithms/ml_dsa.h"
#include <sstream>
#include "components/webcrypto/algorithm_dispatch.h"
#include "components/webcrypto/algorithms/test_helpers.h"
#include "components/webcrypto/status.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
#include "third_party/blink/public/platform/web_crypto_key_algorithm.h"
namespace webcrypto {
namespace {
blink::WebCryptoAlgorithm CreateMlDsaAlgorithm(blink::WebCryptoAlgorithmId id) {
return blink::WebCryptoAlgorithm::AdoptParamsAndCreate(id, nullptr);
}
blink::WebCryptoAlgorithm CreateMlDsaAlgorithmWithContext(
blink::WebCryptoAlgorithmId id,
const std::vector<uint8_t>& context) {
return blink::WebCryptoAlgorithm(
id, std::make_unique<blink::WebCryptoContextParams>(context));
}
class WebCryptoMlDsaTest
: public WebCryptoTestBase,
public ::testing::WithParamInterface<blink::WebCryptoAlgorithmId> {
public:
blink::WebCryptoAlgorithmId GetAlgorithm() { return GetParam(); }
};
TEST_P(WebCryptoMlDsaTest, GenerateKeyMlDsa) {
blink::WebCryptoKey public_key;
blink::WebCryptoKey private_key;
ASSERT_EQ(Status::Success(),
GenerateKeyPair(
CreateMlDsaAlgorithm(GetAlgorithm()), true,
blink::kWebCryptoKeyUsageSign | blink::kWebCryptoKeyUsageVerify,
&public_key, &private_key));
EXPECT_EQ(blink::kWebCryptoKeyTypePublic, public_key.GetType());
EXPECT_EQ(blink::kWebCryptoKeyTypePrivate, private_key.GetType());
EXPECT_EQ(GetAlgorithm(), public_key.Algorithm().Id());
}
TEST_P(WebCryptoMlDsaTest, SignVerifyMlDsa) {
blink::WebCryptoKey public_key;
blink::WebCryptoKey private_key;
blink::WebCryptoAlgorithm algorithm = CreateMlDsaAlgorithm(GetAlgorithm());
ASSERT_EQ(Status::Success(),
GenerateKeyPair(
algorithm, true,
blink::kWebCryptoKeyUsageSign | blink::kWebCryptoKeyUsageVerify,
&public_key, &private_key));
std::vector<uint8_t> message = {1, 2, 3, 4};
std::vector<uint8_t> signature;
ASSERT_EQ(Status::Success(),
Sign(algorithm, private_key, message, &signature));
EXPECT_FALSE(signature.empty());
bool signature_matches = false;
ASSERT_EQ(Status::Success(), Verify(algorithm, public_key, signature, message,
&signature_matches));
EXPECT_TRUE(signature_matches);
// Verification should fail if message is different.
std::vector<uint8_t> wrong_message = {1, 2, 3, 5};
ASSERT_EQ(Status::Success(), Verify(algorithm, public_key, signature,
wrong_message, &signature_matches));
EXPECT_FALSE(signature_matches);
}
TEST_P(WebCryptoMlDsaTest, SignVerifyMlDsaWithContext) {
blink::WebCryptoKey public_key;
blink::WebCryptoKey private_key;
auto context_bytes = HexStringToBytes("deadbeef");
blink::WebCryptoAlgorithm algorithm = CreateMlDsaAlgorithm(GetAlgorithm());
ASSERT_EQ(Status::Success(),
GenerateKeyPair(
algorithm, true,
blink::kWebCryptoKeyUsageSign | blink::kWebCryptoKeyUsageVerify,
&public_key, &private_key));
std::vector<uint8_t> message = {1, 2, 3, 4};
std::vector<uint8_t> signature;
ASSERT_EQ(Status::Success(),
Sign(CreateMlDsaAlgorithmWithContext(GetAlgorithm(), context_bytes),
private_key, message, &signature));
EXPECT_FALSE(signature.empty());
bool signature_matches = false;
ASSERT_EQ(
Status::Success(),
Verify(CreateMlDsaAlgorithmWithContext(GetAlgorithm(), context_bytes),
public_key, signature, message, &signature_matches));
EXPECT_TRUE(signature_matches);
// Verification should fail if context is different
ASSERT_EQ(Status::Success(), Verify(algorithm, public_key, signature, message,
&signature_matches));
EXPECT_FALSE(signature_matches);
}
TEST_P(WebCryptoMlDsaTest, ImportExportJwkMlDsa) {
blink::WebCryptoKey public_key;
blink::WebCryptoKey private_key;
blink::WebCryptoAlgorithm algorithm = CreateMlDsaAlgorithm(GetAlgorithm());
ASSERT_EQ(Status::Success(),
GenerateKeyPair(
algorithm, true,
blink::kWebCryptoKeyUsageSign | blink::kWebCryptoKeyUsageVerify,
&public_key, &private_key));
std::vector<uint8_t> jwk_public;
ASSERT_EQ(Status::Success(),
ExportKey(blink::kWebCryptoKeyFormatJwk, public_key, &jwk_public));
blink::WebCryptoKey imported_public_key;
ASSERT_EQ(
Status::Success(),
ImportKey(blink::kWebCryptoKeyFormatJwk, jwk_public, algorithm, true,
blink::kWebCryptoKeyUsageVerify, &imported_public_key));
EXPECT_EQ(blink::kWebCryptoKeyTypePublic, imported_public_key.GetType());
std::vector<uint8_t> jwk_private;
ASSERT_EQ(Status::Success(), ExportKey(blink::kWebCryptoKeyFormatJwk,
private_key, &jwk_private));
blink::WebCryptoKey imported_private_key;
ASSERT_EQ(
Status::Success(),
ImportKey(blink::kWebCryptoKeyFormatJwk, jwk_private, algorithm, true,
blink::kWebCryptoKeyUsageSign, &imported_private_key));
EXPECT_EQ(blink::kWebCryptoKeyTypePrivate, imported_private_key.GetType());
}
INSTANTIATE_TEST_SUITE_P(
MlDsa,
WebCryptoMlDsaTest,
testing::Values(blink::kWebCryptoAlgorithmIdMlDsa44,
blink::kWebCryptoAlgorithmIdMlDsa65,
blink::kWebCryptoAlgorithmIdMlDsa87),
[](const testing::TestParamInfo<blink::WebCryptoAlgorithmId>& info) {
std::ostringstream oss;
switch (info.param) {
case blink::kWebCryptoAlgorithmIdMlDsa44:
oss << "44";
break;
case blink::kWebCryptoAlgorithmIdMlDsa65:
oss << "65";
break;
case blink::kWebCryptoAlgorithmIdMlDsa87:
oss << "87";
break;
default:
NOTREACHED();
}
return oss.str();
});
} // namespace
} // namespace webcrypto