diem_crypto/
ed25519.rs

1// Copyright (c) The Diem Core Contributors
2// SPDX-License-Identifier: Apache-2.0
3
4//! This module provides an API for the PureEdDSA signature scheme over the ed25519 twisted
5//! Edwards curve as defined in [RFC8032](https://tools.ietf.org/html/rfc8032).
6//!
7//! Signature verification also checks and rejects non-canonical signatures.
8//!
9//! # Examples
10//!
11//! ```
12//! use diem_crypto_derive::{CryptoHasher, BCSCryptoHash};
13//! use diem_crypto::{
14//!     ed25519::*,
15//!     traits::{Signature, SigningKey, Uniform},
16//! };
17//! use rand::{rngs::StdRng, SeedableRng};
18//! use serde::{Serialize, Deserialize};
19//!
20//! #[derive(Serialize, Deserialize, CryptoHasher, BCSCryptoHash)]
21//! pub struct TestCryptoDocTest(String);
22//! let message = TestCryptoDocTest("Test message".to_string());
23//!
24//! let mut rng: StdRng = SeedableRng::from_seed([0; 32]);
25//! let private_key = Ed25519PrivateKey::generate(&mut rng);
26//! let public_key: Ed25519PublicKey = (&private_key).into();
27//! let signature = private_key.sign(&message);
28//! assert!(signature.verify(&message, &public_key).is_ok());
29//! ```
30//! **Note**: The above example generates a private key using a private function intended only for
31//! testing purposes. Production code should find an alternate means for secure key generation.
32#![allow(clippy::integer_arithmetic)]
33
34use crate::{
35    hash::{CryptoHash, CryptoHasher},
36    traits::*,
37};
38use anyhow::{anyhow, Result};
39use core::convert::TryFrom;
40use diem_crypto_derive::{DeserializeKey, SerializeKey, SilentDebug, SilentDisplay};
41use mirai_annotations::*;
42use serde::Serialize;
43use std::{cmp::Ordering, fmt};
44
45pub use ed25519_dalek;
46
47/// The length of the Ed25519PrivateKey
48pub const ED25519_PRIVATE_KEY_LENGTH: usize = ed25519_dalek::SECRET_KEY_LENGTH;
49/// The length of the Ed25519PublicKey
50pub const ED25519_PUBLIC_KEY_LENGTH: usize = ed25519_dalek::PUBLIC_KEY_LENGTH;
51/// The length of the Ed25519Signature
52pub const ED25519_SIGNATURE_LENGTH: usize = ed25519_dalek::SIGNATURE_LENGTH;
53
54/// The order of ed25519 as defined in [RFC8032](https://tools.ietf.org/html/rfc8032).
55const L: [u8; 32] = [
56    0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14,
57    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
58];
59
60/// An Ed25519 private key
61#[derive(DeserializeKey, SerializeKey, SilentDebug, SilentDisplay)]
62pub struct Ed25519PrivateKey(ed25519_dalek::SecretKey);
63
64#[cfg(feature = "assert-private-keys-not-cloneable")]
65static_assertions::assert_not_impl_any!(Ed25519PrivateKey: Clone);
66
67#[cfg(any(test, feature = "cloneable-private-keys"))]
68impl Clone for Ed25519PrivateKey {
69    fn clone(&self) -> Self {
70        let serialized: &[u8] = &(self.to_bytes());
71        Ed25519PrivateKey::try_from(serialized).unwrap()
72    }
73}
74
75/// An Ed25519 public key
76#[derive(DeserializeKey, Clone, SerializeKey)]
77pub struct Ed25519PublicKey(ed25519_dalek::PublicKey);
78
79#[cfg(mirai)]
80use crate::tags::ValidatedPublicKeyTag;
81#[cfg(not(mirai))]
82struct ValidatedPublicKeyTag {}
83
84/// An Ed25519 signature
85#[derive(DeserializeKey, Clone, SerializeKey)]
86pub struct Ed25519Signature(ed25519_dalek::Signature);
87
88impl Ed25519PrivateKey {
89    /// The length of the Ed25519PrivateKey
90    pub const LENGTH: usize = ed25519_dalek::SECRET_KEY_LENGTH;
91
92    /// Serialize an Ed25519PrivateKey.
93    pub fn to_bytes(&self) -> [u8; ED25519_PRIVATE_KEY_LENGTH] {
94        self.0.to_bytes()
95    }
96
97    /// Deserialize an Ed25519PrivateKey without any validation checks apart from expected key size.
98    fn from_bytes_unchecked(
99        bytes: &[u8],
100    ) -> std::result::Result<Ed25519PrivateKey, CryptoMaterialError> {
101        match ed25519_dalek::SecretKey::from_bytes(bytes) {
102            Ok(dalek_secret_key) => Ok(Ed25519PrivateKey(dalek_secret_key)),
103            Err(_) => Err(CryptoMaterialError::DeserializationError),
104        }
105    }
106
107    /// Private function aimed at minimizing code duplication between sign
108    /// methods of the SigningKey implementation. This should remain private.
109    fn sign_arbitrary_message(&self, message: &[u8]) -> Ed25519Signature {
110        let secret_key: &ed25519_dalek::SecretKey = &self.0;
111        let public_key: Ed25519PublicKey = self.into();
112        let expanded_secret_key: ed25519_dalek::ExpandedSecretKey =
113            ed25519_dalek::ExpandedSecretKey::from(secret_key);
114        let sig = expanded_secret_key.sign(message.as_ref(), &public_key.0);
115        Ed25519Signature(sig)
116    }
117}
118
119impl Ed25519PublicKey {
120    /// Serialize an Ed25519PublicKey.
121    pub fn to_bytes(&self) -> [u8; ED25519_PUBLIC_KEY_LENGTH] {
122        self.0.to_bytes()
123    }
124
125    /// Deserialize an Ed25519PublicKey without any validation checks apart from expected key size.
126    pub(crate) fn from_bytes_unchecked(
127        bytes: &[u8],
128    ) -> std::result::Result<Ed25519PublicKey, CryptoMaterialError> {
129        match ed25519_dalek::PublicKey::from_bytes(bytes) {
130            Ok(dalek_public_key) => Ok(Ed25519PublicKey(dalek_public_key)),
131            Err(_) => Err(CryptoMaterialError::DeserializationError),
132        }
133    }
134
135    /// Deserialize an Ed25519PublicKey from its representation as an x25519
136    /// public key, along with an indication of sign. This is meant to
137    /// compensate for the poor key storage capabilities of key management
138    /// solutions, and NOT to promote double usage of keys under several
139    /// schemes, which would lead to BAD vulnerabilities.
140    ///
141    /// Arguments:
142    /// - `x25519_bytes`: bit representation of a public key in clamped
143    ///            Montgomery form, a.k.a. the x25519 public key format.
144    /// - `negative`: whether to interpret the given point as a negative point,
145    ///               as the Montgomery form erases the sign byte. By XEdDSA
146    ///               convention, if you expect to ever convert this back to an
147    ///               x25519 public key, you should pass `false` for this
148    ///               argument.
149    #[cfg(test)]
150    pub(crate) fn from_x25519_public_bytes(
151        x25519_bytes: &[u8],
152        negative: bool,
153    ) -> Result<Self, CryptoMaterialError> {
154        if x25519_bytes.len() != 32 {
155            return Err(CryptoMaterialError::DeserializationError);
156        }
157        let key_bits = {
158            let mut bits = [0u8; 32];
159            bits.copy_from_slice(x25519_bytes);
160            bits
161        };
162        let mtg_point = curve25519_dalek::montgomery::MontgomeryPoint(key_bits);
163        let sign = if negative { 1u8 } else { 0u8 };
164        let ed_point = mtg_point
165            .to_edwards(sign)
166            .ok_or(CryptoMaterialError::DeserializationError)?;
167        Ed25519PublicKey::try_from(&ed_point.compress().as_bytes()[..])
168    }
169}
170
171impl Ed25519Signature {
172    /// The length of the Ed25519Signature
173    pub const LENGTH: usize = ed25519_dalek::SIGNATURE_LENGTH;
174
175    /// Serialize an Ed25519Signature.
176    pub fn to_bytes(&self) -> [u8; ED25519_SIGNATURE_LENGTH] {
177        self.0.to_bytes()
178    }
179
180    /// Deserialize an Ed25519Signature without any validation checks (malleability)
181    /// apart from expected key size.
182    pub(crate) fn from_bytes_unchecked(
183        bytes: &[u8],
184    ) -> std::result::Result<Ed25519Signature, CryptoMaterialError> {
185        match ed25519_dalek::Signature::try_from(bytes) {
186            Ok(dalek_signature) => Ok(Ed25519Signature(dalek_signature)),
187            Err(_) => Err(CryptoMaterialError::DeserializationError),
188        }
189    }
190
191    /// return an all-zero signature (for test only)
192    #[cfg(any(test, feature = "fuzzing"))]
193    pub fn dummy_signature() -> Self {
194        Self::from_bytes_unchecked(&[0u8; Self::LENGTH]).unwrap()
195    }
196
197    /// Check for correct size and third-party based signature malleability issues.
198    /// This method is required to ensure that given a valid signature for some message under some
199    /// key, an attacker cannot produce another valid signature for the same message and key.
200    ///
201    /// According to [RFC8032](https://tools.ietf.org/html/rfc8032), signatures comprise elements
202    /// {R, S} and we should enforce that S is of canonical form (smaller than L, where L is the
203    /// order of edwards25519 curve group) to prevent signature malleability. Without this check,
204    /// one could add a multiple of L into S and still pass signature verification, resulting in
205    /// a distinct yet valid signature.
206    ///
207    /// This method does not check the R component of the signature, because R is hashed during
208    /// signing and verification to compute h = H(ENC(R) || ENC(A) || M), which means that a
209    /// third-party cannot modify R without being detected.
210    ///
211    /// Note: It's true that malicious signers can already produce varying signatures by
212    /// choosing a different nonce, so this method protects against malleability attacks performed
213    /// by a non-signer.
214    pub fn check_malleability(bytes: &[u8]) -> std::result::Result<(), CryptoMaterialError> {
215        if bytes.len() != ED25519_SIGNATURE_LENGTH {
216            return Err(CryptoMaterialError::WrongLengthError);
217        }
218        if !check_s_lt_l(&bytes[32..]) {
219            return Err(CryptoMaterialError::CanonicalRepresentationError);
220        }
221        Ok(())
222    }
223}
224
225///////////////////////
226// PrivateKey Traits //
227///////////////////////
228
229impl PrivateKey for Ed25519PrivateKey {
230    type PublicKeyMaterial = Ed25519PublicKey;
231}
232
233impl SigningKey for Ed25519PrivateKey {
234    type VerifyingKeyMaterial = Ed25519PublicKey;
235    type SignatureMaterial = Ed25519Signature;
236
237    fn sign<T: CryptoHash + Serialize>(&self, message: &T) -> Ed25519Signature {
238        let mut bytes = <T::Hasher as CryptoHasher>::seed().to_vec();
239        bcs::serialize_into(&mut bytes, &message)
240            .map_err(|_| CryptoMaterialError::SerializationError)
241            .expect("Serialization of signable material should not fail.");
242        Ed25519PrivateKey::sign_arbitrary_message(self, bytes.as_ref())
243    }
244
245    #[cfg(any(test, feature = "fuzzing"))]
246    fn sign_arbitrary_message(&self, message: &[u8]) -> Ed25519Signature {
247        Ed25519PrivateKey::sign_arbitrary_message(self, message)
248    }
249}
250
251impl Uniform for Ed25519PrivateKey {
252    fn generate<R>(rng: &mut R) -> Self
253    where
254        R: ::rand::RngCore + ::rand::CryptoRng,
255    {
256        Ed25519PrivateKey(ed25519_dalek::SecretKey::generate(rng))
257    }
258}
259
260impl PartialEq<Self> for Ed25519PrivateKey {
261    fn eq(&self, other: &Self) -> bool {
262        self.to_bytes() == other.to_bytes()
263    }
264}
265
266impl Eq for Ed25519PrivateKey {}
267
268// We could have a distinct kind of validation for the PrivateKey, for
269// ex. checking the derived PublicKey is valid?
270impl TryFrom<&[u8]> for Ed25519PrivateKey {
271    type Error = CryptoMaterialError;
272
273    /// Deserialize an Ed25519PrivateKey. This method will also check for key validity.
274    fn try_from(bytes: &[u8]) -> std::result::Result<Ed25519PrivateKey, CryptoMaterialError> {
275        // Note that the only requirement is that the size of the key is 32 bytes, something that
276        // is already checked during deserialization of ed25519_dalek::SecretKey
277        // Also, the underlying ed25519_dalek implementation ensures that the derived public key
278        // is safe and it will not lie in a small-order group, thus no extra check for PublicKey
279        // validation is required.
280        Ed25519PrivateKey::from_bytes_unchecked(bytes)
281    }
282}
283
284impl Length for Ed25519PrivateKey {
285    fn length(&self) -> usize {
286        Self::LENGTH
287    }
288}
289
290impl ValidCryptoMaterial for Ed25519PrivateKey {
291    fn to_bytes(&self) -> Vec<u8> {
292        self.to_bytes().to_vec()
293    }
294}
295
296impl Genesis for Ed25519PrivateKey {
297    fn genesis() -> Self {
298        let mut buf = [0u8; ED25519_PRIVATE_KEY_LENGTH];
299        buf[ED25519_PRIVATE_KEY_LENGTH - 1] = 1;
300        Self::try_from(buf.as_ref()).unwrap()
301    }
302}
303
304//////////////////////
305// PublicKey Traits //
306//////////////////////
307
308// Implementing From<&PrivateKey<...>> allows to derive a public key in a more elegant fashion
309impl From<&Ed25519PrivateKey> for Ed25519PublicKey {
310    fn from(private_key: &Ed25519PrivateKey) -> Self {
311        let secret: &ed25519_dalek::SecretKey = &private_key.0;
312        let public: ed25519_dalek::PublicKey = secret.into();
313        Ed25519PublicKey(public)
314    }
315}
316
317// We deduce PublicKey from this
318impl PublicKey for Ed25519PublicKey {
319    type PrivateKeyMaterial = Ed25519PrivateKey;
320}
321
322impl std::hash::Hash for Ed25519PublicKey {
323    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
324        let encoded_pubkey = self.to_bytes();
325        state.write(&encoded_pubkey);
326    }
327}
328
329// Those are required by the implementation of hash above
330impl PartialEq for Ed25519PublicKey {
331    fn eq(&self, other: &Ed25519PublicKey) -> bool {
332        self.to_bytes() == other.to_bytes()
333    }
334}
335
336impl Eq for Ed25519PublicKey {}
337
338// We deduce VerifyingKey from pointing to the signature material
339// we get the ability to do `pubkey.validate(msg, signature)`
340impl VerifyingKey for Ed25519PublicKey {
341    type SigningKeyMaterial = Ed25519PrivateKey;
342    type SignatureMaterial = Ed25519Signature;
343}
344
345impl fmt::Display for Ed25519PublicKey {
346    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
347        write!(f, "{}", hex::encode(&self.0.as_bytes()))
348    }
349}
350
351impl fmt::Debug for Ed25519PublicKey {
352    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
353        write!(f, "Ed25519PublicKey({})", self)
354    }
355}
356
357impl TryFrom<&[u8]> for Ed25519PublicKey {
358    type Error = CryptoMaterialError;
359
360    /// Deserialize an Ed25519PublicKey. This method will also check for key validity, for instance
361    ///  it will only deserialize keys that are safe against small subgroup attacks.
362    fn try_from(bytes: &[u8]) -> std::result::Result<Ed25519PublicKey, CryptoMaterialError> {
363        // We need to access the Edwards point which is not directly accessible from
364        // ed25519_dalek::PublicKey, so we need to do some custom deserialization.
365        if bytes.len() != ED25519_PUBLIC_KEY_LENGTH {
366            return Err(CryptoMaterialError::WrongLengthError);
367        }
368
369        let mut bits = [0u8; ED25519_PUBLIC_KEY_LENGTH];
370        bits.copy_from_slice(&bytes[..ED25519_PUBLIC_KEY_LENGTH]);
371
372        let compressed = curve25519_dalek::edwards::CompressedEdwardsY(bits);
373        let point = compressed
374            .decompress()
375            .ok_or(CryptoMaterialError::DeserializationError)?;
376
377        // Check if the point lies on a small subgroup. This is required
378        // when using curves with a small cofactor (in ed25519, cofactor = 8).
379        if point.is_small_order() {
380            return Err(CryptoMaterialError::SmallSubgroupError);
381        }
382
383        // Unfortunately, tuple struct `PublicKey` is private so we cannot
384        // Ok(Ed25519PublicKey(ed25519_dalek::PublicKey(compressed, point)))
385        // and we have to again invoke deserialization.
386        let public_key = Ed25519PublicKey::from_bytes_unchecked(bytes)?;
387        add_tag!(&public_key, ValidatedPublicKeyTag); // This key has gone through validity checks.
388        Ok(public_key)
389    }
390}
391
392impl Length for Ed25519PublicKey {
393    fn length(&self) -> usize {
394        ED25519_PUBLIC_KEY_LENGTH
395    }
396}
397
398impl ValidCryptoMaterial for Ed25519PublicKey {
399    fn to_bytes(&self) -> Vec<u8> {
400        self.0.to_bytes().to_vec()
401    }
402}
403
404//////////////////////
405// Signature Traits //
406//////////////////////
407
408impl Signature for Ed25519Signature {
409    type VerifyingKeyMaterial = Ed25519PublicKey;
410    type SigningKeyMaterial = Ed25519PrivateKey;
411
412    /// Verifies that the provided signature is valid for the provided
413    /// message, according to the RFC8032 algorithm. This strict verification performs the
414    /// recommended check of 5.1.7 ยง3, on top of the required RFC8032 verifications.
415    fn verify<T: CryptoHash + Serialize>(
416        &self,
417        message: &T,
418        public_key: &Ed25519PublicKey,
419    ) -> Result<()> {
420        // Public keys should be validated to be safe against small subgroup attacks, etc.
421        precondition!(has_tag!(public_key, ValidatedPublicKeyTag));
422        let mut bytes = <T::Hasher as CryptoHasher>::seed().to_vec();
423        bcs::serialize_into(&mut bytes, &message)
424            .map_err(|_| CryptoMaterialError::SerializationError)?;
425        Self::verify_arbitrary_msg(self, &bytes, public_key)
426    }
427
428    /// Checks that `self` is valid for an arbitrary &[u8] `message` using `public_key`.
429    /// Outside of this crate, this particular function should only be used for native signature
430    /// verification in move
431    fn verify_arbitrary_msg(&self, message: &[u8], public_key: &Ed25519PublicKey) -> Result<()> {
432        // Public keys should be validated to be safe against small subgroup attacks, etc.
433        precondition!(has_tag!(public_key, ValidatedPublicKeyTag));
434        Ed25519Signature::check_malleability(&self.to_bytes())?;
435
436        public_key
437            .0
438            .verify_strict(message, &self.0)
439            .map_err(|e| anyhow!("{}", e))
440            .and(Ok(()))
441    }
442
443    fn to_bytes(&self) -> Vec<u8> {
444        self.0.to_bytes().to_vec()
445    }
446
447    /// Batch signature verification as described in the original EdDSA article
448    /// by Bernstein et al. "High-speed high-security signatures". Current implementation works for
449    /// signatures on the same message and it checks for malleability.
450    #[cfg(feature = "batch")]
451    fn batch_verify<T: CryptoHash + Serialize>(
452        message: &T,
453        keys_and_signatures: Vec<(Self::VerifyingKeyMaterial, Self)>,
454    ) -> Result<()> {
455        for (_, sig) in keys_and_signatures.iter() {
456            Ed25519Signature::check_malleability(&sig.to_bytes())?
457        }
458        let mut message_bytes = <T::Hasher as CryptoHasher>::seed().to_vec();
459        bcs::serialize_into(&mut message_bytes, &message)
460            .map_err(|_| CryptoMaterialError::SerializationError)?;
461
462        let batch_argument = keys_and_signatures
463            .iter()
464            .map(|(key, signature)| (key.0, signature.0));
465        let (dalek_public_keys, dalek_signatures): (Vec<_>, Vec<_>) = batch_argument.unzip();
466        let message_ref = &(&message_bytes)[..];
467        // The original batching algorithm works for different messages and it expects as many
468        // messages as the number of signatures. In our case, we just populate the same
469        // message to meet dalek's api requirements.
470        let messages = vec![message_ref; dalek_signatures.len()];
471        ed25519_dalek::verify_batch(&messages[..], &dalek_signatures[..], &dalek_public_keys[..])
472            .map_err(|e| anyhow!("{}", e))?;
473        Ok(())
474    }
475}
476
477impl Length for Ed25519Signature {
478    fn length(&self) -> usize {
479        ED25519_SIGNATURE_LENGTH
480    }
481}
482
483impl ValidCryptoMaterial for Ed25519Signature {
484    fn to_bytes(&self) -> Vec<u8> {
485        self.to_bytes().to_vec()
486    }
487}
488
489impl std::hash::Hash for Ed25519Signature {
490    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
491        let encoded_signature = self.to_bytes();
492        state.write(&encoded_signature);
493    }
494}
495
496impl TryFrom<&[u8]> for Ed25519Signature {
497    type Error = CryptoMaterialError;
498
499    fn try_from(bytes: &[u8]) -> std::result::Result<Ed25519Signature, CryptoMaterialError> {
500        Ed25519Signature::check_malleability(bytes)?;
501        Ed25519Signature::from_bytes_unchecked(bytes)
502    }
503}
504
505// Those are required by the implementation of hash above
506impl PartialEq for Ed25519Signature {
507    fn eq(&self, other: &Ed25519Signature) -> bool {
508        self.to_bytes()[..] == other.to_bytes()[..]
509    }
510}
511
512impl Eq for Ed25519Signature {}
513
514impl fmt::Display for Ed25519Signature {
515    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
516        write!(f, "{}", hex::encode(&self.0.to_bytes()[..]))
517    }
518}
519
520impl fmt::Debug for Ed25519Signature {
521    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
522        write!(f, "Ed25519Signature({})", self)
523    }
524}
525
526/// Check if S < L to capture invalid signatures.
527fn check_s_lt_l(s: &[u8]) -> bool {
528    for i in (0..32).rev() {
529        match s[i].cmp(&L[i]) {
530            Ordering::Less => return true,
531            Ordering::Greater => return false,
532            _ => {}
533        }
534    }
535    // As this stage S == L which implies a non canonical S.
536    false
537}
538
539#[cfg(any(test, feature = "fuzzing"))]
540use crate::test_utils::{self, KeyPair};
541
542/// Produces a uniformly random ed25519 keypair from a seed
543#[cfg(any(test, feature = "fuzzing"))]
544pub fn keypair_strategy() -> impl Strategy<Value = KeyPair<Ed25519PrivateKey, Ed25519PublicKey>> {
545    test_utils::uniform_keypair_strategy::<Ed25519PrivateKey, Ed25519PublicKey>()
546}
547
548#[cfg(any(test, feature = "fuzzing"))]
549use proptest::prelude::*;
550
551#[cfg(any(test, feature = "fuzzing"))]
552impl proptest::arbitrary::Arbitrary for Ed25519PublicKey {
553    type Parameters = ();
554    type Strategy = BoxedStrategy<Self>;
555
556    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
557        crate::test_utils::uniform_keypair_strategy::<Ed25519PrivateKey, Ed25519PublicKey>()
558            .prop_map(|v| v.public_key)
559            .boxed()
560    }
561}