1use std::cmp::Ordering;
2use std::sync::Arc;
3use std::{fmt, ops::Deref, str::FromStr};
4
5use ec25519 as ed25519;
6use serde::{Deserialize, Serialize};
7use thiserror::Error;
8
9pub use ed25519::{Error, KeyPair, Seed, edwards25519};
10
11pub extern crate signature;
12
13#[cfg(feature = "ssh")]
14pub mod ssh;
15#[cfg(any(test, feature = "test"))]
16pub mod test;
17
18pub type SharedSecret = [u8; 32];
20
21#[derive(Debug, Clone, Error)]
23#[error(transparent)]
24#[non_exhaustive]
25pub struct SignerError {
26 #[from]
27 source: Arc<dyn std::error::Error + Send + Sync>,
28}
29
30impl SignerError {
31 pub fn new(source: impl std::error::Error + Send + Sync + 'static) -> Self {
32 Self {
33 source: Arc::new(source),
34 }
35 }
36}
37
38pub trait Signer: Send + signature::Signer<Signature> {
39 fn public_key(&self) -> &PublicKey;
41}
42
43impl<S> Signer for S
44where
45 S: Send,
46 S: signature::Signer<Signature>,
47 S: signature::KeypairRef<VerifyingKey = PublicKey>,
48{
49 fn public_key(&self) -> &PublicKey {
50 self.as_ref()
51 }
52}
53
54#[derive(PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)]
56#[serde(into = "String", try_from = "String")]
57pub struct Signature(pub ed25519::Signature);
58
59impl AsRef<[u8]> for Signature {
60 fn as_ref(&self) -> &[u8] {
61 self.0.as_ref()
62 }
63}
64
65impl fmt::Display for Signature {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 let base = multibase::Base::Base58Btc;
68 write!(f, "{}", multibase::encode(base, self.deref()))
69 }
70}
71
72impl fmt::Debug for Signature {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 write!(f, "Signature({self})")
75 }
76}
77
78#[derive(Error, Debug)]
79#[non_exhaustive]
80pub enum SignatureError {
81 #[error("invalid multibase string: {0}")]
82 Multibase(#[from] multibase::Error),
83 #[error("invalid signature: {0}")]
84 Invalid(#[from] ed25519::Error),
85}
86
87impl From<ed25519::Signature> for Signature {
88 fn from(other: ed25519::Signature) -> Self {
89 Self(other)
90 }
91}
92
93impl FromStr for Signature {
94 type Err = SignatureError;
95
96 fn from_str(s: &str) -> Result<Self, Self::Err> {
97 let (_, bytes) = multibase::decode(s)?;
98 let sig = ed25519::Signature::from_slice(bytes.as_slice())?;
99
100 Ok(Self(sig))
101 }
102}
103
104impl Deref for Signature {
105 type Target = ed25519::Signature;
106
107 fn deref(&self) -> &Self::Target {
108 &self.0
109 }
110}
111
112impl From<[u8; 64]> for Signature {
113 fn from(bytes: [u8; 64]) -> Self {
114 Self(ed25519::Signature::new(bytes))
115 }
116}
117
118impl TryFrom<&[u8]> for Signature {
119 type Error = ed25519::Error;
120
121 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
122 ed25519::Signature::from_slice(bytes).map(Self)
123 }
124}
125
126impl From<Signature> for String {
127 fn from(s: Signature) -> Self {
128 s.to_string()
129 }
130}
131
132impl TryFrom<String> for Signature {
133 type Error = SignatureError;
134
135 fn try_from(s: String) -> Result<Self, Self::Error> {
136 Self::from_str(&s)
137 }
138}
139
140#[derive(Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
142#[serde(into = "String", try_from = "String")]
143#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
144#[cfg_attr(
145 feature = "schemars",
146 schemars(
147 title = "Ed25519",
148 description = "An Ed25519 public key in multibase encoding.",
149 extend("examples" = [
150 "z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7",
151 "z6MkvUJtYD9dHDJfpevWRT98mzDDpdAtmUjwyDSkyqksUr7C",
152 "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
153 "z6MkkfM3tPXNPrPevKr3uSiQtHPuwnNhu2yUVjgd2jXVsVz5",
154 ]),
155 ),
156)]
157pub struct PublicKey(amplify::Bytes32);
158
159impl PublicKey {
160 pub fn verify(
162 &self,
163 payload: impl AsRef<[u8]>,
164 signature: &ed25519::Signature,
165 ) -> Result<(), ed25519::Error> {
166 ed25519::PublicKey::new(self.0.to_byte_array()).verify(payload, signature)
167 }
168
169 #[inline]
171 pub fn to_byte_array(&self) -> [u8; 32] {
172 self.0.to_byte_array()
173 }
174}
175
176impl signature::Verifier<Signature> for PublicKey {
177 fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), signature::Error> {
178 self.verify(msg, signature)
179 .map_err(signature::Error::from_source)
180 }
181}
182
183#[cfg(feature = "cyphernet")]
184impl cyphernet::display::MultiDisplay<cyphernet::display::Encoding> for PublicKey {
185 type Display = String;
186
187 fn display_fmt(&self, _: &cyphernet::display::Encoding) -> Self::Display {
188 self.to_string()
189 }
190}
191
192#[cfg(feature = "ssh")]
193impl From<PublicKey> for ssh_key::PublicKey {
194 fn from(key: PublicKey) -> Self {
195 ssh_key::PublicKey::from(ssh_key::public::Ed25519PublicKey(key.to_byte_array()))
196 }
197}
198
199#[cfg(feature = "cyphernet")]
200impl cyphernet::EcPk for PublicKey {
201 const COMPRESSED_LEN: usize = 32;
202 const CURVE_NAME: &'static str = "Edwards25519";
203
204 type Compressed = amplify::Bytes32;
205
206 fn base_point() -> Self {
207 unimplemented!()
208 }
209
210 fn to_pk_compressed(&self) -> Self::Compressed {
211 amplify::Bytes32::from_byte_array(self.to_byte_array())
212 }
213
214 fn from_pk_compressed(pk: Self::Compressed) -> Result<Self, cyphernet::EcPkInvalid> {
215 Ok(PublicKey::from(pk.to_byte_array()))
216 }
217
218 fn from_pk_compressed_slice(slice: &[u8]) -> Result<Self, cyphernet::EcPkInvalid> {
219 ed25519::PublicKey::from_slice(slice)
220 .map_err(|_| cyphernet::EcPkInvalid::default())
221 .map(Self::from)
222 }
223}
224
225#[derive(Clone, Debug, Eq, PartialEq, Hash)]
227pub struct SecretKey(ed25519::SecretKey);
228
229impl SecretKey {
230 pub fn ecdh(&self, pk: &PublicKey) -> Result<[u8; 32], ed25519::Error> {
232 let scalar = self.seed().scalar();
233 let ge = edwards25519::GeP3::from_bytes_vartime(&pk.to_byte_array())
234 .ok_or(Error::InvalidPublicKey)?;
235
236 Ok(edwards25519::ge_scalarmult(&scalar, &ge).to_bytes())
237 }
238}
239
240impl PartialOrd for SecretKey {
241 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
242 Some(self.cmp(other))
243 }
244}
245
246impl Ord for SecretKey {
247 fn cmp(&self, other: &Self) -> Ordering {
248 self.0.cmp(&other.0)
249 }
250}
251
252impl zeroize::Zeroize for SecretKey {
253 fn zeroize(&mut self) {
254 self.0.zeroize();
255 }
256}
257
258impl TryFrom<&[u8]> for SecretKey {
259 type Error = ed25519::Error;
260
261 fn try_from(bytes: &[u8]) -> Result<Self, ed25519::Error> {
262 ed25519::SecretKey::from_slice(bytes).map(Self)
263 }
264}
265
266impl AsRef<[u8]> for SecretKey {
267 fn as_ref(&self) -> &[u8] {
268 &*self.0
269 }
270}
271
272impl From<[u8; 64]> for SecretKey {
273 fn from(bytes: [u8; 64]) -> Self {
274 Self(ed25519::SecretKey::new(bytes))
275 }
276}
277
278impl From<ed25519::SecretKey> for SecretKey {
279 fn from(other: ed25519::SecretKey) -> Self {
280 Self(other)
281 }
282}
283
284impl From<SecretKey> for ed25519::SecretKey {
285 fn from(other: SecretKey) -> Self {
286 other.0
287 }
288}
289
290impl Deref for SecretKey {
291 type Target = ed25519::SecretKey;
292
293 fn deref(&self) -> &Self::Target {
294 &self.0
295 }
296}
297
298#[derive(Error, Debug)]
299#[non_exhaustive]
300pub enum PublicKeyError {
301 #[error("invalid length {0}")]
302 InvalidLength(usize),
303 #[error("invalid multibase string: {0}")]
304 Multibase(#[from] multibase::Error),
305 #[error("invalid multicodec prefix, expected {0:?}")]
306 Multicodec([u8; 2]),
307 #[error("invalid key: {0}")]
308 InvalidKey(#[from] ed25519::Error),
309}
310
311impl fmt::Display for PublicKey {
312 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
313 write!(f, "{}", self.to_human())
314 }
315}
316
317impl From<PublicKey> for String {
318 fn from(other: PublicKey) -> Self {
319 other.to_human()
320 }
321}
322
323impl fmt::Debug for PublicKey {
324 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
325 write!(f, "PublicKey({self})")
326 }
327}
328
329impl From<ed25519::PublicKey> for PublicKey {
330 fn from(other: ed25519::PublicKey) -> Self {
331 Self(amplify::Bytes32::from_byte_array(*other.deref()))
332 }
333}
334
335impl From<PublicKey> for ed25519::PublicKey {
336 fn from(val: PublicKey) -> Self {
337 ed25519::PublicKey::new(val.to_byte_array())
338 }
339}
340
341impl From<[u8; 32]> for PublicKey {
342 fn from(other: [u8; 32]) -> Self {
343 Self(amplify::Bytes32::from_byte_array(other))
344 }
345}
346
347impl TryFrom<&[u8]> for PublicKey {
348 type Error = ed25519::Error;
349
350 fn try_from(other: &[u8]) -> Result<Self, Self::Error> {
351 ed25519::PublicKey::from_slice(other).map(Self::from)
352 }
353}
354
355impl PublicKey {
356 pub const MULTICODEC_TYPE: [u8; 2] = [0xED, 0x1];
358
359 pub fn to_human(&self) -> String {
364 let mut buf = [0; 2 + ed25519::PublicKey::BYTES];
365 buf[..2].copy_from_slice(&Self::MULTICODEC_TYPE);
366 buf[2..].copy_from_slice(self.to_byte_array().as_slice());
367
368 multibase::encode(multibase::Base::Base58Btc, buf)
369 }
370
371 #[cfg(feature = "git-ref-format-core")]
372 pub fn to_namespace(&self) -> git_ref_format_core::RefString {
373 use git_ref_format_core::name::{Component, NAMESPACES, REFS};
374 REFS.to_owned().and(NAMESPACES).and(Component::from(self))
375 }
376
377 #[cfg(feature = "git-ref-format-core")]
378 pub fn to_component(&self) -> git_ref_format_core::Component<'_> {
379 git_ref_format_core::Component::from(self)
380 }
381
382 #[cfg(feature = "git-ref-format-core")]
383 pub fn from_namespaced(
384 refstr: &git_ref_format_core::Namespaced,
385 ) -> Result<Self, PublicKeyError> {
386 let name = refstr.namespace().into_inner();
387
388 Self::from_str(name.deref().as_str())
389 }
390}
391
392impl FromStr for PublicKey {
393 type Err = PublicKeyError;
394
395 fn from_str(s: &str) -> Result<Self, Self::Err> {
396 let (_, bytes) = multibase::decode(s)?;
397
398 if let Some(bytes) = bytes.strip_prefix(&Self::MULTICODEC_TYPE) {
399 let key = ed25519::PublicKey::from_slice(bytes)?;
400
401 Ok(key.into())
402 } else {
403 Err(PublicKeyError::Multicodec(Self::MULTICODEC_TYPE))
404 }
405 }
406}
407
408impl TryFrom<String> for PublicKey {
409 type Error = PublicKeyError;
410
411 fn try_from(value: String) -> Result<Self, Self::Error> {
412 Self::from_str(&value)
413 }
414}
415
416#[cfg(feature = "git-ref-format-core")]
417impl From<&PublicKey> for git_ref_format_core::Component<'_> {
418 fn from(id: &PublicKey) -> Self {
419 use git_ref_format_core::{Component, RefString};
420 let refstr =
421 RefString::try_from(id.to_string()).expect("encoded public keys are valid ref strings");
422 Component::from_refstr(refstr).expect("encoded public keys are valid refname components")
423 }
424}
425
426#[cfg(feature = "sqlite")]
427impl From<&PublicKey> for sqlite::Value {
428 fn from(pk: &PublicKey) -> Self {
429 sqlite::Value::String(pk.to_human())
430 }
431}
432
433#[cfg(feature = "sqlite")]
434impl TryFrom<&sqlite::Value> for PublicKey {
435 type Error = sqlite::Error;
436
437 fn try_from(value: &sqlite::Value) -> Result<Self, Self::Error> {
438 match value {
439 sqlite::Value::String(s) => Self::from_str(s).map_err(|e| sqlite::Error {
440 code: None,
441 message: Some(e.to_string()),
442 }),
443 _ => Err(sqlite::Error {
444 code: None,
445 message: Some("sql: invalid type for public key".to_owned()),
446 }),
447 }
448 }
449}
450
451#[cfg(feature = "sqlite")]
452impl sqlite::BindableWithIndex for &PublicKey {
453 fn bind<I: sqlite::ParameterIndex>(
454 self,
455 stmt: &mut sqlite::Statement<'_>,
456 i: I,
457 ) -> sqlite::Result<()> {
458 sqlite::Value::from(self).bind(stmt, i)
459 }
460}
461
462#[cfg(feature = "sqlite")]
463impl From<&Signature> for sqlite::Value {
464 fn from(sig: &Signature) -> Self {
465 sqlite::Value::Binary(sig.to_vec())
466 }
467}
468
469#[cfg(feature = "sqlite")]
470impl TryFrom<&sqlite::Value> for Signature {
471 type Error = sqlite::Error;
472
473 fn try_from(value: &sqlite::Value) -> Result<Self, Self::Error> {
474 match value {
475 sqlite::Value::Binary(s) => ed25519::Signature::from_slice(s)
476 .map_err(|e| sqlite::Error {
477 code: None,
478 message: Some(e.to_string()),
479 })
480 .map(Self),
481 _ => Err(sqlite::Error {
482 code: None,
483 message: Some("sql: invalid column type for signature".to_owned()),
484 }),
485 }
486 }
487}
488
489#[cfg(feature = "sqlite")]
490impl sqlite::BindableWithIndex for &Signature {
491 fn bind<I: sqlite::ParameterIndex>(
492 self,
493 stmt: &mut sqlite::Statement<'_>,
494 i: I,
495 ) -> sqlite::Result<()> {
496 sqlite::Value::from(self).bind(stmt, i)
497 }
498}
499
500#[cfg(test)]
501mod tests {
502 use super::KeyPair;
503 use crate::{PublicKey, SecretKey};
504 use qcheck_macros::quickcheck;
505 use std::str::FromStr;
506
507 #[test]
508 fn test_e25519_dh() {
509 let kp_a = KeyPair::generate();
510 let kp_b = KeyPair::generate();
511
512 let output_a = SecretKey::from(kp_b.sk).ecdh(&kp_a.pk.into()).unwrap();
513 let output_b = SecretKey::from(kp_a.sk).ecdh(&kp_b.pk.into()).unwrap();
514
515 assert_eq!(output_a, output_b);
516 }
517
518 #[quickcheck]
519 fn prop_encode_decode(input: PublicKey) {
520 let encoded = input.to_string();
521 let decoded = PublicKey::from_str(&encoded).unwrap();
522
523 assert_eq!(input, decoded);
524 }
525
526 #[test]
527 fn test_encode_decode() {
528 let input = "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK";
529 let key = PublicKey::from_str(input).unwrap();
530
531 assert_eq!(key.to_string(), input);
532 }
533
534 #[quickcheck]
535 fn prop_key_equality(a: PublicKey, b: PublicKey) {
536 use std::collections::HashSet;
537
538 assert_ne!(a, b);
539
540 let mut hm = HashSet::new();
541
542 assert!(hm.insert(a));
543 assert!(hm.insert(b));
544 assert!(!hm.insert(a));
545 assert!(!hm.insert(b));
546 }
547}