//! Re-exporting Ed25519 implementations, and related utilities. //! //! Here we re-export types from [`ed25519_dalek`] that implement the //! Ed25519 signature algorithm. (TODO: Eventually, this module //! should probably be replaced with a wrapper that uses the ed25519 //! trait and the Signature trait.) //! //! We additionally provide an `Ed25519Identity` type to represent the //! unvalidated Ed25519 "identity keys" that we use throughout the Tor //! protocol to uniquely identify a relay. use base64ct::{Base64Unpadded, Encoding as _}; use curve25519_dalek::Scalar; use derive_deftly::Deftly; use safelog::util::write_start_redacted; use std::fmt::{self, Debug, Display, Formatter}; use subtle::{Choice, ConstantTimeEq}; #[cfg(feature = "memquota-memcost")] use tor_memquota_cost::derive_deftly_template_HasMemoryCost; use ed25519_dalek::hazmat::ExpandedSecretKey; use ed25519_dalek::{Signer as _, Verifier as _}; use crate::util::{ ct::{ CtByteArray, derive_deftly_template_ConstantTimeEq, derive_deftly_template_PartialEqFromCtEq, }, rng::RngCompat, }; /// An Ed25519 signature. /// /// See [`ed25519_dalek::Signature`] for more information. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct Signature(pub(crate) ed25519_dalek::Signature); /// An Ed25519 keypair. /// /// (We do not provide a separate "private key only" type.) /// /// See [`ed25519_dalek::SigningKey`] for more information. #[derive(Debug, Deftly)] #[derive_deftly(ConstantTimeEq)] pub struct Keypair(pub(crate) ed25519_dalek::SigningKey); /// An Ed25519 public key. /// /// See [`ed25519_dalek::VerifyingKey`] for more information. #[derive(Clone, Copy, Debug, Eq, Deftly)] #[derive_deftly(PartialEqFromCtEq)] pub struct PublicKey(pub(crate) ed25519_dalek::VerifyingKey); impl<'a> From<&'a Keypair> for PublicKey { fn from(value: &'a Keypair) -> Self { PublicKey((&value.0).into()) } } impl ConstantTimeEq for PublicKey { fn ct_eq(&self, other: &Self) -> Choice { self.as_bytes().ct_eq(other.as_bytes()) } } impl PublicKey { /// Construct a public key from its byte representation. pub fn from_bytes(bytes: &[u8; 32]) -> Result { Ok(PublicKey(ed25519_dalek::VerifyingKey::from_bytes(bytes)?)) } /// Return a reference to the byte representation of this public key. pub fn as_bytes(&self) -> &[u8; 32] { self.0.as_bytes() } /// Return the byte representation of this public key. pub fn to_bytes(&self) -> [u8; 32] { self.0.to_bytes() } /// Verify a signature using this public key. /// /// See [`ed25519_dalek::VerifyingKey::verify`] for more information. pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), signature::Error> { self.0.verify(message, &signature.0) } } impl Keypair { /// Generate a new random ed25519 keypair. pub fn generate(csprng: &mut R) -> Self { Self(ed25519_dalek::SigningKey::generate(&mut RngCompat::new( csprng, ))) } /// Construct an ed25519 keypair from the byte representation of its secret key. pub fn from_bytes(bytes: &[u8; 32]) -> Self { Self(ed25519_dalek::SigningKey::from_bytes(bytes)) } /// Return a reference to the byte representation of the secret key in this keypair. pub fn as_bytes(&self) -> &[u8; 32] { self.0.as_bytes() } /// Return to the byte representation of the secret key in this keypair. pub fn to_bytes(&self) -> [u8; 32] { self.0.to_bytes() } /// Return the public key in this keypair. pub fn verifying_key(&self) -> PublicKey { PublicKey(*self.0.as_ref()) } /// Verify a signature generated with this keypair. pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), signature::Error> { self.0.verify(message, &signature.0) } /// Sign a message using this keypair. pub fn sign(&self, message: &[u8]) -> Signature { Signature(self.0.sign(message)) } } impl Signature { /// Construct this signature from its byte representation. pub fn from_bytes(bytes: &[u8; 64]) -> Self { Self(ed25519_dalek::Signature::from_bytes(bytes)) } /// Return the byte representation of this signature. pub fn to_bytes(&self) -> [u8; 64] { self.0.to_bytes() } } impl<'a> TryFrom<&'a [u8]> for PublicKey { type Error = signature::Error; fn try_from(value: &'a [u8]) -> Result { Ok(Self(ed25519_dalek::VerifyingKey::try_from(value)?)) } } impl<'a> From<&'a [u8; 32]> for Keypair { fn from(value: &'a [u8; 32]) -> Self { Self(ed25519_dalek::SigningKey::from(value)) } } impl From<[u8; 64]> for Signature { fn from(value: [u8; 64]) -> Self { Signature(value.into()) } } impl<'a> From<&'a [u8; 64]> for Signature { fn from(value: &'a [u8; 64]) -> Self { Signature(value.into()) } } /// The length of an ED25519 identity, in bytes. pub const ED25519_ID_LEN: usize = 32; /// The length of an ED25519 signature, in bytes. pub const ED25519_SIGNATURE_LEN: usize = 64; /// A variant of [`Keypair`] containing an [`ExpandedSecretKey`]. /// /// In the Tor protocol, we use this type for blinded onion service identity keys /// (KS_hs_blind_id). Since their scalar values are computed, rather than taken /// directly from a /// SHA-512 transformation of a SecretKey, we cannot use the regular `Keypair` /// type. #[allow(clippy::exhaustive_structs)] #[derive(Deftly)] #[derive_deftly(ConstantTimeEq)] pub struct ExpandedKeypair { /// The secret part of the key. pub(crate) secret: ExpandedSecretKey, /// The public part of this key. /// /// NOTE: As with [`ed25519_dalek::SigningKey`], this public key _must_ be /// the public key matching `secret`. Putting a different public key in /// here would enable a class of attacks against ed25519 and enable secret /// key recovery. pub(crate) public: PublicKey, } impl ExpandedKeypair { /// Return the public part of this expanded keypair. pub fn public(&self) -> &PublicKey { &self.public } // NOTE: There is deliberately no secret() function. If we had one, we // would be exposing an unescorted secret key, which is part of // ed25519::hazmat. /// Compute a signature over a message using this keypair. pub fn sign(&self, message: &[u8]) -> Signature { use sha2::Sha512; // See notes on ExpandedKeypair about why this hazmat is okay to use. Signature(ed25519_dalek::hazmat::raw_sign::( &self.secret, message, &self.public.0, )) } /// Return a representation of the secret key in this keypair. /// /// (Since it is an expanded secret key, we represent it as its scalar part /// followed by its hash_prefix.) pub fn to_secret_key_bytes(&self) -> [u8; 64] { let mut output = [0_u8; 64]; output[0..32].copy_from_slice(&self.secret.scalar.to_bytes()); output[32..64].copy_from_slice(&self.secret.hash_prefix); output } /// Reconstruct a key from its byte representation as returned by /// `to_secret_key_bytes()`. /// /// Return None if the input cannot be the output of `to_secret_key_bytes()`. // // NOTE: Returning None is a bit silly, but that's what Dalek does. pub fn from_secret_key_bytes(bytes: [u8; 64]) -> Option { let scalar = Option::from(Scalar::from_bytes_mod_order( bytes[0..32].try_into().expect("wrong length on slice"), ))?; let hash_prefix = bytes[32..64].try_into().expect("wrong length on slice"); let secret = ExpandedSecretKey { scalar, hash_prefix, }; let public = PublicKey((&secret).into()); Some(Self { secret, public }) } // NOTE: There is deliberately no constructor here that takes a (secret, // public) pair. If there were, you could construct a pair with a // mismatched public key. } impl<'a> From<&'a Keypair> for ExpandedKeypair { fn from(kp: &'a Keypair) -> ExpandedKeypair { ExpandedKeypair { secret: kp.as_bytes().into(), public: kp.into(), } } } impl From for PublicKey { fn from(ekp: ExpandedKeypair) -> PublicKey { ekp.public } } /// An unchecked, unvalidated Ed25519 key. /// /// This key is an "identity" in the sense that it identifies (up to) one /// Ed25519 key. It may also represent the identity for a particular entity, /// such as a relay or an onion service. /// /// This type is distinct from an Ed25519 [`PublicKey`] for several reasons: /// * We're storing it in a compact format, whereas the public key /// implementation might want an expanded form for more efficient key /// validation. /// * This type hasn't checked whether the bytes here actually _are_ a valid /// Ed25519 public key. #[derive(Clone, Copy, Hash, PartialOrd, Ord, Eq, PartialEq)] #[cfg_attr( feature = "memquota-memcost", derive(Deftly), derive_deftly(HasMemoryCost) )] pub struct Ed25519Identity { /// A raw unchecked Ed25519 public key. id: CtByteArray, } impl Ed25519Identity { /// Construct a new Ed25519 identity from a 32-byte sequence. /// /// This might or might not actually be a valid Ed25519 public key. /// /// ``` /// use tor_llcrypto::pk::ed25519::{Ed25519Identity, PublicKey}; /// /// let bytes = b"klsadjfkladsfjklsdafkljasdfsdsd!"; /// let id = Ed25519Identity::new(*bytes); /// let pk: Result = (&id).try_into(); /// assert!(pk.is_ok()); /// /// let bytes = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; /// let id = Ed25519Identity::new(*bytes); /// let pk: Result = (&id).try_into(); /// assert!(pk.is_err()); /// ``` pub fn new(id: [u8; 32]) -> Self { Ed25519Identity { id: id.into() } } /// If `id` is of the correct length, wrap it in an Ed25519Identity. pub fn from_bytes(id: &[u8]) -> Option { Some(Ed25519Identity::new(id.try_into().ok()?)) } /// Return a reference to the bytes in this key. pub fn as_bytes(&self) -> &[u8] { &self.id.as_ref()[..] } /// Decode an `Ed25519Identity` from a base64-encoded string. /// /// The string must have no padding, spaces, or any extra characters. // We decode without padding to match the serde deserialize impl. pub fn from_base64(s: &str) -> Option { let bytes = Base64Unpadded::decode_vec(s).ok()?; Ed25519Identity::from_bytes(&bytes) } } impl From<[u8; ED25519_ID_LEN]> for Ed25519Identity { fn from(id: [u8; ED25519_ID_LEN]) -> Self { Ed25519Identity::new(id) } } impl From for [u8; ED25519_ID_LEN] { fn from(value: Ed25519Identity) -> Self { value.id.into() } } impl From for Ed25519Identity { fn from(pk: PublicKey) -> Self { (&pk).into() } } impl From<&PublicKey> for Ed25519Identity { fn from(pk: &PublicKey) -> Self { // This unwrap is safe because the public key is always 32 bytes // long. Ed25519Identity::from_bytes(pk.as_bytes()).expect("Ed25519 public key had wrong length?") } } impl TryFrom<&Ed25519Identity> for PublicKey { type Error = ed25519_dalek::SignatureError; fn try_from(id: &Ed25519Identity) -> Result { PublicKey::from_bytes(id.id.as_ref()) } } impl TryFrom for PublicKey { type Error = ed25519_dalek::SignatureError; fn try_from(id: Ed25519Identity) -> Result { (&id).try_into() } } impl ConstantTimeEq for Ed25519Identity { fn ct_eq(&self, other: &Self) -> Choice { self.id.ct_eq(&other.id) } } impl Display for Ed25519Identity { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "{}", Base64Unpadded::encode_string(self.id.as_ref())) } } impl Debug for Ed25519Identity { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "Ed25519Identity {{ {} }}", self) } } impl safelog::Redactable for Ed25519Identity { /// Warning: This displays 12 bits of the ed25519 identity, which is /// enough to narrow down a public relay by a great deal. fn display_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write_start_redacted(f, &Base64Unpadded::encode_string(self.id.as_ref()), 2, "…") } fn debug_redacted(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Ed25519Identity {{ {} }}", self.redacted()) } } impl serde::Serialize for Ed25519Identity { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { if serializer.is_human_readable() { serializer.serialize_str(&Base64Unpadded::encode_string(self.id.as_ref())) } else { serializer.serialize_bytes(&self.id.as_ref()[..]) } } } impl<'de> serde::Deserialize<'de> for Ed25519Identity { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { if deserializer.is_human_readable() { /// Helper for deserialization struct EdIdentityVisitor; impl<'de> serde::de::Visitor<'de> for EdIdentityVisitor { type Value = Ed25519Identity; fn expecting(&self, fmt: &mut std::fmt::Formatter<'_>) -> fmt::Result { fmt.write_str("base64-encoded Ed25519 public key") } fn visit_str(self, s: &str) -> Result where E: serde::de::Error, { let bytes = Base64Unpadded::decode_vec(s).map_err(E::custom)?; Ed25519Identity::from_bytes(&bytes) .ok_or_else(|| E::custom("wrong length for Ed25519 public key")) } } deserializer.deserialize_str(EdIdentityVisitor) } else { /// Helper for deserialization struct EdIdentityVisitor; impl<'de> serde::de::Visitor<'de> for EdIdentityVisitor { type Value = Ed25519Identity; fn expecting(&self, fmt: &mut std::fmt::Formatter<'_>) -> fmt::Result { fmt.write_str("ed25519 public key") } fn visit_bytes(self, bytes: &[u8]) -> Result where E: serde::de::Error, { Ed25519Identity::from_bytes(bytes) .ok_or_else(|| E::custom("wrong length for ed25519 public key")) } } deserializer.deserialize_bytes(EdIdentityVisitor) } } } /// An ed25519 signature, plus the document that it signs and its /// public key. #[derive(Clone, Debug)] #[cfg_attr( feature = "memquota-memcost", derive(Deftly), derive_deftly(HasMemoryCost) )] pub struct ValidatableEd25519Signature { /// The key that allegedly produced the signature #[cfg_attr(feature = "memquota-memcost", deftly(has_memory_cost(copy)))] key: PublicKey, /// The alleged signature #[cfg_attr(feature = "memquota-memcost", deftly(has_memory_cost(copy)))] sig: Signature, /// The entire body of text that is allegedly signed here. /// /// TODO: It's not so good to have this included here; it /// would be better to have a patch to ed25519_dalek to allow /// us to pre-hash the signed thing, and just store a digest. /// We can't use that with the 'prehash' variant of ed25519, /// since that has different constants. entire_text_of_signed_thing: Vec, } impl ValidatableEd25519Signature { /// Create a new ValidatableEd25519Signature pub fn new(key: PublicKey, sig: Signature, text: &[u8]) -> Self { ValidatableEd25519Signature { key, sig, entire_text_of_signed_thing: text.into(), } } /// View the interior of this signature object. pub(crate) fn as_parts(&self) -> (&PublicKey, &Signature, &[u8]) { (&self.key, &self.sig, &self.entire_text_of_signed_thing[..]) } /// Return a reference to the underlying Signature. pub fn signature(&self) -> &Signature { &self.sig } } impl super::ValidatableSignature for ValidatableEd25519Signature { fn is_valid(&self) -> bool { self.key .verify(&self.entire_text_of_signed_thing[..], &self.sig) .is_ok() } fn as_ed25519(&self) -> Option<&ValidatableEd25519Signature> { Some(self) } } /// Perform a batch verification operation on the provided signatures /// /// Return `true` if _every_ signature is valid; otherwise return `false`. /// /// Note that the mathematics for batch validation are slightly /// different than those for normal one-signature validation. Because /// of this, it is possible for an ostensible signature that passes /// one validation algorithm might fail the other. (Well-formed /// signatures generated by a correct Ed25519 implementation will /// always pass both kinds of validation, and an attacker should not /// be able to forge a signature that passes either kind.) pub fn validate_batch(sigs: &[&ValidatableEd25519Signature]) -> bool { use crate::pk::ValidatableSignature; if sigs.is_empty() { // ed25519_dalek has nonzero cost for a batch-verification of // zero sigs. true } else if sigs.len() == 1 { // Validating one signature in the traditional way is faster. sigs[0].is_valid() } else { let mut ed_msgs = Vec::new(); let mut ed_sigs = Vec::new(); let mut ed_pks = Vec::new(); for ed_sig in sigs { let (pk, sig, msg) = ed_sig.as_parts(); ed_sigs.push(sig.0); ed_pks.push(pk.0); ed_msgs.push(msg); } ed25519_dalek::verify_batch(&ed_msgs[..], &ed_sigs[..], &ed_pks[..]).is_ok() } } /// An object that has an Ed25519 [`PublicKey`]. pub trait Ed25519PublicKey { /// Get the Ed25519 [`PublicKey`]. fn public_key(&self) -> PublicKey; } impl Ed25519PublicKey for Keypair { fn public_key(&self) -> PublicKey { Keypair::verifying_key(self) } } impl Ed25519PublicKey for ExpandedKeypair { fn public_key(&self) -> PublicKey { self.public } } /// An object that can generate Ed25519 signatures. pub trait Ed25519SigningKey { /// Sign a message with this key. fn sign(&self, message: &[u8]) -> Signature; } impl Ed25519SigningKey for Keypair { fn sign(&self, message: &[u8]) -> Signature { Keypair::sign(self, message) } } impl Ed25519SigningKey for ExpandedKeypair { fn sign(&self, message: &[u8]) -> Signature { ExpandedKeypair::sign(self, message) } } #[cfg(test)] mod test { // @@ begin test lint list maintained by maint/add_warning @@ #![allow(clippy::bool_assert_comparison)] #![allow(clippy::clone_on_copy)] #![allow(clippy::dbg_macro)] #![allow(clippy::mixed_attributes_style)] #![allow(clippy::print_stderr)] #![allow(clippy::print_stdout)] #![allow(clippy::single_char_pattern)] #![allow(clippy::unwrap_used)] #![allow(clippy::unchecked_time_subtraction)] #![allow(clippy::useless_vec)] #![allow(clippy::needless_pass_by_value)] #![allow(clippy::string_slice)] // See arti#2571 //! use super::*; #[test] fn ed_from_base64() { let id = Ed25519Identity::from_base64("qpL/LxLYVEXghU76iG3LsSI/UW7MBpIROZK0AB18560").unwrap(); assert_eq!( id, Ed25519Identity::from([ 0xaa, 0x92, 0xff, 0x2f, 0x12, 0xd8, 0x54, 0x45, 0xe0, 0x85, 0x4e, 0xfa, 0x88, 0x6d, 0xcb, 0xb1, 0x22, 0x3f, 0x51, 0x6e, 0xcc, 0x06, 0x92, 0x11, 0x39, 0x92, 0xb4, 0x00, 0x1d, 0x7c, 0xe7, 0xad ]), ); } }