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