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