-
Notifications
You must be signed in to change notification settings - Fork 574
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
cd66c3c
commit 4c54efa
Showing
10 changed files
with
3,028 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
<defines> | ||
X448 -> 20240219 | ||
</defines> | ||
|
||
<module_info> | ||
name -> "X448" | ||
brief -> "X448 key agreement algorithm" | ||
</module_info> | ||
|
||
<header:public> | ||
x448.h | ||
</header:public> | ||
|
||
<header:internal> | ||
x448_internal.h | ||
</header:internal> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
/* | ||
* X448 | ||
* (C) 2024 Jack Lloyd | ||
* 2024 Fabian Albert - Rohde & Schwarz Cybersecurity | ||
* | ||
* Botan is released under the Simplified BSD License (see license.txt) | ||
*/ | ||
|
||
#include <botan/x448.h> | ||
|
||
#include <botan/ber_dec.h> | ||
#include <botan/der_enc.h> | ||
#include <botan/rng.h> | ||
#include <botan/internal/ct_utils.h> | ||
#include <botan/internal/pk_ops_impl.h> | ||
#include <botan/internal/x448_internal.h> | ||
|
||
namespace Botan { | ||
|
||
namespace { | ||
void x448_basepoint_from_data(std::span<uint8_t, X448_LEN> mypublic, std::span<const uint8_t, X448_LEN> secret) { | ||
auto bp = x448_basepoint(decode_scalar(secret)); | ||
auto bp_bytes = encode_point(bp); | ||
copy_mem(mypublic, bp_bytes); | ||
} | ||
|
||
secure_vector<uint8_t> ber_decode_sk(std::span<const uint8_t> key_bits) { | ||
secure_vector<uint8_t> decoded_bits; | ||
BER_Decoder(key_bits).decode(decoded_bits, ASN1_Type::OctetString).verify_end(); | ||
BOTAN_ASSERT_NOMSG(decoded_bits.size() == X448_LEN); | ||
return decoded_bits; | ||
} | ||
|
||
} // namespace | ||
|
||
AlgorithmIdentifier X448_PublicKey::algorithm_identifier() const { | ||
return AlgorithmIdentifier(object_identifier(), AlgorithmIdentifier::USE_EMPTY_PARAM); | ||
} | ||
|
||
bool X448_PublicKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const { | ||
return true; // no tests possible? | ||
} | ||
|
||
std::vector<uint8_t> X448_PublicKey::public_key_bits() const { | ||
return public_value(); | ||
} | ||
|
||
std::unique_ptr<Private_Key> X448_PublicKey::generate_another(RandomNumberGenerator& rng) const { | ||
return std::make_unique<X448_PrivateKey>(rng); | ||
} | ||
|
||
X448_PublicKey::X448_PublicKey(const AlgorithmIdentifier& /*alg_id*/, std::span<const uint8_t> key_bits) : | ||
X448_PublicKey(key_bits) {} | ||
|
||
X448_PublicKey::X448_PublicKey(std::span<const uint8_t> pub) { | ||
BOTAN_ARG_CHECK(pub.size() == X448_LEN, "Invalid size for X448 public key"); | ||
copy_mem(m_public, pub); | ||
} | ||
|
||
X448_PrivateKey::X448_PrivateKey(const AlgorithmIdentifier& /*alg_id*/, std::span<const uint8_t> key_bits) : | ||
X448_PrivateKey(ber_decode_sk(key_bits)) {} | ||
|
||
X448_PrivateKey::X448_PrivateKey(std::span<const uint8_t> secret_key) { | ||
BOTAN_ARG_CHECK(secret_key.size() == X448_LEN, "Invalid size for X448 private key"); | ||
m_private = {secret_key.begin(), secret_key.end()}; | ||
x448_basepoint_from_data(m_public, std::span(m_private).first<X448_LEN>()); | ||
} | ||
|
||
X448_PrivateKey::X448_PrivateKey(RandomNumberGenerator& rng) { | ||
m_private.resize(X448_LEN); | ||
rng.randomize(m_private); | ||
x448_basepoint_from_data(m_public, std::span(m_private).first<X448_LEN>()); | ||
} | ||
|
||
std::unique_ptr<Public_Key> X448_PrivateKey::public_key() const { | ||
return std::make_unique<X448_PublicKey>(public_value()); | ||
} | ||
|
||
secure_vector<uint8_t> X448_PrivateKey::private_key_bits() const { | ||
return DER_Encoder().encode(m_private, ASN1_Type::OctetString).get_contents(); | ||
} | ||
|
||
bool X448_PrivateKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const { | ||
std::array<uint8_t, X448_LEN> public_point; | ||
BOTAN_ASSERT_NOMSG(m_private.size() == X448_LEN); | ||
x448_basepoint_from_data(public_point, std::span(m_private).first<X448_LEN>()); | ||
return CT::is_equal(public_point.data(), m_public.data(), m_public.size()).as_bool(); | ||
} | ||
|
||
namespace { | ||
|
||
/** | ||
* X448 operation | ||
*/ | ||
class X448_KA_Operation final : public PK_Ops::Key_Agreement_with_KDF { | ||
public: | ||
X448_KA_Operation(std::span<const uint8_t> sk, std::string_view kdf) : | ||
PK_Ops::Key_Agreement_with_KDF(kdf), m_sk(sk.begin(), sk.end()) { | ||
BOTAN_ARG_CHECK(sk.size() == X448_LEN, "Invalid size for X448 private key"); | ||
} | ||
|
||
size_t agreed_value_size() const override { return X448_LEN; } | ||
|
||
secure_vector<uint8_t> raw_agree(const uint8_t w_data[], size_t w_len) override { | ||
std::span<const uint8_t> w(w_data, w_len); | ||
BOTAN_ARG_CHECK(w.size() == X448_LEN, "Invalid size for X448 private key"); | ||
BOTAN_ASSERT_NOMSG(m_sk.size() == X448_LEN); | ||
const auto k = decode_scalar(m_sk); | ||
const auto u = decode_point(w); | ||
|
||
return encode_point(x448(k, u)); | ||
} | ||
|
||
private: | ||
secure_vector<uint8_t> m_sk; | ||
}; | ||
|
||
} // namespace | ||
|
||
std::unique_ptr<PK_Ops::Key_Agreement> X448_PrivateKey::create_key_agreement_op(RandomNumberGenerator& /*rng*/, | ||
std::string_view params, | ||
std::string_view provider) const { | ||
if(provider == "base" || provider.empty()) { | ||
return std::make_unique<X448_KA_Operation>(m_private, params); | ||
} | ||
throw Provider_Not_Found(algo_name(), provider); | ||
} | ||
|
||
} // namespace Botan |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/* | ||
* X448 | ||
* (C) 2024 Jack Lloyd | ||
* 2024 Fabian Albert - Rohde & Schwarz Cybersecurity | ||
* | ||
* Botan is released under the Simplified BSD License (see license.txt) | ||
*/ | ||
#ifndef BOTAN_X448_H_ | ||
#define BOTAN_X448_H_ | ||
|
||
#include <botan/pk_keys.h> | ||
|
||
#include <array> | ||
|
||
namespace Botan { | ||
/** | ||
* @brief A public key for the X448 key agreement scheme according to RFC 7748. | ||
*/ | ||
class BOTAN_PUBLIC_API(3, 4) X448_PublicKey : public virtual Public_Key { | ||
public: | ||
/** | ||
* Create a Curve25519 Public Key. | ||
* @param alg_id the X.509 algorithm identifier | ||
* @param key_bits DER encoded public key bits | ||
*/ | ||
X448_PublicKey(const AlgorithmIdentifier& alg_id, std::span<const uint8_t> key_bits); | ||
|
||
/** | ||
* Create a X448 Public Key. | ||
* @param pub 56-byte raw public key | ||
*/ | ||
explicit X448_PublicKey(std::span<const uint8_t> pub); | ||
|
||
std::string algo_name() const override { return "X448"; } | ||
|
||
size_t estimated_strength() const override { return 224; } | ||
|
||
size_t key_length() const override { return 448; } | ||
|
||
bool check_key(RandomNumberGenerator& rng, bool strong) const override; | ||
|
||
AlgorithmIdentifier algorithm_identifier() const override; | ||
|
||
std::vector<uint8_t> public_value() const { return {m_public.begin(), m_public.end()}; } | ||
|
||
std::vector<uint8_t> public_key_bits() const override; | ||
|
||
bool supports_operation(PublicKeyOperation op) const override { return (op == PublicKeyOperation::KeyAgreement); } | ||
|
||
std::unique_ptr<Private_Key> generate_another(RandomNumberGenerator& rng) const final; | ||
|
||
protected: | ||
X448_PublicKey() = default; | ||
std::array<uint8_t, 56> m_public; | ||
}; | ||
|
||
BOTAN_DIAGNOSTIC_PUSH | ||
BOTAN_DIAGNOSTIC_IGNORE_INHERITED_VIA_DOMINANCE | ||
|
||
/** | ||
* @brief A private key for the X448 key agreement scheme according to RFC 7748. | ||
*/ | ||
class BOTAN_PUBLIC_API(3, 4) X448_PrivateKey final : public X448_PublicKey, | ||
public virtual Private_Key, | ||
public virtual PK_Key_Agreement_Key { | ||
public: | ||
/** | ||
* Construct a private key from the specified parameters. | ||
* @param alg_id the X.509 algorithm identifier | ||
* @param key_bits PKCS #8 structure | ||
*/ | ||
X448_PrivateKey(const AlgorithmIdentifier& alg_id, std::span<const uint8_t> key_bits); | ||
|
||
/** | ||
* Generate a private key. | ||
* @param rng the RNG to use | ||
*/ | ||
explicit X448_PrivateKey(RandomNumberGenerator& rng); | ||
|
||
/** | ||
* Construct a private key from the specified parameters. | ||
* @param secret_key the private key | ||
*/ | ||
explicit X448_PrivateKey(std::span<const uint8_t> secret_key); | ||
|
||
std::vector<uint8_t> public_value() const override { return X448_PublicKey::public_key_bits(); } | ||
|
||
secure_vector<uint8_t> raw_private_key_bits() const override { return {m_private.begin(), m_private.end()}; } | ||
|
||
secure_vector<uint8_t> private_key_bits() const override; | ||
|
||
std::unique_ptr<Public_Key> public_key() const override; | ||
|
||
bool check_key(RandomNumberGenerator& rng, bool strong) const override; | ||
|
||
std::unique_ptr<PK_Ops::Key_Agreement> create_key_agreement_op(RandomNumberGenerator& rng, | ||
std::string_view params, | ||
std::string_view provider) const override; | ||
|
||
private: | ||
secure_vector<uint8_t> m_private; | ||
}; | ||
|
||
BOTAN_DIAGNOSTIC_POP | ||
|
||
} // namespace Botan | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/* | ||
* X448 Internal | ||
* (C) 2024 Jack Lloyd | ||
* 2024 Fabian Albert - Rohde & Schwarz Cybersecurity | ||
* | ||
* Botan is released under the Simplified BSD License (see license.txt) | ||
*/ | ||
#include <botan/internal/x448_internal.h> | ||
|
||
#include <botan/internal/ct_utils.h> | ||
#include <botan/internal/curve448_gf.h> | ||
|
||
namespace Botan { | ||
|
||
namespace { | ||
uint64_t get_bit(const ScalarX448& scalar, size_t bit) { | ||
return (scalar[bit / 8] >> (bit % 8)) & 1; | ||
} | ||
} // namespace | ||
|
||
secure_vector<uint8_t> encode_point(const Point448& p) { | ||
return {p.begin(), p.end()}; | ||
} | ||
|
||
Point448 decode_point(std::span<const uint8_t> p_bytes) { | ||
BOTAN_ARG_CHECK(p_bytes.size() == X448_LEN, "Invalid size for X448 point"); | ||
return typecast_copy<Point448>(p_bytes); | ||
} | ||
|
||
ScalarX448 decode_scalar(std::span<const uint8_t> scalar_bytes) { | ||
BOTAN_ARG_CHECK(scalar_bytes.size() == X448_LEN, "Invalid size for X448 scalar"); | ||
auto buf = typecast_copy<ScalarX448>(scalar_bytes); | ||
|
||
buf[0] &= 0xfc; | ||
buf[55] |= 0x80; | ||
|
||
return buf; | ||
} | ||
|
||
/// Multiply a scalar with the base group element (5) | ||
Point448 x448_basepoint(const ScalarX448& k) { | ||
const Point448 u({5}); | ||
return x448(k, u); | ||
} | ||
|
||
// Algorithm see RFC 7748, Section 5: | ||
// https://datatracker.ietf.org/doc/html/rfc7748#section-5 | ||
Point448 x448(const ScalarX448& k, const Point448& u) { | ||
const Gf448Elem a24 = 39081; | ||
|
||
Gf448Elem x_1 = Gf448Elem(u.get()); | ||
Gf448Elem x_2 = 1; | ||
Gf448Elem z_2 = 0; | ||
Gf448Elem x_3 = Gf448Elem(u.get()); | ||
Gf448Elem z_3 = 1; | ||
auto swap = CT::Mask<uint64_t>::cleared(); | ||
|
||
for(int16_t t = 448 - 1; t >= 0; --t) { | ||
auto k_t = CT::Mask<uint64_t>::expand(get_bit(k, t)); | ||
swap ^= k_t; | ||
|
||
x_2.ct_cond_swap(swap.as_bool(), x_3); | ||
z_2.ct_cond_swap(swap.as_bool(), z_3); | ||
swap = k_t; | ||
|
||
const auto A = x_2 + z_2; | ||
const auto AA = square(A); | ||
const auto B = x_2 - z_2; | ||
const auto BB = square(B); | ||
const auto E = AA - BB; | ||
const auto C = x_3 + z_3; | ||
const auto D = x_3 - z_3; | ||
const auto DA = D * A; | ||
const auto CB = C * B; | ||
x_3 = square(DA + CB); | ||
z_3 = x_1 * square(DA - CB); | ||
x_2 = AA * BB; | ||
z_2 = E * (AA + a24 * E); | ||
} | ||
|
||
x_2.ct_cond_swap(swap.as_bool(), x_3); | ||
z_2.ct_cond_swap(swap.as_bool(), z_3); | ||
|
||
const auto res = x_2 / z_2; | ||
|
||
return Point448(res.to_bytes()); | ||
} | ||
|
||
} // namespace Botan |
Oops, something went wrong.