From a3d56a4c26f34808b7e324cc13f2a70593045f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Meusel?= Date: Fri, 16 Aug 2024 11:24:38 +0200 Subject: [PATCH] Introduce PK_Signer/PK_Verifier ::set_associated_data() This is a new concept introduced by FIPS 204 and 205 (ML-DSA, SLH-DSA) where applications get the chance to provide some context that is incorporated into their signatures. It is conceptually similar to the associated data in an AEAD, therefore it behaves similarly in the Signer/Verifier. Note that algorithms that don't support AD, are supposed to always throw when an application calls set_associated_data() on them. There is also a predicate function is_valid_associated_data_length() for applications to generically check for the support of it. Co-Authored-By: Fabian Albert --- src/lib/pubkey/pk_ops.cpp | 10 +++++++++ src/lib/pubkey/pk_ops.h | 42 +++++++++++++++++++++++++++++++++++++ src/lib/pubkey/pubkey.cpp | 22 ++++++++++++++++++++ src/lib/pubkey/pubkey.h | 44 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+) diff --git a/src/lib/pubkey/pk_ops.cpp b/src/lib/pubkey/pk_ops.cpp index 5a01d61c7e6..f1806b23f57 100644 --- a/src/lib/pubkey/pk_ops.cpp +++ b/src/lib/pubkey/pk_ops.cpp @@ -82,6 +82,16 @@ secure_vector PK_Ops::Key_Agreement_with_KDF::agree(size_t key_len, return z; } +void PK_Ops::Signature::set_associated_data(std::span associated_data) { + BOTAN_UNUSED(associated_data); + throw Not_Implemented("This signature scheme does not support labels for signing"); +} + +void PK_Ops::Verification::set_associated_data(std::span associated_data) { + BOTAN_UNUSED(associated_data); + throw Not_Implemented("This signature scheme does not support labels for verification"); +} + namespace { std::unique_ptr create_signature_hash(std::string_view padding) { diff --git a/src/lib/pubkey/pk_ops.h b/src/lib/pubkey/pk_ops.h index 330268cd465..94608d45786 100644 --- a/src/lib/pubkey/pk_ops.h +++ b/src/lib/pubkey/pk_ops.h @@ -79,6 +79,18 @@ class BOTAN_UNSTABLE_API Decryption { */ class BOTAN_UNSTABLE_API Verification { public: + /** + * Incorporate an application-defined associated data into the signature + * verification. This is useful for domain separation, but is not supported + * by all signature schemes. The valid associated data's length is checked + * beforehand, using is_valid_associated_data_length(). + * + * Note: The associated data is meant to be kept between individual message + * verifications. It is the implementer's responsibility to handle + * this state correctly. + */ + virtual void set_associated_data(std::span label); + /** * Add more data to the message currently being signed * @param input the input to be hashed/verified @@ -96,6 +108,15 @@ class BOTAN_UNSTABLE_API Verification { */ virtual std::string hash_function() const = 0; + /** + * @returns true if the associated data length is valid for this signature scheme. + * Schemes that don't support associated data always return false. + */ + virtual bool is_valid_associated_data_length(size_t length) const { + BOTAN_UNUSED(length); + return false; + } + virtual ~Verification() = default; }; @@ -104,6 +125,18 @@ class BOTAN_UNSTABLE_API Verification { */ class BOTAN_UNSTABLE_API Signature { public: + /** + * Incorporate an application-defined label into the signature. This is + * useful for domain separation, but is not supported by all signature + * schemes. The valid length of the label is checked beforehand, using + * is_valid_associated_data_length(). + * + * Note: The associated data is meant to be kept between individual message + * signings. It is the implementer's responsibility to handle this + * state correctly. + */ + virtual void set_associated_data(std::span associated_data); + /** * Add more data to the message currently being signed * @param input the input to be hashed/signed @@ -133,6 +166,15 @@ class BOTAN_UNSTABLE_API Signature { */ virtual std::string hash_function() const = 0; + /** + * @returns true if the label length is valid for this signature scheme + * Schemes that don't support labels always return false. + */ + virtual bool is_valid_associated_data_length(size_t length) const { + BOTAN_UNUSED(length); + return false; + } + virtual ~Signature() = default; }; diff --git a/src/lib/pubkey/pubkey.cpp b/src/lib/pubkey/pubkey.cpp index c7add4f8cb4..0762bb8766c 100644 --- a/src/lib/pubkey/pubkey.cpp +++ b/src/lib/pubkey/pubkey.cpp @@ -272,6 +272,13 @@ PK_Signer::~PK_Signer() = default; PK_Signer::PK_Signer(PK_Signer&&) noexcept = default; PK_Signer& PK_Signer::operator=(PK_Signer&&) noexcept = default; +void PK_Signer::set_associated_data(std::span associated_data) { + if(!is_valid_associated_data_length(associated_data.size())) { + throw Invalid_Argument("PK_Signer: Associated data does not have a supported length"); + } + m_op->set_associated_data(associated_data); +} + void PK_Signer::update(std::string_view in) { this->update(cast_char_ptr_to_uint8(in.data()), in.size()); } @@ -313,6 +320,10 @@ size_t PK_Signer::signature_length() const { } } +bool PK_Signer::is_valid_associated_data_length(size_t length) const { + return m_op->is_valid_associated_data_length(length); +} + std::vector PK_Signer::signature(RandomNumberGenerator& rng) { std::vector sig = m_op->sign(rng); @@ -368,6 +379,17 @@ void PK_Verifier::set_input_format(Signature_Format format) { m_sig_format = format; } +bool PK_Verifier::is_valid_associated_data_length(size_t length) const { + return m_op->is_valid_associated_data_length(length); +} + +void PK_Verifier::set_associated_data(std::span associated_data) { + if(!is_valid_associated_data_length(associated_data.size())) { + throw Invalid_Argument("PK_Verifier: Associated data does not have a supported length"); + } + m_op->set_associated_data(associated_data); +} + void PK_Verifier::update(std::string_view in) { this->update(cast_char_ptr_to_uint8(in.data()), in.size()); } diff --git a/src/lib/pubkey/pubkey.h b/src/lib/pubkey/pubkey.h index 624840bec8b..7f02af94bb8 100644 --- a/src/lib/pubkey/pubkey.h +++ b/src/lib/pubkey/pubkey.h @@ -198,6 +198,23 @@ class BOTAN_PUBLIC_API(2, 0) PK_Signer final { return sign_message(in.data(), in.size(), rng); } + /** + * Incorporate application-defined associated data into the signature. This + * is useful for (e.g.) domain separation, but is not supported by all + * signature schemes. This must be called at most once and before any calls + * to update(). + * + * Unless reset by another call to set_associated_data(), it is kept between + * signature creations. + * + * @sa is_valid_associated_data_length + * @throws Invalid_Argument if associated data is not supported, or the data's + * length is invalid + * + * @param associated_data an application-defined associated data + */ + void set_associated_data(std::span associated_data); + /** * Add a message part (single byte). * @param in the byte to add @@ -258,6 +275,11 @@ class BOTAN_PUBLIC_API(2, 0) PK_Signer final { */ std::string hash_function() const; + /** + * @returns true if the associated data's length is valid for this signature scheme + */ + bool is_valid_associated_data_length(size_t length) const; + private: std::unique_ptr m_op; Signature_Format m_sig_format; @@ -329,6 +351,23 @@ class BOTAN_PUBLIC_API(2, 0) PK_Verifier final { return verify_message(msg.data(), msg.size(), sig.data(), sig.size()); } + /** + * Incorporate application-defined associated data into the signature. This + * is useful for (e.g.) domain separation, but is not supported by all + * signature schemes. This must be called at most once and before any calls + * to update(). + * + * Unless reset by another call to set_associated_data(), it is kept between + * signature verifications. + * + * @sa is_valid_associated_data_length + * @throws Invalid_Argument if associated data is not supported, or the data's + * length is invalid + * + * @param associated_data an application-defined associated data + */ + void set_associated_data(std::span associated_data); + /** * Add a message part (single byte) of the message corresponding to the * signature to be verified. @@ -388,6 +427,11 @@ class BOTAN_PUBLIC_API(2, 0) PK_Verifier final { */ std::string hash_function() const; + /** + * @returns true if the associated data's length is valid for this signature scheme + */ + bool is_valid_associated_data_length(size_t length) const; + private: std::unique_ptr m_op; Signature_Format m_sig_format;