Botan 3.12.0
Crypto and TLS for C&
ed25519_key.cpp
Go to the documentation of this file.
1/*
2* Ed25519
3* (C) 2017 Ribose Inc
4* 2025 Jack Lloyd
5*
6* Botan is released under the Simplified BSD License (see license.txt)
7*/
8
9#include <botan/ed25519.h>
10
11#include <botan/ber_dec.h>
12#include <botan/der_enc.h>
13#include <botan/hash.h>
14#include <botan/rng.h>
15#include <botan/internal/ct_utils.h>
16#include <botan/internal/ed25519_internal.h>
17#include <botan/internal/pk_ops_impl.h>
18
19namespace Botan {
20
24
25bool Ed25519_PublicKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const {
26 if(m_public.size() != 32) {
27 return false;
28 }
29
30 /*
31 This function was derived from public domain code in Tor's blinding.c
32 */
33
34 const uint8_t identity_element[32] = {1};
35 if(CT::is_equal(m_public.data(), identity_element, 32).as_bool()) {
36 return false;
37 }
38
39 // The order of the Ed25519 group encoded
40 const uint8_t modm_m[32] = {0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7,
41 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
42 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
43
44 const unsigned char zero[32] = {0};
45
46 unsigned char pkcopy[32];
47
48 copy_mem(pkcopy, m_public.data(), 32);
49 pkcopy[31] ^= (1 << 7); // flip sign
50
51 return signature_check(pkcopy, modm_m, identity_element, zero);
52}
53
54Ed25519_PublicKey::Ed25519_PublicKey(const uint8_t pub_key[], size_t pub_len) {
55 if(pub_len != 32) {
56 throw Decoding_Error("Invalid length for Ed25519 key");
57 }
58 m_public.assign(pub_key, pub_key + pub_len);
59}
60
61Ed25519_PublicKey::Ed25519_PublicKey(const AlgorithmIdentifier& /*unused*/, std::span<const uint8_t> key_bits) {
62 m_public.assign(key_bits.begin(), key_bits.end());
63
64 if(m_public.size() != 32) {
65 throw Decoding_Error("Invalid size for Ed25519 public key");
66 }
67}
68
69std::vector<uint8_t> Ed25519_PublicKey::raw_public_key_bits() const {
70 return m_public;
71}
72
73std::vector<uint8_t> Ed25519_PublicKey::public_key_bits() const {
74 return raw_public_key_bits();
75}
76
77std::unique_ptr<Private_Key> Ed25519_PublicKey::generate_another(RandomNumberGenerator& rng) const {
78 return std::make_unique<Ed25519_PrivateKey>(rng);
79}
80
81Ed25519_PrivateKey::Ed25519_PrivateKey(std::span<const uint8_t> secret_key) {
82 if(secret_key.size() == 64) {
83 m_private.assign(secret_key.begin(), secret_key.end());
84 m_public.assign(m_private.begin() + 32, m_private.end());
85 } else if(secret_key.size() == 32) {
86 m_public.resize(32);
87 m_private.resize(64);
88 ed25519_gen_keypair(m_public.data(), m_private.data(), secret_key.data());
89 } else {
90 throw Decoding_Error("Invalid size for Ed25519 private key");
91 }
92}
93
94//static
95Ed25519_PrivateKey Ed25519_PrivateKey::from_seed(std::span<const uint8_t> seed) {
96 BOTAN_ARG_CHECK(seed.size() == 32, "Ed25519 seed must be exactly 32 bytes long");
97 return Ed25519_PrivateKey(seed);
98}
99
100//static
101Ed25519_PrivateKey Ed25519_PrivateKey::from_bytes(std::span<const uint8_t> bytes) {
102 BOTAN_ARG_CHECK(bytes.size() == 64, "Ed25519 private key must be exactly 64 bytes long");
103 return Ed25519_PrivateKey(bytes);
104}
105
107 const secure_vector<uint8_t> seed = rng.random_vec(32);
108 m_public.resize(32);
109 m_private.resize(64);
110 ed25519_gen_keypair(m_public.data(), m_private.data(), seed.data());
111}
112
113Ed25519_PrivateKey::Ed25519_PrivateKey(const AlgorithmIdentifier& /*unused*/, std::span<const uint8_t> key_bits) {
116
117 if(bits.size() != 32) {
118 throw Decoding_Error("Invalid size for Ed25519 private key");
119 }
120 m_public.resize(32);
121 m_private.resize(64);
122 ed25519_gen_keypair(m_public.data(), m_private.data(), bits.data());
123}
124
125std::unique_ptr<Public_Key> Ed25519_PrivateKey::public_key() const {
126 return std::make_unique<Ed25519_PublicKey>(get_public_key());
127}
128
130 const secure_vector<uint8_t> bits(m_private.data(), &m_private[32]);
132}
133
134bool Ed25519_PrivateKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const {
135 std::vector<uint8_t> public_point(32);
136 secure_vector<uint8_t> private_key(64); // discarded
137 ed25519_gen_keypair(public_point.data(), private_key.data(), m_private.data());
138 // Variable time comparison is fine here
139 return public_point == m_public;
140}
141
142namespace {
143
144/**
145* Ed25519 verifying operation
146*/
147class Ed25519_Pure_Verify_Operation final : public PK_Ops::Verification {
148 public:
149 explicit Ed25519_Pure_Verify_Operation(const Ed25519_PublicKey& key) : m_key(key.get_public_key()) {}
150
151 void update(std::span<const uint8_t> msg) override { m_msg.insert(m_msg.end(), msg.begin(), msg.end()); }
152
153 bool is_valid_signature(std::span<const uint8_t> sig) override {
154 if(sig.size() != 64) {
155 m_msg.clear();
156 return false;
157 }
158
159 BOTAN_ASSERT_EQUAL(m_key.size(), 32, "Expected size");
160 const bool ok = ed25519_verify(m_msg.data(), m_msg.size(), sig.data(), m_key.data(), nullptr, 0);
161 m_msg.clear();
162 return ok;
163 }
164
165 std::string hash_function() const override { return "SHA-512"; }
166
167 private:
168 std::vector<uint8_t> m_msg;
169 std::vector<uint8_t> m_key;
170};
171
172/**
173* Ed25519 verifying operation with pre-hash
174*/
175class Ed25519_Hashed_Verify_Operation final : public PK_Ops::Verification_with_Hash {
176 public:
177 Ed25519_Hashed_Verify_Operation(const Ed25519_PublicKey& key, std::string_view hash, bool rfc8032) :
178 PK_Ops::Verification_with_Hash(hash), m_key(key.get_public_key()) {
179 if(rfc8032) {
180 m_domain_sep = {0x53, 0x69, 0x67, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x6E,
181 0x6F, 0x20, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x63, 0x6F,
182 0x6C, 0x6C, 0x69, 0x73, 0x69, 0x6F, 0x6E, 0x73, 0x01, 0x00};
183 }
184 }
185
186 bool verify(std::span<const uint8_t> ph, std::span<const uint8_t> sig) override {
187 if(sig.size() != 64) {
188 return false;
189 }
190
191 BOTAN_ASSERT_EQUAL(m_key.size(), 32, "Expected size");
192 return ed25519_verify(
193 ph.data(), ph.size(), sig.data(), m_key.data(), m_domain_sep.data(), m_domain_sep.size());
194 }
195
196 private:
197 std::vector<uint8_t> m_key;
198 std::vector<uint8_t> m_domain_sep;
199};
200
201/**
202* Ed25519 signing operation ('pure' - signs message directly)
203*/
204class Ed25519_Pure_Sign_Operation final : public PK_Ops::Signature {
205 public:
206 explicit Ed25519_Pure_Sign_Operation(const Ed25519_PrivateKey& key) : m_key(key.raw_private_key_bits()) {}
207
208 void update(std::span<const uint8_t> msg) override { m_msg.insert(m_msg.end(), msg.begin(), msg.end()); }
209
210 std::vector<uint8_t> sign(RandomNumberGenerator& /*rng*/) override {
211 std::vector<uint8_t> sig(64);
212 ed25519_sign(sig.data(), m_msg.data(), m_msg.size(), m_key.data(), nullptr, 0);
213 m_msg.clear();
214 return sig;
215 }
216
217 size_t signature_length() const override { return 64; }
218
219 AlgorithmIdentifier algorithm_identifier() const override;
220
221 std::string hash_function() const override { return "SHA-512"; }
222
223 private:
224 std::vector<uint8_t> m_msg;
226};
227
228AlgorithmIdentifier Ed25519_Pure_Sign_Operation::algorithm_identifier() const {
229 return AlgorithmIdentifier(OID::from_string("Ed25519"), AlgorithmIdentifier::USE_EMPTY_PARAM);
230}
231
232/**
233* Ed25519 signing operation with pre-hash
234*/
235class Ed25519_Hashed_Sign_Operation final : public PK_Ops::Signature_with_Hash {
236 public:
237 Ed25519_Hashed_Sign_Operation(const Ed25519_PrivateKey& key, std::string_view hash, bool rfc8032) :
238 PK_Ops::Signature_with_Hash(hash), m_key(key.raw_private_key_bits()) {
239 if(rfc8032) {
240 m_domain_sep = std::vector<uint8_t>{0x53, 0x69, 0x67, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x6E,
241 0x6F, 0x20, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x63, 0x6F,
242 0x6C, 0x6C, 0x69, 0x73, 0x69, 0x6F, 0x6E, 0x73, 0x01, 0x00};
243 }
244 }
245
246 size_t signature_length() const override { return 64; }
247
248 std::vector<uint8_t> raw_sign(std::span<const uint8_t> ph, RandomNumberGenerator& /*rng*/) override {
249 std::vector<uint8_t> sig(64);
250 ed25519_sign(sig.data(), ph.data(), ph.size(), m_key.data(), m_domain_sep.data(), m_domain_sep.size());
251 return sig;
252 }
253
254 private:
255 secure_vector<uint8_t> m_key;
256 std::vector<uint8_t> m_domain_sep;
257};
258
259} // namespace
260
261std::unique_ptr<PK_Ops::Verification> Ed25519_PublicKey::create_verification_op(std::string_view params,
262 std::string_view provider) const {
263 if(provider == "base" || provider.empty()) {
264 if(params.empty() || params == "Identity" || params == "Pure") {
265 return std::make_unique<Ed25519_Pure_Verify_Operation>(*this);
266 } else if(params == "Ed25519ph") {
267 return std::make_unique<Ed25519_Hashed_Verify_Operation>(*this, "SHA-512", true);
268 } else {
269 return std::make_unique<Ed25519_Hashed_Verify_Operation>(*this, params, false);
270 }
271 }
272 throw Provider_Not_Found(algo_name(), provider);
273}
274
275std::unique_ptr<PK_Ops::Verification> Ed25519_PublicKey::create_x509_verification_op(const AlgorithmIdentifier& alg_id,
276 std::string_view provider) const {
277 if(provider == "base" || provider.empty()) {
278 if(alg_id != this->algorithm_identifier()) {
279 throw Decoding_Error("Unexpected AlgorithmIdentifier for Ed25519 X509 signature");
280 }
281
282 return std::make_unique<Ed25519_Pure_Verify_Operation>(*this);
283 }
284 throw Provider_Not_Found(algo_name(), provider);
285}
286
287std::unique_ptr<PK_Ops::Signature> Ed25519_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
288 std::string_view params,
289 std::string_view provider) const {
290 if(provider == "base" || provider.empty()) {
291 if(params.empty() || params == "Identity" || params == "Pure") {
292 return std::make_unique<Ed25519_Pure_Sign_Operation>(*this);
293 } else if(params == "Ed25519ph") {
294 return std::make_unique<Ed25519_Hashed_Sign_Operation>(*this, "SHA-512", true);
295 } else {
296 return std::make_unique<Ed25519_Hashed_Sign_Operation>(*this, params, false);
297 }
298 }
299 throw Provider_Not_Found(algo_name(), provider);
300}
301
302} // namespace Botan
#define BOTAN_ASSERT_EQUAL(expr1, expr2, assertion_made)
Definition assert.h:88
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
virtual OID object_identifier() const
Definition pk_keys.cpp:22
static Limits DER()
Definition ber_dec.h:35
BER_Decoder & decode(bool &out)
Definition ber_dec.h:220
BER_Decoder & discard_remaining()
Definition ber_dec.cpp:398
secure_vector< uint8_t > get_contents()
Definition der_enc.cpp:134
DER_Encoder & encode(bool b)
Definition der_enc.cpp:245
Ed25519_PrivateKey(const AlgorithmIdentifier &alg_id, std::span< const uint8_t > key_bits)
static Ed25519_PrivateKey from_seed(std::span< const uint8_t > seed)
std::unique_ptr< Public_Key > public_key() const override
bool check_key(RandomNumberGenerator &rng, bool strong) const override
static Ed25519_PrivateKey from_bytes(std::span< const uint8_t > bytes)
std::unique_ptr< PK_Ops::Signature > create_signature_op(RandomNumberGenerator &rng, std::string_view params, std::string_view provider) const override
secure_vector< uint8_t > private_key_bits() const override
std::vector< uint8_t > m_public
Definition ed25519.h:61
std::unique_ptr< PK_Ops::Verification > create_verification_op(std::string_view params, std::string_view provider) const override
std::unique_ptr< PK_Ops::Verification > create_x509_verification_op(const AlgorithmIdentifier &signature_algorithm, std::string_view provider) const override
const std::vector< uint8_t > & get_public_key() const
Definition ed25519.h:37
bool check_key(RandomNumberGenerator &rng, bool strong) const override
std::string algo_name() const override
Definition ed25519.h:19
std::unique_ptr< Private_Key > generate_another(RandomNumberGenerator &rng) const final
std::vector< uint8_t > public_key_bits() const override
AlgorithmIdentifier algorithm_identifier() const override
std::vector< uint8_t > raw_public_key_bits() const override
void random_vec(std::span< uint8_t > v)
Definition rng.h:204
constexpr CT::Mask< T > is_equal(const T x[], const T y[], size_t len)
Definition ct_utils.h:798
void ed25519_sign(uint8_t sig[64], const uint8_t m[], size_t mlen, const uint8_t sk[64], const uint8_t domain_sep[], size_t domain_sep_len)
Definition ed25519.cpp:34
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:144
bool signature_check(std::span< const uint8_t, 32 > pk, const uint8_t h[32], const uint8_t r[32], const uint8_t s[32])
Definition ge.cpp:1904
void ed25519_gen_keypair(uint8_t pk[32], uint8_t sk[64], const uint8_t seed[32])
Definition ed25519.cpp:18
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:68
bool ed25519_verify(const uint8_t *m, size_t mlen, const uint8_t sig[64], const uint8_t *pk, const uint8_t domain_sep[], size_t domain_sep_len)
Definition ed25519.cpp:70