Skip to main content

tor_llcrypto/pk/
ed25519.rs

1//! Re-exporting Ed25519 implementations, and related utilities.
2//!
3//! Here we re-export types from [`ed25519_dalek`] that implement the
4//! Ed25519 signature algorithm.  (TODO: Eventually, this module
5//! should probably be replaced with a wrapper that uses the ed25519
6//! trait and the Signature trait.)
7//!
8//! We additionally provide an `Ed25519Identity` type to represent the
9//! unvalidated Ed25519 "identity keys" that we use throughout the Tor
10//! protocol to uniquely identify a relay.
11
12use base64ct::{Base64Unpadded, Encoding as _};
13use curve25519_dalek::Scalar;
14use derive_deftly::Deftly;
15use safelog::util::write_start_redacted;
16use std::fmt::{self, Debug, Display, Formatter};
17use subtle::{Choice, ConstantTimeEq};
18
19#[cfg(feature = "memquota-memcost")]
20use tor_memquota_cost::derive_deftly_template_HasMemoryCost;
21
22use ed25519_dalek::hazmat::ExpandedSecretKey;
23use ed25519_dalek::{Signer as _, Verifier as _};
24
25use crate::util::{
26    ct::{
27        CtByteArray, derive_deftly_template_ConstantTimeEq,
28        derive_deftly_template_PartialEqFromCtEq,
29    },
30    rng::RngCompat,
31};
32
33/// An Ed25519 signature.
34///
35/// See [`ed25519_dalek::Signature`] for more information.
36#[derive(Clone, Copy, Debug, Eq, PartialEq)]
37pub struct Signature(pub(crate) ed25519_dalek::Signature);
38
39/// An Ed25519 keypair.
40///
41/// (We do not provide a separate "private key only" type.)
42///
43/// See [`ed25519_dalek::SigningKey`] for more information.
44#[derive(Debug, Deftly)]
45#[derive_deftly(ConstantTimeEq)]
46pub struct Keypair(pub(crate) ed25519_dalek::SigningKey);
47
48/// An Ed25519 public key.
49///
50/// See [`ed25519_dalek::VerifyingKey`] for more information.
51#[derive(Clone, Copy, Debug, Eq, Deftly)]
52#[derive_deftly(PartialEqFromCtEq)]
53pub struct PublicKey(pub(crate) ed25519_dalek::VerifyingKey);
54
55impl<'a> From<&'a Keypair> for PublicKey {
56    fn from(value: &'a Keypair) -> Self {
57        PublicKey((&value.0).into())
58    }
59}
60
61impl ConstantTimeEq for PublicKey {
62    fn ct_eq(&self, other: &Self) -> Choice {
63        self.as_bytes().ct_eq(other.as_bytes())
64    }
65}
66
67impl PublicKey {
68    /// Construct a public key from its byte representation.
69    pub fn from_bytes(bytes: &[u8; 32]) -> Result<Self, signature::Error> {
70        Ok(PublicKey(ed25519_dalek::VerifyingKey::from_bytes(bytes)?))
71    }
72
73    /// Return a reference to the byte representation of this public key.
74    pub fn as_bytes(&self) -> &[u8; 32] {
75        self.0.as_bytes()
76    }
77    /// Return the byte representation of this public key.
78    pub fn to_bytes(&self) -> [u8; 32] {
79        self.0.to_bytes()
80    }
81    /// Verify a signature using this public key.
82    ///
83    /// See [`ed25519_dalek::VerifyingKey::verify`] for more information.
84    pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), signature::Error> {
85        self.0.verify(message, &signature.0)
86    }
87}
88impl Keypair {
89    /// Generate a new random ed25519 keypair.
90    pub fn generate<R: rand_core::Rng + rand_core::CryptoRng>(csprng: &mut R) -> Self {
91        Self(ed25519_dalek::SigningKey::generate(&mut RngCompat::new(
92            csprng,
93        )))
94    }
95    /// Construct an ed25519 keypair from the byte representation of its secret key.
96    pub fn from_bytes(bytes: &[u8; 32]) -> Self {
97        Self(ed25519_dalek::SigningKey::from_bytes(bytes))
98    }
99    /// Return a reference to the byte representation of the secret key in this keypair.
100    pub fn as_bytes(&self) -> &[u8; 32] {
101        self.0.as_bytes()
102    }
103    /// Return to the byte representation of the secret key in this keypair.
104    pub fn to_bytes(&self) -> [u8; 32] {
105        self.0.to_bytes()
106    }
107    /// Return the public key in this keypair.
108    pub fn verifying_key(&self) -> PublicKey {
109        PublicKey(*self.0.as_ref())
110    }
111    /// Verify a signature generated with this keypair.
112    pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), signature::Error> {
113        self.0.verify(message, &signature.0)
114    }
115    /// Sign a message using this keypair.
116    pub fn sign(&self, message: &[u8]) -> Signature {
117        Signature(self.0.sign(message))
118    }
119}
120impl Signature {
121    /// Construct this signature from its byte representation.
122    pub fn from_bytes(bytes: &[u8; 64]) -> Self {
123        Self(ed25519_dalek::Signature::from_bytes(bytes))
124    }
125    /// Return the byte representation of this signature.
126    pub fn to_bytes(&self) -> [u8; 64] {
127        self.0.to_bytes()
128    }
129}
130impl<'a> TryFrom<&'a [u8]> for PublicKey {
131    type Error = signature::Error;
132
133    fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
134        Ok(Self(ed25519_dalek::VerifyingKey::try_from(value)?))
135    }
136}
137impl<'a> From<&'a [u8; 32]> for Keypair {
138    fn from(value: &'a [u8; 32]) -> Self {
139        Self(ed25519_dalek::SigningKey::from(value))
140    }
141}
142impl From<[u8; 64]> for Signature {
143    fn from(value: [u8; 64]) -> Self {
144        Signature(value.into())
145    }
146}
147impl<'a> From<&'a [u8; 64]> for Signature {
148    fn from(value: &'a [u8; 64]) -> Self {
149        Signature(value.into())
150    }
151}
152
153/// The length of an ED25519 identity, in bytes.
154pub const ED25519_ID_LEN: usize = 32;
155
156/// The length of an ED25519 signature, in bytes.
157pub const ED25519_SIGNATURE_LEN: usize = 64;
158
159/// A variant of [`Keypair`] containing an [`ExpandedSecretKey`].
160///
161/// In the Tor protocol, we use this type for blinded onion service identity keys
162/// (KS_hs_blind_id).  Since their scalar values are computed, rather than taken
163/// directly from a
164/// SHA-512 transformation of a SecretKey, we cannot use the regular `Keypair`
165/// type.
166#[allow(clippy::exhaustive_structs)]
167#[derive(Deftly)]
168#[derive_deftly(ConstantTimeEq)]
169pub struct ExpandedKeypair {
170    /// The secret part of the key.
171    pub(crate) secret: ExpandedSecretKey,
172    /// The public part of this key.
173    ///
174    /// NOTE: As with [`ed25519_dalek::SigningKey`], this public key _must_ be
175    /// the public key matching `secret`.  Putting a different public key in
176    /// here would enable a class of attacks against ed25519 and enable secret
177    /// key recovery.
178    pub(crate) public: PublicKey,
179}
180
181impl ExpandedKeypair {
182    /// Return the public part of this expanded keypair.
183    pub fn public(&self) -> &PublicKey {
184        &self.public
185    }
186
187    // NOTE: There is deliberately no secret() function.  If we had one, we
188    // would be exposing an unescorted secret key, which is part of
189    // ed25519::hazmat.
190
191    /// Compute a signature over a message using this keypair.
192    pub fn sign(&self, message: &[u8]) -> Signature {
193        use sha2::Sha512;
194        // See notes on ExpandedKeypair about why this hazmat is okay to use.
195        Signature(ed25519_dalek::hazmat::raw_sign::<Sha512>(
196            &self.secret,
197            message,
198            &self.public.0,
199        ))
200    }
201
202    /// Return a representation of the secret key in this keypair.
203    ///
204    /// (Since it is an expanded secret key, we represent it as its scalar part
205    /// followed by its hash_prefix.)
206    pub fn to_secret_key_bytes(&self) -> [u8; 64] {
207        let mut output = [0_u8; 64];
208        output[0..32].copy_from_slice(&self.secret.scalar.to_bytes());
209        output[32..64].copy_from_slice(&self.secret.hash_prefix);
210        output
211    }
212
213    /// Reconstruct a key from its byte representation as returned by
214    /// `to_secret_key_bytes()`.
215    ///
216    /// Return None if the input cannot be the output of `to_secret_key_bytes()`.
217    //
218    // NOTE: Returning None is a bit silly, but that's what Dalek does.
219    pub fn from_secret_key_bytes(bytes: [u8; 64]) -> Option<Self> {
220        let scalar = Option::from(Scalar::from_bytes_mod_order(
221            bytes[0..32].try_into().expect("wrong length on slice"),
222        ))?;
223        let hash_prefix = bytes[32..64].try_into().expect("wrong length on slice");
224        let secret = ExpandedSecretKey {
225            scalar,
226            hash_prefix,
227        };
228        let public = PublicKey((&secret).into());
229        Some(Self { secret, public })
230    }
231
232    // NOTE: There is deliberately no constructor here that takes a (secret,
233    // public) pair.  If there were, you could construct a pair with a
234    // mismatched public key.
235}
236
237impl<'a> From<&'a Keypair> for ExpandedKeypair {
238    fn from(kp: &'a Keypair) -> ExpandedKeypair {
239        ExpandedKeypair {
240            secret: kp.as_bytes().into(),
241            public: kp.into(),
242        }
243    }
244}
245
246impl From<ExpandedKeypair> for PublicKey {
247    fn from(ekp: ExpandedKeypair) -> PublicKey {
248        ekp.public
249    }
250}
251
252/// An unchecked, unvalidated Ed25519 key.
253///
254/// This key is an "identity" in the sense that it identifies (up to) one
255/// Ed25519 key.  It may also represent the identity for a particular entity,
256/// such as a relay or an onion service.
257///
258/// This type is distinct from an Ed25519 [`PublicKey`] for several reasons:
259///  * We're storing it in a compact format, whereas the public key
260///    implementation might want an expanded form for more efficient key
261///    validation.
262///  * This type hasn't checked whether the bytes here actually _are_ a valid
263///    Ed25519 public key.
264#[derive(Clone, Copy, Hash, PartialOrd, Ord, Eq, PartialEq)]
265#[cfg_attr(
266    feature = "memquota-memcost",
267    derive(Deftly),
268    derive_deftly(HasMemoryCost)
269)]
270pub struct Ed25519Identity {
271    /// A raw unchecked Ed25519 public key.
272    id: CtByteArray<ED25519_ID_LEN>,
273}
274
275impl Ed25519Identity {
276    /// Construct a new Ed25519 identity from a 32-byte sequence.
277    ///
278    /// This might or might not actually be a valid Ed25519 public key.
279    ///
280    /// ```
281    /// use tor_llcrypto::pk::ed25519::{Ed25519Identity, PublicKey};
282    ///
283    /// let bytes = b"klsadjfkladsfjklsdafkljasdfsdsd!";
284    /// let id = Ed25519Identity::new(*bytes);
285    /// let pk: Result<PublicKey,_> = (&id).try_into();
286    /// assert!(pk.is_ok());
287    ///
288    /// let bytes = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
289    /// let id = Ed25519Identity::new(*bytes);
290    /// let pk: Result<PublicKey,_> = (&id).try_into();
291    /// assert!(pk.is_err());
292    /// ```
293    pub fn new(id: [u8; 32]) -> Self {
294        Ed25519Identity { id: id.into() }
295    }
296    /// If `id` is of the correct length, wrap it in an Ed25519Identity.
297    pub fn from_bytes(id: &[u8]) -> Option<Self> {
298        Some(Ed25519Identity::new(id.try_into().ok()?))
299    }
300    /// Return a reference to the bytes in this key.
301    pub fn as_bytes(&self) -> &[u8] {
302        &self.id.as_ref()[..]
303    }
304    /// Decode an `Ed25519Identity` from a base64-encoded string.
305    ///
306    /// The string must have no padding, spaces, or any extra characters.
307    // We decode without padding to match the serde deserialize impl.
308    pub fn from_base64(s: &str) -> Option<Self> {
309        let bytes = Base64Unpadded::decode_vec(s).ok()?;
310        Ed25519Identity::from_bytes(&bytes)
311    }
312}
313
314impl From<[u8; ED25519_ID_LEN]> for Ed25519Identity {
315    fn from(id: [u8; ED25519_ID_LEN]) -> Self {
316        Ed25519Identity::new(id)
317    }
318}
319
320impl From<Ed25519Identity> for [u8; ED25519_ID_LEN] {
321    fn from(value: Ed25519Identity) -> Self {
322        value.id.into()
323    }
324}
325
326impl From<PublicKey> for Ed25519Identity {
327    fn from(pk: PublicKey) -> Self {
328        (&pk).into()
329    }
330}
331
332impl From<&PublicKey> for Ed25519Identity {
333    fn from(pk: &PublicKey) -> Self {
334        // This unwrap is safe because the public key is always 32 bytes
335        // long.
336        Ed25519Identity::from_bytes(pk.as_bytes()).expect("Ed25519 public key had wrong length?")
337    }
338}
339
340impl TryFrom<&Ed25519Identity> for PublicKey {
341    type Error = ed25519_dalek::SignatureError;
342    fn try_from(id: &Ed25519Identity) -> Result<PublicKey, Self::Error> {
343        PublicKey::from_bytes(id.id.as_ref())
344    }
345}
346
347impl TryFrom<Ed25519Identity> for PublicKey {
348    type Error = ed25519_dalek::SignatureError;
349    fn try_from(id: Ed25519Identity) -> Result<PublicKey, Self::Error> {
350        (&id).try_into()
351    }
352}
353
354impl ConstantTimeEq for Ed25519Identity {
355    fn ct_eq(&self, other: &Self) -> Choice {
356        self.id.ct_eq(&other.id)
357    }
358}
359
360impl Display for Ed25519Identity {
361    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
362        write!(f, "{}", Base64Unpadded::encode_string(self.id.as_ref()))
363    }
364}
365
366impl Debug for Ed25519Identity {
367    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
368        write!(f, "Ed25519Identity {{ {} }}", self)
369    }
370}
371
372impl safelog::Redactable for Ed25519Identity {
373    /// Warning: This displays 12 bits of the ed25519 identity, which is
374    /// enough to narrow down a public relay by a great deal.
375    fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
376        write_start_redacted(f, &Base64Unpadded::encode_string(self.id.as_ref()), 2, "…")
377    }
378
379    fn debug_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
380        write!(f, "Ed25519Identity {{ {} }}", self.redacted())
381    }
382}
383
384impl serde::Serialize for Ed25519Identity {
385    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
386    where
387        S: serde::Serializer,
388    {
389        if serializer.is_human_readable() {
390            serializer.serialize_str(&Base64Unpadded::encode_string(self.id.as_ref()))
391        } else {
392            serializer.serialize_bytes(&self.id.as_ref()[..])
393        }
394    }
395}
396
397impl<'de> serde::Deserialize<'de> for Ed25519Identity {
398    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
399    where
400        D: serde::Deserializer<'de>,
401    {
402        if deserializer.is_human_readable() {
403            /// Helper for deserialization
404            struct EdIdentityVisitor;
405            impl<'de> serde::de::Visitor<'de> for EdIdentityVisitor {
406                type Value = Ed25519Identity;
407                fn expecting(&self, fmt: &mut std::fmt::Formatter<'_>) -> fmt::Result {
408                    fmt.write_str("base64-encoded Ed25519 public key")
409                }
410                fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
411                where
412                    E: serde::de::Error,
413                {
414                    let bytes = Base64Unpadded::decode_vec(s).map_err(E::custom)?;
415                    Ed25519Identity::from_bytes(&bytes)
416                        .ok_or_else(|| E::custom("wrong length for Ed25519 public key"))
417                }
418            }
419
420            deserializer.deserialize_str(EdIdentityVisitor)
421        } else {
422            /// Helper for deserialization
423            struct EdIdentityVisitor;
424            impl<'de> serde::de::Visitor<'de> for EdIdentityVisitor {
425                type Value = Ed25519Identity;
426                fn expecting(&self, fmt: &mut std::fmt::Formatter<'_>) -> fmt::Result {
427                    fmt.write_str("ed25519 public key")
428                }
429                fn visit_bytes<E>(self, bytes: &[u8]) -> Result<Self::Value, E>
430                where
431                    E: serde::de::Error,
432                {
433                    Ed25519Identity::from_bytes(bytes)
434                        .ok_or_else(|| E::custom("wrong length for ed25519 public key"))
435                }
436            }
437            deserializer.deserialize_bytes(EdIdentityVisitor)
438        }
439    }
440}
441
442/// An ed25519 signature, plus the document that it signs and its
443/// public key.
444#[derive(Clone, Debug)]
445#[cfg_attr(
446    feature = "memquota-memcost",
447    derive(Deftly),
448    derive_deftly(HasMemoryCost)
449)]
450pub struct ValidatableEd25519Signature {
451    /// The key that allegedly produced the signature
452    #[cfg_attr(feature = "memquota-memcost", deftly(has_memory_cost(copy)))]
453    key: PublicKey,
454    /// The alleged signature
455    #[cfg_attr(feature = "memquota-memcost", deftly(has_memory_cost(copy)))]
456    sig: Signature,
457    /// The entire body of text that is allegedly signed here.
458    ///
459    /// TODO: It's not so good to have this included here; it
460    /// would be better to have a patch to ed25519_dalek to allow
461    /// us to pre-hash the signed thing, and just store a digest.
462    /// We can't use that with the 'prehash' variant of ed25519,
463    /// since that has different constants.
464    entire_text_of_signed_thing: Vec<u8>,
465}
466
467impl ValidatableEd25519Signature {
468    /// Create a new ValidatableEd25519Signature
469    pub fn new(key: PublicKey, sig: Signature, text: &[u8]) -> Self {
470        ValidatableEd25519Signature {
471            key,
472            sig,
473            entire_text_of_signed_thing: text.into(),
474        }
475    }
476
477    /// View the interior of this signature object.
478    pub(crate) fn as_parts(&self) -> (&PublicKey, &Signature, &[u8]) {
479        (&self.key, &self.sig, &self.entire_text_of_signed_thing[..])
480    }
481
482    /// Return a reference to the underlying Signature.
483    pub fn signature(&self) -> &Signature {
484        &self.sig
485    }
486}
487
488impl super::ValidatableSignature for ValidatableEd25519Signature {
489    fn is_valid(&self) -> bool {
490        self.key
491            .verify(&self.entire_text_of_signed_thing[..], &self.sig)
492            .is_ok()
493    }
494
495    fn as_ed25519(&self) -> Option<&ValidatableEd25519Signature> {
496        Some(self)
497    }
498}
499
500/// Perform a batch verification operation on the provided signatures
501///
502/// Return `true` if _every_ signature is valid; otherwise return `false`.
503///
504/// Note that the mathematics for batch validation are slightly
505/// different than those for normal one-signature validation.  Because
506/// of this, it is possible for an ostensible signature that passes
507/// one validation algorithm might fail the other.  (Well-formed
508/// signatures generated by a correct Ed25519 implementation will
509/// always pass both kinds of validation, and an attacker should not
510/// be able to forge a signature that passes either kind.)
511pub fn validate_batch(sigs: &[&ValidatableEd25519Signature]) -> bool {
512    use crate::pk::ValidatableSignature;
513    if sigs.is_empty() {
514        // ed25519_dalek has nonzero cost for a batch-verification of
515        // zero sigs.
516        true
517    } else if sigs.len() == 1 {
518        // Validating one signature in the traditional way is faster.
519        sigs[0].is_valid()
520    } else {
521        let mut ed_msgs = Vec::new();
522        let mut ed_sigs = Vec::new();
523        let mut ed_pks = Vec::new();
524        for ed_sig in sigs {
525            let (pk, sig, msg) = ed_sig.as_parts();
526            ed_sigs.push(sig.0);
527            ed_pks.push(pk.0);
528            ed_msgs.push(msg);
529        }
530        ed25519_dalek::verify_batch(&ed_msgs[..], &ed_sigs[..], &ed_pks[..]).is_ok()
531    }
532}
533
534/// An object that has an Ed25519 [`PublicKey`].
535pub trait Ed25519PublicKey {
536    /// Get the Ed25519 [`PublicKey`].
537    fn public_key(&self) -> PublicKey;
538}
539
540impl Ed25519PublicKey for Keypair {
541    fn public_key(&self) -> PublicKey {
542        Keypair::verifying_key(self)
543    }
544}
545
546impl Ed25519PublicKey for ExpandedKeypair {
547    fn public_key(&self) -> PublicKey {
548        self.public
549    }
550}
551
552/// An object that can generate Ed25519 signatures.
553pub trait Ed25519SigningKey {
554    /// Sign a message with this key.
555    fn sign(&self, message: &[u8]) -> Signature;
556}
557
558impl Ed25519SigningKey for Keypair {
559    fn sign(&self, message: &[u8]) -> Signature {
560        Keypair::sign(self, message)
561    }
562}
563impl Ed25519SigningKey for ExpandedKeypair {
564    fn sign(&self, message: &[u8]) -> Signature {
565        ExpandedKeypair::sign(self, message)
566    }
567}
568
569#[cfg(test)]
570mod test {
571    // @@ begin test lint list maintained by maint/add_warning @@
572    #![allow(clippy::bool_assert_comparison)]
573    #![allow(clippy::clone_on_copy)]
574    #![allow(clippy::dbg_macro)]
575    #![allow(clippy::mixed_attributes_style)]
576    #![allow(clippy::print_stderr)]
577    #![allow(clippy::print_stdout)]
578    #![allow(clippy::single_char_pattern)]
579    #![allow(clippy::unwrap_used)]
580    #![allow(clippy::unchecked_time_subtraction)]
581    #![allow(clippy::useless_vec)]
582    #![allow(clippy::needless_pass_by_value)]
583    #![allow(clippy::string_slice)] // See arti#2571
584    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
585
586    use super::*;
587
588    #[test]
589    fn ed_from_base64() {
590        let id =
591            Ed25519Identity::from_base64("qpL/LxLYVEXghU76iG3LsSI/UW7MBpIROZK0AB18560").unwrap();
592
593        assert_eq!(
594            id,
595            Ed25519Identity::from([
596                0xaa, 0x92, 0xff, 0x2f, 0x12, 0xd8, 0x54, 0x45, 0xe0, 0x85, 0x4e, 0xfa, 0x88, 0x6d,
597                0xcb, 0xb1, 0x22, 0x3f, 0x51, 0x6e, 0xcc, 0x06, 0x92, 0x11, 0x39, 0x92, 0xb4, 0x00,
598                0x1d, 0x7c, 0xe7, 0xad
599            ]),
600        );
601    }
602}