Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Context support for ML-DSA and SLH-DSA #4567

Draft
wants to merge 18 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 18 additions & 39 deletions src/cli/pubkey.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,25 +97,11 @@ BOTAN_REGISTER_COMMAND("keygen", PK_Keygen);

namespace {

std::string choose_sig_padding(const std::string& key, const std::string& padding, const std::string& hash) {
if(key == "RSA") {
std::ostringstream oss;
if(padding.empty()) {
oss << "PSS";
} else {
oss << padding;
}

oss << "(" << hash << ")";
return oss.str();
} else if(padding.empty()) {
return hash;
} else if(hash.empty()) {
return padding;
std::string_view normalize_padding(const std::string& algo, const std::string& requested_padding) {
if(algo == "RSA" && requested_padding.empty()) {
return "PSS";
} else {
std::ostringstream oss;
oss << padding << "(" << hash << ")";
return oss.str();
return requested_padding;
}
}

Expand Down Expand Up @@ -196,21 +182,17 @@ class PK_Sign final : public Command {
throw CLI_Error_Unsupported("hashing", hash_fn);
}

const std::string sig_padding = choose_sig_padding(key->algo_name(), get_arg("padding"), hash_fn);
auto signer_builder = key->signer()
.with_rng(rng())
.with_hash(hash_fn)
.with_der_encoded_signature(flag_set("der-format"))
.with_provider(get_arg("provider"));

auto format = Botan::Signature_Format::Standard;

if(flag_set("der-format")) {
if(key->message_parts() == 1) {
throw CLI_Usage_Error("Key type " + key->algo_name() +
" does not support DER formatting for signatures");
}
format = Botan::Signature_Format::DerSequence;
if(const auto padding = normalize_padding(key->algo_name(), get_arg("padding")); !padding.empty()) {
signer_builder.with_padding(padding);
}

const std::string provider = get_arg("provider");

Botan::PK_Signer signer(*key, rng(), sig_padding, format, provider);
auto signer = signer_builder.create();

auto onData = [&signer](const uint8_t b[], size_t l) { signer.update(b, l); };
Command::read_file(get_arg("file"), onData);
Expand Down Expand Up @@ -254,18 +236,15 @@ class PK_Verify final : public Command {
throw CLI_Error_Unsupported("hashing", hash_fn);
}

const std::string sig_padding = choose_sig_padding(key->algo_name(), get_arg("padding"), hash_fn);
auto verifier_builder =
key->signature_verifier().with_hash(hash_fn).with_der_encoded_signature(flag_set("der-format"));

auto format = Botan::Signature_Format::Standard;
if(flag_set("der-format")) {
if(key->message_parts() == 1) {
throw CLI_Usage_Error("Key type " + key->algo_name() +
" does not support DER formatting for signatures");
}
format = Botan::Signature_Format::DerSequence;
if(const auto padding = normalize_padding(key->algo_name(), get_arg("padding")); !padding.empty()) {
verifier_builder.with_padding(padding);
}

Botan::PK_Verifier verifier(*key, sig_padding, format);
auto verifier = verifier_builder.create();

auto onData = [&verifier](const uint8_t b[], size_t l) { verifier.update(b, l); };
Command::read_file(get_arg("file"), onData);

Expand Down
4 changes: 2 additions & 2 deletions src/examples/ecdsa.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ int main() {
const std::string message("This is a tasty burger!");

// sign data
Botan::PK_Signer signer(key, rng, "SHA-256");
auto signer = key.signer().with_hash("SHA-256").with_rng(rng).create();
signer.update(message);
std::vector<uint8_t> signature = signer.signature(rng);
std::cout << "Signature:\n" << Botan::hex_encode(signature);

// now verify the signature
Botan::PK_Verifier verifier(key, "SHA-256");
auto verifier = key.signature_verifier().with_hash("SHA-256").create();
verifier.update(message);
std::cout << "\nis " << (verifier.check_signature(signature) ? "valid" : "invalid");
return 0;
Expand Down
4 changes: 2 additions & 2 deletions src/examples/pkcs11_ecdsa.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,10 @@ int main() {

std::vector<uint8_t> plaintext(20, 0x01);

Botan::PK_Signer signer(key_pair.second, rng, "Raw", Botan::Signature_Format::Standard, "pkcs11");
auto signer = key_pair.second.signer().with_rng(rng).with_hash("Raw").create();
auto signature = signer.sign_message(plaintext, rng);

Botan::PK_Verifier token_verifier(key_pair.first, "Raw", Botan::Signature_Format::Standard, "pkcs11");
auto token_verifier = key_pair.first.signature_verifier().with_hash("Raw").create();
bool ecdsa_ok = token_verifier.verify_message(plaintext, signature);

return ecdsa_ok ? 0 : 1;
Expand Down
4 changes: 2 additions & 2 deletions src/examples/pkcs11_rsa.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,12 @@ int main() {

/************ RSA sign *************/

Botan::PK_Signer signer(rsa_keypair.second, rng, "EMSA4(SHA-256)", Botan::Signature_Format::Standard);
auto signer = rsa_keypair.second.signer().with_rng(rng).with_hash("SHA-256").with_padding("PSS").create();
auto signature = signer.sign_message(plaintext, rng);

/************ RSA verify *************/

Botan::PK_Verifier verifier(rsa_keypair.first, "EMSA4(SHA-256)", Botan::Signature_Format::Standard);
auto verifier = rsa_keypair.first.signature_verifier().with_hash("SHA-256").with_padding("PSS").create();
auto ok = verifier.verify_message(plaintext, signature);

return ok ? 0 : 1;
Expand Down
5 changes: 2 additions & 3 deletions src/examples/xmss.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include <botan/xmss.h>

#include <iostream>
#include <vector>

int main() {
// Create a random number generator used for key generation.
Expand All @@ -16,14 +15,14 @@ int main() {
const Botan::XMSS_PublicKey& public_key(private_key);

// create Public Key Signer using the private key.
Botan::PK_Signer signer(private_key, rng, "");
auto signer = private_key.signer().with_rng(rng).create();

// create and sign a message using the Public Key Signer.
Botan::secure_vector<uint8_t> msg{0x01, 0x02, 0x03, 0x04};
auto sig = signer.sign_message(msg, rng);

// create Public Key Verifier using the public key
Botan::PK_Verifier verifier(public_key, "");
auto verifier = public_key.signature_verifier().create();

// verify the signature for the previously generated message.
if(verifier.verify_message(msg, sig)) {
Expand Down
1 change: 1 addition & 0 deletions src/lib/math/pcurves/pcurves_impl/pcurves_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#define BOTAN_PCURVES_IMPL_H_

#include <botan/rng.h>
#include <botan/template_utils.h>
#include <botan/internal/ct_utils.h>
#include <botan/internal/loadstor.h>
#include <botan/internal/pcurves_util.h>
Expand Down
132 changes: 50 additions & 82 deletions src/lib/pk_pad/emsa.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* (C) 2015 Jack Lloyd
* (C) 2015,2024 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
Expand All @@ -8,7 +8,7 @@

#include <botan/exceptn.h>
#include <botan/hash.h>
#include <botan/internal/scan_name.h>
#include <botan/pk_options.h>

#if defined(BOTAN_HAS_EMSA_X931)
#include <botan/internal/emsa_x931.h>
Expand All @@ -32,108 +32,76 @@

namespace Botan {

std::unique_ptr<EMSA> EMSA::create(std::string_view algo_spec) {
SCAN_Name req(algo_spec);
std::unique_ptr<EMSA> EMSA::create_or_throw(PK_Signature_Options& options) {
const auto hash = options.hash_function().optional();
const auto padding = options.padding().optional();
const bool is_raw_hash = !hash.has_value() || hash.value() == "Raw";
const bool is_raw_padding = !padding.has_value() || padding.value() == "Raw";

#if defined(BOTAN_HAS_EMSA_PKCS1)
if(req.algo_name() == "EMSA_PKCS1" || req.algo_name() == "PKCS1v15" || req.algo_name() == "EMSA-PKCS1-v1_5" ||
req.algo_name() == "EMSA3") {
if(req.arg_count() == 2 && req.arg(0) == "Raw") {
return std::make_unique<EMSA_PKCS1v15_Raw>(req.arg(1));
} else if(req.arg_count() == 1) {
if(req.arg(0) == "Raw") {
return std::make_unique<EMSA_PKCS1v15_Raw>();
} else {
if(auto hash = HashFunction::create(req.arg(0))) {
return std::make_unique<EMSA_PKCS1v15>(std::move(hash));
if(is_raw_padding) {
// Only valid possibility for empty padding is no hash / "Raw" hash

#if defined(BOTAN_HAS_EMSA_RAW)
if(is_raw_hash) {
if(auto prehash = options.prehash().optional(); prehash.has_value() && prehash->has_value()) {
if(auto prehash_fn = HashFunction::create(prehash->value())) {
return std::make_unique<EMSA_Raw>(prehash_fn->output_length());
}
} else {
return std::make_unique<EMSA_Raw>();
}
}
}
#endif
} else {
// null if raw_hash
auto hash_fn = [&]() -> std::unique_ptr<HashFunction> {
if(is_raw_hash) {
return nullptr;
} else {
return HashFunction::create(hash.value());
}
}();

#if defined(BOTAN_HAS_EMSA_PSSR)
if(req.algo_name() == "PSS_Raw" || req.algo_name() == "PSSR_Raw") {
if(req.arg_count_between(1, 3) && req.arg(1, "MGF1") == "MGF1") {
if(auto hash = HashFunction::create(req.arg(0))) {
if(req.arg_count() == 3) {
const size_t salt_size = req.arg_as_integer(2, 0);
return std::make_unique<PSSR_Raw>(std::move(hash), salt_size);
} else {
return std::make_unique<PSSR_Raw>(std::move(hash));
}
#if defined(BOTAN_HAS_EMSA_PKCS1)
if(padding == "PKCS1v15") {
if(is_raw_hash) {
return std::make_unique<EMSA_PKCS1v15_Raw>(options.prehash().or_default(std::nullopt));
} else if(hash_fn) {
return std::make_unique<EMSA_PKCS1v15>(std::move(hash_fn));
}
}
}
#endif

if(req.algo_name() == "PSS" || req.algo_name() == "PSSR" || req.algo_name() == "EMSA-PSS" ||
req.algo_name() == "PSS-MGF1" || req.algo_name() == "EMSA4") {
if(req.arg_count_between(1, 3) && req.arg(1, "MGF1") == "MGF1") {
if(auto hash = HashFunction::create(req.arg(0))) {
if(req.arg_count() == 3) {
const size_t salt_size = req.arg_as_integer(2, 0);
return std::make_unique<PSSR>(std::move(hash), salt_size);
} else {
return std::make_unique<PSSR>(std::move(hash));
}
}
#if defined(BOTAN_HAS_EMSA_PSSR)
if(padding == "PSS_Raw" && hash_fn) {
return std::make_unique<PSSR_Raw>(std::move(hash_fn), options.salt_size().optional());
}

if(padding == "PSS" && hash_fn) {
return std::make_unique<PSSR>(std::move(hash_fn), options.salt_size().optional());
}
}
#endif

#if defined(BOTAN_HAS_ISO_9796)
if(req.algo_name() == "ISO_9796_DS2") {
if(req.arg_count_between(1, 3)) {
if(auto hash = HashFunction::create(req.arg(0))) {
const size_t salt_size = req.arg_as_integer(2, hash->output_length());
const bool implicit = req.arg(1, "exp") == "imp";
return std::make_unique<ISO_9796_DS2>(std::move(hash), implicit, salt_size);
}
if(padding == "ISO_9796_DS2" && hash_fn) {
return std::make_unique<ISO_9796_DS2>(
std::move(hash_fn), !options.using_explicit_trailer_field(), options.salt_size().optional());
}
}
//ISO-9796-2 DS 3 is deterministic and DS2 without a salt
if(req.algo_name() == "ISO_9796_DS3") {
if(req.arg_count_between(1, 2)) {
if(auto hash = HashFunction::create(req.arg(0))) {
const bool implicit = req.arg(1, "exp") == "imp";
return std::make_unique<ISO_9796_DS3>(std::move(hash), implicit);
}

//ISO-9796-2 DS 3 is deterministic and DS2 without a salt
if(padding == "ISO_9796_DS3" && hash_fn) {
return std::make_unique<ISO_9796_DS3>(std::move(hash_fn), !options.using_explicit_trailer_field());
}
}
#endif

#if defined(BOTAN_HAS_EMSA_X931)
if(req.algo_name() == "EMSA_X931" || req.algo_name() == "EMSA2" || req.algo_name() == "X9.31") {
if(req.arg_count() == 1) {
if(auto hash = HashFunction::create(req.arg(0))) {
return std::make_unique<EMSA_X931>(std::move(hash));
}
if(padding == "X9.31" && hash_fn) {
return std::make_unique<EMSA_X931>(std::move(hash_fn));
}
}
#endif

#if defined(BOTAN_HAS_EMSA_RAW)
if(req.algo_name() == "Raw") {
if(req.arg_count() == 0) {
return std::make_unique<EMSA_Raw>();
} else {
auto hash = HashFunction::create(req.arg(0));
if(hash) {
return std::make_unique<EMSA_Raw>(hash->output_length());
}
}
}
#endif

return nullptr;
}

std::unique_ptr<EMSA> EMSA::create_or_throw(std::string_view algo_spec) {
auto emsa = EMSA::create(algo_spec);
if(emsa) {
return emsa;
}
throw Algorithm_Not_Found(algo_spec);
throw Lookup_Error("Invalid or unavailable signature padding scheme\n" + options.to_string());
}

} // namespace Botan
13 changes: 3 additions & 10 deletions src/lib/pk_pad/emsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
namespace Botan {

class RandomNumberGenerator;
class PK_Signature_Options;

/**
* EMSA, from IEEE 1363s Encoding Method for Signatures, Appendix
Expand All @@ -27,18 +28,10 @@ class BOTAN_TEST_API EMSA {
/**
* Factory method for EMSA (message-encoding methods for signatures
* with appendix) objects
* @param algo_spec the name of the EMSA to create
* @return pointer to newly allocated object of that type, or nullptr
*/
static std::unique_ptr<EMSA> create(std::string_view algo_spec);

/**
* Factory method for EMSA (message-encoding methods for signatures
* with appendix) objects
* @param algo_spec the name of the EMSA to create
* @param options the algorithm parameters
* @return pointer to newly allocated object of that type, or throws
*/
static std::unique_ptr<EMSA> create_or_throw(std::string_view algo_spec);
static std::unique_ptr<EMSA> create_or_throw(PK_Signature_Options& options);

/**
* Add more data to the signature computation
Expand Down
20 changes: 10 additions & 10 deletions src/lib/pk_pad/emsa_pkcs1/emsa_pkcs1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,16 @@ EMSA_PKCS1v15::EMSA_PKCS1v15(std::unique_ptr<HashFunction> hash) : m_hash(std::m
m_hash_id = pkcs_hash_id(m_hash->name());
}

EMSA_PKCS1v15_Raw::EMSA_PKCS1v15_Raw() {
m_hash_output_len = 0;
// m_hash_id, m_hash_name left empty
}

EMSA_PKCS1v15_Raw::EMSA_PKCS1v15_Raw(std::string_view hash_algo) {
std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw(hash_algo));
m_hash_id = pkcs_hash_id(hash_algo);
m_hash_name = hash->name();
m_hash_output_len = hash->output_length();
EMSA_PKCS1v15_Raw::EMSA_PKCS1v15_Raw(const std::optional<std::string>& hash_algo) {
if(hash_algo) {
std::unique_ptr<HashFunction> hash(HashFunction::create_or_throw(hash_algo.value()));
m_hash_id = pkcs_hash_id(hash->name());
m_hash_name = hash->name();
m_hash_output_len = hash->output_length();
} else {
m_hash_output_len = 0;
// m_hash_id, m_hash_name left empty
}
}

void EMSA_PKCS1v15_Raw::update(const uint8_t input[], size_t length) {
Expand Down
Loading
Loading