From da49b9c548b9a4b63ae436eb0f3aae8b2e07ed56 Mon Sep 17 00:00:00 2001 From: Falko Strenzke Date: Wed, 4 Feb 2026 15:52:46 +0100 Subject: [PATCH 01/25] first try --- src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp index 3512ca74ac0..08e041b749e 100644 --- a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp +++ b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp @@ -6,17 +6,28 @@ * Botan is released under the Simplified BSD License (see license.txt) */ +#include "botan/asn1_obj.h" #include #include +#include + +#include + namespace Botan { secure_vector ML_DSA_Expanding_Keypair_Codec::encode_keypair(DilithiumInternalKeypair keypair) const { BOTAN_ASSERT_NONNULL(keypair.second); const auto& seed = keypair.second->seed(); + const DilithiumSerializedPrivateKey& expandedKey_strong = Dilithium_Algos::encode_keypair(keypair); BOTAN_ARG_CHECK(seed.has_value(), "Cannot encode keypair without the private seed"); - return seed.value().get(); + secure_vector result; + DER_Encoder der_enc(result); + der_enc.start_explicit(0).encode(seed.value().get(), ASN1_Type(Botan::ASN1_Type::OctetString) ).end_explicit(); + std::cerr << "println encoder called\n"; + //return result; + return seed.value().get(); } DilithiumInternalKeypair ML_DSA_Expanding_Keypair_Codec::decode_keypair(std::span private_key_seed, From f34956cb413b01b41779d824914ef95e2291d9bc Mon Sep 17 00:00:00 2001 From: Falko Strenzke Date: Wed, 4 Feb 2026 17:58:31 +0100 Subject: [PATCH 02/25] some more attempts ml-dsa priv key --- .../pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp | 25 +++++++- src/tests/data/x509/mldsa/mldsa-priv-both.pem | 59 +++++++++++++++++++ .../x509/mldsa/mldsa-priv-expandend-only.pem | 58 ++++++++++++++++++ .../data/x509/mldsa/mldsa-priv-seed-only.pem | 4 ++ 4 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 src/tests/data/x509/mldsa/mldsa-priv-both.pem create mode 100644 src/tests/data/x509/mldsa/mldsa-priv-expandend-only.pem create mode 100644 src/tests/data/x509/mldsa/mldsa-priv-seed-only.pem diff --git a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp index 08e041b749e..d78b060a4b7 100644 --- a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp +++ b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp @@ -24,10 +24,29 @@ secure_vector ML_DSA_Expanding_Keypair_Codec::encode_keypair(DilithiumI BOTAN_ARG_CHECK(seed.has_value(), "Cannot encode keypair without the private seed"); secure_vector result; DER_Encoder der_enc(result); - der_enc.start_explicit(0).encode(seed.value().get(), ASN1_Type(Botan::ASN1_Type::OctetString) ).end_explicit(); + der_enc.start_context_specific(0).encode(seed.value().get(), ASN1_Type(Botan::ASN1_Type::OctetString) ).end_cons(); + + /* + * der_enc.start_context_specific(0).encode(seed.value().get(), ASN1_Type(Botan::ASN1_Type::OctetString) ).end_cons(); + * leads to + * + + 0 34: [0] { + <04 20> + 2 32: OCTET STRING + : BF B1 FE C1 89 8C F2 38 02 79 3C 34 98 1D C1 4D + : DC 18 10 0B F0 79 57 42 F5 FE FD E3 78 60 8F D2 + : } + * which OpenSSL cannot decode. The previous format, which only contains the seed as OS without the [0]-tag, it can decode. + * Reason: it has to look like this test vector from : + <80 20> + 0 32: [0] + : 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F + : 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F + */ std::cerr << "println encoder called\n"; - //return result; - return seed.value().get(); + return result; + //return seed.value().get(); } DilithiumInternalKeypair ML_DSA_Expanding_Keypair_Codec::decode_keypair(std::span private_key_seed, diff --git a/src/tests/data/x509/mldsa/mldsa-priv-both.pem b/src/tests/data/x509/mldsa/mldsa-priv-both.pem new file mode 100644 index 00000000000..505c61df888 --- /dev/null +++ b/src/tests/data/x509/mldsa/mldsa-priv-both.pem @@ -0,0 +1,59 @@ +-----BEGIN PRIVATE KEY----- +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 +-----END PRIVATE KEY----- + + diff --git a/src/tests/data/x509/mldsa/mldsa-priv-expandend-only.pem b/src/tests/data/x509/mldsa/mldsa-priv-expandend-only.pem new file mode 100644 index 00000000000..67414c3fc51 --- /dev/null +++ b/src/tests/data/x509/mldsa/mldsa-priv-expandend-only.pem @@ -0,0 +1,58 @@ +-----BEGIN PRIVATE KEY----- +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 +-----END PRIVATE KEY----- + + diff --git a/src/tests/data/x509/mldsa/mldsa-priv-seed-only.pem b/src/tests/data/x509/mldsa/mldsa-priv-seed-only.pem new file mode 100644 index 00000000000..93febbe04c7 --- /dev/null +++ b/src/tests/data/x509/mldsa/mldsa-priv-seed-only.pem @@ -0,0 +1,4 @@ +-----BEGIN PRIVATE KEY----- +MDQCAQAwCwYJYIZIAWUDBAMRBCKAIAABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZ +GhscHR4f +-----END PRIVATE KEY----- From 3af9918d1137166d91f61ed0251efc1165b96edf Mon Sep 17 00:00:00 2001 From: Falko Strenzke Date: Thu, 5 Feb 2026 08:53:27 +0100 Subject: [PATCH 03/25] correct RFC-conforming seed encoding for ML-DSA --- .../pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp index d78b060a4b7..f76212bc404 100644 --- a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp +++ b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp @@ -24,29 +24,18 @@ secure_vector ML_DSA_Expanding_Keypair_Codec::encode_keypair(DilithiumI BOTAN_ARG_CHECK(seed.has_value(), "Cannot encode keypair without the private seed"); secure_vector result; DER_Encoder der_enc(result); - der_enc.start_context_specific(0).encode(seed.value().get(), ASN1_Type(Botan::ASN1_Type::OctetString) ).end_cons(); - + der_enc.encode(seed.value().get(), ASN1_Type(Botan::ASN1_Type::OctetString) /*real_type*/ , ASN1_Type(Botan::ASN1_Type(0)), Botan::ASN1_Class::ContextSpecific ); /* - * der_enc.start_context_specific(0).encode(seed.value().get(), ASN1_Type(Botan::ASN1_Type::OctetString) ).end_cons(); - * leads to * - - 0 34: [0] { - <04 20> - 2 32: OCTET STRING - : BF B1 FE C1 89 8C F2 38 02 79 3C 34 98 1D C1 4D - : DC 18 10 0B F0 79 57 42 F5 FE FD E3 78 60 8F D2 - : } - * which OpenSSL cannot decode. The previous format, which only contains the seed as OS without the [0]-tag, it can decode. - * Reason: it has to look like this test vector from : <80 20> 0 32: [0] : 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F : 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F - */ - std::cerr << "println encoder called\n"; + * + * The previous format, which only contains the seed as OS without the [0]-tag, it can decode. Apparentyl, it has this feature as a non-standard compatibility fallback for legacy formats. + * Reason: it has to look like this test vector from : + */ return result; - //return seed.value().get(); } DilithiumInternalKeypair ML_DSA_Expanding_Keypair_Codec::decode_keypair(std::span private_key_seed, From 17c051bf206916351ab109a50cc25de67ffe51b1 Mon Sep 17 00:00:00 2001 From: Falko Strenzke Date: Thu, 5 Feb 2026 13:25:38 +0100 Subject: [PATCH 04/25] decoding of seed-only RFC format --- .../pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp index f76212bc404..628ab205634 100644 --- a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp +++ b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp @@ -7,13 +7,25 @@ */ #include "botan/asn1_obj.h" +#include "botan/exceptn.h" #include #include #include +#include -#include +namespace { + + Botan::DilithiumInternalKeypair try_decode_seed_only(std::span key_bits, + Botan::DilithiumConstants mode) + { + Botan::secure_vector seed; + Botan::BER_Decoder(key_bits).decode(seed, Botan::ASN1_Type::OctetString, Botan::ASN1_Type(0), Botan::ASN1_Class::ContextSpecific).verify_end(); + return Botan::Dilithium_Algos::expand_keypair(Botan::DilithiumSeedRandomness(seed), std::move(mode)); + } + +} namespace Botan { @@ -38,9 +50,17 @@ secure_vector ML_DSA_Expanding_Keypair_Codec::encode_keypair(DilithiumI return result; } -DilithiumInternalKeypair ML_DSA_Expanding_Keypair_Codec::decode_keypair(std::span private_key_seed, +DilithiumInternalKeypair ML_DSA_Expanding_Keypair_Codec::decode_keypair(std::span private_key_bits, DilithiumConstants mode) const { - return Dilithium_Algos::expand_keypair(DilithiumSeedRandomness(private_key_seed), std::move(mode)); + /* we have to check 3 different format: "seed-only", "expanded-only", and "both" + */ + try { + return try_decode_seed_only(private_key_bits, mode); + } catch (Botan::Decoding_Error const& e) + { + // pass + } + throw Decoding_Error("unsupported ML-DSA private key format"); } } // namespace Botan From c3d19d28569ec0c334313518c2055733470193e7 Mon Sep 17 00:00:00 2001 From: Falko Strenzke Date: Thu, 5 Feb 2026 14:18:05 +0100 Subject: [PATCH 05/25] refactoring only --- src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp index 628ab205634..94f0f3777e7 100644 --- a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp +++ b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp @@ -16,6 +16,7 @@ #include namespace { +typedef Botan::DilithiumInternalKeypair (*decoding_func)(std::span key_bits, Botan::DilithiumConstants mode); Botan::DilithiumInternalKeypair try_decode_seed_only(std::span key_bits, Botan::DilithiumConstants mode) @@ -24,6 +25,16 @@ namespace { Botan::BER_Decoder(key_bits).decode(seed, Botan::ASN1_Type::OctetString, Botan::ASN1_Type(0), Botan::ASN1_Class::ContextSpecific).verify_end(); return Botan::Dilithium_Algos::expand_keypair(Botan::DilithiumSeedRandomness(seed), std::move(mode)); } + Botan::DilithiumInternalKeypair try_decode_expanded_only(std::span /*key_bits*/, + Botan::DilithiumConstants /*mode*/) + { + throw Botan::Decoding_Error("ML-DSA private format 'expanded-only' not supported"); + } + Botan::DilithiumInternalKeypair try_decode_both(std::span /*key_bits*/, + Botan::DilithiumConstants /*mode*/) + { + throw Botan::Decoding_Error("ML-DSA private format 'both' not supported"); + } } @@ -54,8 +65,12 @@ DilithiumInternalKeypair ML_DSA_Expanding_Keypair_Codec::decode_keypair(std::spa DilithiumConstants mode) const { /* we have to check 3 different format: "seed-only", "expanded-only", and "both" */ + decoding_func fn_arr [3] = {try_decode_both, try_decode_seed_only, try_decode_expanded_only}; try { - return try_decode_seed_only(private_key_bits, mode); + for(auto fn : fn_arr) + { + return fn(private_key_bits, mode); + } } catch (Botan::Decoding_Error const& e) { // pass From 9e2702a842c1f47e67775fa6d299ed10343fa3d0 Mon Sep 17 00:00:00 2001 From: Falko Strenzke Date: Mon, 9 Feb 2026 11:27:03 +0100 Subject: [PATCH 06/25] merge with test-check-func renaming --- .../dilithium_common/dilithium_algos.cpp | 2 +- .../pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp | 59 +++++++++++++------ src/tests/test_dilithium.cpp | 6 +- 3 files changed, 46 insertions(+), 21 deletions(-) diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.cpp b/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.cpp index 5b320b49d52..98bfad6531c 100644 --- a/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.cpp +++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.cpp @@ -15,6 +15,7 @@ * Botan is released under the Simplified BSD License (see license.txt) */ +#include #include #include @@ -396,7 +397,6 @@ DilithiumSerializedPrivateKey encode_keypair(const DilithiumInternalKeypair& key BOTAN_ASSERT_NOMSG(stuffer.full()); CT::unpoison(serialization); - return serialization; } diff --git a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp index 94f0f3777e7..43a88e5feda 100644 --- a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp +++ b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp @@ -14,10 +14,13 @@ #include #include +#include + namespace { typedef Botan::DilithiumInternalKeypair (*decoding_func)(std::span key_bits, Botan::DilithiumConstants mode); + Botan::DilithiumInternalKeypair try_decode_seed_only(std::span key_bits, Botan::DilithiumConstants mode) { @@ -40,15 +43,30 @@ typedef Botan::DilithiumInternalKeypair (*decoding_func)(std::span ML_DSA_Expanding_Keypair_Codec::encode_keypair(DilithiumInternalKeypair keypair) const { - BOTAN_ASSERT_NONNULL(keypair.second); - const auto& seed = keypair.second->seed(); - const DilithiumSerializedPrivateKey& expandedKey_strong = Dilithium_Algos::encode_keypair(keypair); - BOTAN_ARG_CHECK(seed.has_value(), "Cannot encode keypair without the private seed"); - secure_vector result; - DER_Encoder der_enc(result); - der_enc.encode(seed.value().get(), ASN1_Type(Botan::ASN1_Type::OctetString) /*real_type*/ , ASN1_Type(Botan::ASN1_Type(0)), Botan::ASN1_Class::ContextSpecific ); - /* +secure_vector ML_DSA_Expanding_Keypair_Codec::encode_keypair( + DilithiumInternalKeypair keypair) const { + BOTAN_ASSERT_NONNULL(keypair.second); + const auto &seed = keypair.second->seed(); + BOTAN_ARG_CHECK( + seed.has_value(), + "Cannot encode keypair without the private seed"); // TODO: WHEN NOT + // HAVING SEED, + // ENCODE AS + // EXPANDED-ONLY IN + // CASE OF ML-DSA. + // DILITHIUM FAILS WITHOUT SEED. + if(!keypair.second->mode().is_ml_dsa()) + { + // return the raw seed for dilithium + return seed.value().get(); + } + secure_vector result; + DER_Encoder der_enc(result); + der_enc.encode(seed.value().get(), + ASN1_Type(Botan::ASN1_Type::OctetString) /*real_type*/, + ASN1_Type(Botan::ASN1_Type(0)), + Botan::ASN1_Class::ContextSpecific); + /* * <80 20> 0 32: [0] @@ -63,19 +81,26 @@ secure_vector ML_DSA_Expanding_Keypair_Codec::encode_keypair(DilithiumI DilithiumInternalKeypair ML_DSA_Expanding_Keypair_Codec::decode_keypair(std::span private_key_bits, DilithiumConstants mode) const { + if(private_key_bits.size() == 32) + { + // backwards compatibility (not RFC 9881 conforming) to raw seed format. + return Botan::Dilithium_Algos::expand_keypair(Botan::DilithiumSeedRandomness(private_key_bits), std::move(mode)); + } /* we have to check 3 different format: "seed-only", "expanded-only", and "both" */ decoding_func fn_arr [3] = {try_decode_both, try_decode_seed_only, try_decode_expanded_only}; - try { for(auto fn : fn_arr) { - return fn(private_key_bits, mode); - } - } catch (Botan::Decoding_Error const& e) - { - // pass - } - throw Decoding_Error("unsupported ML-DSA private key format"); + try + { + return fn(private_key_bits, mode); + } + catch (Botan::Decoding_Error const& e) + { + // pass + } + } + throw Decoding_Error("unsupported ML-DSA private key format, key size in bytes: " + std::to_string(private_key_bits.size())); } } // namespace Botan diff --git a/src/tests/test_dilithium.cpp b/src/tests/test_dilithium.cpp index 06d527347bc..40732b13126 100644 --- a/src/tests/test_dilithium.cpp +++ b/src/tests/test_dilithium.cpp @@ -49,9 +49,9 @@ class Dilithium_KAT_Tests : public Text_Based_Test { auto dilithium_test_rng = std::make_unique(ref_seed); const Botan::Dilithium_PrivateKey priv_key(*dilithium_test_rng, DerivedT::mode); - - result.test_bin_eq( - "generated expected private key hash", sha3_256->process(priv_key.private_key_bits()), ref_sk_hash); + // TODO: PROBLEM WITH PRIVATE KEY HASH + // result.test_bin_eq( + // "generated expected private key hash", sha3_256->process(priv_key.private_key_bits()), ref_sk_hash); result.test_bin_eq( "generated expected public key hash", sha3_256->process(priv_key.public_key_bits()), ref_pk_hash); From 9367dbef8ac87f066ac42d3c24c7abb2fc417fab Mon Sep 17 00:00:00 2001 From: Falko Strenzke Date: Tue, 10 Feb 2026 10:05:21 +0100 Subject: [PATCH 07/25] merge with test-func-renaming --- .../dilithium/dilithium_common/dilithium.cpp | 5 + .../dilithium/dilithium_common/dilithium.h | 4 + .../dilithium_common/dilithium_constants.h | 13 ++ .../dilithium_common/dilithium_keys.h | 8 ++ .../pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp | 136 ++++++++++-------- src/lib/pubkey/pqcrystals/pqcrystals.h | 4 + src/tests/test_dilithium.cpp | 60 +++++++- 7 files changed, 165 insertions(+), 65 deletions(-) diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp b/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp index ea94b520fa7..3eb53a557a3 100644 --- a/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp +++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp @@ -427,6 +427,11 @@ secure_vector Dilithium_PrivateKey::private_key_bits() const { return m_private->mode().keypair_codec().encode_keypair({m_public, m_private}); } +bool Dilithium_PrivateKey::is_mldsa() const { + return m_private->mode().is_ml_dsa(); +} + + std::unique_ptr Dilithium_PrivateKey::create_signature_op(RandomNumberGenerator& rng, std::string_view params, std::string_view provider) const { diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium.h b/src/lib/pubkey/dilithium/dilithium_common/dilithium.h index cd5efae0aca..a8609279da7 100644 --- a/src/lib/pubkey/dilithium/dilithium_common/dilithium.h +++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium.h @@ -52,6 +52,8 @@ class BOTAN_PUBLIC_API(3, 0) DilithiumMode { Mode mode() const { return m_mode; } + bool operator==(const DilithiumMode& other) const = default; + private: Mode m_mode; }; @@ -142,6 +144,8 @@ class BOTAN_PUBLIC_API(3, 0) Dilithium_PrivateKey final : public virtual Dilithi std::string_view params, std::string_view provider) const override; + bool is_mldsa() const; + private: friend class Dilithium_Signature_Operation; diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium_constants.h b/src/lib/pubkey/dilithium/dilithium_common/dilithium_constants.h index 8fdcab94b6a..6a0b6df5be1 100644 --- a/src/lib/pubkey/dilithium/dilithium_common/dilithium_constants.h +++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium_constants.h @@ -168,6 +168,19 @@ class DilithiumConstants final { Dilithium_Keypair_Codec& keypair_codec() const { return *m_keypair_codec; } + bool operator==(const DilithiumConstants& other) const { + return m_mode == other.m_mode && m_tau == other.m_tau && m_lambda == other.m_lambda && + m_gamma1 == other.m_gamma1 && m_gamma2 == other.m_gamma2 && m_k == other.m_k && m_l == other.m_l && + m_eta == other.m_eta && m_beta == other.m_beta && m_omega == other.m_omega && + m_public_key_bytes == other.m_public_key_bytes && + m_commitment_hash_full_bytes == other.m_commitment_hash_full_bytes && + m_private_key_bytes == other.m_private_key_bytes && m_public_key_bytes == other.m_public_key_bytes && + m_signature_bytes == other.m_signature_bytes && + m_serialized_commitment_bytes == + other + .m_serialized_commitment_bytes; // && *m_symmetric_primitives == *other.m_symmetric_primitives); + } + private: DilithiumMode m_mode; diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium_keys.h b/src/lib/pubkey/dilithium/dilithium_common/dilithium_keys.h index cde8df3b7ec..a691a8d3807 100644 --- a/src/lib/pubkey/dilithium/dilithium_common/dilithium_keys.h +++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium_keys.h @@ -58,6 +58,8 @@ class Dilithium_PublicKeyInternal { const DilithiumConstants& mode() const { return m_mode; } + bool operator==(const Dilithium_PublicKeyInternal& other) const = default; + private: const DilithiumConstants m_mode; DilithiumSeedRho m_rho; @@ -93,6 +95,12 @@ class Dilithium_PrivateKeyInternal { const DilithiumPolyVec& t0() const { return m_t0; } + bool operator==(const Dilithium_PrivateKeyInternal& other) const { + // exclude the generative seed from the comparison, as it might not have been provided + return this->m_s1 == other.m_s1 && this->m_s2 == other.m_s2 && this->m_t0 == other.m_t0 && + this->m_signing_seed == other.m_signing_seed; + } + void _const_time_poison() const { // Note: m_rho and m_tr is public knowledge CT::poison_all(m_signing_seed, m_s1, m_s2, m_t0); diff --git a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp index 43a88e5feda..7aa2ce13da5 100644 --- a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp +++ b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp @@ -8,65 +8,81 @@ #include "botan/asn1_obj.h" #include "botan/exceptn.h" +#include "botan/internal/dilithium_types.h" #include #include -#include #include +#include #include - namespace { -typedef Botan::DilithiumInternalKeypair (*decoding_func)(std::span key_bits, Botan::DilithiumConstants mode); +typedef Botan::DilithiumInternalKeypair (*decoding_func)(std::span key_bits, + Botan::DilithiumConstants mode); +Botan::DilithiumInternalKeypair try_decode_seed_only(std::span key_bits, + Botan::DilithiumConstants mode) { + Botan::secure_vector seed; + Botan::BER_Decoder(key_bits) + .decode(seed, Botan::ASN1_Type::OctetString, Botan::ASN1_Type(0), Botan::ASN1_Class::ContextSpecific) + .verify_end(); + return Botan::Dilithium_Algos::expand_keypair(Botan::DilithiumSeedRandomness(seed), std::move(mode)); +} - Botan::DilithiumInternalKeypair try_decode_seed_only(std::span key_bits, - Botan::DilithiumConstants mode) - { - Botan::secure_vector seed; - Botan::BER_Decoder(key_bits).decode(seed, Botan::ASN1_Type::OctetString, Botan::ASN1_Type(0), Botan::ASN1_Class::ContextSpecific).verify_end(); - return Botan::Dilithium_Algos::expand_keypair(Botan::DilithiumSeedRandomness(seed), std::move(mode)); - } - Botan::DilithiumInternalKeypair try_decode_expanded_only(std::span /*key_bits*/, - Botan::DilithiumConstants /*mode*/) - { - throw Botan::Decoding_Error("ML-DSA private format 'expanded-only' not supported"); - } - Botan::DilithiumInternalKeypair try_decode_both(std::span /*key_bits*/, - Botan::DilithiumConstants /*mode*/) - { - throw Botan::Decoding_Error("ML-DSA private format 'both' not supported"); - } +Botan::DilithiumInternalKeypair try_decode_expanded_only(std::span key_bits, + Botan::DilithiumConstants mode) { + Botan::secure_vector expanded; + Botan::BER_Decoder(key_bits).decode(expanded, Botan::ASN1_Type::OctetString).verify_end(); + Botan::DilithiumInternalKeypair key_pair = + Botan::Dilithium_Algos::decode_keypair(Botan::DilithiumSerializedPrivateKey(expanded), mode); + return key_pair; +} +Botan::DilithiumInternalKeypair try_decode_both(std::span key_bits, Botan::DilithiumConstants mode) { + Botan::secure_vector expanded; + Botan::secure_vector seed; + Botan::BER_Decoder(key_bits) + .start_sequence() + .decode(seed, Botan::ASN1_Type::OctetString) + .decode(expanded, Botan::ASN1_Type::OctetString) + .end_cons() + .verify_end(); + Botan::DilithiumInternalKeypair key_pair = + Botan::Dilithium_Algos::decode_keypair(Botan::DilithiumSerializedPrivateKey(expanded), mode); + Botan::DilithiumInternalKeypair key_pair_from_seed = + Botan::Dilithium_Algos::expand_keypair(Botan::DilithiumSeedRandomness(seed), std::move(mode)); + if(*key_pair_from_seed.second != *key_pair.second) { + throw Botan::Decoding_Error("seed and expanded key in ML-DSA serialized key do not match"); + } + return key_pair; } +} // namespace + namespace Botan { -secure_vector ML_DSA_Expanding_Keypair_Codec::encode_keypair( - DilithiumInternalKeypair keypair) const { - BOTAN_ASSERT_NONNULL(keypair.second); - const auto &seed = keypair.second->seed(); - BOTAN_ARG_CHECK( - seed.has_value(), - "Cannot encode keypair without the private seed"); // TODO: WHEN NOT - // HAVING SEED, - // ENCODE AS - // EXPANDED-ONLY IN - // CASE OF ML-DSA. - // DILITHIUM FAILS WITHOUT SEED. - if(!keypair.second->mode().is_ml_dsa()) - { +secure_vector ML_DSA_Expanding_Keypair_Codec::encode_keypair(DilithiumInternalKeypair keypair) const { + BOTAN_ASSERT_NONNULL(keypair.second); + const auto& seed = keypair.second->seed(); + BOTAN_ARG_CHECK(seed.has_value(), + "Cannot encode keypair without the private seed"); // TODO: WHEN NOT + // HAVING SEED, + // ENCODE AS + // EXPANDED-ONLY IN + // CASE OF ML-DSA. + // DILITHIUM FAILS WITHOUT SEED. + if(!keypair.second->mode().is_ml_dsa()) { // return the raw seed for dilithium return seed.value().get(); - } - secure_vector result; - DER_Encoder der_enc(result); - der_enc.encode(seed.value().get(), - ASN1_Type(Botan::ASN1_Type::OctetString) /*real_type*/, - ASN1_Type(Botan::ASN1_Type(0)), - Botan::ASN1_Class::ContextSpecific); - /* + } + secure_vector result; + DER_Encoder der_enc(result); + der_enc.encode(seed.value().get(), + ASN1_Type(Botan::ASN1_Type::OctetString) /*real_type*/, + ASN1_Type(Botan::ASN1_Type(0)), + Botan::ASN1_Class::ContextSpecific); + /* * <80 20> 0 32: [0] @@ -76,31 +92,27 @@ secure_vector ML_DSA_Expanding_Keypair_Codec::encode_keypair( * The previous format, which only contains the seed as OS without the [0]-tag, it can decode. Apparentyl, it has this feature as a non-standard compatibility fallback for legacy formats. * Reason: it has to look like this test vector from : */ - return result; + return result; } DilithiumInternalKeypair ML_DSA_Expanding_Keypair_Codec::decode_keypair(std::span private_key_bits, DilithiumConstants mode) const { - if(private_key_bits.size() == 32) - { - // backwards compatibility (not RFC 9881 conforming) to raw seed format. - return Botan::Dilithium_Algos::expand_keypair(Botan::DilithiumSeedRandomness(private_key_bits), std::move(mode)); - } - /* we have to check 3 different format: "seed-only", "expanded-only", and "both" + if(private_key_bits.size() == 32) { + // backwards compatibility (not RFC 9881 conforming) to raw seed format. + return Botan::Dilithium_Algos::expand_keypair(Botan::DilithiumSeedRandomness(private_key_bits), std::move(mode)); + } + /* we have to check 3 different format: "seed-only", "expanded-only", and "both" */ - decoding_func fn_arr [3] = {try_decode_both, try_decode_seed_only, try_decode_expanded_only}; - for(auto fn : fn_arr) - { - try - { - return fn(private_key_bits, mode); - } - catch (Botan::Decoding_Error const& e) - { - // pass - } - } - throw Decoding_Error("unsupported ML-DSA private key format, key size in bytes: " + std::to_string(private_key_bits.size())); + decoding_func fn_arr[3] = {try_decode_both, try_decode_seed_only, try_decode_expanded_only}; + for(auto fn : fn_arr) { + try { + return fn(private_key_bits, mode); + } catch(const Botan::Decoding_Error& e) { + // pass + } + } + throw Decoding_Error("unsupported ML-DSA private key format, key size in bytes: " + + std::to_string(private_key_bits.size())); } } // namespace Botan diff --git a/src/lib/pubkey/pqcrystals/pqcrystals.h b/src/lib/pubkey/pqcrystals/pqcrystals.h index d3ee23eb60a..aff94875893 100644 --- a/src/lib/pubkey/pqcrystals/pqcrystals.h +++ b/src/lib/pubkey/pqcrystals/pqcrystals.h @@ -271,6 +271,8 @@ class Polynomial { return *this; } + bool operator==(const ThisPolynomial& other) const { return std::equal(this->m_coeffs.begin(), this->m_coeffs.end(), other.m_coeffs.begin(), other.m_coeffs.end()); } + ~Polynomial() = default; constexpr size_t size() const { return m_coeffs.size(); } @@ -411,6 +413,8 @@ class PolynomialVector { ThisPolynomialVector& operator=(ThisPolynomialVector&& other) noexcept = default; ~PolynomialVector() = default; + bool operator==(const ThisPolynomialVector& other) const { return m_vec == other.m_vec; } + size_t size() const { return m_vec.size(); } constexpr Domain domain() const noexcept { return D; } diff --git a/src/tests/test_dilithium.cpp b/src/tests/test_dilithium.cpp index 40732b13126..c3ca4732bd7 100644 --- a/src/tests/test_dilithium.cpp +++ b/src/tests/test_dilithium.cpp @@ -9,7 +9,11 @@ * Botan is released under the Simplified BSD License (see license.txt) */ +#include "botan/pk_keys.h" +#include "test_rng.h" #include "tests.h" +#include +#include #if defined(BOTAN_HAS_DILITHIUM_COMMON) #include @@ -49,9 +53,11 @@ class Dilithium_KAT_Tests : public Text_Based_Test { auto dilithium_test_rng = std::make_unique(ref_seed); const Botan::Dilithium_PrivateKey priv_key(*dilithium_test_rng, DerivedT::mode); - // TODO: PROBLEM WITH PRIVATE KEY HASH - // result.test_bin_eq( - // "generated expected private key hash", sha3_256->process(priv_key.private_key_bits()), ref_sk_hash); + + if(!priv_key.is_mldsa()) { + result.test_bin_eq( + "generated expected private key hash", sha3_256->process(priv_key.private_key_bits()), ref_sk_hash); + } result.test_bin_eq( "generated expected public key hash", sha3_256->process(priv_key.public_key_bits()), ref_pk_hash); @@ -288,4 +294,52 @@ BOTAN_REGISTER_TEST("pubkey", "dilithium_keygen", Dilithium_Keygen_Tests); } // namespace +#if defined(BOTAN_HAS_DILITHIUM_COMMON) && defined(BOTAN_HAS_SHA3) +class MLDSA_Privkey_Tests : public Text_Based_Test { + public: + MLDSA_Privkey_Tests() : Text_Based_Test("mldsa_pkcs8.vec", "key") {} + + Test::Result run_one_test(const std::string& name, const VarMap& vars) override { + Test::Result result(name); + const std::vector key_bits = vars.get_req_bin("key"); + bool expect_decoding_failure = false; + Botan::DilithiumMode mode = Botan::DilithiumMode::ML_DSA_4x4; + if(name.starts_with("mldsa-44")) { + mode = Botan::DilithiumMode::ML_DSA_4x4; + } else if(name.starts_with("mldsa-65")) { + mode = Botan::DilithiumMode::ML_DSA_6x5; + } else if(name.starts_with("mldsa-87")) { + mode = Botan::DilithiumMode::ML_DSA_8x7; + } else { + throw Botan_Tests::Test_Error( + "internal error for ML-DSA test vector: encountered unknown ml-dsa mode string (test-case-specific token)"); + } + if(name.ends_with("-invalid")) { + expect_decoding_failure = true; + } + std::unique_ptr priv_key; + try { + priv_key = std::make_unique(key_bits, mode); + } catch(const Botan::Decoding_Error& e) { + result.confirm("invalid ML-DSA key rejected", expect_decoding_failure); + return result; + } + std::vector ref_msg = {0, 1, 2, 4}; + std::vector rng_seed(48); + Botan_Tests::CTR_DRBG_AES256 rng(rng_seed); + auto signer = Botan::PK_Signer(*priv_key, rng, "Randomized"); + auto signature = signer.sign_message(ref_msg.data(), ref_msg.size(), rng); + + const Botan::Dilithium_PublicKey pub_key(priv_key->public_key_bits(), mode); + auto verifier = Botan::PK_Verifier(pub_key, ""); + verifier.update(ref_msg.data(), ref_msg.size()); + result.confirm("signature verifies", verifier.check_signature(signature.data(), signature.size())); + return result; + } +}; + +BOTAN_REGISTER_TEST("pubkey", "mldsa-pkcs8-decoding", MLDSA_Privkey_Tests); + +#endif + } // namespace Botan_Tests From 35bfbe83989a963421779191ce93ab09312ca058 Mon Sep 17 00:00:00 2001 From: Falko Strenzke Date: Tue, 10 Feb 2026 10:43:15 +0100 Subject: [PATCH 08/25] add mldsa priv key encoding w/o seed --- .../pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp | 44 +++++++------- src/tests/data/mldsa_privkey.vec | 23 ++++++++ src/tests/data/x509/mldsa/mldsa-priv-both.pem | 59 ------------------- .../x509/mldsa/mldsa-priv-expandend-only.pem | 58 ------------------ .../data/x509/mldsa/mldsa-priv-seed-only.pem | 4 -- src/tests/test_dilithium.cpp | 6 +- 6 files changed, 51 insertions(+), 143 deletions(-) create mode 100644 src/tests/data/mldsa_privkey.vec delete mode 100644 src/tests/data/x509/mldsa/mldsa-priv-both.pem delete mode 100644 src/tests/data/x509/mldsa/mldsa-priv-expandend-only.pem delete mode 100644 src/tests/data/x509/mldsa/mldsa-priv-seed-only.pem diff --git a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp index 7aa2ce13da5..2946a3274b3 100644 --- a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp +++ b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp @@ -65,33 +65,35 @@ namespace Botan { secure_vector ML_DSA_Expanding_Keypair_Codec::encode_keypair(DilithiumInternalKeypair keypair) const { BOTAN_ASSERT_NONNULL(keypair.second); const auto& seed = keypair.second->seed(); - BOTAN_ARG_CHECK(seed.has_value(), - "Cannot encode keypair without the private seed"); // TODO: WHEN NOT - // HAVING SEED, - // ENCODE AS - // EXPANDED-ONLY IN - // CASE OF ML-DSA. - // DILITHIUM FAILS WITHOUT SEED. if(!keypair.second->mode().is_ml_dsa()) { // return the raw seed for dilithium + BOTAN_ARG_CHECK(seed.has_value(), "Cannot encode keypair without the private seed"); return seed.value().get(); } secure_vector result; DER_Encoder der_enc(result); - der_enc.encode(seed.value().get(), - ASN1_Type(Botan::ASN1_Type::OctetString) /*real_type*/, - ASN1_Type(Botan::ASN1_Type(0)), - Botan::ASN1_Class::ContextSpecific); - /* - * - <80 20> - 0 32: [0] - : 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F - : 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F - * - * The previous format, which only contains the seed as OS without the [0]-tag, it can decode. Apparentyl, it has this feature as a non-standard compatibility fallback for legacy formats. - * Reason: it has to look like this test vector from : - */ + if(!seed.has_value()) { + // encode expanded-only format + DilithiumSerializedPrivateKey expanded = Dilithium_Algos::encode_keypair(keypair); + der_enc.encode(expanded.get(), ASN1_Type::OctetString); + } else { + // encode seed-only format + der_enc.encode(seed.value().get(), + ASN1_Type(Botan::ASN1_Type::OctetString) /*real_type*/, + ASN1_Type(Botan::ASN1_Type(0)), + Botan::ASN1_Class::ContextSpecific); + /* + * + <80 20> + 0 32: [0] + : 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F + : 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F + * + * Note: The previous format, which only contains the seed as an OCTET STRING without the [0]-tag, can still be decoded by OpenSSL. Apparently, it has this feature as a non-standard compatibility fallback for legacy formats. + * Reason: it has to look like this test vector from : + */ + } + return result; } diff --git a/src/tests/data/mldsa_privkey.vec b/src/tests/data/mldsa_privkey.vec new file mode 100644 index 00000000000..baea2a45d4e --- /dev/null +++ b/src/tests/data/mldsa_privkey.vec @@ -0,0 +1,23 @@ +# Test vectors from https://www.rfc-editor.org/rfc/rfc9881.html#name-example-private-keys +[mldsa-44] +# seed-only +key = 8020000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +# expanded-only +key = 04820a00d7b2b47254aae0db45e7930d4a98d2c97d8f1397d1789dafa17024b316e9bec939ce0f7f77f8db5644dcda366bfe4734bd95f435ff9a613aa54aa41c2c694c04329a07b1fabb48f52a309f11a1898f848e2322ffe623ec810db3bee33685854a88269da320d5120bfcfe89a18e30f7114d83aa404a646b6c997389860d12522ee0006e2384819186619b260d118664d4a62822184482402898146148a6614c4248a19208c2382951244808a125c2083108c47120140914836c18a78084106ec9c07022b56408b0610c070498124451886959004622932041062e42b64c01164914284c41a85180460a5116515a0820022244dc9849d13251e13065d3c08592a85112a1640039220946621cc70cd9086dd0062652408580443091062c50c80924c5841a966d4a982c99066da4443220a7645a326e11b57020926124138e04852c0a4872c8a051d3082a99208058242024074e59148810a46460c06de0b28d1b1909203422c024410943710a212061a2015222521b80809a340013934dd3322922170a9892691a14512027219cc02062a2814818691a854d8344695b2041031242cb184601a90d0c023183b0215a224ac89205d9906904306a4b064ad2b2011c404081423252327254a6405a18100c321292c2805212625c82280bb46c03428d53100c14010ee1365288842491020a63462620062911c228d0204802b36ca236095a8648cbb4618b4662c440821a890910024d24b24520122524c90588288cc9c04d5948220a276ec134644c90605b445082864943880443b28c603080a2882d84a46d8ca629d0c68442064689885100a98d01498de4380da4068dd3947142b26c1a84611ba32842b42808a0711ac531e0a04c013765242862142890091061d940221b3360090292d02481200408491844a3222d5c8844149808a446610195640b390a0c9450ca406ad2b220c0380182308e13b908918084148829c0189112350da02422e20406d9c2850428121cc989180272d24029c20812d8062a9994719bb8682384291a2289144511dc82445096450c4484c0b2049aa60543862c44326e88442120a84c9a3070e3b82d63268803254903438c48a809ca147253344e1243081ba704593022d99480e234228142129c302a9434266104452426281346094a326d11280918b82562281113410d41b21190844c8b1212a2c688c9c030220606d2188e848630904452128831d9207113c52843060e033060cca6845826524c88011ef72562c85ffa43acfa49217f2b172d7bbc14620e6d980a71aabbdf0c45e9a206ecb1423fee15decc17601300149d9223cd6e6c6e1fa8e41fc7c64938ab68905fd3dcda50d87082e7d0d71d1bc9b2b84c85523ca8fe6cad294adf83be15b108ff721d0cc87bc3dd3a7590184b0e845663a91fc9e1c3c53a61d867420b04f092355753bc65a06368fd41295fd09924132c6f91f67964c142674a725c343914c4cecf58c074bcaf4558c97bf7911e07aa6d0938f2ee2bb3c1a8c595d635e84342fdea01dc24b211ad2fc281cf77e59110c7abc54bf0c86d480b9be276471dc9d603cee98cfdab3e9fcfb703793560549ea4450fa7b33fb9169c44b4d25fb9c457f49791cd3da03eac96095813c105132ccda4e63e49228cd23d8a1f37856f142d93b90db09f82af89258c63aab8047a80c036c9357ea2046f8dc6354f0c5295f342bb417d3cfeb0b1fd33622c29e14cbbd92e1363c65ebd4504b7512329b9670e32e1b2c67a54e7f1a55f8b9f9ea04e8ca3a705e62a3c5e637374afb7aeb6ddea612cde28f01a202d7aa4e34722d27dd3f9b89894d019fd5d4d7119efe3723bba104cb8bb0981e074de3afe200daaaead826cc45f244dbf431afab34efbdf782474d2fd57118f646214934ed99cba3b003e8d67a3836f6f19fc41910ce5163ee3ae99eb84d514eb761e63684ea56f9791d2dd4aac6e6168b948c817f75a222acb0e8cdc03cc4afe8f67157e1a363b7faeff9f172b98913677c5a1dd085e9ee4c22052c1af58193116673dcd3bfc5f34b855dcc6c77885649e9e71f43d4aea0f4b72ca7eda0578ba13d31a658d2d060a9a66ff69ed1be7997a2fb1d2723d38f9bfabe18f8e7b3cda906e4e9b5e942c8eaeb296070ebfd364947a940cc978bed66b37749e6d5dcd7be8c494440e2b84cecfefb98c0bedfb3c41e3359d2cd7197fbe720c48aa6c6b6465c1ee63e3569c2adc744491370b7f7826fe0b77a1d19d64101d032b918106b42d2ef73747e5601fe4ba50f23ede521f031a817d15294a43722e8378784b6db0cf1ba9e8ae911d9201b9ce9cc3019c6f5c27cb98da26144b64225a7c932b30f761e78a2d59a1d8b83ec6344a2f6dd47e765706d00bf4a79a6a926c3ba91d812c8f2c797ab1796709e5d16856778293529f0286d015c3b5399619642a333e9e593d6e3f5353994208e9e6a332851d7f652522a928b917e27e2d6d42137dfe2ebfa6fb1c67b26c0254528685f7ebdbe315a68eaa2da769e8a9f42d3e60007c71330926b2c0012d83ead4e4fd1ed872ccd1972201d2b027f3545ac2d30cd78bc1d740feccbc6fc2a0446c6e30eac51f5a69098aa2d447f2085b4e4e4b92ccc26921d2de478518cd090ce267aea2d27ada57fd88b4976d89fb843cdccf49a76ca2679e6801bfa7fb031896fb50629704b9923936bb5dd385311121cadfb11995e59b73034cf67ed03ab813867648d025828087e949a9afd16b95d72d99b1edca257aac132ffb7a0709aed5a9c0ff05fb0f2bbf28409eed7b5f5801be964ced019e1cb7851d3851f10290674e19ffb008b301c4acf641a2bb14216e1d69cabf52b5ef227496b0f30799a855d117fad3744a6fa33503ea798b52ddd7ee5426609dbfcd3f0c13b164d6c051f7ed4a119719a712e388d328402081ff1354b554d2c237afed3b151c4ba8e9f4bdeb8499a3066e26bbc69e8af089dec71731d1dc529eab17ef7374734c0fe475494c83836bdd34a03b9bc89914716061bfb98ec6e61c3ed4438edcaf25243c647086b9ea7018b0d9a8a0b00cecb00abde2498d69c2336101a772cbe4f571523f51bd05882cdf358b849cc140aa1faf22423a12851ce0e33fd48975a4959fa5c5fe418c93908191ab6e741b77bfe02cbd698ee795c466d615619e6441382c6eac01834ee9ab73cea80bbe235c78da91bd79b6f82f899785d68700d393e675c2224d6b7a1ad21320495679adaed70167b50866713a53109db7b6f7d81304ecdfd83b319b1ef248306b45ad29e7ddcc863dac56048b5d69ea175011f7614c00a86a863cde1872a8932878b9ac7e1ac5bda4997b72064f0cd75f4c814e034de11acb9013cf7ea926b4e7eaace070c7ba2188efad2e431e1223d45dd05c4d8403c2e45cee6413ecbe7527e873e455c4e610a61839aacc0bd56d2483e78f298b66a478eb2f558cbafca86be847baeb02c5b216c8cd88fea4df249b09e670a20703abac24b0a91abc4a5646601442ba10becfd30993880051d07f56a05a9379e7a8e6befee3f22faa106398f7706006e42e9be1ef89d25c272f11a95095c587d713732284de9dbd3c7217b0689e21d8eb0ff69668 +# both +key = 30820a260420000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f04820a00d7b2b47254aae0db45e7930d4a98d2c97d8f1397d1789dafa17024b316e9bec939ce0f7f77f8db5644dcda366bfe4734bd95f435ff9a613aa54aa41c2c694c04329a07b1fabb48f52a309f11a1898f848e2322ffe623ec810db3bee33685854a88269da320d5120bfcfe89a18e30f7114d83aa404a646b6c997389860d12522ee0006e2384819186619b260d118664d4a62822184482402898146148a6614c4248a19208c2382951244808a125c2083108c47120140914836c18a78084106ec9c07022b56408b0610c070498124451886959004622932041062e42b64c01164914284c41a85180460a5116515a0820022244dc9849d13251e13065d3c08592a85112a1640039220946621cc70cd9086dd0062652408580443091062c50c80924c5841a966d4a982c99066da4443220a7645a326e11b57020926124138e04852c0a4872c8a051d3082a99208058242024074e59148810a46460c06de0b28d1b1909203422c024410943710a212061a2015222521b80809a340013934dd3322922170a9892691a14512027219cc02062a2814818691a854d8344695b2041031242cb184601a90d0c023183b0215a224ac89205d9906904306a4b064ad2b2011c404081423252327254a6405a18100c321292c2805212625c82280bb46c03428d53100c14010ee1365288842491020a63462620062911c228d0204802b36ca236095a8648cbb4618b4662c440821a890910024d24b24520122524c90588288cc9c04d5948220a276ec134644c90605b445082864943880443b28c603080a2882d84a46d8ca629d0c68442064689885100a98d01498de4380da4068dd3947142b26c1a84611ba32842b42808a0711ac531e0a04c013765242862142890091061d940221b3360090292d02481200408491844a3222d5c8844149808a446610195640b390a0c9450ca406ad2b220c0380182308e13b908918084148829c0189112350da02422e20406d9c2850428121cc989180272d24029c20812d8062a9994719bb8682384291a2289144511dc82445096450c4484c0b2049aa60543862c44326e88442120a84c9a3070e3b82d63268803254903438c48a809ca147253344e1243081ba704593022d99480e234228142129c302a9434266104452426281346094a326d11280918b82562281113410d41b21190844c8b1212a2c688c9c030220606d2188e848630904452128831d9207113c52843060e033060cca6845826524c88011ef72562c85ffa43acfa49217f2b172d7bbc14620e6d980a71aabbdf0c45e9a206ecb1423fee15decc17601300149d9223cd6e6c6e1fa8e41fc7c64938ab68905fd3dcda50d87082e7d0d71d1bc9b2b84c85523ca8fe6cad294adf83be15b108ff721d0cc87bc3dd3a7590184b0e845663a91fc9e1c3c53a61d867420b04f092355753bc65a06368fd41295fd09924132c6f91f67964c142674a725c343914c4cecf58c074bcaf4558c97bf7911e07aa6d0938f2ee2bb3c1a8c595d635e84342fdea01dc24b211ad2fc281cf77e59110c7abc54bf0c86d480b9be276471dc9d603cee98cfdab3e9fcfb703793560549ea4450fa7b33fb9169c44b4d25fb9c457f49791cd3da03eac96095813c105132ccda4e63e49228cd23d8a1f37856f142d93b90db09f82af89258c63aab8047a80c036c9357ea2046f8dc6354f0c5295f342bb417d3cfeb0b1fd33622c29e14cbbd92e1363c65ebd4504b7512329b9670e32e1b2c67a54e7f1a55f8b9f9ea04e8ca3a705e62a3c5e637374afb7aeb6ddea612cde28f01a202d7aa4e34722d27dd3f9b89894d019fd5d4d7119efe3723bba104cb8bb0981e074de3afe200daaaead826cc45f244dbf431afab34efbdf782474d2fd57118f646214934ed99cba3b003e8d67a3836f6f19fc41910ce5163ee3ae99eb84d514eb761e63684ea56f9791d2dd4aac6e6168b948c817f75a222acb0e8cdc03cc4afe8f67157e1a363b7faeff9f172b98913677c5a1dd085e9ee4c22052c1af58193116673dcd3bfc5f34b855dcc6c77885649e9e71f43d4aea0f4b72ca7eda0578ba13d31a658d2d060a9a66ff69ed1be7997a2fb1d2723d38f9bfabe18f8e7b3cda906e4e9b5e942c8eaeb296070ebfd364947a940cc978bed66b37749e6d5dcd7be8c494440e2b84cecfefb98c0bedfb3c41e3359d2cd7197fbe720c48aa6c6b6465c1ee63e3569c2adc744491370b7f7826fe0b77a1d19d64101d032b918106b42d2ef73747e5601fe4ba50f23ede521f031a817d15294a43722e8378784b6db0cf1ba9e8ae911d9201b9ce9cc3019c6f5c27cb98da26144b64225a7c932b30f761e78a2d59a1d8b83ec6344a2f6dd47e765706d00bf4a79a6a926c3ba91d812c8f2c797ab1796709e5d16856778293529f0286d015c3b5399619642a333e9e593d6e3f5353994208e9e6a332851d7f652522a928b917e27e2d6d42137dfe2ebfa6fb1c67b26c0254528685f7ebdbe315a68eaa2da769e8a9f42d3e60007c71330926b2c0012d83ead4e4fd1ed872ccd1972201d2b027f3545ac2d30cd78bc1d740feccbc6fc2a0446c6e30eac51f5a69098aa2d447f2085b4e4e4b92ccc26921d2de478518cd090ce267aea2d27ada57fd88b4976d89fb843cdccf49a76ca2679e6801bfa7fb031896fb50629704b9923936bb5dd385311121cadfb11995e59b73034cf67ed03ab813867648d025828087e949a9afd16b95d72d99b1edca257aac132ffb7a0709aed5a9c0ff05fb0f2bbf28409eed7b5f5801be964ced019e1cb7851d3851f10290674e19ffb008b301c4acf641a2bb14216e1d69cabf52b5ef227496b0f30799a855d117fad3744a6fa33503ea798b52ddd7ee5426609dbfcd3f0c13b164d6c051f7ed4a119719a712e388d328402081ff1354b554d2c237afed3b151c4ba8e9f4bdeb8499a3066e26bbc69e8af089dec71731d1dc529eab17ef7374734c0fe475494c83836bdd34a03b9bc89914716061bfb98ec6e61c3ed4438edcaf25243c647086b9ea7018b0d9a8a0b00cecb00abde2498d69c2336101a772cbe4f571523f51bd05882cdf358b849cc140aa1faf22423a12851ce0e33fd48975a4959fa5c5fe418c93908191ab6e741b77bfe02cbd698ee795c466d615619e6441382c6eac01834ee9ab73cea80bbe235c78da91bd79b6f82f899785d68700d393e675c2224d6b7a1ad21320495679adaed70167b50866713a53109db7b6f7d81304ecdfd83b319b1ef248306b45ad29e7ddcc863dac56048b5d69ea175011f7614c00a86a863cde1872a8932878b9ac7e1ac5bda4997b72064f0cd75f4c814e034de11acb9013cf7ea926b4e7eaace070c7ba2188efad2e431e1223d45dd05c4d8403c2e45cee6413ecbe7527e873e455c4e610a61839aacc0bd56d2483e78f298b66a478eb2f558cbafca86be847baeb02c5b216c8cd88fea4df249b09e670a20703abac24b0a91abc4a5646601442ba10becfd30993880051d07f56a05a9379e7a8e6befee3f22faa106398f7706006e42e9be1ef89d25c272f11a95095c587d713732284de9dbd3c7217b0689e21d8eb0ff69668 +[mldsa-65] +# seed-only +key = 8020000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +#expanded-only +key = 04820fc048683d91978e31eb3dddb8b0473482d2b88a5f625949fd8f58a561e696bd4c27d853fa69b8199023e8cd678dd9fabf9047646ffd0cb3cc7f795805a71e70d2371b0563e3cd3346149c8c9ebcf23b0a4e5a900eea9c6562790a7c63e38663daa2dddb6e480dc405a1e701948b74841ef5cc1c3f2bf327972e9510510cd5375ecc0855717711872221862381000424778061475007501717035504515125471838046175722244108868608646012747567180870666864332444122043638667502823634244322057364106455547722755681433614625508206437685468754353751068718333805475052580752818843811087260202008588301836113828212061711578768788878643754601657155084718866072732880664741856762180318276641578245025646643113504364780126673143011660655864718368863503847861101202356116137860785321240075478823043666116604255418285605367785638434430632610770731784272141116530385276867460150823735320766107504681248066603032652312445408800318088767217307182472151278011654474866172233380866064468352158420368011802118183317735453488100448653674370577258833460384232856810060426042584560235682051838638432421224245645858677145728504788717180618836086864156508116502646700608266227383172407257300727288620667588682607064020330343663155464245345667187345658370225084685628807036708462371710065717584778708655537822351446772856730322870014332061715845526632502651334777380355164313473510662751757402468881706743468186017652453330872104343401032287635155265081307745444168154183636411204026873043677712808846355453006245810458365124842780345166635843785601465115742321436685224777313450178362420550006484471234408800604735405783336308210615225207248851348637067622588571265673476816464684258708122705500838320023208066345336003346857247063554003577122752307142536874374570056643224482852072183330205337334077278055253063525040673346131807280717248377634573185851602333443625164338160858773462428830070365853755007552315037021324630437086806361503030043586357080211066473463522620330438021085287578321078867480856347436734284058466841437005510873426447721127384736526472577144704178644260247118740812216605847178137067680817058185585471363421075580163583585184403847110338742628247741365544270734635777500662562684202124683864616646031225388845400845734464754472560546166846630880638271563287183840652247681160662130330186802801384630505657238758365723230688046122606651675570532413227673517080153001628460134887701118815571315464311704732882856368234555041862765631111687505104254414427852211171788153685157447166255365583630250285576875327137103723705714761713651841242366444664143520521085157033363860258426628148110546268173038756433216588568663632813406254012040886547886171657623726234867030115115632050753502122108426531435567111525720106853630150557586058784314313278788087384788637881813873426178388524667733506021151464238232680135440783475385535752832335187601152134325773333655188615816168241842212230841448151201103024777242544366067717707603014525403500183873237735265086357113734481605277456553730085837785035121115480628850180268138652053468013207241803213005723864076427114101838525510632607104865176833828572762354518735083132886376661426311675033112553764176031433177212234418a82e4f5c9ea0faf99eb04d78a7332711117c33f18eca21f8743376ada5219804a7ed9a5557fcd67a3550b3a4b8c588629c021475fa3d56d5d6cfbb1a09bda8d14de622ddff16d8bc99b14278a8af1d76bed157672dd9c32316f97e8daadef8d9da69586725567fb96b59990d4bf0bc9c195b90b74295f5675b24257c2710c175b0153f2911328c2eb7abb9ad46e70a8b53c39ea642cee4b3cb42620e863ce8b650ce8adcd923721a1687023c673a8cbb6b03d51cd197e8c346ebadce93950f88cee201db9e320843e29f300d9a19500d70a4caf272c69e4eef69fbb8a55efd7ca2bed990d2d3b582848f9c45c2abc54cfc47d34f06c0ffa56fcd762ab9cba9146d7725218963b240d72b6d22c93171fbd47788b76e72042def0878d23df631a1a1e5a6027686de5b4a10e91069c8f2ba0259b04d6409da96567ca52da497026e583a0ecefc1f01e6b988e21f9767a2b7e1672deb9a1e2a3fcc863aa91517c334620601b4fe79730e934935f4b6fbc4e32695145c2b5f6a127fecc0a277451ebc3fd523444f9ee7c9c34534f356db544fc31c1bfde5f65c77ea2f7c2eae4c55ebaf104271c566fd4ebac71c7a62c74952817ae675504d9599b1b762b6aca168a83248c9d9adb0ceb1556e5759490bbc0c7900795ad72123038b662f64f106a9993681a25d59af7bc97a235be9284c5bc45a6c90cb1c2999c663d96b478e2307f85548957d65740e2673e9ebd1352829038f462b8fd3b5681da55c0252523853525ea0ad647e71ac2c5a8893e603ac97e56c04ceb2f26f5c5b4b6d94ab811380fd00f2208fe86535086aebfd35c29120624c04fbb6113929d9c556350253766c209fdba83c95fccd342a28099355d00bc863f4eef596eb0b42ebcc7c79491cceae205ea0b8059fbb8a5726c5949d2b15e7e29c51fc9b02ee1a4fc357b5f1bef9c4add46a2a920c2fbf08a37eb1514bfa15110a4392a74c6f13c50c5cffd97531098d7cd23b60eb35c4a428b46c55386e1010c4ba7f70e4c7ecb7575f3063a71e84dfdcf09a58b2cdb0f99f27ed378610d25cbad7bfa6ba0d59189cfe88eab9b46d7e6db0307eabe4198e99bd71f779ab66581e0912fc7b1d2585245e9a12687a975cd5e8e1dcc045d5f891c4c685db07cf81e77389b363eb6bdfe39b27ff84c97eefee162e3b451fe6914719cb6436d855960ff915d7cea6adeafdfc1c05786c49f923a474ffdfc3153a06e6ed0b0ad220d72524434d5273c0aab6dde4e91476d581a2695a60de6d9f44d77aa08266e938eeb4a9597c9b64986059e49262a4eab2454e14015ad0536c42733a5d77d7995c2a20446009ebfe5632c80c08ed2b97af35066489f597eb1b1f11f04f60e0c9040159c44ab3e60e0a15229d191228bed17bbc3ac939b3c67cee135f352c27216c9c31f72a3e87040c5f619306eb0b6cca2a9ce7b22a1694d00ca9c05e315126457f26ce84f9617241860782f864b473d84017491902b1bdc8cdc5800dd46127fb80a71c095b473a562529b3b1e7e437e158a5f6666e9974d005b062c2309e6dce98f9b658c6e3f9a216d58c8c9142bd1c8c85a9da872ebbfad3fea9d9aba2b68c0e8f19c6ff5f00584d45daf9d6c9d69ed04b8da8d687258b77807927612c530446fea7697ae3f926698929bc6a5a8cf3e2024c0f0c5ee57b5869bf981881caf9e3665fc7f7efc678929f87a56eaa42ea4d1ff6691822dd79a47096b776d1d8f01456e5873b0738406c382c573ae9cde2d9e7f231b6cc5c676e7cf43963373013a58075381ff0949be084546d72e4f8a3e5fe4aa5091add234e2afe0030b1b663ae9d2d32410986b9402aaaf2465b74a5e2d0bc38e3a92bbddd8a1fed7b948c23cce6f8c08fe356835ba65b0f984068616ef48138efd89bf357a54d2ebbf376cbdcc69c5f1f61c64d2794bc06ccb9abdf66e25085d8c830e2ae3b0fe0f07a7af8b9320bf342970997d67d7c12593a8fbfade635aac53083a7022c47d5f77a52b57b598da9392ae6d86afc46fc06455181b9c75a646dc21f81e4bf213753de737fd2a140027920add35a223f9f5f4465ceb60c03ed0455a333a5cc83adbf43f1f42c2ccb8328c21c7ab7faed2b21cfade2da55223aaab2af9b41c7332341746341b39aa2f43815650f5480511424cfa6901779c4d18b638cc0287aaaf31680338d20b17c7449fdc6a278a8d96a82ee4c4eca40125e2d65290071c7aef1be6a991598fb9d59512523bcd4b38c566b8e80a73ae333e134414327ef1d83c47c49dfe7936df1338a5e247787868fc84fdcb95ac89c185c4bb5fd57b2338ac42b41c10a823df39624f36b15a2f067584e06ca2e08ccaff1618fe01dd06df3512e0b724dec8506da24215acacc2c51b82ad8d302002fb41068b1da4f8bb147987b3516bad5dbddf01318fd3fa9bc43702ac498c719d95f2e841b622a5e4848a3c5c262959992ea7a7d72ca8a368028f497dfad93355cbb1bb9786d14ff2cf590317848f95856427110dda36f5192a816ce9c8816cc7bbfc804efc40085a3850b89f1e7fe5656dba410f906a97c32336c1ae7e81737a83e087354e428da8538d948dbf5dfacb59dd2b5fd3bc803f4ba432c9a739df2cfa9ed9484320f97edff1a48c6b86b3002cfb772dd5e562bc4c3d683ed964b6199fa0514b0790d958095b7b85c6be875fbb559e1930146ccea63a388a194fe09c3dea03be52de27e901017afe809af630a7382bf5c4cd4d1b8f41579fb4348ede4ca05f4cd3f139a31b2544e516dbe4086b9bb4b2bed47e2d230982dd5192429d377b7c0745cc068e2f5a4aa04c7ff87209ed1259976a0fc9b25e9e851d4e3502c02c85d6dff029e211d01ebf0e9e7188d568f8437d813b0f122f2fb17603b693ed9c38f17cfd50b815e6d9dfc0ed2ccf19f6399274a1420f235a59d8bf724345e14e45d9e4be8934dfc3fa92678db61d7118bf53cb8a2225b335f7eae50e3f941237628db76d8ea38f77a72af3a26c81fe43523b335535a5d1db7c38f341082bb5734d089e8ae309cfda3a0bcb5cd5b097113c8edf9616aa4f6e6631b9125276fb3f680a34341c3db668dc6cad45fc93b2708ca2af75ccce734fd191c50089dad53982fddae02531ff93e1f21ff395fc0a12874edf06b6f9647e95a7324586c71dfd91d901d621858190fecd00ccd110bbac59f96cb884c3c93994748a56f41283bfc41fb89052153a894588c3cb9017f3d66326c985637e575acb812346342654025d602de3ba940c19ac1a633dffda977b529b8013e19c1d6d0680f4dae62c924450ae66aab82f21473061dab3d62b247f907e3551939ad3f5465e9d08a82bfea17eea1b6b2b923757477f993000b2f43b70f28aaab1fe9a26ad1fd3361616c0b0e242fe76604b7033a1f30e97e28f526ca3c880fe2b8d9d1b0c9ff188b31cb9d97425acab9b216d98a6ae355e583da71e8864ee3d16b0759796190ef545c1e62bfef92af6ca147b13244d6c892fc8ef223ab3f43f924c2f466097ee8 +# both +key = 30820fe60420000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f04820fc048683d91978e31eb3dddb8b0473482d2b88a5f625949fd8f58a561e696bd4c27d853fa69b8199023e8cd678dd9fabf9047646ffd0cb3cc7f795805a71e70d2371b0563e3cd3346149c8c9ebcf23b0a4e5a900eea9c6562790a7c63e38663daa2dddb6e480dc405a1e701948b74841ef5cc1c3f2bf327972e9510510cd5375ecc0855717711872221862381000424778061475007501717035504515125471838046175722244108868608646012747567180870666864332444122043638667502823634244322057364106455547722755681433614625508206437685468754353751068718333805475052580752818843811087260202008588301836113828212061711578768788878643754601657155084718866072732880664741856762180318276641578245025646643113504364780126673143011660655864718368863503847861101202356116137860785321240075478823043666116604255418285605367785638434430632610770731784272141116530385276867460150823735320766107504681248066603032652312445408800318088767217307182472151278011654474866172233380866064468352158420368011802118183317735453488100448653674370577258833460384232856810060426042584560235682051838638432421224245645858677145728504788717180618836086864156508116502646700608266227383172407257300727288620667588682607064020330343663155464245345667187345658370225084685628807036708462371710065717584778708655537822351446772856730322870014332061715845526632502651334777380355164313473510662751757402468881706743468186017652453330872104343401032287635155265081307745444168154183636411204026873043677712808846355453006245810458365124842780345166635843785601465115742321436685224777313450178362420550006484471234408800604735405783336308210615225207248851348637067622588571265673476816464684258708122705500838320023208066345336003346857247063554003577122752307142536874374570056643224482852072183330205337334077278055253063525040673346131807280717248377634573185851602333443625164338160858773462428830070365853755007552315037021324630437086806361503030043586357080211066473463522620330438021085287578321078867480856347436734284058466841437005510873426447721127384736526472577144704178644260247118740812216605847178137067680817058185585471363421075580163583585184403847110338742628247741365544270734635777500662562684202124683864616646031225388845400845734464754472560546166846630880638271563287183840652247681160662130330186802801384630505657238758365723230688046122606651675570532413227673517080153001628460134887701118815571315464311704732882856368234555041862765631111687505104254414427852211171788153685157447166255365583630250285576875327137103723705714761713651841242366444664143520521085157033363860258426628148110546268173038756433216588568663632813406254012040886547886171657623726234867030115115632050753502122108426531435567111525720106853630150557586058784314313278788087384788637881813873426178388524667733506021151464238232680135440783475385535752832335187601152134325773333655188615816168241842212230841448151201103024777242544366067717707603014525403500183873237735265086357113734481605277456553730085837785035121115480628850180268138652053468013207241803213005723864076427114101838525510632607104865176833828572762354518735083132886376661426311675033112553764176031433177212234418a82e4f5c9ea0faf99eb04d78a7332711117c33f18eca21f8743376ada5219804a7ed9a5557fcd67a3550b3a4b8c588629c021475fa3d56d5d6cfbb1a09bda8d14de622ddff16d8bc99b14278a8af1d76bed157672dd9c32316f97e8daadef8d9da69586725567fb96b59990d4bf0bc9c195b90b74295f5675b24257c2710c175b0153f2911328c2eb7abb9ad46e70a8b53c39ea642cee4b3cb42620e863ce8b650ce8adcd923721a1687023c673a8cbb6b03d51cd197e8c346ebadce93950f88cee201db9e320843e29f300d9a19500d70a4caf272c69e4eef69fbb8a55efd7ca2bed990d2d3b582848f9c45c2abc54cfc47d34f06c0ffa56fcd762ab9cba9146d7725218963b240d72b6d22c93171fbd47788b76e72042def0878d23df631a1a1e5a6027686de5b4a10e91069c8f2ba0259b04d6409da96567ca52da497026e583a0ecefc1f01e6b988e21f9767a2b7e1672deb9a1e2a3fcc863aa91517c334620601b4fe79730e934935f4b6fbc4e32695145c2b5f6a127fecc0a277451ebc3fd523444f9ee7c9c34534f356db544fc31c1bfde5f65c77ea2f7c2eae4c55ebaf104271c566fd4ebac71c7a62c74952817ae675504d9599b1b762b6aca168a83248c9d9adb0ceb1556e5759490bbc0c7900795ad72123038b662f64f106a9993681a25d59af7bc97a235be9284c5bc45a6c90cb1c2999c663d96b478e2307f85548957d65740e2673e9ebd1352829038f462b8fd3b5681da55c0252523853525ea0ad647e71ac2c5a8893e603ac97e56c04ceb2f26f5c5b4b6d94ab811380fd00f2208fe86535086aebfd35c29120624c04fbb6113929d9c556350253766c209fdba83c95fccd342a28099355d00bc863f4eef596eb0b42ebcc7c79491cceae205ea0b8059fbb8a5726c5949d2b15e7e29c51fc9b02ee1a4fc357b5f1bef9c4add46a2a920c2fbf08a37eb1514bfa15110a4392a74c6f13c50c5cffd97531098d7cd23b60eb35c4a428b46c55386e1010c4ba7f70e4c7ecb7575f3063a71e84dfdcf09a58b2cdb0f99f27ed378610d25cbad7bfa6ba0d59189cfe88eab9b46d7e6db0307eabe4198e99bd71f779ab66581e0912fc7b1d2585245e9a12687a975cd5e8e1dcc045d5f891c4c685db07cf81e77389b363eb6bdfe39b27ff84c97eefee162e3b451fe6914719cb6436d855960ff915d7cea6adeafdfc1c05786c49f923a474ffdfc3153a06e6ed0b0ad220d72524434d5273c0aab6dde4e91476d581a2695a60de6d9f44d77aa08266e938eeb4a9597c9b64986059e49262a4eab2454e14015ad0536c42733a5d77d7995c2a20446009ebfe5632c80c08ed2b97af35066489f597eb1b1f11f04f60e0c9040159c44ab3e60e0a15229d191228bed17bbc3ac939b3c67cee135f352c27216c9c31f72a3e87040c5f619306eb0b6cca2a9ce7b22a1694d00ca9c05e315126457f26ce84f9617241860782f864b473d84017491902b1bdc8cdc5800dd46127fb80a71c095b473a562529b3b1e7e437e158a5f6666e9974d005b062c2309e6dce98f9b658c6e3f9a216d58c8c9142bd1c8c85a9da872ebbfad3fea9d9aba2b68c0e8f19c6ff5f00584d45daf9d6c9d69ed04b8da8d687258b77807927612c530446fea7697ae3f926698929bc6a5a8cf3e2024c0f0c5ee57b5869bf981881caf9e3665fc7f7efc678929f87a56eaa42ea4d1ff6691822dd79a47096b776d1d8f01456e5873b0738406c382c573ae9cde2d9e7f231b6cc5c676e7cf43963373013a58075381ff0949be084546d72e4f8a3e5fe4aa5091add234e2afe0030b1b663ae9d2d32410986b9402aaaf2465b74a5e2d0bc38e3a92bbddd8a1fed7b948c23cce6f8c08fe356835ba65b0f984068616ef48138efd89bf357a54d2ebbf376cbdcc69c5f1f61c64d2794bc06ccb9abdf66e25085d8c830e2ae3b0fe0f07a7af8b9320bf342970997d67d7c12593a8fbfade635aac53083a7022c47d5f77a52b57b598da9392ae6d86afc46fc06455181b9c75a646dc21f81e4bf213753de737fd2a140027920add35a223f9f5f4465ceb60c03ed0455a333a5cc83adbf43f1f42c2ccb8328c21c7ab7faed2b21cfade2da55223aaab2af9b41c7332341746341b39aa2f43815650f5480511424cfa6901779c4d18b638cc0287aaaf31680338d20b17c7449fdc6a278a8d96a82ee4c4eca40125e2d65290071c7aef1be6a991598fb9d59512523bcd4b38c566b8e80a73ae333e134414327ef1d83c47c49dfe7936df1338a5e247787868fc84fdcb95ac89c185c4bb5fd57b2338ac42b41c10a823df39624f36b15a2f067584e06ca2e08ccaff1618fe01dd06df3512e0b724dec8506da24215acacc2c51b82ad8d302002fb41068b1da4f8bb147987b3516bad5dbddf01318fd3fa9bc43702ac498c719d95f2e841b622a5e4848a3c5c262959992ea7a7d72ca8a368028f497dfad93355cbb1bb9786d14ff2cf590317848f95856427110dda36f5192a816ce9c8816cc7bbfc804efc40085a3850b89f1e7fe5656dba410f906a97c32336c1ae7e81737a83e087354e428da8538d948dbf5dfacb59dd2b5fd3bc803f4ba432c9a739df2cfa9ed9484320f97edff1a48c6b86b3002cfb772dd5e562bc4c3d683ed964b6199fa0514b0790d958095b7b85c6be875fbb559e1930146ccea63a388a194fe09c3dea03be52de27e901017afe809af630a7382bf5c4cd4d1b8f41579fb4348ede4ca05f4cd3f139a31b2544e516dbe4086b9bb4b2bed47e2d230982dd5192429d377b7c0745cc068e2f5a4aa04c7ff87209ed1259976a0fc9b25e9e851d4e3502c02c85d6dff029e211d01ebf0e9e7188d568f8437d813b0f122f2fb17603b693ed9c38f17cfd50b815e6d9dfc0ed2ccf19f6399274a1420f235a59d8bf724345e14e45d9e4be8934dfc3fa92678db61d7118bf53cb8a2225b335f7eae50e3f941237628db76d8ea38f77a72af3a26c81fe43523b335535a5d1db7c38f341082bb5734d089e8ae309cfda3a0bcb5cd5b097113c8edf9616aa4f6e6631b9125276fb3f680a34341c3db668dc6cad45fc93b2708ca2af75ccce734fd191c50089dad53982fddae02531ff93e1f21ff395fc0a12874edf06b6f9647e95a7324586c71dfd91d901d621858190fecd00ccd110bbac59f96cb884c3c93994748a56f41283bfc41fb89052153a894588c3cb9017f3d66326c985637e575acb812346342654025d602de3ba940c19ac1a633dffda977b529b8013e19c1d6d0680f4dae62c924450ae66aab82f21473061dab3d62b247f907e3551939ad3f5465e9d08a82bfea17eea1b6b2b923757477f993000b2f43b70f28aaab1fe9a26ad1fd3361616c0b0e242fe76604b7033a1f30e97e28f526ca3c880fe2b8d9d1b0c9ff188b31cb9d97425acab9b216d98a6ae355e583da71e8864ee3d16b0759796190ef545c1e62bfef92af6ca147b13244d6c892fc8ef223ab3f43f924c2f466097ee8 +[mldsa-87] +# seed-only +key = 8020000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +#both +key = 308213460420000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f048213209792bcec2f2430686a82fccf3c2f5ff665e771d7ab41b90258cfa7e90ec97124d8e9ee4e90a16c602f5ec9bc38517dc30e329d5ab27673bd85f4c9b0300f776389886750b57c24db3fc012e61ede59753337374fa7124991549af243496d0637cb3be05a5948235bf79875f896d8fe0cab30c84948db4d6315aaaf160ac6243664220148161109112c94028922452c62b84500452a08967090126e149370d446108444515896910ca92982b241c90871c428680496894840859b226d1c28645912419cb891840489449005cb3462a086904026922099291305695c3468a4328e19269259461009a44923424d1236615810650128901a334c998631d3a249098225431428c0388103154d5b2886088748233152942225c3c04da49821984020d14286cb40705bb0719c962cc112065346090c450214466e91b42154b08ce446429a208c0121251341055a402213c90ca0184052c230cb342c4bc8681ba4604984846330294aa0695b8004d2380a14264ce2b2448ba211244649c414520b427103b210922880012488e308110a052819c481002022dc446842122244002ac9266a0c8731e0c04499148418360d11374222188c63b2910c9808a1a0010892440413245c987182847184325251b0319c422e1aa82802089101c0890bc7058a246522c2644c88915c826813a56450208621040291944448223022394a02988409a28819192944488c22950ca104720487701221104206841b498589064ad33608dbc04058b6510ca7098c24619990648cc2905b249010a34903256143328a119844c8b22004384110472c19c6441c252c048830d946699b20001b46825aa4805ba0491890250026800bc2315a407254c620c1b03124b14d10952814000aa0c84d54a28823988160900402162c13214091868d08c291911426d0b40c09c66051442e04112600291193c20863a431220028c114080c402c4114069c20725422068b084da148691030411c284944188ecc9648d942501b06490c458823040d20302e23852c14073040b6854cc02044862019006c540248cc886c59063249148404c750134928e40609d3c610c8284c23394452a464cca8494438320a898400342c22858d1031090932651c898c40402921850009a16d84c064e2022d48044012098ee0422e93440812106a01840592308acb348ea2262e5c86110b3508181000023426242389d1840024466013b249242846180271a03890891444d3962da31840232721c0185043c80441428d5c264144a26d48120e4032250b14820a482ecb828803a3601b25268cb82024b08598042108a72c833864543289010401234984029569d1a44d13a40c91460d6194809038455cc65001172053c6289b1810411268901221c01084421692538229812649d8a4059a2624240329e04026d20248112468119989981485c9200d50128c1c0810021000009528c1289059b485d314650a406e11296518c24c213465d830691030521931668c188ac8c0084c9830a3a62041162218052e22252a64b8250ab30163208409800919280e021101a39411e3986c58202109411060362208067112c2855aa085c0c6845c3806cbb669148484532282a1a640cc8600c42622a0a808983472d4204143c4904816681b16521a370250204248488a201141e2006cc0c20c14064911314d19060a8946091b816544c800820670001672cc24508a42899c969064287092b268982662619440c11689d842641a214e62906421c8248b286d5c4292a0c64d0c8580cc884dd4428d42348a0b0451c32686242581123506a04404c894815bb4311c08065c240803276a20c225e1809019b46da3460c4b186050c62c1b922d111504a2000421482ed81606d2108a83a22508310d093851d948490b164c2332251919024a4409d1b2210b832c23258593168544a0441b83500222724b04809b146521936018130ad9460d224561c8b440a1422d02b8090014449bb6110b978c40104a82146ada90051c028e0c1972a3b48d24305011870964c628e4189298b46c61165140460e1c3248da205188368a23b1218290281a1532e2186192048e13b690131368c984684c406d0b330081464dd2380c049681a4885002908522b004d3a471d28010ca964051a641a48428e008520b308cd2380a0c2951c38209ca2091d83692a3a628924222a216011a348637d9a659169881ec21cf4811869d1d7f139f0537e96f1184585405fd17808af1e06239d3b34e5aca8bf1369677b447ac718ac47d850c4d77b0be31dc9f508e3978f24274ab0185f727abdff59f4490371bf04610e364e64ec875ef9d20dc94077e1e166327a879b8ab516160b2a3f77437b9b3cc7d17aeaddc84db62746a35ac096f782f62a7f01aa6d6693deec90b23c66985a02307e0a1cae598a67324dba0f52f22432275e93257065c3b7e5e1cfe1dfd4d0df086df21243414a2d27e20230a829be4eb4c82c16d35f78b0e5e198332e00074bb64612fab17d4c8971cb68e5edab0369f1157b3469abd8384e2d9553f1b78e786e1ee9d0b98d39f83ccecf37d1ebd3a9d63aec766164a10171a4fd8c63daf182c421258c5f529aa55cb7ebae2e1652315e1f71e8a74131410d03247ede11d34db91f6f08aa2478fd789679c04949f71bc0171e07e3a8bb5753dbbdaa411a6350ab46eefbf86fc551c29efe4cdd7661d5cf6c3db22d0cedde599854459d97f20df7455bdf356a198d0f7eb6d34111fc940b25c0543b788edda9d26810eac3d6cc9c51327c2cf83e887d4089e19695e11add837f6f440cc360f93f32fee8a9663712c6bbd38c84ab7b54823ec363eb7e42eb59fc1fce60fbd55307b3ec85fd9daf3206d7b4b3917f1c8b7a92e3c67d89880fdf2e47f5a0c994595db170af41babf5a25b4dc1c42dd6a9db271e764de2fb015a49a850c7919be47006a336e2e325fde53ac599554d0a7de4ef45ec40c39d6baff311beee75d89e02ad31f4be4bd20ae9194f5edddaa6650776116e9f270f77714ad7a8e89acef74b7ff7d8dbec27f8020a985247e2cdacef4894a4d68ba37ca912d6be73501c995181e5b77723350b3631da3700e13fd366e131bf06b36eb6b0345093209f0a7beffae1fdd875b00687c1163c353d7d2ac90937b34e978e92f821adc9662202ece89a17e7bb65ae17d83b90dbbe6a501a4e1345bee4e5a5b53af2e5ba3d1ef3f4e05adf0b3a4cf2e530360fee64929902b571f6fd2e305652a4cb010f79f815e18f2bbb8cc89fa6fc76f77c89e293cf175a0b195800fe72d2ccdd7d75e5bd90bc6ac435d6a440ef852e9a1c8c53de03bf193365d735aaf29c5162a617e364e7f944168d0fb48fef40558f454297cc3dd508662cf23fb88e1954aa45d1c5e115bcc36f05b3e098d555220f40be2629b34507b8464c54c27b5dec78da8f22650514797af86a2512bcb7e2923379ef6d73c137006c1b38f51e37f93585e29041a3e4e3af46007ce13b8b5f7b17d5d65d7d5668e427bcbe7ec1d7c408c054a48c1ae797bf99acbc8d2607522935fd665ea7822d930f23eabff783bb23697569e204b943141e00c08810956be0525365dbab54ed48cb76964ccdf5cbd3aee7282d4a0000d2784d7b8fab16b2f7f0d5225732b1efbc4eb1cfedeb43fde79b69ecc0fbeaa1e6b40728673bd4b2e98a0d4a8f02f853950730f28d35eb12fcc79768b8e18e4bda0e58a331a2f71d7ccc2d451b32b1c65c312acf47ee513b21954c41c00c873872ee94cf14f46037425361f4bdb54821f711460cebae8c07508a9219f88fa6bedaa678eed501944a16ae6f7b5bb7a2e1e357e70d7b98461a2c71cb0fa762d6ad9824081d37f292fd4be8b84c36110dc744360201beebe0bd6c9d05e869256d2ff3f99517b7efd2a33774056cb5671675a8b492e9f5f2620eb8ef9381d3d1df19938b7b5ffaac59bc8110fa87ba8d7a3d0165f8e41dd0f804f11b9ded0f352a597835d06307a8e0c6ef4d21904339e1cf458923a3e89e025d945347366c02f3dd6368d4e47e85d3d2a9705bd57961852e5a579f93b1c514c539f49ea1163a2a493b0efcb47f4748f6a99e10bf7078282e4ace18136e2a8b3ee0a380dcd3b3ef3e65e1b8157289d62467ad488ba0392b2e90a1ededcbdc931dc17298ccef76645c7d330a05c2ce40f89b85468f357a217751e154631304ec4e04bb45b3678909c74af51ce370364d8f4f7eb1e61e00287429c9961de8322ca9a2629b1309d800e92bc1dc5055dcc797f33866eb0cfd8d490250d48ffca8022f49290e2d5376162fbaa982d16453c825b35f6515635ea92bea72367baa54de3f9eaea69542a81a4127f71cbaa257f324fefef14f08fbd65a049cd2fb362594a8e23ff1a2617db5b158f6f01cf50ab0ed95c6e709841164108b06e1b40ab0ab11c408301d3d9d8ea69e968a9600b3d17f38011ce28074e2c2e10bf6197c602d8d0ce7d3a3ef2d89623bc9f12ea338791e9266bb8ce02b124c6c7929baea693244098454a080eb7523e13bb1b7c5b6775fabababbe9075fe5687aa451397bb9cfccd051243e9bf5aef24062d335de5fce24e9ddbde1191052d80c36df9f8434872f277ed4f5a1ce8ebd3b960824a4e4f1001b04cb685f9bee4d0ddb0c571598ac2021a6606fd23345c6fbb84f0ce05fe52734521b7b07c6388d3a3b99318bf0131504aa9dfbaf548f9d32a9cd4c6893524b11330a2d3aad3ed2a58966ebb0134465d543fd7797af549f568eaebe957f64fec854674902b97558756986946ea3ab7a251cbbea11a687bd43f5d0bd89cd2caba61d5218374990ee8b92219ed25dca011c68a9757c013bd837b2dd734e3751f64fcb4b23dcd6bc57ea567f5716e17367244751e2303b22a953e772756956cdcc013ffd2c32490754422a572529d4c92f1ebb19f1dad4d036f2fdf31ca9101bdf81aea948aedcf217aa8fccd7a0771aa2753e1a823bf41c95377a2ffa61b2265138153ce86d2c87dd07a4b32d27f5f2872641431ce9a18a502aaefd9afc5b0d13cd46c357e38e69e1ee945add1992932a5b1e5c5629c9f48f7661853da00787c9d78fb925553bf07a50dd5b9d935853420e4d1a71ae62ff90ca193cdd6c2f4bed263415aaf9a35094bc2a22e2a663c7645001cd190b7bc17c75feadf8e87ce5c24b763b6584ed32e71b0268142ea3ed6898157bf923bebf0192d1bf5ee30a7d351634a60b504dde38a2e114f7ae9bf176d4a18ba2895a7bb4b47444a9ba8dbb4c124cd41bbb32f4bcb1de48c4abb510607a001b5a000bba43618b6c19e43517b45b42405928b67c713881858bad3a42511c2716ff9cd332034b672b52ff16610805cdbe7544a8a84b66e1c745a73c1b6bcda5b77b951f36c0f7a5372de9e5d1f9bbcde8843c6909002dda4875e67571af0bec581856c32c09c240e664e761e57cd0d8dc8a71cb918a5762d111285cd8b5613ddbd0ca08ac0342b2bdee38f96fa754bb2b087179c113c93986a810356eb94540b93cb9dec4aa9290ff12ec1aa2e656c9be3d590753c366c601406c061bc22033a1fd1f4e1111d039b8813b983cb506c3ea7ff3057983e8bf01682fbb00f43005313c82c1392918a6165a13338ffe11a992c1fb3d1032aa679a418c8ba4f8a0bc199e10cf6bd77a14fdd6a06093514348e3a8974434ae8a3676369c6be2cf90e672b343fce04ac6b22e0cf47568bc45d70a68e68c649a4830ae218590c1a437e7a23a54efe44f67086eb697b9fa57835f0b8f70f0a929226efb336c0e21833a028218cd63732c80aa477e62d141dba81854f70da68daff4a84cb6de779254e8a97e73565374af4092af05cbd6654afc3fd72f0ae232695cb6668eafecc4069bd90bb528b83efa2fbcdbd93b289929621ed74d808738fc103eeb105510851fc9319f171ea0ced0b97b5b9fb5ef985186bc52098f9eb476f67b7cc7665d47587975cb45a50fc64100719bf76345f0fdf1e09efe9fb800dc114e46be0879a195cc06870e23d2631dae71c3994481c8761c40d07c5bfca95e718b7b22585af03ed34175a46d57af3518e32a7fc1aa4482732a81a87f724f8d2e780b3a39d451a380f75c2d680cc7213eab1d4a59d394ae3810a1c90818d52f93fb203e2d8b1b5fa8f60b2d585d9135d648846f138b86953242d2bb1f2ecdf389b4de7651817b8e4e64b333f1aac523a93f2748a9c38ffbc29ced457b6f9781b08a67a1975d031ccd71545c0037434056c2434d13e6c4beebf46fc12222c0b2eccd6159d5aea8e554d7a09652b06bf7ca699a7199e716d05dd553041a8f2b303d236a9babaafb9fa528f28a2ca2aa780b940383c099aa65a0074b83fd1f0bc5b7b5e46c25e54838b3cbcfc95f87f1d471b3ba894434fa58952fdcb77f161372693306dba4e8f216d1c8e5caff0fe8360a51c60763644169fdc6a8267f2e3f909a61b2a678bce6ae90403a836b1a7b7e8cd8b54c37087a9e14446d95e6908d2eedbfcc653e02fdf771f701a79b9e5a26ed0a947842070f3b5701742211219e761762c37f0d0a1d1b9750fee577e1208115c66ac07ec091e6a3fc4aa6a253bcba868edd3154dcaf5162f615e85490a6ca342f34c43ac61a3ea6bfeefd850e190eb1d8da4d28b5eceeb1678c02433ecd5d48b2536404257e8ca7bef5855f2b813ed2f4c409445a3317c9be1a35ae2fb4d2b87921b904bf2c14db514cee045251cfc276374db15c99dea15acde197c6eb524988e39b63287beb8676865aaa3bad1b43b8cab15cbf27a498759e3203abf369e97242f0b0154149f14ac233cdb73a22b7fb8f09325bf2ace83bb6b5db8a121a2b682149a69131ccce52229840b113fc7b0bcc58405bfe87f1f95ffc2e96fc5596567e94364dfaa6d9d5a6eb99ae4ddf424 +[mldsa-44-invalid] +# both (first test vector with changed seed) +key = 30820a2604200001F2030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f04820a00d7b2b47254aae0db45e7930d4a98d2c97d8f1397d1789dafa17024b316e9bec939ce0f7f77f8db5644dcda366bfe4734bd95f435ff9a613aa54aa41c2c694c04329a07b1fabb48f52a309f11a1898f848e2322ffe623ec810db3bee33685854a88269da320d5120bfcfe89a18e30f7114d83aa404a646b6c997389860d12522ee0006e2384819186619b260d118664d4a62822184482402898146148a6614c4248a19208c2382951244808a125c2083108c47120140914836c18a78084106ec9c07022b56408b0610c070498124451886959004622932041062e42b64c01164914284c41a85180460a5116515a0820022244dc9849d13251e13065d3c08592a85112a1640039220946621cc70cd9086dd0062652408580443091062c50c80924c5841a966d4a982c99066da4443220a7645a326e11b57020926124138e04852c0a4872c8a051d3082a99208058242024074e59148810a46460c06de0b28d1b1909203422c024410943710a212061a2015222521b80809a340013934dd3322922170a9892691a14512027219cc02062a2814818691a854d8344695b2041031242cb184601a90d0c023183b0215a224ac89205d9906904306a4b064ad2b2011c404081423252327254a6405a18100c321292c2805212625c82280bb46c03428d53100c14010ee1365288842491020a63462620062911c228d0204802b36ca236095a8648cbb4618b4662c440821a890910024d24b24520122524c90588288cc9c04d5948220a276ec134644c90605b445082864943880443b28c603080a2882d84a46d8ca629d0c68442064689885100a98d01498de4380da4068dd3947142b26c1a84611ba32842b42808a0711ac531e0a04c013765242862142890091061d940221b3360090292d02481200408491844a3222d5c8844149808a446610195640b390a0c9450ca406ad2b220c0380182308e13b908918084148829c0189112350da02422e20406d9c2850428121cc989180272d24029c20812d8062a9994719bb8682384291a2289144511dc82445096450c4484c0b2049aa60543862c44326e88442120a84c9a3070e3b82d63268803254903438c48a809ca147253344e1243081ba704593022d99480e234228142129c302a9434266104452426281346094a326d11280918b82562281113410d41b21190844c8b1212a2c688c9c030220606d2188e848630904452128831d9207113c52843060e033060cca6845826524c88011ef72562c85ffa43acfa49217f2b172d7bbc14620e6d980a71aabbdf0c45e9a206ecb1423fee15decc17601300149d9223cd6e6c6e1fa8e41fc7c64938ab68905fd3dcda50d87082e7d0d71d1bc9b2b84c85523ca8fe6cad294adf83be15b108ff721d0cc87bc3dd3a7590184b0e845663a91fc9e1c3c53a61d867420b04f092355753bc65a06368fd41295fd09924132c6f91f67964c142674a725c343914c4cecf58c074bcaf4558c97bf7911e07aa6d0938f2ee2bb3c1a8c595d635e84342fdea01dc24b211ad2fc281cf77e59110c7abc54bf0c86d480b9be276471dc9d603cee98cfdab3e9fcfb703793560549ea4450fa7b33fb9169c44b4d25fb9c457f49791cd3da03eac96095813c105132ccda4e63e49228cd23d8a1f37856f142d93b90db09f82af89258c63aab8047a80c036c9357ea2046f8dc6354f0c5295f342bb417d3cfeb0b1fd33622c29e14cbbd92e1363c65ebd4504b7512329b9670e32e1b2c67a54e7f1a55f8b9f9ea04e8ca3a705e62a3c5e637374afb7aeb6ddea612cde28f01a202d7aa4e34722d27dd3f9b89894d019fd5d4d7119efe3723bba104cb8bb0981e074de3afe200daaaead826cc45f244dbf431afab34efbdf782474d2fd57118f646214934ed99cba3b003e8d67a3836f6f19fc41910ce5163ee3ae99eb84d514eb761e63684ea56f9791d2dd4aac6e6168b948c817f75a222acb0e8cdc03cc4afe8f67157e1a363b7faeff9f172b98913677c5a1dd085e9ee4c22052c1af58193116673dcd3bfc5f34b855dcc6c77885649e9e71f43d4aea0f4b72ca7eda0578ba13d31a658d2d060a9a66ff69ed1be7997a2fb1d2723d38f9bfabe18f8e7b3cda906e4e9b5e942c8eaeb296070ebfd364947a940cc978bed66b37749e6d5dcd7be8c494440e2b84cecfefb98c0bedfb3c41e3359d2cd7197fbe720c48aa6c6b6465c1ee63e3569c2adc744491370b7f7826fe0b77a1d19d64101d032b918106b42d2ef73747e5601fe4ba50f23ede521f031a817d15294a43722e8378784b6db0cf1ba9e8ae911d9201b9ce9cc3019c6f5c27cb98da26144b64225a7c932b30f761e78a2d59a1d8b83ec6344a2f6dd47e765706d00bf4a79a6a926c3ba91d812c8f2c797ab1796709e5d16856778293529f0286d015c3b5399619642a333e9e593d6e3f5353994208e9e6a332851d7f652522a928b917e27e2d6d42137dfe2ebfa6fb1c67b26c0254528685f7ebdbe315a68eaa2da769e8a9f42d3e60007c71330926b2c0012d83ead4e4fd1ed872ccd1972201d2b027f3545ac2d30cd78bc1d740feccbc6fc2a0446c6e30eac51f5a69098aa2d447f2085b4e4e4b92ccc26921d2de478518cd090ce267aea2d27ada57fd88b4976d89fb843cdccf49a76ca2679e6801bfa7fb031896fb50629704b9923936bb5dd385311121cadfb11995e59b73034cf67ed03ab813867648d025828087e949a9afd16b95d72d99b1edca257aac132ffb7a0709aed5a9c0ff05fb0f2bbf28409eed7b5f5801be964ced019e1cb7851d3851f10290674e19ffb008b301c4acf641a2bb14216e1d69cabf52b5ef227496b0f30799a855d117fad3744a6fa33503ea798b52ddd7ee5426609dbfcd3f0c13b164d6c051f7ed4a119719a712e388d328402081ff1354b554d2c237afed3b151c4ba8e9f4bdeb8499a3066e26bbc69e8af089dec71731d1dc529eab17ef7374734c0fe475494c83836bdd34a03b9bc89914716061bfb98ec6e61c3ed4438edcaf25243c647086b9ea7018b0d9a8a0b00cecb00abde2498d69c2336101a772cbe4f571523f51bd05882cdf358b849cc140aa1faf22423a12851ce0e33fd48975a4959fa5c5fe418c93908191ab6e741b77bfe02cbd698ee795c466d615619e6441382c6eac01834ee9ab73cea80bbe235c78da91bd79b6f82f899785d68700d393e675c2224d6b7a1ad21320495679adaed70167b50866713a53109db7b6f7d81304ecdfd83b319b1ef248306b45ad29e7ddcc863dac56048b5d69ea175011f7614c00a86a863cde1872a8932878b9ac7e1ac5bda4997b72064f0cd75f4c814e034de11acb9013cf7ea926b4e7eaace070c7ba2188efad2e431e1223d45dd05c4d8403c2e45cee6413ecbe7527e873e455c4e610a61839aacc0bd56d2483e78f298b66a478eb2f558cbafca86be847baeb02c5b216c8cd88fea4df249b09e670a20703abac24b0a91abc4a5646601442ba10becfd30993880051d07f56a05a9379e7a8e6befee3f22faa106398f7706006e42e9be1ef89d25c272f11a95095c587d713732284de9dbd3c7217b0689e21d8eb0ff69668 diff --git a/src/tests/data/x509/mldsa/mldsa-priv-both.pem b/src/tests/data/x509/mldsa/mldsa-priv-both.pem deleted file mode 100644 index 505c61df888..00000000000 --- a/src/tests/data/x509/mldsa/mldsa-priv-both.pem +++ /dev/null @@ -1,59 +0,0 @@ ------BEGIN PRIVATE KEY----- -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 ------END PRIVATE KEY----- - - diff --git a/src/tests/data/x509/mldsa/mldsa-priv-expandend-only.pem b/src/tests/data/x509/mldsa/mldsa-priv-expandend-only.pem deleted file mode 100644 index 67414c3fc51..00000000000 --- a/src/tests/data/x509/mldsa/mldsa-priv-expandend-only.pem +++ /dev/null @@ -1,58 +0,0 @@ ------BEGIN PRIVATE KEY----- -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 ------END PRIVATE KEY----- - - diff --git a/src/tests/data/x509/mldsa/mldsa-priv-seed-only.pem b/src/tests/data/x509/mldsa/mldsa-priv-seed-only.pem deleted file mode 100644 index 93febbe04c7..00000000000 --- a/src/tests/data/x509/mldsa/mldsa-priv-seed-only.pem +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PRIVATE KEY----- -MDQCAQAwCwYJYIZIAWUDBAMRBCKAIAABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZ -GhscHR4f ------END PRIVATE KEY----- diff --git a/src/tests/test_dilithium.cpp b/src/tests/test_dilithium.cpp index c3ca4732bd7..4e54e067afd 100644 --- a/src/tests/test_dilithium.cpp +++ b/src/tests/test_dilithium.cpp @@ -297,7 +297,7 @@ BOTAN_REGISTER_TEST("pubkey", "dilithium_keygen", Dilithium_Keygen_Tests); #if defined(BOTAN_HAS_DILITHIUM_COMMON) && defined(BOTAN_HAS_SHA3) class MLDSA_Privkey_Tests : public Text_Based_Test { public: - MLDSA_Privkey_Tests() : Text_Based_Test("mldsa_pkcs8.vec", "key") {} + MLDSA_Privkey_Tests() : Text_Based_Test("mldsa_privkey.vec", "key") {} Test::Result run_one_test(const std::string& name, const VarMap& vars) override { Test::Result result(name); @@ -334,6 +334,10 @@ class MLDSA_Privkey_Tests : public Text_Based_Test { auto verifier = Botan::PK_Verifier(pub_key, ""); verifier.update(ref_msg.data(), ref_msg.size()); result.confirm("signature verifies", verifier.check_signature(signature.data(), signature.size())); + + auto reencoded_priv_key = priv_key->private_key_bits(); + auto redecoded_priv_key = Botan::Dilithium_PrivateKey(reencoded_priv_key, mode); + result.confirm("re-encoding and subsequent re-decoding of private ML-DSA key without error", true); return result; } }; From f2cb6c5a490a499b0cc2d947baad597687295574 Mon Sep 17 00:00:00 2001 From: Falko Strenzke Date: Tue, 10 Feb 2026 12:32:39 +0100 Subject: [PATCH 09/25] switched to comparing encoded mldsa keys, removed all the op== I had introduced --- .../pubkey/dilithium/dilithium_common/dilithium.h | 2 -- .../dilithium/dilithium_common/dilithium_algos.cpp | 2 +- .../dilithium_common/dilithium_constants.h | 13 ------------- .../dilithium/dilithium_common/dilithium_keys.h | 8 -------- src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp | 13 ++++++++++--- src/lib/pubkey/pqcrystals/pqcrystals.h | 4 ---- 6 files changed, 11 insertions(+), 31 deletions(-) diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium.h b/src/lib/pubkey/dilithium/dilithium_common/dilithium.h index a8609279da7..909a7202dbf 100644 --- a/src/lib/pubkey/dilithium/dilithium_common/dilithium.h +++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium.h @@ -52,8 +52,6 @@ class BOTAN_PUBLIC_API(3, 0) DilithiumMode { Mode mode() const { return m_mode; } - bool operator==(const DilithiumMode& other) const = default; - private: Mode m_mode; }; diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.cpp b/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.cpp index 98bfad6531c..5b320b49d52 100644 --- a/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.cpp +++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.cpp @@ -15,7 +15,6 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include #include #include @@ -397,6 +396,7 @@ DilithiumSerializedPrivateKey encode_keypair(const DilithiumInternalKeypair& key BOTAN_ASSERT_NOMSG(stuffer.full()); CT::unpoison(serialization); + return serialization; } diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium_constants.h b/src/lib/pubkey/dilithium/dilithium_common/dilithium_constants.h index 6a0b6df5be1..8fdcab94b6a 100644 --- a/src/lib/pubkey/dilithium/dilithium_common/dilithium_constants.h +++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium_constants.h @@ -168,19 +168,6 @@ class DilithiumConstants final { Dilithium_Keypair_Codec& keypair_codec() const { return *m_keypair_codec; } - bool operator==(const DilithiumConstants& other) const { - return m_mode == other.m_mode && m_tau == other.m_tau && m_lambda == other.m_lambda && - m_gamma1 == other.m_gamma1 && m_gamma2 == other.m_gamma2 && m_k == other.m_k && m_l == other.m_l && - m_eta == other.m_eta && m_beta == other.m_beta && m_omega == other.m_omega && - m_public_key_bytes == other.m_public_key_bytes && - m_commitment_hash_full_bytes == other.m_commitment_hash_full_bytes && - m_private_key_bytes == other.m_private_key_bytes && m_public_key_bytes == other.m_public_key_bytes && - m_signature_bytes == other.m_signature_bytes && - m_serialized_commitment_bytes == - other - .m_serialized_commitment_bytes; // && *m_symmetric_primitives == *other.m_symmetric_primitives); - } - private: DilithiumMode m_mode; diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium_keys.h b/src/lib/pubkey/dilithium/dilithium_common/dilithium_keys.h index a691a8d3807..cde8df3b7ec 100644 --- a/src/lib/pubkey/dilithium/dilithium_common/dilithium_keys.h +++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium_keys.h @@ -58,8 +58,6 @@ class Dilithium_PublicKeyInternal { const DilithiumConstants& mode() const { return m_mode; } - bool operator==(const Dilithium_PublicKeyInternal& other) const = default; - private: const DilithiumConstants m_mode; DilithiumSeedRho m_rho; @@ -95,12 +93,6 @@ class Dilithium_PrivateKeyInternal { const DilithiumPolyVec& t0() const { return m_t0; } - bool operator==(const Dilithium_PrivateKeyInternal& other) const { - // exclude the generative seed from the comparison, as it might not have been provided - return this->m_s1 == other.m_s1 && this->m_s2 == other.m_s2 && this->m_t0 == other.m_t0 && - this->m_signing_seed == other.m_signing_seed; - } - void _const_time_poison() const { // Note: m_rho and m_tr is public knowledge CT::poison_all(m_signing_seed, m_s1, m_s2, m_t0); diff --git a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp index 2946a3274b3..d5ca3bd8170 100644 --- a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp +++ b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp @@ -16,6 +16,10 @@ #include #include #include +#include + + +namespace Botan { namespace { typedef Botan::DilithiumInternalKeypair (*decoding_func)(std::span key_bits, @@ -35,7 +39,7 @@ Botan::DilithiumInternalKeypair try_decode_expanded_only(std::span expanded; Botan::BER_Decoder(key_bits).decode(expanded, Botan::ASN1_Type::OctetString).verify_end(); Botan::DilithiumInternalKeypair key_pair = - Botan::Dilithium_Algos::decode_keypair(Botan::DilithiumSerializedPrivateKey(expanded), mode); + Botan::Dilithium_Algos::decode_keypair(Botan::DilithiumSerializedPrivateKey(expanded), std::move(mode)); return key_pair; } @@ -52,7 +56,11 @@ Botan::DilithiumInternalKeypair try_decode_both(std::span key_bit Botan::Dilithium_Algos::decode_keypair(Botan::DilithiumSerializedPrivateKey(expanded), mode); Botan::DilithiumInternalKeypair key_pair_from_seed = Botan::Dilithium_Algos::expand_keypair(Botan::DilithiumSeedRandomness(seed), std::move(mode)); - if(*key_pair_from_seed.second != *key_pair.second) { + + DilithiumSerializedPrivateKey expanded_from_seed = Dilithium_Algos::encode_keypair(key_pair_from_seed); + + + if(expanded_from_seed.get() != expanded) { throw Botan::Decoding_Error("seed and expanded key in ML-DSA serialized key do not match"); } return key_pair; @@ -60,7 +68,6 @@ Botan::DilithiumInternalKeypair try_decode_both(std::span key_bit } // namespace -namespace Botan { secure_vector ML_DSA_Expanding_Keypair_Codec::encode_keypair(DilithiumInternalKeypair keypair) const { BOTAN_ASSERT_NONNULL(keypair.second); diff --git a/src/lib/pubkey/pqcrystals/pqcrystals.h b/src/lib/pubkey/pqcrystals/pqcrystals.h index aff94875893..d3ee23eb60a 100644 --- a/src/lib/pubkey/pqcrystals/pqcrystals.h +++ b/src/lib/pubkey/pqcrystals/pqcrystals.h @@ -271,8 +271,6 @@ class Polynomial { return *this; } - bool operator==(const ThisPolynomial& other) const { return std::equal(this->m_coeffs.begin(), this->m_coeffs.end(), other.m_coeffs.begin(), other.m_coeffs.end()); } - ~Polynomial() = default; constexpr size_t size() const { return m_coeffs.size(); } @@ -413,8 +411,6 @@ class PolynomialVector { ThisPolynomialVector& operator=(ThisPolynomialVector&& other) noexcept = default; ~PolynomialVector() = default; - bool operator==(const ThisPolynomialVector& other) const { return m_vec == other.m_vec; } - size_t size() const { return m_vec.size(); } constexpr Domain domain() const noexcept { return D; } From a52440d7c6be1d6b561d2c6ff1fd7b4af2d34eef Mon Sep 17 00:00:00 2001 From: Falko Strenzke Date: Tue, 10 Feb 2026 12:40:53 +0100 Subject: [PATCH 10/25] only code formatting --- src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp | 3 +-- src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp | 5 +---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp b/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp index 3eb53a557a3..0b4c0535c7b 100644 --- a/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp +++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp @@ -428,10 +428,9 @@ secure_vector Dilithium_PrivateKey::private_key_bits() const { } bool Dilithium_PrivateKey::is_mldsa() const { - return m_private->mode().is_ml_dsa(); + return m_private->mode().is_ml_dsa(); } - std::unique_ptr Dilithium_PrivateKey::create_signature_op(RandomNumberGenerator& rng, std::string_view params, std::string_view provider) const { diff --git a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp index d5ca3bd8170..d836ac739ab 100644 --- a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp +++ b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp @@ -18,7 +18,6 @@ #include #include - namespace Botan { namespace { @@ -57,8 +56,7 @@ Botan::DilithiumInternalKeypair try_decode_both(std::span key_bit Botan::DilithiumInternalKeypair key_pair_from_seed = Botan::Dilithium_Algos::expand_keypair(Botan::DilithiumSeedRandomness(seed), std::move(mode)); - DilithiumSerializedPrivateKey expanded_from_seed = Dilithium_Algos::encode_keypair(key_pair_from_seed); - + DilithiumSerializedPrivateKey expanded_from_seed = Dilithium_Algos::encode_keypair(key_pair_from_seed); if(expanded_from_seed.get() != expanded) { throw Botan::Decoding_Error("seed and expanded key in ML-DSA serialized key do not match"); @@ -68,7 +66,6 @@ Botan::DilithiumInternalKeypair try_decode_both(std::span key_bit } // namespace - secure_vector ML_DSA_Expanding_Keypair_Codec::encode_keypair(DilithiumInternalKeypair keypair) const { BOTAN_ASSERT_NONNULL(keypair.second); const auto& seed = keypair.second->seed(); From 8d78efb80a0e30e7dce0ea9094946ee265234125 Mon Sep 17 00:00:00 2001 From: Falko Strenzke Date: Wed, 11 Feb 2026 10:43:53 +0100 Subject: [PATCH 11/25] renamed private namespace functions --- src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp index d836ac739ab..005f0765824 100644 --- a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp +++ b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp @@ -24,7 +24,7 @@ namespace { typedef Botan::DilithiumInternalKeypair (*decoding_func)(std::span key_bits, Botan::DilithiumConstants mode); -Botan::DilithiumInternalKeypair try_decode_seed_only(std::span key_bits, +Botan::DilithiumInternalKeypair decode_seed_only_or_throw(std::span key_bits, Botan::DilithiumConstants mode) { Botan::secure_vector seed; Botan::BER_Decoder(key_bits) @@ -33,7 +33,7 @@ Botan::DilithiumInternalKeypair try_decode_seed_only(std::span ke return Botan::Dilithium_Algos::expand_keypair(Botan::DilithiumSeedRandomness(seed), std::move(mode)); } -Botan::DilithiumInternalKeypair try_decode_expanded_only(std::span key_bits, +Botan::DilithiumInternalKeypair decode_expanded_only_or_throw(std::span key_bits, Botan::DilithiumConstants mode) { Botan::secure_vector expanded; Botan::BER_Decoder(key_bits).decode(expanded, Botan::ASN1_Type::OctetString).verify_end(); @@ -42,7 +42,7 @@ Botan::DilithiumInternalKeypair try_decode_expanded_only(std::span key_bits, Botan::DilithiumConstants mode) { +Botan::DilithiumInternalKeypair decode_seed_plus_expanded_or_throw(std::span key_bits, Botan::DilithiumConstants mode) { Botan::secure_vector expanded; Botan::secure_vector seed; Botan::BER_Decoder(key_bits) @@ -87,14 +87,13 @@ secure_vector ML_DSA_Expanding_Keypair_Codec::encode_keypair(DilithiumI ASN1_Type(Botan::ASN1_Type(0)), Botan::ASN1_Class::ContextSpecific); /* - * + * This yields the following ASN.1/DER structure: <80 20> 0 32: [0] : 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F : 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F * * Note: The previous format, which only contains the seed as an OCTET STRING without the [0]-tag, can still be decoded by OpenSSL. Apparently, it has this feature as a non-standard compatibility fallback for legacy formats. - * Reason: it has to look like this test vector from : */ } @@ -109,7 +108,7 @@ DilithiumInternalKeypair ML_DSA_Expanding_Keypair_Codec::decode_keypair(std::spa } /* we have to check 3 different format: "seed-only", "expanded-only", and "both" */ - decoding_func fn_arr[3] = {try_decode_both, try_decode_seed_only, try_decode_expanded_only}; + decoding_func fn_arr[3] = {decode_seed_plus_expanded_or_throw, decode_seed_only_or_throw, decode_expanded_only_or_throw}; for(auto fn : fn_arr) { try { return fn(private_key_bits, mode); From b45a18e93c27378678362c9321868cee2578ab8e Mon Sep 17 00:00:00 2001 From: Falko Strenzke Date: Thu, 12 Feb 2026 10:06:27 +0100 Subject: [PATCH 12/25] add further invalid keys from RFC 9881 --- src/tests/data/mldsa_privkey.vec | 6 ++++++ src/tests/test_dilithium.cpp | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/tests/data/mldsa_privkey.vec b/src/tests/data/mldsa_privkey.vec index baea2a45d4e..a4baba1859b 100644 --- a/src/tests/data/mldsa_privkey.vec +++ b/src/tests/data/mldsa_privkey.vec @@ -21,3 +21,9 @@ key = 308213460420000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e [mldsa-44-invalid] # both (first test vector with changed seed) key = 30820a2604200001F2030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f04820a00d7b2b47254aae0db45e7930d4a98d2c97d8f1397d1789dafa17024b316e9bec939ce0f7f77f8db5644dcda366bfe4734bd95f435ff9a613aa54aa41c2c694c04329a07b1fabb48f52a309f11a1898f848e2322ffe623ec810db3bee33685854a88269da320d5120bfcfe89a18e30f7114d83aa404a646b6c997389860d12522ee0006e2384819186619b260d118664d4a62822184482402898146148a6614c4248a19208c2382951244808a125c2083108c47120140914836c18a78084106ec9c07022b56408b0610c070498124451886959004622932041062e42b64c01164914284c41a85180460a5116515a0820022244dc9849d13251e13065d3c08592a85112a1640039220946621cc70cd9086dd0062652408580443091062c50c80924c5841a966d4a982c99066da4443220a7645a326e11b57020926124138e04852c0a4872c8a051d3082a99208058242024074e59148810a46460c06de0b28d1b1909203422c024410943710a212061a2015222521b80809a340013934dd3322922170a9892691a14512027219cc02062a2814818691a854d8344695b2041031242cb184601a90d0c023183b0215a224ac89205d9906904306a4b064ad2b2011c404081423252327254a6405a18100c321292c2805212625c82280bb46c03428d53100c14010ee1365288842491020a63462620062911c228d0204802b36ca236095a8648cbb4618b4662c440821a890910024d24b24520122524c90588288cc9c04d5948220a276ec134644c90605b445082864943880443b28c603080a2882d84a46d8ca629d0c68442064689885100a98d01498de4380da4068dd3947142b26c1a84611ba32842b42808a0711ac531e0a04c013765242862142890091061d940221b3360090292d02481200408491844a3222d5c8844149808a446610195640b390a0c9450ca406ad2b220c0380182308e13b908918084148829c0189112350da02422e20406d9c2850428121cc989180272d24029c20812d8062a9994719bb8682384291a2289144511dc82445096450c4484c0b2049aa60543862c44326e88442120a84c9a3070e3b82d63268803254903438c48a809ca147253344e1243081ba704593022d99480e234228142129c302a9434266104452426281346094a326d11280918b82562281113410d41b21190844c8b1212a2c688c9c030220606d2188e848630904452128831d9207113c52843060e033060cca6845826524c88011ef72562c85ffa43acfa49217f2b172d7bbc14620e6d980a71aabbdf0c45e9a206ecb1423fee15decc17601300149d9223cd6e6c6e1fa8e41fc7c64938ab68905fd3dcda50d87082e7d0d71d1bc9b2b84c85523ca8fe6cad294adf83be15b108ff721d0cc87bc3dd3a7590184b0e845663a91fc9e1c3c53a61d867420b04f092355753bc65a06368fd41295fd09924132c6f91f67964c142674a725c343914c4cecf58c074bcaf4558c97bf7911e07aa6d0938f2ee2bb3c1a8c595d635e84342fdea01dc24b211ad2fc281cf77e59110c7abc54bf0c86d480b9be276471dc9d603cee98cfdab3e9fcfb703793560549ea4450fa7b33fb9169c44b4d25fb9c457f49791cd3da03eac96095813c105132ccda4e63e49228cd23d8a1f37856f142d93b90db09f82af89258c63aab8047a80c036c9357ea2046f8dc6354f0c5295f342bb417d3cfeb0b1fd33622c29e14cbbd92e1363c65ebd4504b7512329b9670e32e1b2c67a54e7f1a55f8b9f9ea04e8ca3a705e62a3c5e637374afb7aeb6ddea612cde28f01a202d7aa4e34722d27dd3f9b89894d019fd5d4d7119efe3723bba104cb8bb0981e074de3afe200daaaead826cc45f244dbf431afab34efbdf782474d2fd57118f646214934ed99cba3b003e8d67a3836f6f19fc41910ce5163ee3ae99eb84d514eb761e63684ea56f9791d2dd4aac6e6168b948c817f75a222acb0e8cdc03cc4afe8f67157e1a363b7faeff9f172b98913677c5a1dd085e9ee4c22052c1af58193116673dcd3bfc5f34b855dcc6c77885649e9e71f43d4aea0f4b72ca7eda0578ba13d31a658d2d060a9a66ff69ed1be7997a2fb1d2723d38f9bfabe18f8e7b3cda906e4e9b5e942c8eaeb296070ebfd364947a940cc978bed66b37749e6d5dcd7be8c494440e2b84cecfefb98c0bedfb3c41e3359d2cd7197fbe720c48aa6c6b6465c1ee63e3569c2adc744491370b7f7826fe0b77a1d19d64101d032b918106b42d2ef73747e5601fe4ba50f23ede521f031a817d15294a43722e8378784b6db0cf1ba9e8ae911d9201b9ce9cc3019c6f5c27cb98da26144b64225a7c932b30f761e78a2d59a1d8b83ec6344a2f6dd47e765706d00bf4a79a6a926c3ba91d812c8f2c797ab1796709e5d16856778293529f0286d015c3b5399619642a333e9e593d6e3f5353994208e9e6a332851d7f652522a928b917e27e2d6d42137dfe2ebfa6fb1c67b26c0254528685f7ebdbe315a68eaa2da769e8a9f42d3e60007c71330926b2c0012d83ead4e4fd1ed872ccd1972201d2b027f3545ac2d30cd78bc1d740feccbc6fc2a0446c6e30eac51f5a69098aa2d447f2085b4e4e4b92ccc26921d2de478518cd090ce267aea2d27ada57fd88b4976d89fb843cdccf49a76ca2679e6801bfa7fb031896fb50629704b9923936bb5dd385311121cadfb11995e59b73034cf67ed03ab813867648d025828087e949a9afd16b95d72d99b1edca257aac132ffb7a0709aed5a9c0ff05fb0f2bbf28409eed7b5f5801be964ced019e1cb7851d3851f10290674e19ffb008b301c4acf641a2bb14216e1d69cabf52b5ef227496b0f30799a855d117fad3744a6fa33503ea798b52ddd7ee5426609dbfcd3f0c13b164d6c051f7ed4a119719a712e388d328402081ff1354b554d2c237afed3b151c4ba8e9f4bdeb8499a3066e26bbc69e8af089dec71731d1dc529eab17ef7374734c0fe475494c83836bdd34a03b9bc89914716061bfb98ec6e61c3ed4438edcaf25243c647086b9ea7018b0d9a8a0b00cecb00abde2498d69c2336101a772cbe4f571523f51bd05882cdf358b849cc140aa1faf22423a12851ce0e33fd48975a4959fa5c5fe418c93908191ab6e741b77bfe02cbd698ee795c466d615619e6441382c6eac01834ee9ab73cea80bbe235c78da91bd79b6f82f899785d68700d393e675c2224d6b7a1ad21320495679adaed70167b50866713a53109db7b6f7d81304ecdfd83b319b1ef248306b45ad29e7ddcc863dac56048b5d69ea175011f7614c00a86a863cde1872a8932878b9ac7e1ac5bda4997b72064f0cd75f4c814e034de11acb9013cf7ea926b4e7eaace070c7ba2188efad2e431e1223d45dd05c4d8403c2e45cee6413ecbe7527e873e455c4e610a61839aacc0bd56d2483e78f298b66a478eb2f558cbafca86be847baeb02c5b216c8cd88fea4df249b09e670a20703abac24b0a91abc4a5646601442ba10becfd30993880051d07f56a05a9379e7a8e6befee3f22faa106398f7706006e42e9be1ef89d25c272f11a95095c587d713732284de9dbd3c7217b0689e21d8eb0ff69668 +# includes the both CHOICE , i.e., both seed and expandedKey are included. The seed and expandedKey values can be checked for inconsistencies: +key = 30820a3e020100300b060960864801650304031104820a2a30820a260420000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f04820a00510c9bfd1dd7374f4e8ae71dd58281106a93412ed8fa357f74bbb4661ecb1921d3a75fc93b88ef0e6a9b851becec19999be81068c859d6df115c608c1501170eb6c055c22f6255924976d895a5f54d2c38596c403ccef6f973e22a5f8c7c2ba83633447090a78b22bb41cfdedafbcb2edecf9070cce612e42ab0a1f13eb9dc3c14903189c281e0b031da9484821208a3b430d2284a040720c34442e0402e44a07101a92c03266412412418322400222e4a96600b19902233205286451b084c93188419006d53800c10a42850060a20060c231132d2c440d2120204348623950ca0b64c5b48918c12842019029118810a25101ca06960902803c63093049011b890044532c1204cdac290d9882c184624cab00523b16c8a108c048170cba430d84662c1c610c8c845c1960c04120ae428661c25709cb28862806c62384584804ca3a00d23070ec8462024952924460548087013214c60842190b20dd0368019804d1b96611410851a26409cc088a3181003022192325198083222398a40126e0c264118194e220264c2301142161263162c11a384e28025d246629a920011b80d8b8600519671c84624088941031540a1226e24356980242ec2888504318c5ac08942448d58c6009932921b2120cb026e181970d8c62441367219906558386e08200c02142a63168501b42550c26809195022b24dc3182a2418229b4668103542203662db34058bb26c48108a01c88d21300e8c14264224040b378c430629424886d81885c8a40414b631d1360c22832900a24d5cb80d648845c8004814384c62a268d4062ed9020dccb611613864c8329251c849528001e0c03100220ac1446680b48450083012c628a408604ab061cac64112c86852b0719b000c08476661186c5114898146860939448302310a9169e12251594641a20632da067099302cc8262dda9290c10020dab0658224328344855cc46c00432289c610a30400618424a3401251200d18328041362ac8b0292047081a1244c3b69122298cd83266c396106226721b4565c3a665d9c060141008d0c46d493285d0060d11244ce3b40001a9450033804230604a8049823420431808120626924482ca3001e1368e5b22222407065410894ba04198460a4c144a0a348420234ec0160503462c80b641149331cb260011986d0a120ed8106290360c14017189062ac82666520451e40640dc9221c8107164182ee4b8005290688ba8284b244911268199889154360d0a9881bc691c7976bf7f80190f7e1c19af09f80d29933ebfd59f4d4975d7a6cadb5db6478b099e035bbf8629bed9f12e1f1d8ec996cbed7ad1043324404bcfc834b0734c1ac6588450a96c5fdd872bc461b9d4d0ce96f76b607ef09f248e92eea6375d8130cc0b11f2a498e494ef8ee8f3588713ec223366cf1feac6ede3faac8ac1ecd458ebba1d7de0466e82c779275fefd53718e59ccd9885079c2d646ccda15a62131001340c9d402e0e2483efa7147450bac4ad5cfdcb7dffce3f4a219272fac6aa7424acb2e5f791b6af561650cfb7b2b6eb360238bbb925a03dbe0cec5a81a05729a1b1d7fe356f1c3f28681b764ce8edb8e6c5ba34149c3733b6beb67adba4accf88c32c8b478582a457aff6a18cf993fd4c2b0b6c40eefeb54c552f357672a5924621cbfa5482c194da38356d30fcdd689bf2568094a6605d2a8b06cc8fda1a6653aebe997ff681047dac2ce18094850df88818fa359d4db27a540c27c60e9e21cdbfc2396512b2966445a43668f903d5a17327c13834785fdcdd408e3d3739f52165529c4c48fb6d8d82c71273e5a899c72dbfcf64c5a5a9f77e144fa2e067c77ca0c7e911773548cf634271235109e65fc7c46fb32b873569b77a2d4e1f3069e72ab34a94d485e72a2af1bebc188b29cff3279e2dd9f3ef9aad7c7f8733156b2a137babed2f466dc81cb2576ea38ebc4e10ca5fd192f38e4581f90f5af99e90c44713b69565b0e867c41f70716173d61cdd3b3d5968cbfcf64de26206a37663481de510773f30c1cd7adb5a27b29a39625ceeaee152a2baf3cff46ecb2837887345991bf648236e59cce20054d277c1c1b489f335699ea81785ec75b586c0a9468474d9957888cf91105ae3a8f547877cea51118649dbb5cb6f6e2ba336be3529d30f0d7f2bafcdb1c8cb6849cf73f2f64899a2d7c5e243a0536e43d756fefc871a55abea071094033f4de1cdbd6811993bcade0be5fd90f5ae344f0d63b7b78f86263dc5a9a5919f5e763d3cd574a4bca12de89f7afd969356a170b5582c4ea5fdc144eaac664e1903f2e4eb67b87be49994a8398a3558ed256e4d612e57499433e1b161b1d5efa440f91798f0af63543e96590e48ee10a5eef35965d78178c31f7c83befa77a2c8ff34d86b4e9aac30610e199ec8341916132d96ee7be5778564674fd75215a3850f48240acdcfc033196b8cdad0c36c0d649fade890c426aef9312eeae42ee0bcfa41777a9d99b350c0b16fc828fca215dca6a7352e0c35d8114db66e4bd895d795effb426855acc1244b02739e477e6ceaa4cbdb8f6c8d4daf57f0d3e34905ea8226dc55be39e0577ac4f7b13546acc7171687410be3bbe88afe3d3798a5d07ace9b7ba51622b633f3e4616788f094ada73c39251ccd3ed29dac36585adc1625f85a1bb97c59ba0e737be2e8afb6a2222cb5f319fae0cc59e1004fde916929e5b76092a82dc2527b1b8994a27c2b2e568e727eb2465eb96460996fce63245c2b2cb296b8275afd00da8c81a9ef2e8e3bf9303d8e8b53aafe6c6d03e6bc200434a2a38b174d2e73dd507175e851d5e743e64209e348520d6c84ccb1fb6bfab26c8c2ee485110dd1d7b06bd52830ea5bb6803d125cc60e5fa87cc345b0f669751eeb61e719b6d666223cb89e8d3def3cec96ac9350ee983897fd151814c6fb5cda3797e8a5a673aa42628e7f841fb77e1bc4552c6854d2091ba65acda830081c08829ceefa6f70ae05a9b69e0b6343304f52549402a5cfb46d7bac983607e7f380f280821f2fb992c46973fbfd778d33035ee90b0db94f607e59fb4edfa9a98a709b58075a4aa8af5c585f2fb599dd82b77b4b6f39eea4b1487d0dcd54191e0d6f8d56fa4fbc2f8217f35e7e44aecce6813e54da8adc4c1c435e185c51e5c7f44a9e378f9f6218a75606d241095f2e59fe2a460446f1f75b7d0fa5d6a009131e2ff2be07d87b26e9e4bf09e6651297bd7e7a1bc12d4104da1ccca48bfa7a2aa717268008db704b59b6c057489541b23e0e09db777c0ffa21fa5646c341215475683da9fdad14fd11f714264a0e1c05d998412de1c459640e0abd6fd16ebcfeb96516e21661ccaa3a88c312ea7ba0585cd24e1a53076fe741c90169377a168fbdb3fece5cfce039b254c4235d8265f60652862fbdb45578289f1a673fe409db9f996c2d2c9287201b95917fe1a9b60ef178385f25afda966964819e5bbefd97cc10135308d98b7a82b4e1237b32b5bf9d33726fa945bbf5c96f276f3c06a45930948d3b6bd4228e2f3eac8cfd672ecb8eb9a92d159ecfb3af4cc2daea37cf72ea2b9edaad80013519bc095d95c5a3aa1fad46bd8c94ac +# includes only expandedKey. The public key fails to match the tr hash value in the private key: +key = 30820a18020100300b060960864801650304031104820a0404820a00d7b2b47254aae0db45e7930d4a98d2c97d8f1397d1789dafa17024b316e9bec939ce0f7f77f8db5644dcda366bfe4734bd95f435ff9a613aa54aa41c2c694c04339a07b1fabb48f52a309f11a1898f848e2322ffe623ec810db3bee33685854a88269da320d5120bfcfe89a18e30f7114d83aa404a646b6c997389860d12522ee0006e2384819186619b260d118664d4a62822184482402898146148a6614c4248a19208c2382951244808a125c2083108c47120140914836c18a78084106ec9c07022b56408b0610c070498124451886959004622932041062e42b64c01164914284c41a85180460a5116515a0820022244dc9849d13251e13065d3c08592a85112a1640039220946621cc70cd9086dd0062652408580443091062c50c80924c5841a966d4a982c99066da4443220a7645a326e11b57020926124138e04852c0a4872c8a051d3082a99208058242024074e59148810a46460c06de0b28d1b1909203422c024410943710a212061a2015222521b80809a340013934dd3322922170a9892691a14512027219cc02062a2814818691a854d8344695b2041031242cb184601a90d0c023183b0215a224ac89205d9906904306a4b064ad2b2011c404081423252327254a6405a18100c321292c2805212625c82280bb46c03428d53100c14010ee1365288842491020a63462620062911c228d0204802b36ca236095a8648cbb4618b4662c440821a890910024d24b24520122524c90588288cc9c04d5948220a276ec134644c90605b445082864943880443b28c603080a2882d84a46d8ca629d0c68442064689885100a98d01498de4380da4068dd3947142b26c1a84611ba32842b42808a0711ac531e0a04c013765242862142890091061d940221b3360090292d02481200408491844a3222d5c8844149808a446610195640b390a0c9450ca406ad2b220c0380182308e13b908918084148829c0189112350da02422e20406d9c2850428121cc989180272d24029c20812d8062a9994719bb8682384291a2289144511dc82445096450c4484c0b2049aa60543862c44326e88442120a84c9a3070e3b82d63268803254903438c48a809ca147253344e1243081ba704593022d99480e234228142129c302a9434266104452426281346094a326d11280918b82562281113410d41b21190844c8b1212a2c688c9c030220606d2188e848630904452128831d9207113c52843060e033060cca6845826524c88011ef72562c85ffa43acfa49217f2b172d7bbc14620e6d980a71aabbdf0c45e9a206ecb1423fee15decc17601300149d9223cd6e6c6e1fa8e41fc7c64938ab68905fd3dcda50d87082e7d0d71d1bc9b2b84c85523ca8fe6cad294adf83be15b108ff721d0cc87bc3dd3a7590184b0e845663a91fc9e1c3c53a61d867420b04f092355753bc65a06368fd41295fd09924132c6f91f67964c142674a725c343914c4cecf58c074bcaf4558c97bf7911e07aa6d0938f2ee2bb3c1a8c595d635e84342fdea01dc24b211ad2fc281cf77e59110c7abc54bf0c86d480b9be276471dc9d603cee98cfdab3e9fcfb703793560549ea4450fa7b33fb9169c44b4d25fb9c457f49791cd3da03eac96095813c105132ccda4e63e49228cd23d8a1f37856f142d93b90db09f82af89258c63aab8047a80c036c9357ea2046f8dc6354f0c5295f342bb417d3cfeb0b1fd33622c29e14cbbd92e1363c65ebd4504b7512329b9670e32e1b2c67a54e7f1a55f8b9f9ea04e8ca3a705e62a3c5e637374afb7aeb6ddea612cde28f01a202d7aa4e34722d27dd3f9b89894d019fd5d4d7119efe3723bba104cb8bb0981e074de3afe200daaaead826cc45f244dbf431afab34efbdf782474d2fd57118f646214934ed99cba3b003e8d67a3836f6f19fc41910ce5163ee3ae99eb84d514eb761e63684ea56f9791d2dd4aac6e6168b948c817f75a222acb0e8cdc03cc4afe8f67157e1a363b7faeff9f172b98913677c5a1dd085e9ee4c22052c1af58193116673dcd3bfc5f34b855dcc6c77885649e9e71f43d4aea0f4b72ca7eda0578ba13d31a658d2d060a9a66ff69ed1be7997a2fb1d2723d38f9bfabe18f8e7b3cda906e4e9b5e942c8eaeb296070ebfd364947a940cc978bed66b37749e6d5dcd7be8c494440e2b84cecfefb98c0bedfb3c41e3359d2cd7197fbe720c48aa6c6b6465c1ee63e3569c2adc744491370b7f7826fe0b77a1d19d64101d032b918106b42d2ef73747e5601fe4ba50f23ede521f031a817d15294a43722e8378784b6db0cf1ba9e8ae911d9201b9ce9cc3019c6f5c27cb98da26144b64225a7c932b30f761e78a2d59a1d8b83ec6344a2f6dd47e765706d00bf4a79a6a926c3ba91d812c8f2c797ab1796709e5d16856778293529f0286d015c3b5399619642a333e9e593d6e3f5353994208e9e6a332851d7f652522a928b917e27e2d6d42137dfe2ebfa6fb1c67b26c0254528685f7ebdbe315a68eaa2da769e8a9f42d3e60007c71330926b2c0012d83ead4e4fd1ed872ccd1972201d2b027f3545ac2d30cd78bc1d740feccbc6fc2a0446c6e30eac51f5a69098aa2d447f2085b4e4e4b92ccc26921d2de478518cd090ce267aea2d27ada57fd88b4976d89fb843cdccf49a76ca2679e6801bfa7fb031896fb50629704b9923936bb5dd385311121cadfb11995e59b73034cf67ed03ab813867648d025828087e949a9afd16b95d72d99b1edca257aac132ffb7a0709aed5a9c0ff05fb0f2bbf28409eed7b5f5801be964ced019e1cb7851d3851f10290674e19ffb008b301c4acf641a2bb14216e1d69cabf52b5ef227496b0f30799a855d117fad3744a6fa33503ea798b52ddd7ee5426609dbfcd3f0c13b164d6c051f7ed4a119719a712e388d328402081ff1354b554d2c237afed3b151c4ba8e9f4bdeb8499a3066e26bbc69e8af089dec71731d1dc529eab17ef7374734c0fe475494c83836bdd34a03b9bc89914716061bfb98ec6e61c3ed4438edcaf25243c647086b9ea7018b0d9a8a0b00cecb00abde2498d69c2336101a772cbe4f571523f51bd05882cdf358b849cc140aa1faf22423a12851ce0e33fd48975a4959fa5c5fe418c93908191ab6e741b77bfe02cbd698ee795c466d615619e6441382c6eac01834ee9ab73cea80bbe235c78da91bd79b6f82f899785d68700d393e675c2224d6b7a1ad21320495679adaed70167b50866713a53109db7b6f7d81304ecdfd83b319b1ef248306b45ad29e7ddcc863dac56048b5d69ea175011f7614c00a86a863cde1872a8932878b9ac7e1ac5bda4997b72064f0cd75f4c814e034de11acb9013cf7ea926b4e7eaace070c7ba2188efad2e431e1223d45dd05c4d8403c2e45cee6413ecbe7527e873e455c4e610a61839aacc0bd56d2483e78f298b66a478eb2f558cbafca86be847baeb02c5b216c8cd88fea4df249b09e670a20703abac24b0a91abc4a5646601442ba10becfd30993880051d07f56a05a9379e7a8e6befee3f22faa106398f7706006e42e9be1ef89d25c272f11a95095c587d713732284de9dbd3c7217b0689e21d8eb0ff69668 +# includes only expandedKey. The private s_1 and s_2 vectors imply a t vector whose private low bits do not match the t_0 vector portion of the private key (its high bits t_1 are the primary content of the public key): +key = 30820a18020100300b060960864801650304031104820a0404820a00d7b2b47254aae0db45e7930d4a98d2c97d8f1397d1789dafa17024b316e9bec939ce0f7f77f8db5644dcda366bfe4734bd95f435ff9a613aa54aa41c2c694c04329a07b1fabb48f52a309f11a1898f848e2322ffe623ec810db3bee33685854a88269da320d5120bfcfe89a18e30f7114d83aa404a646b6c997389860d12522ee0006e2384819186619b260d118664d4a62822184482402898146148a6614c4248a19208c2382951244808a125c2083108c47120140914836c18a78084106ec9c07022b56408b0610c070498124451886959004622932041062e42b64c01164914284c41a85180460a5116515a0820022244dc9849d13251e13065d3c08592a85112a1640039220946621cc70cd9086dd0062652408580443091062c50c80924c5841a966d4a982c99066da4443220a7645a326e11b57020926124138e04852c0a4872c8a051d3082a99208058242024074e59148810a46460c06de0b28d1b1909203422c024410943710a212061a2015222521b80809a340013934dd3322922170a9892691a14512027219cc02062a2814818691a854d8344695b2041031242cb184601a90d0c023183b0215a224ac89205d9906904306a4b064ad2b2011c404081423252327254a6405a18100c321292c2805212625c82280bb46c03428d53100c14010ee1365288842491020a63462620062911c228d0204802b36ca236095a8648cbb4618b4662c440821a890910024d24b24520122524c90588288cc9c04d5948220a276ec134644c90605b445082864943880443b28c603080a2882d84a46d8ca629d0c68442064689885100a98d01498de4380da4068dd3947142b26c1a84611ba32842b42808a0711ac531e0a04c013765242862142890091061d940221b3360090292d02481200408491844a3222d5c8844149808a446610195640b390a0c9450ca406ad2b220c0380182308e13b908918084148829c0189112350da02422e20406d9c2850428121cc989180272d24029c20812d8062a9994719bb8682384291a2289144511dc82445096450c4484c0b2049aa60543862c44326e88442120a84c9a3070e3b82d63268803254903438c48a809ca147253344e1243081ba704593022d99480e234228142129c302a9434266104452426281346094a326d11280918b82562281113410d41b21190844c8b1212a2c688c9c030220606d2188e848630904452128831d9207113c52843060e033060cca6845826524c88011ff72562c85ffa43acfa49217f2b172d7bbc14620e6d980a71aabbdf0c45e9a206ecb1423fee15decc17601300149d9223cd6e6c6e1fa8e41fc7c64938ab68905fd3dcda50d87082e7d0d71d1bc9b2b84c85523ca8fe6cad294adf83be15b108ff721d0cc87bc3dd3a7590184b0e845663a91fc9e1c3c53a61d867420b04f092355753bc65a06368fd41295fd09924132c6f91f67964c142674a725c343914c4cecf58c074bcaf4558c97bf7911e07aa6d0938f2ee2bb3c1a8c595d635e84342fdea01dc24b211ad2fc281cf77e59110c7abc54bf0c86d480b9be276471dc9d603cee98cfdab3e9fcfb703793560549ea4450fa7b33fb9169c44b4d25fb9c457f49791cd3da03eac96095813c105132ccda4e63e49228cd23d8a1f37856f142d93b90db09f82af89258c63aab8047a80c036c9357ea2046f8dc6354f0c5295f342bb417d3cfeb0b1fd33622c29e14cbbd92e1363c65ebd4504b7512329b9670e32e1b2c67a54e7f1a55f8b9f9ea04e8ca3a705e62a3c5e637374afb7aeb6ddea612cde28f01a202d7aa4e34722d27dd3f9b89894d019fd5d4d7119efe3723bba104cb8bb0981e074de3afe200daaaead826cc45f244dbf431afab34efbdf782474d2fd57118f646214934ed99cba3b003e8d67a3836f6f19fc41910ce5163ee3ae99eb84d514eb761e63684ea56f9791d2dd4aac6e6168b948c817f75a222acb0e8cdc03cc4afe8f67157e1a363b7faeff9f172b98913677c5a1dd085e9ee4c22052c1af58193116673dcd3bfc5f34b855dcc6c77885649e9e71f43d4aea0f4b72ca7eda0578ba13d31a658d2d060a9a66ff69ed1be7997a2fb1d2723d38f9bfabe18f8e7b3cda906e4e9b5e942c8eaeb296070ebfd364947a940cc978bed66b37749e6d5dcd7be8c494440e2b84cecfefb98c0bedfb3c41e3359d2cd7197fbe720c48aa6c6b6465c1ee63e3569c2adc744491370b7f7826fe0b77a1d19d64101d032b918106b42d2ef73747e5601fe4ba50f23ede521f031a817d15294a43722e8378784b6db0cf1ba9e8ae911d9201b9ce9cc3019c6f5c27cb98da26144b64225a7c932b30f761e78a2d59a1d8b83ec6344a2f6dd47e765706d00bf4a79a6a926c3ba91d812c8f2c797ab1796709e5d16856778293529f0286d015c3b5399619642a333e9e593d6e3f5353994208e9e6a332851d7f652522a928b917e27e2d6d42137dfe2ebfa6fb1c67b26c0254528685f7ebdbe315a68eaa2da769e8a9f42d3e60007c71330926b2c0012d83ead4e4fd1ed872ccd1972201d2b027f3545ac2d30cd78bc1d740feccbc6fc2a0446c6e30eac51f5a69098aa2d447f2085b4e4e4b92ccc26921d2de478518cd090ce267aea2d27ada57fd88b4976d89fb843cdccf49a76ca2679e6801bfa7fb031896fb50629704b9923936bb5dd385311121cadfb11995e59b73034cf67ed03ab813867648d025828087e949a9afd16b95d72d99b1edca257aac132ffb7a0709aed5a9c0ff05fb0f2bbf28409eed7b5f5801be964ced019e1cb7851d3851f10290674e19ffb008b301c4acf641a2bb14216e1d69cabf52b5ef227496b0f30799a855d117fad3744a6fa33503ea798b52ddd7ee5426609dbfcd3f0c13b164d6c051f7ed4a119719a712e388d328402081ff1354b554d2c237afed3b151c4ba8e9f4bdeb8499a3066e26bbc69e8af089dec71731d1dc529eab17ef7374734c0fe475494c83836bdd34a03b9bc89914716061bfb98ec6e61c3ed4438edcaf25243c647086b9ea7018b0d9a8a0b00cecb00abde2498d69c2336101a772cbe4f571523f51bd05882cdf358b849cc140aa1faf22423a12851ce0e33fd48975a4959fa5c5fe418c93908191ab6e741b77bfe02cbd698ee795c466d615619e6441382c6eac01834ee9ab73cea80bbe235c78da91bd79b6f82f899785d68700d393e675c2224d6b7a1ad21320495679adaed70167b50866713a53109db7b6f7d81304ecdfd83b319b1ef248306b45ad29e7ddcc863dac56048b5d69ea175011f7614c00a86a863cde1872a8932878b9ac7e1ac5bda4997b72064f0cd75f4c814e034de11acb9013cf7ea926b4e7eaace070c7ba2188efad2e431e1223d45dd05c4d8403c2e45cee6413ecbe7527e873e455c4e610a61839aacc0bd56d2483e78f298b66a478eb2f558cbafca86be847baeb02c5b216c8cd88fea4df249b09e670a20703abac24b0a91abc4a5646601442ba10becfd30993880051d07f56a05a9379e7a8e6befee3f22faa106398f7706006e42e9be1ef89d25c272f11a95095c587d713732284de9dbd3c7217b0689e21d8eb0ff69668 diff --git a/src/tests/test_dilithium.cpp b/src/tests/test_dilithium.cpp index 4e54e067afd..3d104fec24a 100644 --- a/src/tests/test_dilithium.cpp +++ b/src/tests/test_dilithium.cpp @@ -342,7 +342,7 @@ class MLDSA_Privkey_Tests : public Text_Based_Test { } }; -BOTAN_REGISTER_TEST("pubkey", "mldsa-pkcs8-decoding", MLDSA_Privkey_Tests); +BOTAN_REGISTER_TEST("pubkey", "mldsa_private_key", MLDSA_Privkey_Tests); #endif From 4e72dc4ec4b95deb52676a490f9a5aa6135cf23e Mon Sep 17 00:00:00 2001 From: Falko Strenzke <30287971+falko-strenzke@users.noreply.github.com> Date: Wed, 18 Feb 2026 14:50:25 +0100 Subject: [PATCH 13/25] fixe include formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: René Meusel --- src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp index 005f0765824..d400849baa7 100644 --- a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp +++ b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp @@ -6,15 +6,14 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include "botan/asn1_obj.h" -#include "botan/exceptn.h" -#include "botan/internal/dilithium_types.h" #include -#include - +#include #include #include +#include +#include +#include #include #include From 842ed7d258b4ad4915fd122d66ccce1c8da39774 Mon Sep 17 00:00:00 2001 From: Falko Strenzke Date: Wed, 18 Feb 2026 15:10:58 +0100 Subject: [PATCH 14/25] remove duplicate include --- src/tests/test_dilithium.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tests/test_dilithium.cpp b/src/tests/test_dilithium.cpp index 3d104fec24a..65d06df6a49 100644 --- a/src/tests/test_dilithium.cpp +++ b/src/tests/test_dilithium.cpp @@ -22,7 +22,6 @@ #include #include "test_pubkey.h" - #include "test_rng.h" #endif namespace Botan_Tests { From 9c0fa720d891b4cb28a8216a520306fe252b56d0 Mon Sep 17 00:00:00 2001 From: Falko Strenzke Date: Wed, 18 Feb 2026 15:26:12 +0100 Subject: [PATCH 15/25] decoding mldsa private key without use of exceptions --- .../pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp index d400849baa7..0e80e395ab1 100644 --- a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp +++ b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp @@ -23,7 +23,7 @@ namespace { typedef Botan::DilithiumInternalKeypair (*decoding_func)(std::span key_bits, Botan::DilithiumConstants mode); -Botan::DilithiumInternalKeypair decode_seed_only_or_throw(std::span key_bits, +Botan::DilithiumInternalKeypair decode_seed_only(std::span key_bits, Botan::DilithiumConstants mode) { Botan::secure_vector seed; Botan::BER_Decoder(key_bits) @@ -32,7 +32,7 @@ Botan::DilithiumInternalKeypair decode_seed_only_or_throw(std::span key_bits, +Botan::DilithiumInternalKeypair decode_expanded_only(std::span key_bits, Botan::DilithiumConstants mode) { Botan::secure_vector expanded; Botan::BER_Decoder(key_bits).decode(expanded, Botan::ASN1_Type::OctetString).verify_end(); @@ -41,7 +41,7 @@ Botan::DilithiumInternalKeypair decode_expanded_only_or_throw(std::span key_bits, Botan::DilithiumConstants mode) { +Botan::DilithiumInternalKeypair decode_seed_plus_expanded(std::span key_bits, Botan::DilithiumConstants mode) { Botan::secure_vector expanded; Botan::secure_vector seed; Botan::BER_Decoder(key_bits) @@ -105,18 +105,17 @@ DilithiumInternalKeypair ML_DSA_Expanding_Keypair_Codec::decode_keypair(std::spa // backwards compatibility (not RFC 9881 conforming) to raw seed format. return Botan::Dilithium_Algos::expand_keypair(Botan::DilithiumSeedRandomness(private_key_bits), std::move(mode)); } - /* we have to check 3 different format: "seed-only", "expanded-only", and "both" - */ - decoding_func fn_arr[3] = {decode_seed_plus_expanded_or_throw, decode_seed_only_or_throw, decode_expanded_only_or_throw}; - for(auto fn : fn_arr) { - try { - return fn(private_key_bits, mode); - } catch(const Botan::Decoding_Error& e) { - // pass - } + // "seed-only" format from RFC 9881 + BER_Decoder ber_dec(private_key_bits); + auto obj = ber_dec.peek_next_object(); + if(obj.type() == ASN1_Type(0)) { + return decode_seed_only(private_key_bits, mode); } - throw Decoding_Error("unsupported ML-DSA private key format, key size in bytes: " + - std::to_string(private_key_bits.size())); + // now it could still be "expanded-only" or "both" + if(obj.type() == ASN1_Type::OctetString) { + return decode_expanded_only(private_key_bits, mode); + } + return decode_seed_plus_expanded(private_key_bits, mode); } } // namespace Botan From e593e641359e6455a16e4a2c648c9fa0f3bf1c2e Mon Sep 17 00:00:00 2001 From: Falko Strenzke Date: Wed, 18 Feb 2026 15:32:10 +0100 Subject: [PATCH 16/25] fix returning the right key (with the seed) --- src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp index 0e80e395ab1..a87f5496a24 100644 --- a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp +++ b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp @@ -52,7 +52,7 @@ Botan::DilithiumInternalKeypair decode_seed_plus_expanded(std::span Date: Wed, 18 Feb 2026 15:40:47 +0100 Subject: [PATCH 17/25] fix include formatting --- src/tests/test_dilithium.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tests/test_dilithium.cpp b/src/tests/test_dilithium.cpp index 65d06df6a49..aea7867c8e3 100644 --- a/src/tests/test_dilithium.cpp +++ b/src/tests/test_dilithium.cpp @@ -9,9 +9,9 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#include "botan/pk_keys.h" #include "test_rng.h" #include "tests.h" + #include #include @@ -20,6 +20,7 @@ #include #include #include + #include #include "test_pubkey.h" #endif From ff4cc437d2a9adc5d46a5ad8c6263598247ba94a Mon Sep 17 00:00:00 2001 From: Falko Strenzke Date: Wed, 18 Feb 2026 16:10:35 +0100 Subject: [PATCH 18/25] fix test result function renaming --- src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp | 1 - src/tests/test_dilithium.cpp | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp index a87f5496a24..1884fb67f32 100644 --- a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp +++ b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include namespace Botan { diff --git a/src/tests/test_dilithium.cpp b/src/tests/test_dilithium.cpp index aea7867c8e3..6298b2941c7 100644 --- a/src/tests/test_dilithium.cpp +++ b/src/tests/test_dilithium.cpp @@ -321,7 +321,7 @@ class MLDSA_Privkey_Tests : public Text_Based_Test { try { priv_key = std::make_unique(key_bits, mode); } catch(const Botan::Decoding_Error& e) { - result.confirm("invalid ML-DSA key rejected", expect_decoding_failure); + result.test_is_true("invalid ML-DSA key rejected", expect_decoding_failure); return result; } std::vector ref_msg = {0, 1, 2, 4}; @@ -333,11 +333,11 @@ class MLDSA_Privkey_Tests : public Text_Based_Test { const Botan::Dilithium_PublicKey pub_key(priv_key->public_key_bits(), mode); auto verifier = Botan::PK_Verifier(pub_key, ""); verifier.update(ref_msg.data(), ref_msg.size()); - result.confirm("signature verifies", verifier.check_signature(signature.data(), signature.size())); + result.test_is_true("signature verifies", verifier.check_signature(signature.data(), signature.size())); auto reencoded_priv_key = priv_key->private_key_bits(); auto redecoded_priv_key = Botan::Dilithium_PrivateKey(reencoded_priv_key, mode); - result.confirm("re-encoding and subsequent re-decoding of private ML-DSA key without error", true); + result.test_is_true("re-encoding and subsequent re-decoding of private ML-DSA key without error", true); return result; } }; From 8e30615c68a99063bc245bda939b3458b0876770 Mon Sep 17 00:00:00 2001 From: Falko Strenzke Date: Thu, 19 Feb 2026 08:32:42 +0100 Subject: [PATCH 19/25] add const for clang-tidy --- src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp index 1884fb67f32..0993bab2f2c 100644 --- a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp +++ b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp @@ -49,7 +49,7 @@ Botan::DilithiumInternalKeypair decode_seed_plus_expanded(std::span Date: Thu, 19 Feb 2026 08:42:46 +0100 Subject: [PATCH 20/25] add is_dilithium_round3() to DilithiumPrivateKey --- src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp | 4 ++++ src/lib/pubkey/dilithium/dilithium_common/dilithium.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp b/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp index 0b4c0535c7b..e857dae08b6 100644 --- a/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp +++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp @@ -431,6 +431,10 @@ bool Dilithium_PrivateKey::is_mldsa() const { return m_private->mode().is_ml_dsa(); } +bool Dilithium_PrivateKey::is_dilithium_round3() const { + return !m_private->mode().is_ml_dsa(); +} + std::unique_ptr Dilithium_PrivateKey::create_signature_op(RandomNumberGenerator& rng, std::string_view params, std::string_view provider) const { diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium.h b/src/lib/pubkey/dilithium/dilithium_common/dilithium.h index 909a7202dbf..b9af2d6e9a7 100644 --- a/src/lib/pubkey/dilithium/dilithium_common/dilithium.h +++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium.h @@ -143,6 +143,8 @@ class BOTAN_PUBLIC_API(3, 0) Dilithium_PrivateKey final : public virtual Dilithi std::string_view provider) const override; bool is_mldsa() const; + + bool is_dilithium_round3() const; private: friend class Dilithium_Signature_Operation; From 36102ebe390a9dc8788b797f952830555567cecf Mon Sep 17 00:00:00 2001 From: Falko Strenzke Date: Thu, 19 Feb 2026 12:30:46 +0100 Subject: [PATCH 21/25] fixed code formatting --- .../dilithium/dilithium_common/dilithium.h | 2 +- src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp | 16 ++++++++-------- src/tests/test_dilithium.cpp | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium.h b/src/lib/pubkey/dilithium/dilithium_common/dilithium.h index b9af2d6e9a7..2a6c1b1c5f8 100644 --- a/src/lib/pubkey/dilithium/dilithium_common/dilithium.h +++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium.h @@ -143,7 +143,7 @@ class BOTAN_PUBLIC_API(3, 0) Dilithium_PrivateKey final : public virtual Dilithi std::string_view provider) const override; bool is_mldsa() const; - + bool is_dilithium_round3() const; private: diff --git a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp index 0993bab2f2c..7dfe62b6d85 100644 --- a/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp +++ b/src/lib/pubkey/dilithium/ml_dsa/ml_dsa_impl.cpp @@ -22,8 +22,7 @@ namespace { typedef Botan::DilithiumInternalKeypair (*decoding_func)(std::span key_bits, Botan::DilithiumConstants mode); -Botan::DilithiumInternalKeypair decode_seed_only(std::span key_bits, - Botan::DilithiumConstants mode) { +Botan::DilithiumInternalKeypair decode_seed_only(std::span key_bits, Botan::DilithiumConstants mode) { Botan::secure_vector seed; Botan::BER_Decoder(key_bits) .decode(seed, Botan::ASN1_Type::OctetString, Botan::ASN1_Type(0), Botan::ASN1_Class::ContextSpecific) @@ -32,7 +31,7 @@ Botan::DilithiumInternalKeypair decode_seed_only(std::span key_bi } Botan::DilithiumInternalKeypair decode_expanded_only(std::span key_bits, - Botan::DilithiumConstants mode) { + Botan::DilithiumConstants mode) { Botan::secure_vector expanded; Botan::BER_Decoder(key_bits).decode(expanded, Botan::ASN1_Type::OctetString).verify_end(); Botan::DilithiumInternalKeypair key_pair = @@ -40,7 +39,8 @@ Botan::DilithiumInternalKeypair decode_expanded_only(std::span ke return key_pair; } -Botan::DilithiumInternalKeypair decode_seed_plus_expanded(std::span key_bits, Botan::DilithiumConstants mode) { +Botan::DilithiumInternalKeypair decode_seed_plus_expanded(std::span key_bits, + Botan::DilithiumConstants mode) { Botan::secure_vector expanded; Botan::secure_vector seed; Botan::BER_Decoder(key_bits) @@ -49,9 +49,9 @@ Botan::DilithiumInternalKeypair decode_seed_plus_expanded(std::span #include #include - #include #include + #include #include "test_pubkey.h" #endif From 3a4c5e9e392a38c47c48ec08a767ef5575eef6e9 Mon Sep 17 00:00:00 2001 From: Falko Strenzke Date: Tue, 24 Feb 2026 07:57:09 +0100 Subject: [PATCH 22/25] remove #if defined(BOTAN_NEEDS_DILITHIUM_PRIVATE_KEY_ENCODING) breaking some CIs --- .../pubkey/dilithium/dilithium_common/dilithium_algos.cpp | 4 ---- src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.h | 5 ----- 2 files changed, 9 deletions(-) diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.cpp b/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.cpp index 5b320b49d52..e7d311cc99c 100644 --- a/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.cpp +++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.cpp @@ -363,8 +363,6 @@ std::pair decode_public_key(StrongSpan decode_public_key(StrongSpan pk, const DilithiumConstants& mode); - -#if BOTAN_NEEDS_DILITHIUM_PRIVATE_KEY_ENCODING - DilithiumSerializedPrivateKey encode_keypair(const DilithiumInternalKeypair& keypair); DilithiumInternalKeypair decode_keypair(StrongSpan sk, DilithiumConstants mode); -#endif - std::pair power2round(const DilithiumPolyVec& vec); std::pair decompose(const DilithiumPolyVec& vec, const DilithiumConstants& mode); From f76a5c8544bf9c506822b728f60961446c10684a Mon Sep 17 00:00:00 2001 From: Falko Strenzke Date: Tue, 24 Feb 2026 10:07:11 +0100 Subject: [PATCH 23/25] complete removal of BOTAN_NEEDS_DILITHIUM_PRIVATE_KEY_ENCODING This was introduced under the assumption that ML-DSA private key encoding doesn't use the expanded format which is no longer true. --- .../pubkey/dilithium/dilithium_common/dilithium_algos.cpp | 8 -------- .../pubkey/dilithium/dilithium_common/dilithium_algos.h | 6 ------ 2 files changed, 14 deletions(-) diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.cpp b/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.cpp index e7d311cc99c..47f92141968 100644 --- a/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.cpp +++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.cpp @@ -137,8 +137,6 @@ void poly_pack_gamma1(const DilithiumPoly& p, BufferStuffer& stuffer, const Dili BOTAN_ASSERT_UNREACHABLE(); } -#if defined(BOTAN_NEEDS_DILITHIUM_PRIVATE_KEY_ENCODING) - /** * NIST FIPS 204, Algorithm 17 (BitPack) * (for a = -eta, b = eta) @@ -164,8 +162,6 @@ void poly_pack_t0(const DilithiumPoly& p, BufferStuffer& stuffer) { poly_pack(p, stuffer); } -#endif - /** * NIST FIPS 204, Algorithm 18 (SimpleBitUnpack) * (for a = 2^(bitlen(q-1)-d) - 1) @@ -196,8 +192,6 @@ void poly_unpack_gamma1(DilithiumPoly& p, ByteSourceT& byte_source, const Dilith BOTAN_ASSERT_UNREACHABLE(); } -#if defined(BOTAN_NEEDS_DILITHIUM_PRIVATE_KEY_ENCODING) - /** * NIST FIPS 204, Algorithm 19 (BitUnpack) * (for a = -eta, b = eta) @@ -223,8 +217,6 @@ void poly_unpack_t0(DilithiumPoly& p, BufferSlicer& slicer) { poly_unpack(p, slicer); } -#endif - /** * NIST FIPS 204, Algorithm 20 (HintBitPack) */ diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.h b/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.h index 63ddd3b8b27..525eef09b88 100644 --- a/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.h +++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium_algos.h @@ -14,12 +14,6 @@ #include -// ML-DSA does encode the private key only by its random seeds. -#if defined(BOTAN_HAS_DILITHIUM) || defined(BOTAN_HAS_DILITHIUM_AES) - // NOLINTNEXTLINE(*-macro-usage) - #define BOTAN_NEEDS_DILITHIUM_PRIVATE_KEY_ENCODING 1 -#endif - namespace Botan::Dilithium_Algos { DilithiumInternalKeypair expand_keypair(DilithiumSeedRandomness xi, DilithiumConstants mode); From 35168f73f01c2fcbb56cdf122de679edbc310275 Mon Sep 17 00:00:00 2001 From: Falko Strenzke Date: Tue, 24 Feb 2026 13:25:58 +0100 Subject: [PATCH 24/25] fix CI --- src/tests/test_dilithium.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/test_dilithium.cpp b/src/tests/test_dilithium.cpp index dd966ba5e16..37c0adf6f44 100644 --- a/src/tests/test_dilithium.cpp +++ b/src/tests/test_dilithium.cpp @@ -320,7 +320,7 @@ class MLDSA_Privkey_Tests : public Text_Based_Test { std::unique_ptr priv_key; try { priv_key = std::make_unique(key_bits, mode); - } catch(const Botan::Decoding_Error& e) { + } catch(const Botan::Decoding_Error&) { result.test_is_true("invalid ML-DSA key rejected", expect_decoding_failure); return result; } From 287703d23c66344bd989c97036141ef06c34d0bc Mon Sep 17 00:00:00 2001 From: Falko Strenzke Date: Mon, 9 Mar 2026 08:05:12 +0100 Subject: [PATCH 25/25] raw_private_key_bits() return seed or throws --- src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp b/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp index e857dae08b6..6f38795a7f0 100644 --- a/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp +++ b/src/lib/pubkey/dilithium/dilithium_common/dilithium.cpp @@ -420,7 +420,12 @@ Dilithium_PrivateKey::Dilithium_PrivateKey(std::span sk, Dilithiu } secure_vector Dilithium_PrivateKey::raw_private_key_bits() const { - return this->private_key_bits(); + const auto& seed_opt = this->m_private->seed(); + if(!seed_opt.has_value()) { + throw Invalid_State( + "cannot return Dilithium or ML-DSA private key raw bits, since the key does not contain the seed"); + } + return seed_opt.value().get(); } secure_vector Dilithium_PrivateKey::private_key_bits() const {