Skip to content

Commit

Permalink
Go all-in on the builder pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
reneme committed Sep 11, 2024
1 parent dea945b commit 6817258
Show file tree
Hide file tree
Showing 9 changed files with 364 additions and 155 deletions.
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, Botan::PK_Signature_Options_Builder().with_hash("SHA-256").commit());
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, Botan::PK_Signature_Options_Builder().with_hash("SHA-256").commit());
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
10 changes: 5 additions & 5 deletions src/examples/xmss.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,19 @@ 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};
signer.update(msg.data(), msg.size());
signer.update(msg);
std::vector<uint8_t> sig = signer.signature(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.
verifier.update(msg.data(), msg.size());
if(verifier.check_signature(sig.data(), sig.size())) {
verifier.update(msg);
if(verifier.check_signature(sig)) {
std::cout << "Success.\n";
return 0;
} else {
Expand Down
11 changes: 9 additions & 2 deletions src/lib/pubkey/pk_keys.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,14 +141,21 @@ std::unique_ptr<PK_Ops::Key_Agreement> Private_Key::create_key_agreement_op(Rand

std::unique_ptr<PK_Ops::Verification> Public_Key::create_verification_op(std::string_view params,
std::string_view provider) const {
auto opts = PK_Signature_Options_Builder(algo_name(), params, provider).commit();
auto opts = PK_Verification_Options_Builder(
*this /* won't be consumed */, params, Signature_Format::Standard /* won't be consumed */, provider)
.commit();
return this->_create_verification_op(opts);
}

std::unique_ptr<PK_Ops::Signature> Private_Key::create_signature_op(RandomNumberGenerator& rng,
std::string_view params,
std::string_view provider) const {
auto opts = PK_Signature_Options_Builder(algo_name(), params, provider).commit();
auto opts = PK_Signature_Options_Builder(*this /* won't be consumed */,
rng /* won't be consumed */,
params,
Signature_Format::Standard /* won't be consumed */,
provider)
.commit();
return this->_create_signature_op(rng, opts);
}

Expand Down
34 changes: 34 additions & 0 deletions src/lib/pubkey/pk_keys.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,24 @@ class BOTAN_PUBLIC_API(2, 0) Public_Key : public virtual Asymmetric_Key {
return (this->message_parts() >= 2) ? Signature_Format::DerSequence : Signature_Format::Standard;
}

/**
* Initiate the creation of a signature verification operation.
* This is a builder-style interface which allows setting various
* options for the operation.
*
* Typical usage:
*
* auto verifier = pub_key.signature_verifier()
* .with_padding("PSS(SHA-256)")
* .with_der_encoded_signature()
* .create();
*
* @return a builder object for verification options
*/
PK_Verification_Options_Builder signature_verifier() const {
return PK_Verification_Options_Builder().with_public_key(*this);
}

/**
* This is an internal library function exposed on key types.
* In almost all cases applications should use wrappers in pubkey.h
Expand Down Expand Up @@ -320,6 +338,22 @@ class BOTAN_PUBLIC_API(2, 0) Private_Key : public virtual Public_Key {
*/
std::string fingerprint_private(std::string_view alg) const;

/**
* Initiate the creation of a signature creation operation.
* This is a builder-style interface which allows setting various
* options for the operation.
*
* Typical usage:
*
* auto signer = pub_key.signer()
* .with_padding("PSS(SHA-256)")
* .with_der_encoded_signature()
* .create();
*
* @return a builder object for signing options
*/
PK_Signature_Options_Builder signer() const { return PK_Signature_Options_Builder().with_private_key(*this); }

/**
* This is an internal library function exposed on key types.
* In all cases applications should use wrappers in pubkey.h
Expand Down
110 changes: 75 additions & 35 deletions src/lib/pubkey/pk_options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,14 @@

namespace Botan {

PK_Signature_Options_Builder::PK_Signature_Options_Builder(std::string_view algo,
std::string_view params,
std::string_view provider) {
namespace {

template <typename SigOptsBaseT>
void retrofit_legacy_parameters(SigOptsBaseT& builder,
const Public_Key& key,
std::string_view params,
Signature_Format format,
std::string_view provider) {
/*
* This is a convoluted mess because we must handle dispatch for every algorithm
* specific detail of how padding strings were formatted in versions prior to 3.6.
Expand All @@ -24,17 +29,25 @@ PK_Signature_Options_Builder::PK_Signature_Options_Builder(std::string_view algo
* are removed in Botan4.
*/

const auto algo = key.algo_name();

if(key.message_parts() != 1) {
builder.with_der_encoded_signature(format == Signature_Format::DerSequence);
}

if(!provider.empty() && provider != "base") {
with_provider(provider);
builder.with_provider(provider);
}

if(algo.starts_with("Dilithium") || algo == "SPHINCS+") {
if(!params.empty() && params != "Randomized" && params != "Deterministic") {
throw Invalid_Argument(fmt("Unexpected parameters for signing with {}", algo));
}

if(params == "Deterministic") {
with_deterministic_signature();
if constexpr(std::is_same_v<SigOptsBaseT, PK_Signature_Options_Builder>) {
if(params == "Deterministic") {
builder.with_deterministic_signature();
}
}
} else if(algo == "SM2") {
/*
Expand All @@ -43,7 +56,7 @@ PK_Signature_Options_Builder::PK_Signature_Options_Builder(std::string_view algo
* Ident,Hash [since 2.3.0]
*/
if(params.empty()) {
with_hash("SM3");
builder.with_hash("SM3");
} else {
const auto [userid, hash] = [&]() -> std::pair<std::string_view, std::string_view> {
if(const auto comma = params.find(','); comma != std::string::npos) {
Expand All @@ -53,23 +66,25 @@ PK_Signature_Options_Builder::PK_Signature_Options_Builder(std::string_view algo
}
}();

with_context(userid);
with_hash(hash);
builder.with_hash(hash);
if(hash != "Raw") {
builder.with_context(userid);
}
}
} else if(algo == "Ed25519") {
if(!params.empty() && params != "Identity" && params != "Pure") {
if(params == "Ed25519ph") {
with_prehash();
builder.with_prehash();
} else {
with_prehash(std::string(params));
builder.with_prehash(std::string(params));
}
}
} else if(algo == "Ed448") {
if(!params.empty() && params != "Identity" && params != "Pure" && params != "Ed448") {
if(params == "Ed448ph") {
with_prehash();
builder.with_prehash();
} else {
with_prehash(std::string(params));
builder.with_prehash(std::string(params));
}
}
} else if(algo == "RSA") {
Expand Down Expand Up @@ -98,30 +113,26 @@ PK_Signature_Options_Builder::PK_Signature_Options_Builder(std::string_view algo

if(padding == "Raw") {
if(req.arg_count() == 0) {
with_padding(padding);
builder.with_padding(padding);
} else if(req.arg_count() == 1) {
with_padding(padding).with_prehash(req.arg(0));
builder.with_padding(padding).with_prehash(req.arg(0));
} else {
throw Invalid_Argument("Raw padding with more than one parameter");
}
} else if(padding == "PKCS1v15") {
if(req.arg_count() == 2 && req.arg(0) == "Raw") {
with_padding(padding);
with_hash(req.arg(0));
with_prehash(req.arg(1));
builder.with_padding(padding).with_hash(req.arg(0)).with_prehash(req.arg(1));
} else if(req.arg_count() == 1) {
with_padding(padding);
with_hash(req.arg(0));
builder.with_padding(padding).with_hash(req.arg(0));
} else {
throw Lookup_Error("PKCS1v15 padding with unexpected parameters");
}
} else if(padding == "PSS_Raw" || padding == "PSS") {
if(req.arg_count_between(1, 3) && req.arg(1, "MGF1") == "MGF1") {
with_padding(padding);
with_hash(req.arg(0));
builder.with_padding(padding).with_hash(req.arg(0));

if(req.arg_count() == 3) {
with_salt_size(req.arg_as_integer(2));
builder.with_salt_size(req.arg_as_integer(2));
}
} else {
throw Lookup_Error("PSS padding with unexpected parameters");
Expand All @@ -131,37 +142,34 @@ PK_Signature_Options_Builder::PK_Signature_Options_Builder(std::string_view algo
// FIXME
const bool implicit = req.arg(1, "exp") == "imp";

with_padding(padding);
with_hash(req.arg(0));
builder.with_padding(padding).with_hash(req.arg(0));

if(req.arg_count() == 3) {
if(implicit) {
with_salt_size(req.arg_as_integer(2));
builder.with_salt_size(req.arg_as_integer(2));
} else {
with_salt_size(req.arg_as_integer(2));
with_explicit_trailer_field();
builder.with_salt_size(req.arg_as_integer(2)).with_explicit_trailer_field();
}
} else if(!implicit) {
with_explicit_trailer_field();
builder.with_explicit_trailer_field();
}
} else {
throw Lookup_Error("ISO-9796-2 DS2 padding with unexpected parameters");
}
} else if(padding == "ISO_9796_DS3") {
//ISO-9796-2 DS 3 is deterministic and DS2 without a salt
with_padding(padding);
builder.with_padding(padding);
if(req.arg_count_between(1, 2)) {
with_hash(req.arg(0));
builder.with_hash(req.arg(0));
if(req.arg(1, "exp") != "imp") {
with_explicit_trailer_field();
builder.with_explicit_trailer_field();
}
} else {
throw Lookup_Error("ISO-9796-2 DS3 padding with unexpected parameters");
}
} else if(padding == "X9.31") {
if(req.arg_count() == 1) {
with_padding(padding);
with_hash(req.arg(0));
builder.with_padding(padding).with_hash(req.arg(0));
} else {
throw Lookup_Error("X9.31 padding with unexpected parameters");
}
Expand All @@ -179,11 +187,43 @@ PK_Signature_Options_Builder::PK_Signature_Options_Builder(std::string_view algo
}
};

with_hash(hash());
builder.with_hash(hash());
}
}
}

} // namespace

PK_Signature_Options_Builder::PK_Signature_Options_Builder(const Private_Key& key,
RandomNumberGenerator& rng,
std::string_view params,
Signature_Format format,
std::string_view provider) {
with_private_key(key);
with_rng(rng);
retrofit_legacy_parameters(*this, key, params, format, provider);
}

PK_Verification_Options_Builder::PK_Verification_Options_Builder(const Public_Key& key,
std::string_view params,
Signature_Format format,
std::string_view provider) {
with_public_key(key);
retrofit_legacy_parameters(*this, key, params, format, provider);
}

PK_Signature_Options_Builder& PK_Signature_Options_Builder::with_private_key(const Private_Key& key) & {
with_product_name(key.algo_name());
set_or_throw(options().private_key, std::cref(key));
return *this;
}

PK_Verification_Options_Builder& PK_Verification_Options_Builder::with_public_key(const Public_Key& key) & {
with_product_name(key.algo_name());
set_or_throw(options().public_key, std::cref(key));
return *this;
}

void PK_Signature_Options::validate_for_hash_based_signature_algorithm(
std::string_view algo_name, std::optional<std::string_view> acceptable_hash) {
if(auto hash = hash_function().optional()) {
Expand Down
Loading

0 comments on commit 6817258

Please sign in to comment.