1#![no_std]
2
3#[cfg(any(test, feature = "alloc"))]
4extern crate alloc;
5
6#[cfg(any(test, feature = "alloc"))]
7#[allow(unused_imports)]
8use alloc::{
9 string::{String, ToString as _},
10 vec::Vec,
11};
12
13#[cfg(feature = "std")]
14extern crate std;
15
16mod dalek {
20 pub(crate) extern crate curve25519_dalek as curve;
21 pub(crate) extern crate ed25519_dalek as ed;
22}
23
24pub use dalek::ed::ed25519::{Signature, signature};
27
28#[cfg(all(feature = "ssh", feature = "alloc"))]
29pub mod ssh;
30
31mod seed;
32pub use seed::Seed;
33
34pub type SharedSecret = [u8; 32];
36
37pub trait Signer
48where
49 Self: signature::Signer<Signature>,
50 Self: signature::Keypair<VerifyingKey = VerifyingKey>,
51 Self: AsRef<PublicKey>,
52{
53 fn public_key(&self) -> &PublicKey {
57 self.as_ref()
58 }
59}
60
61impl<T: ?Sized> Signer for T
62where
63 Self: signature::Signer<Signature>,
64 Self: signature::Keypair<VerifyingKey = VerifyingKey>,
65 Self: AsRef<PublicKey>,
66{
67}
68
69#[allow(dead_code)]
80mod future {
81 use super::*;
82
83 const fn r#dyn(_: &dyn Signer) {}
85
86 const fn r#impl<Witness: Signer>() {}
88
89 const IMPL_SECRET_KEY: () = r#impl::<SigningKey>();
91
92 #[cfg(all(feature = "ssh", feature = "std"))]
94 const IMPL_AGENT_SIGNER: () = r#impl::<ssh::agent::AgentSigner>();
95}
96
97#[cfg(feature = "multibase")]
99pub const MULTICODEC_TYPE: [u8; 2] = [0xED, 0x01];
100
101pub type PublicKeyBytes = [u8; dalek::ed::PUBLIC_KEY_LENGTH];
102
103#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug, PartialOrd, Ord)]
111#[cfg_attr(
112 all(feature = "serde", feature = "alloc", feature = "multibase"),
113 derive(serde::Serialize, serde::Deserialize),
114 serde(into = "String", try_from = "String")
115)]
116#[cfg_attr(
117 all(feature = "schemars", feature = "serde", feature = "alloc", feature = "multibase"),
118 derive(schemars::JsonSchema),
119 schemars(
120 title = "Ed25519",
121 description = "An Ed25519 public key in multibase encoding.",
122 extend("examples" = [
123 "z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7",
124 "z6MkvUJtYD9dHDJfpevWRT98mzDDpdAtmUjwyDSkyqksUr7C",
125 "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
126 "z6MkkfM3tPXNPrPevKr3uSiQtHPuwnNhu2yUVjgd2jXVsVz5",
127 ]),
128 ),
129)]
130#[repr(transparent)]
131pub struct PublicKey(PublicKeyBytes);
132
133impl PublicKey {
134 pub const fn from_bytes(bytes: PublicKeyBytes) -> Self {
135 Self(bytes)
136 }
137
138 pub fn into_inner(self) -> PublicKeyBytes {
139 self.0
140 }
141}
142
143impl<'a> From<&'a PublicKeyBytes> for &'a PublicKey {
144 fn from(other: &'a PublicKeyBytes) -> Self {
145 let ptr = std::ptr::from_ref(other).cast::<PublicKey>();
146 unsafe { &*ptr }
149 }
150}
151
152impl From<PublicKeyBytes> for PublicKey {
153 fn from(bytes: PublicKeyBytes) -> Self {
154 Self(bytes)
155 }
156}
157
158#[cfg(feature = "alloc")]
159impl alloc::borrow::Borrow<PublicKeyBytes> for PublicKey {
160 fn borrow(&self) -> &PublicKeyBytes {
161 &self.0
162 }
163}
164
165#[cfg(all(feature = "alloc", feature = "multibase"))]
166impl alloc::fmt::Display for PublicKey {
167 fn fmt(&self, f: &mut alloc::fmt::Formatter<'_>) -> alloc::fmt::Result {
168 write!(f, "{}", self.to_human())
169 }
170}
171
172#[cfg(feature = "ssh")]
173impl From<PublicKey> for ssh_key::public::Ed25519PublicKey {
174 fn from(key: PublicKey) -> Self {
175 ssh_key::public::Ed25519PublicKey(key.0)
176 }
177}
178
179#[cfg(feature = "ssh")]
180impl From<ssh_key::public::Ed25519PublicKey> for PublicKey {
181 fn from(key: ssh_key::public::Ed25519PublicKey) -> Self {
182 Self(key.0)
183 }
184}
185
186#[cfg(all(feature = "alloc", feature = "multibase"))]
187#[derive(thiserror::Error, Debug)]
188#[non_exhaustive]
189pub enum PublicKeyError {
190 #[error("invalid length {0}")]
191 InvalidLength(usize),
192 #[error("invalid multibase string: {0}")]
193 Multibase(#[cfg_attr(feature = "std", source)] multibase::Error),
194 #[error("invalid multicodec prefix, expected {0:?}")]
195 Multicodec([u8; 2]),
196 #[error("invalid public key")]
197 Invalid(#[cfg_attr(feature = "std", source)] signature::Error),
198}
199
200#[cfg(all(feature = "alloc", feature = "multibase"))]
201impl From<PublicKey> for String {
202 fn from(other: PublicKey) -> Self {
203 other.to_human()
204 }
205}
206
207impl PublicKey {
208 #[cfg(all(feature = "alloc", feature = "multibase"))]
213 pub fn to_human(&self) -> String {
214 let mut buf = [0; 2 + dalek::ed::PUBLIC_KEY_LENGTH];
215 buf[..2].copy_from_slice(&MULTICODEC_TYPE);
216 buf[2..].copy_from_slice(&self.0);
217
218 multibase::encode(multibase::Base::Base58Btc, buf)
219 }
220
221 #[cfg(all(
228 feature = "git-ref-format-core",
229 feature = "alloc",
230 feature = "multibase"
231 ))]
232 pub fn to_namespace(&self) -> git_ref_format_core::RefString {
233 use alloc::borrow::ToOwned as _;
234 use git_ref_format_core::name::{NAMESPACES, REFS};
235 REFS.to_owned().and(NAMESPACES).and(self.to_component())
236 }
237
238 #[cfg(all(
241 feature = "git-ref-format-core",
242 feature = "alloc",
243 feature = "multibase"
244 ))]
245 pub fn to_component(&self) -> git_ref_format_core::Component<'_> {
246 git_ref_format_core::Component::from(self)
247 }
248
249 #[cfg(all(
257 feature = "git-ref-format-core",
258 feature = "alloc",
259 feature = "multibase"
260 ))]
261 pub fn from_namespaced(
262 refstr: &git_ref_format_core::Namespaced,
263 ) -> Result<Self, PublicKeyError> {
264 use alloc::str::FromStr as _;
265
266 let name = refstr.namespace().into_inner();
267 Self::from_str(name.as_str())
268 }
269}
270
271#[cfg(all(feature = "alloc", feature = "multibase"))]
272impl alloc::str::FromStr for PublicKey {
273 type Err = PublicKeyError;
274
275 fn from_str(s: &str) -> Result<Self, Self::Err> {
276 let (_, bytes) = multibase::decode(s).map_err(PublicKeyError::Multibase)?;
277
278 if bytes.len() < 2 {
279 return Err(PublicKeyError::InvalidLength(bytes.len()));
280 }
281
282 if bytes[..MULTICODEC_TYPE.len()] != MULTICODEC_TYPE {
283 return Err(PublicKeyError::Multicodec(MULTICODEC_TYPE));
284 }
285
286 Ok(PublicKey(
287 bytes[MULTICODEC_TYPE.len()..]
288 .try_into()
289 .map_err(|_| PublicKeyError::InvalidLength(bytes.len()))?,
290 ))
291 }
292}
293
294#[cfg(all(
295 feature = "git-ref-format-core",
296 feature = "alloc",
297 feature = "multibase"
298))]
299impl From<&PublicKey> for git_ref_format_core::Component<'_> {
300 fn from(id: &PublicKey) -> Self {
301 use git_ref_format_core::{Component, RefString};
302 let refstr =
303 RefString::try_from(id.to_string()).expect("encoded public keys are valid ref strings");
304 Component::from_refstr(refstr).expect("encoded public keys are valid refname components")
305 }
306}
307
308#[cfg(all(feature = "sqlite", feature = "alloc", feature = "multibase"))]
309impl TryFrom<&sqlite::Value> for PublicKey {
310 type Error = sqlite::Error;
311
312 fn try_from(value: &sqlite::Value) -> Result<Self, Self::Error> {
313 use alloc::str::FromStr as _;
314
315 match value {
316 sqlite::Value::String(s) => Self::from_str(s).map_err(|e| sqlite::Error {
317 code: None,
318 message: Some(e.to_string()),
319 }),
320 _ => Err(sqlite::Error {
321 code: None,
322 message: Some(String::from("sql: invalid type for public key")),
323 }),
324 }
325 }
326}
327
328#[cfg(all(feature = "sqlite", feature = "alloc", feature = "multibase"))]
329impl sqlite::BindableWithIndex for &PublicKey {
330 fn bind<I: sqlite::ParameterIndex>(
331 self,
332 stmt: &mut sqlite::Statement<'_>,
333 i: I,
334 ) -> sqlite::Result<()> {
335 sqlite::Value::from(self).bind(stmt, i)
336 }
337}
338
339#[cfg(all(feature = "sqlite", feature = "alloc", feature = "multibase"))]
340impl From<&PublicKey> for sqlite::Value {
341 fn from(pk: &PublicKey) -> Self {
342 sqlite::Value::String(pk.to_human())
343 }
344}
345
346#[cfg(feature = "cyphernet")]
347impl AsRef<[u8]> for PublicKey {
348 fn as_ref(&self) -> &[u8] {
349 &self.0
350 }
351}
352
353#[cfg(all(feature = "cyphernet", feature = "alloc", feature = "multibase"))]
354impl cyphernet::display::MultiDisplay<cyphernet::display::Encoding> for PublicKey {
355 type Display = String;
356
357 fn display_fmt(&self, encoding: &cyphernet::display::Encoding) -> Self::Display {
358 match encoding {
359 cyphernet::display::Encoding::Base58
360 | cyphernet::display::Encoding::Multibase(multibase::Base::Base58Btc) => {
361 self.to_string()
362 }
363 _ => unimplemented!(),
364 }
365 }
366}
367
368#[cfg(all(feature = "cyphernet", feature = "alloc"))]
369impl cyphernet::EcPk for PublicKey {
370 const COMPRESSED_LEN: usize = dalek::ed::PUBLIC_KEY_LENGTH;
371 const CURVE_NAME: &'static str = "Edwards25519";
372
373 type Compressed = PublicKey;
374
375 fn base_point() -> Self {
376 unimplemented!()
377 }
378
379 fn to_pk_compressed(&self) -> Self::Compressed {
380 *self
381 }
382
383 fn from_pk_compressed(pk: Self::Compressed) -> Result<Self, cyphernet::EcPkInvalid> {
384 Ok(pk)
385 }
386
387 fn from_pk_compressed_slice(pk: &[u8]) -> Result<Self, cyphernet::EcPkInvalid> {
388 Ok(PublicKey(
389 PublicKeyBytes::try_from(pk).map_err(|_| cyphernet::EcPkInvalid::default())?,
390 ))
391 }
392}
393
394#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
400pub struct VerifyingKey(dalek::ed::VerifyingKey);
401
402impl VerifyingKey {
403 #[allow(clippy::wrong_self_convention)] #[inline]
405 #[cfg(any(feature = "diffie-hellman", feature = "ssh"))]
406 pub(crate) fn to_bytes(&self) -> PublicKeyBytes {
407 self.0.to_bytes()
408 }
409}
410
411impl TryFrom<&PublicKey> for VerifyingKey {
412 type Error = signature::Error;
413
414 fn try_from(key: &PublicKey) -> Result<Self, Self::Error> {
415 dalek::ed::VerifyingKey::from_bytes(&key.0).map(Self)
416 }
417}
418
419impl<'a> VerifyingKey {
420 pub fn public_key(&'a self) -> &'a PublicKey {
421 self.0.as_bytes().into()
422 }
423}
424
425impl AsRef<PublicKeyBytes> for VerifyingKey {
426 fn as_ref(&self) -> &PublicKeyBytes {
427 self.0.as_bytes()
428 }
429}
430
431impl From<dalek::ed::VerifyingKey> for VerifyingKey {
432 fn from(other: dalek::ed::VerifyingKey) -> Self {
433 Self(other)
434 }
435}
436
437impl signature::Verifier<Signature> for VerifyingKey {
438 fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), signature::Error> {
439 self.0.verify(msg, signature)
440 }
441}
442
443#[cfg(all(feature = "alloc", feature = "multibase"))]
444impl alloc::fmt::Display for VerifyingKey {
445 fn fmt(&self, f: &mut alloc::fmt::Formatter<'_>) -> alloc::fmt::Result {
446 self.public_key().fmt(f)
447 }
448}
449
450#[cfg(all(feature = "cyphernet", feature = "alloc", feature = "multibase"))]
451impl cyphernet::display::MultiDisplay<cyphernet::display::Encoding> for VerifyingKey {
452 type Display = String;
453
454 fn display_fmt(&self, encoding: &cyphernet::display::Encoding) -> Self::Display {
455 self.public_key().display_fmt(encoding)
456 }
457}
458
459#[cfg(feature = "cyphernet")]
460impl From<&cyphernet::ed25519::PublicKey> for PublicKey {
461 fn from(value: &cyphernet::ed25519::PublicKey) -> Self {
462 use core::ops::Deref as _;
463 Self(*value.deref().deref())
464 }
465}
466
467#[cfg(feature = "cyphernet")]
468impl From<PublicKey> for cyphernet::ed25519::PublicKey {
469 fn from(value: PublicKey) -> Self {
470 use cyphernet::EcPk as _;
471 cyphernet::ed25519::PublicKey::from_pk_compressed(value.into_inner().into())
472 .expect("implementation is infallible")
473 }
474}
475
476#[cfg(all(feature = "cyphernet", feature = "alloc"))]
477impl cyphernet::EcPk for VerifyingKey {
478 const COMPRESSED_LEN: usize = dalek::ed::PUBLIC_KEY_LENGTH;
479 const CURVE_NAME: &'static str = "Edwards25519";
480
481 type Compressed = PublicKey;
482
483 fn base_point() -> Self {
484 unimplemented!()
485 }
486
487 fn to_pk_compressed(&self) -> Self::Compressed {
488 *self.public_key()
489 }
490
491 fn from_pk_compressed(pk: Self::Compressed) -> Result<Self, cyphernet::EcPkInvalid> {
492 dalek::ed::VerifyingKey::from_bytes(&pk.0)
493 .map_err(|_| cyphernet::EcPkInvalid::default())
494 .map(Self)
495 }
496
497 fn from_pk_compressed_slice(slice: &[u8]) -> Result<Self, cyphernet::EcPkInvalid> {
498 Self::from_pk_compressed(PublicKey(
499 slice
500 .try_into()
501 .map_err(|_| cyphernet::EcPkInvalid::default())?,
502 ))
503 }
504}
505
506#[cfg(all(feature = "ssh", feature = "std"))]
507#[derive(thiserror::Error, Debug)]
508#[non_exhaustive]
509pub enum LoadError {
510 #[error(transparent)]
511 Keystore(#[from] ssh::keystore::Error),
512 #[error("key not found in '{0}'")]
513 NotFound(std::path::PathBuf),
514 #[error("invalid passphrase")]
515 InvalidPassphrase,
516 #[error("secret key '{secret}' and public key '{public}' do not match")]
517 KeyMismatch {
518 secret: std::path::PathBuf,
519 public: std::path::PathBuf,
520 },
521}
522
523#[derive(Clone, Debug, Eq, PartialEq)]
526pub struct SigningKey(dalek::ed::SigningKey);
527
528impl SigningKey {
529 fn public_key(&self) -> &PublicKey {
530 self.0.as_ref().as_bytes().into()
531 }
532
533 pub fn from_seed(seed: Seed) -> Self {
541 Self(dalek::ed::SigningKey::from_bytes(seed.as_ref()))
542 }
543
544 #[cfg(any(test, all(feature = "test", feature = "alloc")))]
545 pub fn mock(id: usize) -> Self {
546 Self::from_seed(Seed::mock(id))
547 }
548
549 pub fn to_keypair_bytes(&self) -> [u8; dalek::ed::KEYPAIR_LENGTH] {
551 self.0.to_keypair_bytes()
552 }
553
554 pub fn as_bytes(&self) -> &[u8; dalek::ed::SECRET_KEY_LENGTH] {
557 self.0.as_bytes()
558 }
559
560 #[cfg(all(feature = "ssh", feature = "std"))]
562 pub fn load(
563 keystore: &ssh::Keystore,
564 passphrase: Option<ssh::Passphrase>,
565 ) -> Result<Self, LoadError> {
566 let secret = keystore
567 .secret_key(passphrase)
568 .map_err(|e| {
569 if e.is_crypto_err() {
570 LoadError::InvalidPassphrase
571 } else {
572 e.into()
573 }
574 })?
575 .ok_or_else(|| LoadError::NotFound(keystore.secret_key_path().to_path_buf()))?;
576
577 let Some(public_path) = keystore.public_key_path() else {
578 return Ok(secret);
581 };
582
583 let public = keystore
584 .public_key()?
585 .ok_or_else(|| LoadError::NotFound(public_path.to_path_buf()))?;
586
587 if secret.public_key() != &public {
588 return Err(LoadError::KeyMismatch {
589 secret: keystore.secret_key_path().to_path_buf(),
590 public: public_path.to_path_buf(),
591 });
592 }
593
594 Ok(secret)
595 }
596
597 #[cfg(feature = "diffie-hellman")]
599 pub fn diffie_hellman(&self, their_public: &VerifyingKey) -> Option<SharedSecret> {
600 let scalar = self.0.to_scalar();
601
602 dalek::curve::edwards::CompressedEdwardsY(their_public.to_bytes())
603 .decompress()
604 .map(|point| (scalar * point).compress().to_bytes())
605 }
606}
607
608impl AsRef<PublicKey> for SigningKey {
609 fn as_ref(&self) -> &PublicKey {
610 self.public_key()
611 }
612}
613
614impl PartialOrd for SigningKey {
615 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
616 Some(self.cmp(other))
617 }
618}
619
620impl Ord for SigningKey {
621 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
622 self.0.as_bytes().cmp(other.0.as_bytes())
623 }
624}
625
626impl TryFrom<[u8; dalek::ed::KEYPAIR_LENGTH]> for SigningKey {
627 type Error = signature::Error;
628
629 fn try_from(bytes: [u8; dalek::ed::KEYPAIR_LENGTH]) -> Result<Self, Self::Error> {
630 dalek::ed::SigningKey::from_keypair_bytes(&bytes).map(Self)
631 }
632}
633
634impl From<dalek::ed::SigningKey> for SigningKey {
635 fn from(other: dalek::ed::SigningKey) -> Self {
636 Self(other)
637 }
638}
639
640impl From<SigningKey> for dalek::ed::SigningKey {
641 fn from(other: SigningKey) -> Self {
642 other.0
643 }
644}
645
646impl signature::Signer<Signature> for SigningKey {
647 fn try_sign(&self, msg: &[u8]) -> Result<Signature, signature::Error> {
648 self.0.try_sign(msg)
649 }
650}
651
652impl signature::Keypair for SigningKey {
653 type VerifyingKey = VerifyingKey;
654
655 fn verifying_key(&self) -> Self::VerifyingKey {
656 VerifyingKey(self.0.verifying_key())
657 }
658}
659
660#[cfg(all(feature = "cyphernet", feature = "alloc"))]
661impl cyphernet::EcSk for SigningKey {
662 type Pk = VerifyingKey;
663
664 fn generate_keypair() -> (Self, Self::Pk)
665 where
666 Self: Sized,
667 {
668 let signing_key = dalek::ed::SigningKey::from_bytes(&[154; 32]);
669 let verifying_key = signature::Keypair::verifying_key(&signing_key);
670 (SigningKey(signing_key), VerifyingKey(verifying_key))
671 }
672
673 fn to_pk(&self) -> Result<Self::Pk, cyphernet::EcSkInvalid> {
674 use signature::Keypair as _;
675
676 Ok(self.verifying_key())
677 }
678}
679
680#[cfg(all(feature = "cyphernet", feature = "diffie-hellman"))]
681impl cyphernet::Ecdh for SigningKey {
682 type SharedSecret = SharedSecret;
683
684 fn ecdh(&self, pk: &Self::Pk) -> Result<Self::SharedSecret, cyphernet::EcdhError> {
685 self.diffie_hellman(pk)
686 .ok_or(cyphernet::EcdhError::InvalidPk(
687 cyphernet::EcPkInvalid::default(),
688 ))
689 }
690}
691
692#[cfg(feature = "qcheck")]
693impl qcheck::Arbitrary for SigningKey {
694 fn arbitrary(g: &mut qcheck::Gen) -> Self {
695 SigningKey::mock(usize::arbitrary(g))
696 }
697}
698
699#[cfg(all(feature = "alloc", feature = "multibase", feature = "cyphernet"))]
700impl alloc::str::FromStr for VerifyingKey {
701 type Err = PublicKeyError;
702
703 fn from_str(s: &str) -> Result<Self, Self::Err> {
704 let pk = PublicKey::from_str(s)?;
705 dalek::ed::VerifyingKey::from_bytes(&pk.0)
706 .map(Self)
707 .map_err(PublicKeyError::Invalid)
708 }
709}
710
711#[cfg(all(feature = "alloc", feature = "multibase"))]
712impl TryFrom<String> for PublicKey {
713 type Error = PublicKeyError;
714
715 fn try_from(value: String) -> Result<Self, Self::Error> {
716 use alloc::str::FromStr as _;
717
718 Self::from_str(&value)
719 }
720}
721
722#[cfg(all(feature = "alloc", feature = "multibase", feature = "cyphernet"))]
723impl TryFrom<String> for VerifyingKey {
724 type Error = PublicKeyError;
725
726 fn try_from(value: String) -> Result<Self, Self::Error> {
727 use alloc::str::FromStr as _;
728
729 Self::from_str(&value)
730 }
731}
732
733#[cfg(feature = "qcheck")]
734impl qcheck::Arbitrary for PublicKey {
735 fn arbitrary(g: &mut qcheck::Gen) -> Self {
736 *SigningKey::from_seed(Seed::arbitrary(g)).public_key()
737 }
738}
739
740#[derive(Debug, Clone, PartialEq, Eq)]
743pub struct ExtendedSignature<PublicKey = crate::PublicKey, Signature = crate::Signature> {
744 key: PublicKey,
745 sig: Signature,
746}
747
748impl ExtendedSignature {
749 pub fn try_sign(signer: &impl Signer, payload: &[u8]) -> Result<Self, signature::Error> {
750 Ok(Self {
751 key: *signer.public_key(),
752 sig: signer.try_sign(payload)?,
753 })
754 }
755}
756
757impl<VerifyingKey, Signature> ExtendedSignature<VerifyingKey, Signature>
758where
759 VerifyingKey: signature::Verifier<Signature>,
760{
761 pub fn verify(&self, msg: &[u8]) -> Result<(), signature::Error> {
763 self.key.verify(msg, &self.sig)
764 }
765}
766
767impl<VerifyingKey, Signature> ExtendedSignature<VerifyingKey, Signature> {
768 pub fn new(key: VerifyingKey, sig: Signature) -> Self {
770 Self { key, sig }
771 }
772
773 pub fn key(&self) -> &VerifyingKey {
774 &self.key
775 }
776
777 pub fn sig(&self) -> &Signature {
778 &self.sig
779 }
780
781 pub fn into_pair(self) -> (VerifyingKey, Signature) {
782 (self.key, self.sig)
783 }
784}
785
786#[cfg(test)]
787mod tests {
788 use super::*;
789
790 use qcheck_macros::quickcheck;
791
792 use crate::SigningKey;
793
794 const DID_KEY_SAMPLE: &str = "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK";
796
797 #[cfg(feature = "diffie-hellman")]
798 #[quickcheck]
799 fn diffie_hellman(sk_a: SigningKey, sk_b: SigningKey) {
800 use signature::Keypair as _;
801
802 let output_a = sk_b.diffie_hellman(&sk_a.verifying_key()).unwrap();
803 let output_b = sk_a.diffie_hellman(&sk_b.verifying_key()).unwrap();
804
805 assert_eq!(output_a, output_b);
806 }
807
808 #[cfg(feature = "alloc")]
809 #[quickcheck]
810 fn prop_encode_decode(input: PublicKey) {
811 use alloc::str::FromStr as _;
812
813 let encoded = input.to_string();
814 let decoded = PublicKey::from_str(&encoded).unwrap();
815
816 assert_eq!(input, decoded);
817 }
818
819 #[cfg(feature = "alloc")]
820 #[test]
821 fn did_key_sample() {
822 use alloc::str::FromStr as _;
823
824 let key = PublicKey::from_str(DID_KEY_SAMPLE).unwrap();
825
826 assert_eq!(key.to_string(), DID_KEY_SAMPLE);
827 }
828
829 #[cfg(feature = "std")]
830 #[quickcheck]
831 fn prop_key_equality(a: PublicKey, b: PublicKey) {
832 if a == b {
833 return;
834 }
835
836 let mut hm = std::collections::HashSet::new();
837
838 assert!(hm.insert(a));
839 assert!(hm.insert(b));
840 assert!(!hm.insert(a));
841 assert!(!hm.insert(b));
842 }
843
844 #[cfg(feature = "diffie-hellman")]
845 #[test]
846 fn diffie_hellman_fixture() {
847 let sk_a: [u8; 32] = [
848 92, 136, 18, 88, 112, 205, 201, 68, 109, 197, 130, 211, 179, 138, 197, 113, 120, 55,
849 104, 139, 208, 184, 178, 157, 120, 11, 60, 13, 91, 30, 213, 38,
850 ];
851 let sk_b: [u8; 32] = [
852 202, 152, 225, 201, 169, 81, 217, 16, 235, 104, 91, 252, 52, 113, 81, 190, 68, 250, 86,
853 21, 202, 228, 123, 193, 140, 252, 63, 72, 5, 137, 36, 245,
854 ];
855
856 let kp_a = dalek::ed::SigningKey::from_bytes(&sk_a);
857 let kp_b = dalek::ed::SigningKey::from_bytes(&sk_b);
858
859 let output_a = SigningKey::from(kp_b.clone())
860 .diffie_hellman(&kp_a.verifying_key().into())
861 .unwrap();
862 let output_b = SigningKey::from(kp_a)
863 .diffie_hellman(&kp_b.verifying_key().into())
864 .unwrap();
865
866 assert_eq!(output_a, output_b);
867
868 assert_eq!(
869 output_a,
870 [
871 159, 131, 169, 27, 132, 202, 47, 250, 112, 247, 176, 222, 213, 220, 147, 216, 53,
872 7, 33, 232, 232, 77, 254, 105, 125, 237, 61, 243, 209, 172, 93, 100
873 ]
874 )
875 }
876}