1use 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#[derive(Clone, Copy, Debug, Eq, PartialEq)]
37pub struct Signature(pub(crate) ed25519_dalek::Signature);
38
39#[derive(Debug, Deftly)]
45#[derive_deftly(ConstantTimeEq)]
46pub struct Keypair(pub(crate) ed25519_dalek::SigningKey);
47
48#[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 pub fn from_bytes(bytes: &[u8; 32]) -> Result<Self, signature::Error> {
70 Ok(PublicKey(ed25519_dalek::VerifyingKey::from_bytes(bytes)?))
71 }
72
73 pub fn as_bytes(&self) -> &[u8; 32] {
75 self.0.as_bytes()
76 }
77 pub fn to_bytes(&self) -> [u8; 32] {
79 self.0.to_bytes()
80 }
81 pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), signature::Error> {
85 self.0.verify(message, &signature.0)
86 }
87}
88impl Keypair {
89 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 pub fn from_bytes(bytes: &[u8; 32]) -> Self {
97 Self(ed25519_dalek::SigningKey::from_bytes(bytes))
98 }
99 pub fn as_bytes(&self) -> &[u8; 32] {
101 self.0.as_bytes()
102 }
103 pub fn to_bytes(&self) -> [u8; 32] {
105 self.0.to_bytes()
106 }
107 pub fn verifying_key(&self) -> PublicKey {
109 PublicKey(*self.0.as_ref())
110 }
111 pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), signature::Error> {
113 self.0.verify(message, &signature.0)
114 }
115 pub fn sign(&self, message: &[u8]) -> Signature {
117 Signature(self.0.sign(message))
118 }
119}
120impl Signature {
121 pub fn from_bytes(bytes: &[u8; 64]) -> Self {
123 Self(ed25519_dalek::Signature::from_bytes(bytes))
124 }
125 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
153pub const ED25519_ID_LEN: usize = 32;
155
156pub const ED25519_SIGNATURE_LEN: usize = 64;
158
159#[allow(clippy::exhaustive_structs)]
167#[derive(Deftly)]
168#[derive_deftly(ConstantTimeEq)]
169pub struct ExpandedKeypair {
170 pub(crate) secret: ExpandedSecretKey,
172 pub(crate) public: PublicKey,
179}
180
181impl ExpandedKeypair {
182 pub fn public(&self) -> &PublicKey {
184 &self.public
185 }
186
187 pub fn sign(&self, message: &[u8]) -> Signature {
193 use sha2::Sha512;
194 Signature(ed25519_dalek::hazmat::raw_sign::<Sha512>(
196 &self.secret,
197 message,
198 &self.public.0,
199 ))
200 }
201
202 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 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 }
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#[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 id: CtByteArray<ED25519_ID_LEN>,
273}
274
275impl Ed25519Identity {
276 pub fn new(id: [u8; 32]) -> Self {
294 Ed25519Identity { id: id.into() }
295 }
296 pub fn from_bytes(id: &[u8]) -> Option<Self> {
298 Some(Ed25519Identity::new(id.try_into().ok()?))
299 }
300 pub fn as_bytes(&self) -> &[u8] {
302 &self.id.as_ref()[..]
303 }
304 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 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 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 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 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#[derive(Clone, Debug)]
445#[cfg_attr(
446 feature = "memquota-memcost",
447 derive(Deftly),
448 derive_deftly(HasMemoryCost)
449)]
450pub struct ValidatableEd25519Signature {
451 #[cfg_attr(feature = "memquota-memcost", deftly(has_memory_cost(copy)))]
453 key: PublicKey,
454 #[cfg_attr(feature = "memquota-memcost", deftly(has_memory_cost(copy)))]
456 sig: Signature,
457 entire_text_of_signed_thing: Vec<u8>,
465}
466
467impl ValidatableEd25519Signature {
468 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 pub(crate) fn as_parts(&self) -> (&PublicKey, &Signature, &[u8]) {
479 (&self.key, &self.sig, &self.entire_text_of_signed_thing[..])
480 }
481
482 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
500pub fn validate_batch(sigs: &[&ValidatableEd25519Signature]) -> bool {
512 use crate::pk::ValidatableSignature;
513 if sigs.is_empty() {
514 true
517 } else if sigs.len() == 1 {
518 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
534pub trait Ed25519PublicKey {
536 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
552pub trait Ed25519SigningKey {
554 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 #![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)] 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}