xpx_chain_crypto/secret.rs
1// Copyright 2018 ProximaX Limited. All rights reserved.
2// Use of this source code is governed by the Apache 2.0
3// license that can be found in the LICENSE file.
4
5//! ed25519 secret key types.
6
7use core::fmt::Debug;
8use std::vec::Vec;
9
10use clear_on_drop::clear::Clear;
11
12use curve25519_dalek::constants;
13use curve25519_dalek::digest::generic_array::typenum::U64;
14use curve25519_dalek::digest::Digest;
15use curve25519_dalek::edwards::CompressedEdwardsY;
16use curve25519_dalek::scalar::Scalar;
17
18use rand::CryptoRng;
19use rand::Rng;
20
21use sha3::Sha3_512;
22
23#[cfg(feature = "serde")]
24use serde::de::Error as SerdeError;
25#[cfg(feature = "serde")]
26use serde::de::Visitor;
27#[cfg(feature = "serde")]
28use serde::{Deserialize, Serialize};
29#[cfg(feature = "serde")]
30use serde::{Deserializer, Serializer};
31
32use crate::constants::*;
33use crate::errors::*;
34use crate::public::*;
35use crate::signature::*;
36
37/// An EdDSA secret key.
38#[derive(Default, Clone)] // we derive Default in order to use the clear() method in Drop
39pub struct SecretKey(pub(crate) [u8; SECRET_KEY_LENGTH]);
40
41impl Debug for SecretKey {
42 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
43 write!(f, "SecretKey: {:?}", &self.0[..])
44 }
45}
46
47/// Overwrite secret key material with null bytes when it goes out of scope.
48impl Drop for SecretKey {
49 fn drop(&mut self) {
50 self.0.clear();
51 }
52}
53
54impl AsRef<[u8]> for SecretKey {
55 fn as_ref(&self) -> &[u8] {
56 self.as_bytes()
57 }
58}
59
60impl SecretKey {
61 /// Convert this secret key to a byte array.
62 #[inline]
63 pub fn to_bytes(&self) -> [u8; SECRET_KEY_LENGTH] {
64 self.0
65 }
66
67 /// View this secret key as a byte array.
68 #[inline]
69 pub fn as_bytes(&self) -> &[u8; SECRET_KEY_LENGTH] {
70 &self.0
71 }
72
73 /// Construct a `SecretKey` from a slice of bytes.
74 ///
75 /// # Example
76 ///
77 /// ```
78 /// # extern crate xpx_chain_crypto;
79 /// #
80 /// use xpx_chain_crypto::SecretKey;
81 /// use xpx_chain_crypto::SECRET_KEY_LENGTH;
82 /// use xpx_chain_crypto::SignatureError;
83 ///
84 /// # fn doctest() -> Result<SecretKey, SignatureError> {
85 /// let secret_key_bytes: [u8; SECRET_KEY_LENGTH] = [
86 /// 157, 097, 177, 157, 239, 253, 090, 096,
87 /// 186, 132, 074, 244, 146, 236, 044, 196,
88 /// 068, 073, 197, 105, 123, 050, 105, 025,
89 /// 112, 059, 172, 003, 028, 174, 127, 096, ];
90 ///
91 /// let secret_key: SecretKey = SecretKey::from_bytes(secret_key_bytes.to_vec())?;
92 /// #
93 /// # Ok(secret_key)
94 /// # }
95 /// #
96 /// # fn main() {
97 /// # let result = doctest();
98 /// # assert!(result.is_ok());
99 /// # }
100 /// ```
101 ///
102 /// # Returns
103 ///
104 /// A `Result` whose okay value is an EdDSA `SecretKey` or whose error value
105 /// is an `SignatureError` wrapping the internal error that occurred.
106 #[inline]
107 pub fn from_bytes(bytes: Vec<u8>) -> Result<SecretKey, SignatureError> {
108 if bytes.len() != SECRET_KEY_LENGTH {
109 return Err(SignatureError(InternalError::BytesLengthError {
110 name: "SecretKey",
111 length: SECRET_KEY_LENGTH,
112 }));
113 }
114 let mut bits: [u8; 32] = [0u8; 32];
115 bits.copy_from_slice(&bytes[..32]);
116
117 Ok(SecretKey(bits))
118 }
119
120 /// Generate a `SecretKey` from a `csprng`.
121 ///
122 /// # Example
123 ///
124 /// ```
125 /// extern crate rand;
126 /// extern crate sha3;
127 /// extern crate xpx_chain_crypto;
128 ///
129 /// # #[cfg(feature = "std")]
130 /// # fn main() {
131 /// #
132 /// use rand::Rng;
133 /// use rand::rngs::OsRng;
134 /// use sha3::Sha3_512;
135 /// use xpx_chain_crypto::PublicKey;
136 /// use xpx_chain_crypto::SecretKey;
137 /// use xpx_chain_crypto::Signature;
138 ///
139 /// let mut csprng = OsRng{};
140 /// let secret_key: SecretKey = SecretKey::generate(&mut csprng);
141 /// # }
142 /// #
143 /// # #[cfg(not(feature = "std"))]
144 /// # fn main() { }
145 /// ```
146 ///
147 /// Afterwards, you can generate the corresponding public:
148 ///
149 /// ```
150 /// # extern crate rand;
151 /// # extern crate xpx_chain_crypto;
152 /// #
153 /// # fn main() {
154 /// #
155 /// # use rand::Rng;
156 /// # use rand::thread_rng;
157 /// # use xpx_chain_crypto::PublicKey;
158 /// # use xpx_chain_crypto::SecretKey;
159 /// # use xpx_chain_crypto::Signature;
160 /// #
161 /// # let mut csprng = thread_rng();
162 /// # let secret_key: SecretKey = SecretKey::generate(&mut csprng);
163 ///
164 /// let public_key: PublicKey = (&secret_key).into();
165 /// # }
166 /// ```
167 ///
168 /// # Input
169 ///
170 /// A CSPRNG with a `fill_bytes()` method, e.g. `rand::OsRng`
171 pub fn generate<T>(csprng: &mut T) -> SecretKey
172 where
173 T: CryptoRng + Rng,
174 {
175 let mut sk: SecretKey = SecretKey([0u8; 32]);
176
177 csprng.fill_bytes(&mut sk.0);
178
179 sk
180 }
181}
182
183#[cfg(feature = "serde")]
184impl Serialize for SecretKey {
185 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
186 where
187 S: Serializer,
188 {
189 serializer.serialize_bytes(self.as_bytes())
190 }
191}
192
193#[cfg(feature = "serde")]
194impl<'d> Deserialize<'d> for SecretKey {
195 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
196 where
197 D: Deserializer<'d>,
198 {
199 struct SecretKeyVisitor;
200
201 impl<'d> Visitor<'d> for SecretKeyVisitor {
202 type Value = SecretKey;
203
204 fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
205 formatter.write_str("An ed25519 secret key as 32 bytes, as specified in RFC8032.")
206 }
207
208 fn visit_bytes<E>(self, bytes: &[u8]) -> Result<SecretKey, E>
209 where
210 E: SerdeError,
211 {
212 SecretKey::from_bytes(bytes.to_vec()).or(Err(SerdeError::invalid_length(bytes.len(), &self)))
213 }
214 }
215 deserializer.deserialize_bytes(SecretKeyVisitor)
216 }
217}
218
219/// An "expanded" secret key.
220///
221/// This is produced by using an hash function with 512-bits output to digest a
222/// `SecretKey`. The output digest is then split in half, the lower half being
223/// the actual `key` used to sign messages, after twiddling with some bits.¹ The
224/// upper half is used a sort of half-baked, ill-designed² pseudo-domain-separation
225/// "nonce"-like thing, which is used during signature production by
226/// concatenating it with the message to be signed before the message is hashed.
227//
228// ¹ This results in a slight bias towards non-uniformity at one spectrum of
229// the range of valid keys. Oh well: not my idea; not my problem.
230//
231// ² It is the author's view (specifically, isis agora lovecruft, in the event
232// you'd like to complain about me, again) that this is "ill-designed" because
233// this doesn't actually provide true hash domain separation, in that in many
234// real-world applications a user wishes to have one key which is used in
235// several contexts (such as within tor, which does does domain separation
236// manually by pre-concatenating static strings to messages to achieve more
237// robust domain separation). In other real-world applications, such as
238// bitcoind, a user might wish to have one master keypair from which others are
239// derived (à la BIP32) and different domain separators between keys derived at
240// different levels (and similarly for tree-based key derivation constructions,
241// such as hash-based signatures). Leaving the domain separation to
242// application designers, who thus far have produced incompatible,
243// slightly-differing, ad hoc domain separation (at least those application
244// designers who knew enough cryptographic theory to do so!), is therefore a
245// bad design choice on the part of the cryptographer designing primitives
246// which should be simple and as foolproof as possible to use for
247// non-cryptographers. Further, later in the ed25519 signature scheme, as
248// specified in RFC8032, the public key is added into *another* hash digest
249// (along with the message, again); it is unclear to this author why there's
250// not only one but two poorly-thought-out attempts at domain separation in the
251// same signature scheme, and which both fail in exactly the same way. For a
252// better-designed, Schnorr-based signature scheme, see Trevor Perrin's work on
253// "generalised EdDSA" and "VXEdDSA".
254#[derive(Default)] // we derive Default in order to use the clear() method in Drop
255pub struct ExpandedSecretKey {
256 pub(crate) key: Scalar,
257 pub(crate) nonce: [u8; 32],
258}
259
260/// Overwrite secret key material with null bytes when it goes out of scope.
261impl Drop for ExpandedSecretKey {
262 fn drop(&mut self) {
263 self.key.clear();
264 self.nonce.clear();
265 }
266}
267
268impl<'a> From<&'a SecretKey> for ExpandedSecretKey {
269 /// Construct an `ExpandedSecretKey` from a `SecretKey`.
270 ///
271 /// # Examples
272 ///
273 /// ```
274 /// # extern crate rand;
275 /// # extern crate sha3;
276 /// # extern crate xpx_chain_crypto;
277 /// #
278 /// # fn main() {
279 /// #
280 /// use rand::Rng;
281 /// use rand::thread_rng;
282 /// use sha3::Sha3_512;
283 /// use xpx_chain_crypto::{SecretKey, ExpandedSecretKey};
284 ///
285 /// let mut csprng = thread_rng();
286 /// let secret_key: SecretKey = SecretKey::generate(&mut csprng);
287 /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key);
288 /// # }
289 /// ```
290 fn from(secret_key: &'a SecretKey) -> ExpandedSecretKey {
291 let mut h: Sha3_512 = Sha3_512::default();
292 let mut hash: [u8; 64] = [0u8; 64];
293 let mut lower: [u8; 32] = [0u8; 32];
294 let mut upper: [u8; 32] = [0u8; 32];
295
296 let secret_key = secret_key.to_bytes();
297 h.update(secret_key);
298 hash.copy_from_slice(h.finalize().as_slice());
299
300 lower.copy_from_slice(&hash[00..32]);
301 upper.copy_from_slice(&hash[32..64]);
302
303 lower[0] &= 248;
304 lower[31] &= 127;
305 lower[31] |= 64;
306
307 ExpandedSecretKey {
308 key: Scalar::from_bits(lower),
309 nonce: upper,
310 }
311 }
312}
313
314impl ExpandedSecretKey {
315 /// Convert this `ExpandedSecretKey` into an array of 64 bytes.
316 ///
317 /// # Returns
318 ///
319 /// An array of 64 bytes. The first 32 bytes represent the "expanded"
320 /// secret key, and the last 32 bytes represent the "domain-separation"
321 /// "nonce".
322 ///
323 /// # Examples
324 ///
325 /// ```
326 /// # extern crate rand;
327 /// # extern crate sha3;
328 /// # extern crate xpx_chain_crypto;
329 /// #
330 /// # #[cfg(all(feature = "sha3", feature = "std"))]
331 /// # fn main() {
332 /// #
333 /// use rand::Rng;
334 /// use rand::rngs::OsRng;
335 /// use sha3::Sha3_512;
336 /// use xpx_chain_crypto::{SecretKey, ExpandedSecretKey};
337 ///
338 /// let mut csprng = OsRng{};
339 /// let secret_key: SecretKey = SecretKey::generate(&mut csprng);
340 /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key);
341 /// let expanded_secret_key_bytes: [u8; 64] = expanded_secret_key.to_bytes();
342 ///
343 /// assert!(&expanded_secret_key_bytes[..] != &[0u8; 64][..]);
344 /// # }
345 /// #
346 /// # #[cfg(any(not(feature = "sha3"), not(feature = "std")))]
347 /// # fn main() { }
348 /// ```
349 #[inline]
350 pub fn to_bytes(&self) -> [u8; EXPANDED_SECRET_KEY_LENGTH] {
351 let mut bytes: [u8; 64] = [0u8; 64];
352
353 bytes[..32].copy_from_slice(self.key.as_bytes());
354 bytes[32..].copy_from_slice(&self.nonce[..]);
355 bytes
356 }
357
358 /// Construct an `ExpandedSecretKey` from a slice of bytes.
359 ///
360 /// # Returns
361 ///
362 /// A `Result` whose okay value is an EdDSA `ExpandedSecretKey` or whose
363 /// error value is an `SignatureError` describing the error that occurred.
364 ///
365 /// # Examples
366 ///
367 /// ```
368 /// # extern crate rand;
369 /// # extern crate sha3;
370 /// # extern crate xpx_chain_crypto;
371 /// #
372 /// # use xpx_chain_crypto::{ExpandedSecretKey, SignatureError};
373 /// #
374 /// # #[cfg(all(feature = "sha3", feature = "std"))]
375 /// # fn do_test() -> Result<ExpandedSecretKey, SignatureError> {
376 /// #
377 /// use rand::Rng;
378 /// use rand::rngs::OsRng;
379 /// use xpx_chain_crypto::{SecretKey, ExpandedSecretKey};
380 /// use xpx_chain_crypto::SignatureError;
381 ///
382 /// let mut csprng = OsRng{};
383 /// let secret_key: SecretKey = SecretKey::generate(&mut csprng);
384 /// let expanded_secret_key: ExpandedSecretKey = ExpandedSecretKey::from(&secret_key);
385 /// let bytes: [u8; 64] = expanded_secret_key.to_bytes();
386 /// let expanded_secret_key_again = ExpandedSecretKey::from_bytes(&bytes)?;
387 /// #
388 /// # Ok(expanded_secret_key_again)
389 /// # }
390 /// #
391 /// # #[cfg(all(feature = "sha3", feature = "std"))]
392 /// # fn main() {
393 /// # let result = do_test();
394 /// # assert!(result.is_ok());
395 /// # }
396 /// #
397 /// # #[cfg(any(not(feature = "sha3"), not(feature = "std")))]
398 /// # fn main() { }
399 /// ```
400 #[inline]
401 pub fn from_bytes(bytes: &[u8]) -> Result<ExpandedSecretKey, SignatureError> {
402 if bytes.len() != EXPANDED_SECRET_KEY_LENGTH {
403 return Err(SignatureError(InternalError::BytesLengthError {
404 name: "ExpandedSecretKey",
405 length: EXPANDED_SECRET_KEY_LENGTH,
406 }));
407 }
408 let mut lower: [u8; 32] = [0u8; 32];
409 let mut upper: [u8; 32] = [0u8; 32];
410
411 lower.copy_from_slice(&bytes[00..32]);
412 upper.copy_from_slice(&bytes[32..64]);
413
414 Ok(ExpandedSecretKey {
415 key: Scalar::from_bits(lower),
416 nonce: upper,
417 })
418 }
419
420 /// Sign a message with this `ExpandedSecretKey`.
421 #[allow(non_snake_case)]
422 pub fn sign(&self, message: &[u8], public_key: &PublicKey) -> Signature {
423 let mut h: Sha3_512 = Sha3_512::new();
424 let R: CompressedEdwardsY;
425 let r: Scalar;
426 let s: Scalar;
427 let k: Scalar;
428
429 h.update(&self.nonce);
430 h.update(&message);
431
432 r = Scalar::from_hash(h);
433 R = (&r * &constants::ED25519_BASEPOINT_TABLE).compress();
434
435 h = Sha3_512::new();
436 h.update(R.as_bytes());
437 h.update(public_key.as_bytes());
438 h.update(&message);
439
440 k = Scalar::from_hash(h);
441 s = &(&k * &self.key) + &r;
442
443 Signature { R, s }
444 }
445
446 /// Sign a `prehashed_message` with this `ExpandedSecretKey` using the
447 /// Ed25519ph algorithm defined in [RFC8032 §5.1][rfc8032].
448 ///
449 /// # Inputs
450 ///
451 /// * `prehashed_message` is an instantiated hash digest with 512-bits of
452 /// output which has had the message to be signed previously fed into its
453 /// state.
454 /// * `public_key` is a [`PublicKey`] which corresponds to this secret key.
455 /// * `context` is an optional context string, up to 255 bytes inclusive,
456 /// which may be used to provide additional domain separation. If not
457 /// set, this will default to an empty string.
458 ///
459 /// # Returns
460 ///
461 /// An Ed25519ph [`Signature`] on the `prehashed_message`.
462 ///
463 /// [rfc8032]: https://tools.ietf.org/html/rfc8032#section-5.1
464 #[allow(non_snake_case)]
465 pub fn sign_prehashed<D>(
466 &self,
467 prehashed_message: D,
468 public_key: &PublicKey,
469 context: Option<&'static [u8]>,
470 ) -> Signature
471 where
472 D: Digest<OutputSize = U64>,
473 {
474 let mut h: Sha3_512;
475 let mut prehash: [u8; 64] = [0u8; 64];
476 let R: CompressedEdwardsY;
477 let r: Scalar;
478 let s: Scalar;
479 let k: Scalar;
480
481 let ctx: &[u8] = context.unwrap_or(b""); // By default, the context is an empty string.
482
483 debug_assert!(
484 ctx.len() <= 255,
485 "The context must not be longer than 255 octets."
486 );
487
488 let ctx_len: u8 = ctx.len() as u8;
489
490 // Get the result of the pre-hashed message.
491 prehash.copy_from_slice(prehashed_message.finalize().as_slice());
492
493 // This is the dumbest, ten-years-late, non-admission of fucking up the
494 // domain separation I have ever seen. Why am I still required to put
495 // the upper half "prefix" of the hashed "secret key" in here? Why
496 // can't the user just supply their own nonce and decide for themselves
497 // whether or not they want a deterministic signature scheme? Why does
498 // the message go into what's ostensibly the signature domain separation
499 // hash? Why wasn't there always a way to provide a context string?
500 //
501 // ...
502 //
503 // This is a really fucking stupid bandaid, and the damned scheme is
504 // still bleeding from malleability, for fuck's sake.
505 h = Sha3_512::new()
506 .chain(b"SigEd25519 no Ed25519 collisions")
507 .chain(&[1]) // Ed25519ph
508 .chain(&[ctx_len])
509 .chain(ctx)
510 .chain(&self.nonce)
511 .chain(&prehash[..]);
512
513 r = Scalar::from_hash(h);
514 R = (&r * &constants::ED25519_BASEPOINT_TABLE).compress();
515
516 h = Sha3_512::new()
517 .chain(b"SigEd25519 no Ed25519 collisions")
518 .chain(&[1]) // Ed25519ph
519 .chain(&[ctx_len])
520 .chain(ctx)
521 .chain(R.as_bytes())
522 .chain(public_key.as_bytes())
523 .chain(&prehash[..]);
524
525 k = Scalar::from_hash(h);
526 s = &(&k * &self.key) + &r;
527
528 Signature { R, s }
529 }
530}
531
532#[cfg(feature = "serde")]
533impl Serialize for ExpandedSecretKey {
534 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
535 where
536 S: Serializer,
537 {
538 serializer.serialize_bytes(&self.to_bytes()[..])
539 }
540}
541
542#[cfg(feature = "serde")]
543impl<'d> Deserialize<'d> for ExpandedSecretKey {
544 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
545 where
546 D: Deserializer<'d>,
547 {
548 struct ExpandedSecretKeyVisitor;
549
550 impl<'d> Visitor<'d> for ExpandedSecretKeyVisitor {
551 type Value = ExpandedSecretKey;
552
553 fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
554 formatter.write_str(
555 "An ed25519 expanded secret key as 64 bytes, as specified in RFC8032.",
556 )
557 }
558
559 fn visit_bytes<E>(self, bytes: &[u8]) -> Result<ExpandedSecretKey, E>
560 where
561 E: SerdeError,
562 {
563 ExpandedSecretKey::from_bytes(bytes)
564 .or(Err(SerdeError::invalid_length(bytes.len(), &self)))
565 }
566 }
567 deserializer.deserialize_bytes(ExpandedSecretKeyVisitor)
568 }
569}