Skip to content

Commit

Permalink
Ed448 Implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
FAlbertDev committed Mar 25, 2024
1 parent 4c54efa commit 6ac72f5
Show file tree
Hide file tree
Showing 10 changed files with 1,633 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/build-data/oids.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
1.3.101.110 = Curve25519
1.3.101.111 = Curve448
1.3.101.112 = Ed25519
1.3.101.113 = Ed448

# FrodoKEM OIDs are currently in Botan's private arc
1.3.6.1.4.1.25258.1.14.1 = FrodoKEM-640-SHAKE
Expand Down
2 changes: 2 additions & 0 deletions src/lib/asn1/oid_maps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ std::unordered_map<std::string, std::string> OID_Map::load_oid2str_map() {
{"1.3.101.110", "Curve25519"},
{"1.3.101.111", "X448"},
{"1.3.101.112", "Ed25519"},
{"1.3.101.113", "Ed448"},
{"1.3.132.0.10", "secp256k1"},
{"1.3.132.0.30", "secp160r2"},
{"1.3.132.0.31", "secp192k1"},
Expand Down Expand Up @@ -380,6 +381,7 @@ std::unordered_map<std::string, OID> OID_Map::load_str2oid_map() {
{"ECKCDSA/SHA-224", OID({1, 2, 410, 200004, 1, 100, 4, 4})},
{"ECKCDSA/SHA-256", OID({1, 2, 410, 200004, 1, 100, 4, 5})},
{"Ed25519", OID({1, 3, 101, 112})},
{"Ed448", OID({1, 3, 101, 113})},
{"ElGamal", OID({1, 3, 6, 1, 4, 1, 3029, 1, 2, 1})},
{"FrodoKEM-1344-AES", OID({1, 3, 6, 1, 4, 1, 25258, 1, 15, 3})},
{"FrodoKEM-1344-SHAKE", OID({1, 3, 6, 1, 4, 1, 25258, 1, 14, 3})},
Expand Down
254 changes: 254 additions & 0 deletions src/lib/pubkey/curve448/ed448/ed448.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
/*
* Ed448 Signature Algorithm (RFC 8032)
* (C) 2024 Jack Lloyd
* 2024 Fabian Albert - Rohde & Schwarz Cybersecurity
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/ed448.h>

#include <botan/ber_dec.h>
#include <botan/der_enc.h>
#include <botan/hash.h>
#include <botan/rng.h>
#include <botan/internal/ed448_internal.h>
#include <botan/internal/pk_ops_impl.h>

#include <utility>

namespace Botan {

AlgorithmIdentifier Ed448_PublicKey::algorithm_identifier() const {
return AlgorithmIdentifier(object_identifier(), AlgorithmIdentifier::USE_EMPTY_PARAM);
}

bool Ed448_PublicKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const {
try {
Ed448Point::decode(m_public);
} catch(Decoding_Error&) {
return false;
}
return true;
}

Ed448_PublicKey::Ed448_PublicKey(const AlgorithmIdentifier& /* unused */, std::span<const uint8_t> key_bits) :
Ed448_PublicKey(key_bits) {}

Ed448_PublicKey::Ed448_PublicKey(std::span<const uint8_t> key_bits) {
if(key_bits.size() != ED448_LEN) {
throw Decoding_Error("Invalid length for Ed448 public key");
}
copy_mem(m_public, key_bits.first<ED448_LEN>());
}

std::vector<uint8_t> Ed448_PublicKey::public_key_bits() const {
return {m_public.begin(), m_public.end()};
}

std::unique_ptr<Private_Key> Ed448_PublicKey::generate_another(RandomNumberGenerator& rng) const {
return std::make_unique<Ed448_PrivateKey>(rng);
}

Ed448_PrivateKey::Ed448_PrivateKey(const AlgorithmIdentifier& /*unused*/, std::span<const uint8_t> key_bits) {
secure_vector<uint8_t> bits;
BER_Decoder(key_bits).decode(bits, ASN1_Type::OctetString).verify_end();

if(bits.size() != ED448_LEN) {
throw Decoding_Error("Invalid size for Ed448 private key");
}
m_private = std::move(bits);
m_public = create_pk_from_sk(std::span(m_private).first<ED448_LEN>());
}

Ed448_PrivateKey::Ed448_PrivateKey(RandomNumberGenerator& rng) {
m_private.resize(ED448_LEN);
rng.randomize(m_private);
m_public = create_pk_from_sk(std::span(m_private).first<ED448_LEN>());
}

Ed448_PrivateKey::Ed448_PrivateKey(std::span<const uint8_t> key_bits) {
if(key_bits.size() != ED448_LEN) {
throw Decoding_Error("Invalid size for Ed448 private key");
}
m_private = {key_bits.begin(), key_bits.end()};
m_public = create_pk_from_sk(std::span(m_private).first<ED448_LEN>());
}

std::unique_ptr<Public_Key> Ed448_PrivateKey::public_key() const {
return std::make_unique<Ed448_PublicKey>(m_public);
}

secure_vector<uint8_t> Ed448_PrivateKey::private_key_bits() const {
BOTAN_ASSERT_NOMSG(m_private.size() == ED448_LEN);
return DER_Encoder().encode(m_private, ASN1_Type::OctetString).get_contents();
}

bool Ed448_PrivateKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const {
return true;
}

namespace {

/// Interface to abstract either a pure message or a prehashed message
class Ed448_Message {
public:
virtual void update(std::span<const uint8_t> msg) = 0;
virtual std::vector<uint8_t> get_and_clear() = 0;

Ed448_Message() = default;
virtual ~Ed448_Message() = default;
Ed448_Message(const Ed448_Message&) = delete;
Ed448_Message& operator=(const Ed448_Message&) = delete;
Ed448_Message(Ed448_Message&&) = delete;
Ed448_Message& operator=(Ed448_Message&&) = delete;
};

class Prehashed_Ed448_Message final : public Ed448_Message {
public:
void update(std::span<const uint8_t> msg) override { m_hash->update(msg); }

std::vector<uint8_t> get_and_clear() override { return m_hash->final_stdvec(); }

Prehashed_Ed448_Message(std::string_view hash) : m_hash(HashFunction::create_or_throw(hash)) {}

private:
std::unique_ptr<HashFunction> m_hash;
};

class Pure_Ed448_Message final : public Ed448_Message {
public:
void update(std::span<const uint8_t> msg) override { m_msg.insert(m_msg.end(), msg.begin(), msg.end()); }

std::vector<uint8_t> get_and_clear() override { return std::exchange(m_msg, {}); }

private:
std::vector<uint8_t> m_msg;
};

/**
* Ed448 verifying operation
*/
class Ed448_Verify_Operation final : public PK_Ops::Verification {
public:
explicit Ed448_Verify_Operation(const Ed448_PublicKey& key,
std::optional<std::string> prehash_function = std::nullopt) :
m_prehash_function(std::move(prehash_function)) {
const auto pk_bits = key.public_key_bits();
copy_mem(m_pk, std::span(pk_bits).first<ED448_LEN>());
if(m_prehash_function) {
m_message = std::make_unique<Prehashed_Ed448_Message>(*m_prehash_function);
} else {
m_message = std::make_unique<Pure_Ed448_Message>();
}
}

void update(const uint8_t msg[], size_t msg_len) override { m_message->update({msg, msg_len}); }

bool is_valid_signature(const uint8_t sig[], size_t sig_len) override {
const auto msg = m_message->get_and_clear();
try {
return verify_signature(m_pk, m_prehash_function.has_value(), {}, {sig, sig_len}, msg);
} catch(Decoding_Error&) {
return false;
}
}

std::string hash_function() const override { return m_prehash_function.value_or("SHAKE-256(912)"); }

private:
std::array<uint8_t, ED448_LEN> m_pk;
std::unique_ptr<Ed448_Message> m_message;
std::optional<std::string> m_prehash_function;
};

/**
* Ed448 signing operation
*/
class Ed448_Sign_Operation final : public PK_Ops::Signature {
public:
explicit Ed448_Sign_Operation(const Ed448_PrivateKey& key,
std::optional<std::string> prehash_function = std::nullopt) :
m_prehash_function(std::move(prehash_function)) {
const auto pk_bits = key.public_key_bits();
copy_mem(m_pk, std::span(pk_bits).first<ED448_LEN>());
const auto sk_bits = key.raw_private_key_bits();
BOTAN_ASSERT_NOMSG(sk_bits.size() == ED448_LEN);
m_sk = {sk_bits.begin(), sk_bits.end()};
if(m_prehash_function) {
m_message = std::make_unique<Prehashed_Ed448_Message>(*m_prehash_function);
} else {
m_message = std::make_unique<Pure_Ed448_Message>();
}
}

void update(const uint8_t msg[], size_t msg_len) override { m_message->update({msg, msg_len}); }

secure_vector<uint8_t> sign(RandomNumberGenerator& /*rng*/) override {
BOTAN_ASSERT_NOMSG(m_sk.size() == ED448_LEN);
const auto sig = sign_message(
std::span(m_sk).first<ED448_LEN>(), m_pk, m_prehash_function.has_value(), {}, m_message->get_and_clear());
return {sig.begin(), sig.end()};
}

size_t signature_length() const override { return 2 * ED448_LEN; }

AlgorithmIdentifier algorithm_identifier() const override;

std::string hash_function() const override { return m_prehash_function.value_or("SHAKE-256(912)"); }

private:
std::array<uint8_t, ED448_LEN> m_pk;
secure_vector<uint8_t> m_sk;
std::unique_ptr<Ed448_Message> m_message;
std::optional<std::string> m_prehash_function;
};

AlgorithmIdentifier Ed448_Sign_Operation::algorithm_identifier() const {
return AlgorithmIdentifier(OID::from_string("Ed448"), AlgorithmIdentifier::USE_EMPTY_PARAM);
}

} // namespace

std::unique_ptr<PK_Ops::Verification> Ed448_PublicKey::create_verification_op(std::string_view params,
std::string_view provider) const {
if(provider == "base" || provider.empty()) {
if(params.empty() || params == "Identity" || params == "Pure" || params == "Ed448") {
return std::make_unique<Ed448_Verify_Operation>(*this);
} else if(params == "Ed448ph") {
return std::make_unique<Ed448_Verify_Operation>(*this, "SHAKE-256(512)");
} else {
return std::make_unique<Ed448_Verify_Operation>(*this, std::string(params));
}
}
throw Provider_Not_Found(algo_name(), provider);
}

std::unique_ptr<PK_Ops::Verification> Ed448_PublicKey::create_x509_verification_op(const AlgorithmIdentifier& alg_id,
std::string_view provider) const {
if(provider == "base" || provider.empty()) {
if(alg_id != this->algorithm_identifier()) {
throw Decoding_Error("Unexpected AlgorithmIdentifier for Ed448 X509 signature");
}

return std::make_unique<Ed448_Verify_Operation>(*this);
}
throw Provider_Not_Found(algo_name(), provider);
}

std::unique_ptr<PK_Ops::Signature> Ed448_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
std::string_view params,
std::string_view provider) const {
if(provider == "base" || provider.empty()) {
if(params.empty() || params == "Identity" || params == "Pure" || params == "Ed448") {
return std::make_unique<Ed448_Sign_Operation>(*this);
} else if(params == "Ed448ph") {
return std::make_unique<Ed448_Sign_Operation>(*this, "SHAKE-256(512)");
} else {
return std::make_unique<Ed448_Sign_Operation>(*this, std::string(params));
}
}
throw Provider_Not_Found(algo_name(), provider);
}

} // namespace Botan
Loading

0 comments on commit 6ac72f5

Please sign in to comment.