Botan 3.13.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
21class Ed25519_PublicKey_Data final {
22 public:
23 explicit Ed25519_PublicKey_Data(std::vector<uint8_t> key) : m_key(std::move(key)) {}
24
25 const std::vector<uint8_t>& key() const { return m_key; }
26
27 private:
28 std::vector<uint8_t> m_key;
29};
30
31class Ed25519_PrivateKey_Data final {
32 public:
33 explicit Ed25519_PrivateKey_Data(secure_vector<uint8_t> key) : m_key(std::move(key)) {}
34
35 const secure_vector<uint8_t>& key() const { return m_key; }
36
37 private:
39};
40
41const std::vector<uint8_t>& Ed25519_PublicKey::get_public_key() const {
42 return m_public->key();
43}
44
46 return m_private->key();
47}
48
50 return m_private->key();
51}
52
56
57bool Ed25519_PublicKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const {
58 const std::vector<uint8_t>& pub = m_public->key();
59
60 if(pub.size() != 32) {
61 return false;
62 }
63
64 /*
65 This function was derived from public domain code in Tor's blinding.c
66 */
67
68 const uint8_t identity_element[32] = {1};
69 if(CT::is_equal(pub.data(), identity_element, 32).as_bool()) {
70 return false;
71 }
72
73 // Also reject the non-canonical encoding of the identity (y = 1 with the
74 // sign bit set). The subgroup check below flips the sign bit before decoding,
75 // which would otherwise normalize {0x01, .., 0x80} to the canonical identity
76 // and let it pass.
77 uint8_t noncanonical_identity[32] = {1};
78 noncanonical_identity[31] = 0x80;
79 if(CT::is_equal(pub.data(), noncanonical_identity, 32).as_bool()) {
80 return false;
81 }
82
83 // The order of the Ed25519 group encoded
84 const uint8_t modm_m[32] = {0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7,
85 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
86 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10};
87
88 const unsigned char zero[32] = {0};
89
90 unsigned char pkcopy[32];
91
92 copy_mem(pkcopy, pub.data(), 32);
93 pkcopy[31] ^= (1 << 7); // flip sign
94
95 return signature_check(pkcopy, modm_m, identity_element, zero);
96}
97
98Ed25519_PublicKey::Ed25519_PublicKey(const uint8_t pub_key[], size_t pub_len) {
99 if(pub_len != 32) {
100 throw Decoding_Error("Invalid length for Ed25519 key");
101 }
102 m_public = std::make_shared<const Ed25519_PublicKey_Data>(std::vector<uint8_t>(pub_key, pub_key + pub_len));
103}
104
105Ed25519_PublicKey::Ed25519_PublicKey(const AlgorithmIdentifier& alg_id, std::span<const uint8_t> key_bits) {
106 // RFC 8410 Section 3: "the parameters MUST be absent".
107 if(!alg_id.parameters_are_empty()) {
108 throw Decoding_Error("Unexpected parameters for Ed25519 public key");
109 }
110
111 if(key_bits.size() != 32) {
112 throw Decoding_Error("Invalid size for Ed25519 public key");
113 }
114
115 m_public = std::make_shared<const Ed25519_PublicKey_Data>(std::vector<uint8_t>(key_bits.begin(), key_bits.end()));
116}
117
118std::vector<uint8_t> Ed25519_PublicKey::raw_public_key_bits() const {
119 return m_public->key();
120}
121
122std::vector<uint8_t> Ed25519_PublicKey::public_key_bits() const {
123 return raw_public_key_bits();
124}
125
126std::unique_ptr<Private_Key> Ed25519_PublicKey::generate_another(RandomNumberGenerator& rng) const {
127 return std::make_unique<Ed25519_PrivateKey>(rng);
128}
129
130namespace {
131
132// Given the 64-byte expanded private key (32-byte private seed followed by the
133// 32-byte public key) build the immutable public and private key data objects.
134void load_ed25519_keypair(secure_vector<uint8_t> expanded_key,
135 std::shared_ptr<const Ed25519_PublicKey_Data>& pk_out,
136 std::shared_ptr<const Ed25519_PrivateKey_Data>& sk_out) {
137 BOTAN_ASSERT_NOMSG(expanded_key.size() == 64);
138 pk_out = std::make_shared<const Ed25519_PublicKey_Data>(
139 std::vector<uint8_t>(expanded_key.begin() + 32, expanded_key.end()));
140 sk_out = std::make_shared<const Ed25519_PrivateKey_Data>(std::move(expanded_key));
141}
142
143// Generate the 64-byte expanded private key from a 32-byte seed.
144secure_vector<uint8_t> ed25519_expand_seed(std::span<const uint8_t> seed) {
145 BOTAN_ASSERT_NOMSG(seed.size() == 32);
146 std::vector<uint8_t> pk(32); // also written into the expanded private key
148 ed25519_gen_keypair(pk.data(), sk.data(), seed.data());
149 return sk;
150}
151
152} // namespace
153
154Ed25519_PrivateKey::Ed25519_PrivateKey(std::span<const uint8_t> secret_key) {
155 if(secret_key.size() == 64) {
156 load_ed25519_keypair(secure_vector<uint8_t>(secret_key.begin(), secret_key.end()), m_public, m_private);
157 } else if(secret_key.size() == 32) {
158 load_ed25519_keypair(ed25519_expand_seed(secret_key), m_public, m_private);
159 } else {
160 throw Decoding_Error("Invalid size for Ed25519 private key");
161 }
162}
163
164//static
165Ed25519_PrivateKey Ed25519_PrivateKey::from_seed(std::span<const uint8_t> seed) {
166 BOTAN_ARG_CHECK(seed.size() == 32, "Ed25519 seed must be exactly 32 bytes long");
167 return Ed25519_PrivateKey(seed);
168}
169
170//static
171Ed25519_PrivateKey Ed25519_PrivateKey::from_bytes(std::span<const uint8_t> bytes) {
172 BOTAN_ARG_CHECK(bytes.size() == 64, "Ed25519 private key must be exactly 64 bytes long");
173 return Ed25519_PrivateKey(bytes);
174}
175
177 const secure_vector<uint8_t> seed = rng.random_vec(32);
178 load_ed25519_keypair(ed25519_expand_seed(seed), m_public, m_private);
179}
180
181Ed25519_PrivateKey::Ed25519_PrivateKey(const AlgorithmIdentifier& alg_id, std::span<const uint8_t> key_bits) {
182 // RFC 8410 Section 3: "the parameters MUST be absent".
183 if(!alg_id.parameters_are_empty()) {
184 throw Decoding_Error("Unexpected parameters for Ed25519 private key");
185 }
186
189
190 if(bits.size() != 32) {
191 throw Decoding_Error("Invalid size for Ed25519 private key");
192 }
193 load_ed25519_keypair(ed25519_expand_seed(bits), m_public, m_private);
194}
195
196std::unique_ptr<Public_Key> Ed25519_PrivateKey::public_key() const {
197 return std::make_unique<Ed25519_PublicKey>(raw_public_key_bits());
198}
199
201 const auto& priv = m_private->key();
202 const secure_vector<uint8_t> bits(priv.begin(), priv.begin() + 32);
204}
205
206bool Ed25519_PrivateKey::check_key(RandomNumberGenerator& /*rng*/, bool /*strong*/) const {
207 std::vector<uint8_t> public_point(32);
208 secure_vector<uint8_t> private_key(64); // discarded
209 ed25519_gen_keypair(public_point.data(), private_key.data(), m_private->key().data());
210 // Variable time comparison is fine here
211 return public_point == m_public->key();
212}
213
214namespace {
215
216/**
217* Ed25519 verifying operation
218*/
219class Ed25519_Pure_Verify_Operation final : public PK_Ops::Verification {
220 public:
221 explicit Ed25519_Pure_Verify_Operation(std::shared_ptr<const Ed25519_PublicKey_Data> key) :
222 m_key(std::move(key)) {}
223
224 void update(std::span<const uint8_t> msg) override { m_msg.insert(m_msg.end(), msg.begin(), msg.end()); }
225
226 bool is_valid_signature(std::span<const uint8_t> sig) override {
227 if(sig.size() != 64) {
228 m_msg.clear();
229 return false;
230 }
231
232 const auto& key = m_key->key();
233 BOTAN_ASSERT_EQUAL(key.size(), 32, "Expected size");
234 const bool ok = ed25519_verify(m_msg.data(), m_msg.size(), sig.data(), key.data(), nullptr, 0);
235 m_msg.clear();
236 return ok;
237 }
238
239 std::string hash_function() const override { return "SHA-512"; }
240
241 private:
242 std::vector<uint8_t> m_msg;
243 std::shared_ptr<const Ed25519_PublicKey_Data> m_key;
244};
245
246/**
247* Ed25519 verifying operation with pre-hash
248*/
249class Ed25519_Hashed_Verify_Operation final : public PK_Ops::Verification_with_Hash {
250 public:
251 Ed25519_Hashed_Verify_Operation(std::shared_ptr<const Ed25519_PublicKey_Data> key,
252 std::string_view hash,
253 bool rfc8032) :
254 PK_Ops::Verification_with_Hash(hash), m_key(std::move(key)) {
255 if(rfc8032) {
256 m_domain_sep = {0x53, 0x69, 0x67, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x6E,
257 0x6F, 0x20, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x63, 0x6F,
258 0x6C, 0x6C, 0x69, 0x73, 0x69, 0x6F, 0x6E, 0x73, 0x01, 0x00};
259 }
260 }
261
262 bool verify(std::span<const uint8_t> ph, std::span<const uint8_t> sig) override {
263 if(sig.size() != 64) {
264 return false;
265 }
266
267 const auto& key = m_key->key();
268 BOTAN_ASSERT_EQUAL(key.size(), 32, "Expected size");
269 return ed25519_verify(ph.data(), ph.size(), sig.data(), key.data(), m_domain_sep.data(), m_domain_sep.size());
270 }
271
272 private:
273 std::shared_ptr<const Ed25519_PublicKey_Data> m_key;
274 std::vector<uint8_t> m_domain_sep;
275};
276
277/**
278* Ed25519 signing operation ('pure' - signs message directly)
279*/
280class Ed25519_Pure_Sign_Operation final : public PK_Ops::Signature {
281 public:
282 explicit Ed25519_Pure_Sign_Operation(std::shared_ptr<const Ed25519_PrivateKey_Data> key) :
283 m_key(std::move(key)) {}
284
285 void update(std::span<const uint8_t> msg) override { m_msg.insert(m_msg.end(), msg.begin(), msg.end()); }
286
287 std::vector<uint8_t> sign(RandomNumberGenerator& /*rng*/) override {
288 std::vector<uint8_t> sig(64);
289 const auto& key = m_key->key();
290 ed25519_sign(sig.data(), m_msg.data(), m_msg.size(), key.data(), nullptr, 0);
291 m_msg.clear();
292 return sig;
293 }
294
295 size_t signature_length() const override { return 64; }
296
297 AlgorithmIdentifier algorithm_identifier() const override;
298
299 std::string hash_function() const override { return "SHA-512"; }
300
301 private:
302 std::vector<uint8_t> m_msg;
303 std::shared_ptr<const Ed25519_PrivateKey_Data> m_key;
304};
305
306AlgorithmIdentifier Ed25519_Pure_Sign_Operation::algorithm_identifier() const {
307 return AlgorithmIdentifier(OID::from_string("Ed25519"), AlgorithmIdentifier::USE_EMPTY_PARAM);
308}
309
310/**
311* Ed25519 signing operation with pre-hash
312*/
313class Ed25519_Hashed_Sign_Operation final : public PK_Ops::Signature_with_Hash {
314 public:
315 Ed25519_Hashed_Sign_Operation(std::shared_ptr<const Ed25519_PrivateKey_Data> key,
316 std::string_view hash,
317 bool rfc8032) :
318 PK_Ops::Signature_with_Hash(hash), m_key(std::move(key)) {
319 if(rfc8032) {
320 m_domain_sep = std::vector<uint8_t>{0x53, 0x69, 0x67, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x6E,
321 0x6F, 0x20, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x20, 0x63, 0x6F,
322 0x6C, 0x6C, 0x69, 0x73, 0x69, 0x6F, 0x6E, 0x73, 0x01, 0x00};
323 }
324 }
325
326 size_t signature_length() const override { return 64; }
327
328 std::vector<uint8_t> raw_sign(std::span<const uint8_t> ph, RandomNumberGenerator& /*rng*/) override {
329 std::vector<uint8_t> sig(64);
330 const auto& key = m_key->key();
331 ed25519_sign(sig.data(), ph.data(), ph.size(), key.data(), m_domain_sep.data(), m_domain_sep.size());
332 return sig;
333 }
334
335 private:
336 std::shared_ptr<const Ed25519_PrivateKey_Data> m_key;
337 std::vector<uint8_t> m_domain_sep;
338};
339
340} // namespace
341
342std::unique_ptr<PK_Ops::Verification> Ed25519_PublicKey::create_verification_op(std::string_view params,
343 std::string_view provider) const {
344 if(provider == "base" || provider.empty()) {
345 if(params.empty() || params == "Identity" || params == "Pure") {
346 return std::make_unique<Ed25519_Pure_Verify_Operation>(m_public);
347 } else if(params == "Ed25519ph") {
348 return std::make_unique<Ed25519_Hashed_Verify_Operation>(m_public, "SHA-512", true);
349 } else {
350 return std::make_unique<Ed25519_Hashed_Verify_Operation>(m_public, params, false);
351 }
352 }
353 throw Provider_Not_Found(algo_name(), provider);
354}
355
356std::unique_ptr<PK_Ops::Verification> Ed25519_PublicKey::create_x509_verification_op(const AlgorithmIdentifier& alg_id,
357 std::string_view provider) const {
358 if(provider == "base" || provider.empty()) {
359 if(alg_id != this->algorithm_identifier()) {
360 throw Decoding_Error("Unexpected AlgorithmIdentifier for Ed25519 X509 signature");
361 }
362
363 return std::make_unique<Ed25519_Pure_Verify_Operation>(m_public);
364 }
365 throw Provider_Not_Found(algo_name(), provider);
366}
367
368std::unique_ptr<PK_Ops::Signature> Ed25519_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
369 std::string_view params,
370 std::string_view provider) const {
371 if(provider == "base" || provider.empty()) {
372 if(params.empty() || params == "Identity" || params == "Pure") {
373 return std::make_unique<Ed25519_Pure_Sign_Operation>(m_private);
374 } else if(params == "Ed25519ph") {
375 return std::make_unique<Ed25519_Hashed_Sign_Operation>(m_private, "SHA-512", true);
376 } else {
377 return std::make_unique<Ed25519_Hashed_Sign_Operation>(m_private, params, false);
378 }
379 }
380 throw Provider_Not_Found(algo_name(), provider);
381}
382
383} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_ASSERT_EQUAL(expr1, expr2, assertion_made)
Definition assert.h:88
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
bool parameters_are_empty() const
Definition asn1_obj.h:715
virtual OID object_identifier() const
Definition pk_keys.cpp:22
static Limits DER()
Definition ber_dec.h:42
BER_Decoder & decode(bool &out)
Definition ber_dec.h:358
BER_Decoder & discard_remaining()
Definition ber_dec.cpp:488
secure_vector< uint8_t > get_contents()
Definition der_enc.cpp:161
DER_Encoder & encode(bool b)
Definition der_enc.cpp:313
Ed25519_PrivateKey(const AlgorithmIdentifier &alg_id, std::span< const uint8_t > key_bits)
const secure_vector< uint8_t > & get_private_key() const
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 > raw_private_key_bits() const override
secure_vector< uint8_t > private_key_bits() const override
std::unique_ptr< PK_Ops::Verification > create_verification_op(std::string_view params, std::string_view provider) const override
std::shared_ptr< const Ed25519_PublicKey_Data > m_public
Definition ed25519.h:64
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
bool check_key(RandomNumberGenerator &rng, bool strong) const override
std::string algo_name() const override
Definition ed25519.h:24
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:244
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:35
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:1915
void ed25519_gen_keypair(uint8_t pk[32], uint8_t sk[64], const uint8_t seed[32])
Definition ed25519.cpp:19
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:128
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:71