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
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, 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(pub ed25519::PublicKey);
158
159impl signature::Verifier<Signature> for PublicKey {
160 fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), signature::Error> {
161 self.0
162 .verify(msg, signature)
163 .map_err(signature::Error::from_source)
164 }
165}
166
167#[cfg(feature = "cyphernet")]
168impl cyphernet::display::MultiDisplay<cyphernet::display::Encoding> for PublicKey {
169 type Display = String;
170
171 fn display_fmt(&self, _: &cyphernet::display::Encoding) -> Self::Display {
172 self.to_string()
173 }
174}
175
176#[cfg(feature = "ssh")]
177impl From<PublicKey> for ssh_key::PublicKey {
178 fn from(key: PublicKey) -> Self {
179 ssh_key::PublicKey::from(ssh_key::public::Ed25519PublicKey(**key))
180 }
181}
182
183#[cfg(feature = "cyphernet")]
184impl cyphernet::EcPk for PublicKey {
185 const COMPRESSED_LEN: usize = 32;
186 const CURVE_NAME: &'static str = "Edwards25519";
187
188 type Compressed = [u8; 32];
189
190 fn base_point() -> Self {
191 unimplemented!()
192 }
193
194 fn to_pk_compressed(&self) -> Self::Compressed {
195 *self.0.deref()
196 }
197
198 fn from_pk_compressed(pk: Self::Compressed) -> Result<Self, cyphernet::EcPkInvalid> {
199 Ok(PublicKey::from(pk))
200 }
201
202 fn from_pk_compressed_slice(slice: &[u8]) -> Result<Self, cyphernet::EcPkInvalid> {
203 ed25519::PublicKey::from_slice(slice)
204 .map_err(|_| cyphernet::EcPkInvalid::default())
205 .map(Self)
206 }
207}
208
209#[derive(Clone, Debug, Eq, PartialEq, Hash)]
211pub struct SecretKey(ed25519::SecretKey);
212
213impl SecretKey {
214 pub fn ecdh(&self, pk: &PublicKey) -> Result<[u8; 32], ed25519::Error> {
216 let scalar = self.seed().scalar();
217 let ge = edwards25519::GeP3::from_bytes_vartime(pk).ok_or(Error::InvalidPublicKey)?;
218
219 Ok(edwards25519::ge_scalarmult(&scalar, &ge).to_bytes())
220 }
221}
222
223impl PartialOrd for SecretKey {
224 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
225 Some(self.cmp(other))
226 }
227}
228
229impl Ord for SecretKey {
230 fn cmp(&self, other: &Self) -> Ordering {
231 self.0.cmp(&other.0)
232 }
233}
234
235impl zeroize::Zeroize for SecretKey {
236 fn zeroize(&mut self) {
237 self.0.zeroize();
238 }
239}
240
241impl TryFrom<&[u8]> for SecretKey {
242 type Error = ed25519::Error;
243
244 fn try_from(bytes: &[u8]) -> Result<Self, ed25519::Error> {
245 ed25519::SecretKey::from_slice(bytes).map(Self)
246 }
247}
248
249impl AsRef<[u8]> for SecretKey {
250 fn as_ref(&self) -> &[u8] {
251 &*self.0
252 }
253}
254
255impl From<[u8; 64]> for SecretKey {
256 fn from(bytes: [u8; 64]) -> Self {
257 Self(ed25519::SecretKey::new(bytes))
258 }
259}
260
261impl From<ed25519::SecretKey> for SecretKey {
262 fn from(other: ed25519::SecretKey) -> Self {
263 Self(other)
264 }
265}
266
267impl From<SecretKey> for ed25519::SecretKey {
268 fn from(other: SecretKey) -> Self {
269 other.0
270 }
271}
272
273impl Deref for SecretKey {
274 type Target = ed25519::SecretKey;
275
276 fn deref(&self) -> &Self::Target {
277 &self.0
278 }
279}
280
281#[derive(Error, Debug)]
282#[non_exhaustive]
283pub enum PublicKeyError {
284 #[error("invalid length {0}")]
285 InvalidLength(usize),
286 #[error("invalid multibase string: {0}")]
287 Multibase(#[from] multibase::Error),
288 #[error("invalid multicodec prefix, expected {0:?}")]
289 Multicodec([u8; 2]),
290 #[error("invalid key: {0}")]
291 InvalidKey(#[from] ed25519::Error),
292}
293
294impl PartialOrd for PublicKey {
295 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
296 Some(self.cmp(other))
297 }
298}
299
300impl Ord for PublicKey {
301 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
302 self.0.as_ref().cmp(other.as_ref())
303 }
304}
305
306impl fmt::Display for PublicKey {
307 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
308 write!(f, "{}", self.to_human())
309 }
310}
311
312impl From<PublicKey> for String {
313 fn from(other: PublicKey) -> Self {
314 other.to_human()
315 }
316}
317
318impl fmt::Debug for PublicKey {
319 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
320 write!(f, "PublicKey({self})")
321 }
322}
323
324impl From<ed25519::PublicKey> for PublicKey {
325 fn from(other: ed25519::PublicKey) -> Self {
326 Self(other)
327 }
328}
329
330impl From<[u8; 32]> for PublicKey {
331 fn from(other: [u8; 32]) -> Self {
332 Self(ed25519::PublicKey::new(other))
333 }
334}
335
336impl TryFrom<&[u8]> for PublicKey {
337 type Error = ed25519::Error;
338
339 fn try_from(other: &[u8]) -> Result<Self, Self::Error> {
340 ed25519::PublicKey::from_slice(other).map(Self)
341 }
342}
343
344impl PublicKey {
345 pub const MULTICODEC_TYPE: [u8; 2] = [0xED, 0x1];
347
348 pub fn to_human(&self) -> String {
353 let mut buf = [0; 2 + ed25519::PublicKey::BYTES];
354 buf[..2].copy_from_slice(&Self::MULTICODEC_TYPE);
355 buf[2..].copy_from_slice(self.0.deref());
356
357 multibase::encode(multibase::Base::Base58Btc, buf)
358 }
359
360 #[cfg(feature = "git-ref-format-core")]
361 pub fn to_namespace(&self) -> git_ref_format_core::RefString {
362 use git_ref_format_core::name::{Component, NAMESPACES, REFS};
363 REFS.to_owned().and(NAMESPACES).and(Component::from(self))
364 }
365
366 #[cfg(feature = "git-ref-format-core")]
367 pub fn to_component(&self) -> git_ref_format_core::Component<'_> {
368 git_ref_format_core::Component::from(self)
369 }
370
371 #[cfg(feature = "git-ref-format-core")]
372 pub fn from_namespaced(
373 refstr: &git_ref_format_core::Namespaced,
374 ) -> Result<Self, PublicKeyError> {
375 let name = refstr.namespace().into_inner();
376
377 Self::from_str(name.deref().as_str())
378 }
379}
380
381impl FromStr for PublicKey {
382 type Err = PublicKeyError;
383
384 fn from_str(s: &str) -> Result<Self, Self::Err> {
385 let (_, bytes) = multibase::decode(s)?;
386
387 if let Some(bytes) = bytes.strip_prefix(&Self::MULTICODEC_TYPE) {
388 let key = ed25519::PublicKey::from_slice(bytes)?;
389
390 Ok(Self(key))
391 } else {
392 Err(PublicKeyError::Multicodec(Self::MULTICODEC_TYPE))
393 }
394 }
395}
396
397impl TryFrom<String> for PublicKey {
398 type Error = PublicKeyError;
399
400 fn try_from(value: String) -> Result<Self, Self::Error> {
401 Self::from_str(&value)
402 }
403}
404
405impl Deref for PublicKey {
406 type Target = ed25519::PublicKey;
407
408 fn deref(&self) -> &Self::Target {
409 &self.0
410 }
411}
412
413#[cfg(feature = "git-ref-format-core")]
414impl From<&PublicKey> for git_ref_format_core::Component<'_> {
415 fn from(id: &PublicKey) -> Self {
416 use git_ref_format_core::{Component, RefString};
417 let refstr =
418 RefString::try_from(id.to_string()).expect("encoded public keys are valid ref strings");
419 Component::from_refstr(refstr).expect("encoded public keys are valid refname components")
420 }
421}
422
423#[cfg(feature = "sqlite")]
424impl From<&PublicKey> for sqlite::Value {
425 fn from(pk: &PublicKey) -> Self {
426 sqlite::Value::String(pk.to_human())
427 }
428}
429
430#[cfg(feature = "sqlite")]
431impl TryFrom<&sqlite::Value> for PublicKey {
432 type Error = sqlite::Error;
433
434 fn try_from(value: &sqlite::Value) -> Result<Self, Self::Error> {
435 match value {
436 sqlite::Value::String(s) => Self::from_str(s).map_err(|e| sqlite::Error {
437 code: None,
438 message: Some(e.to_string()),
439 }),
440 _ => Err(sqlite::Error {
441 code: None,
442 message: Some("sql: invalid type for public key".to_owned()),
443 }),
444 }
445 }
446}
447
448#[cfg(feature = "sqlite")]
449impl sqlite::BindableWithIndex for &PublicKey {
450 fn bind<I: sqlite::ParameterIndex>(
451 self,
452 stmt: &mut sqlite::Statement<'_>,
453 i: I,
454 ) -> sqlite::Result<()> {
455 sqlite::Value::from(self).bind(stmt, i)
456 }
457}
458
459#[cfg(feature = "sqlite")]
460impl From<&Signature> for sqlite::Value {
461 fn from(sig: &Signature) -> Self {
462 sqlite::Value::Binary(sig.to_vec())
463 }
464}
465
466#[cfg(feature = "sqlite")]
467impl TryFrom<&sqlite::Value> for Signature {
468 type Error = sqlite::Error;
469
470 fn try_from(value: &sqlite::Value) -> Result<Self, Self::Error> {
471 match value {
472 sqlite::Value::Binary(s) => ed25519::Signature::from_slice(s)
473 .map_err(|e| sqlite::Error {
474 code: None,
475 message: Some(e.to_string()),
476 })
477 .map(Self),
478 _ => Err(sqlite::Error {
479 code: None,
480 message: Some("sql: invalid column type for signature".to_owned()),
481 }),
482 }
483 }
484}
485
486#[cfg(feature = "sqlite")]
487impl sqlite::BindableWithIndex for &Signature {
488 fn bind<I: sqlite::ParameterIndex>(
489 self,
490 stmt: &mut sqlite::Statement<'_>,
491 i: I,
492 ) -> sqlite::Result<()> {
493 sqlite::Value::from(self).bind(stmt, i)
494 }
495}
496
497#[cfg(test)]
498mod tests {
499 use super::KeyPair;
500 use crate::{PublicKey, SecretKey};
501 use qcheck_macros::quickcheck;
502 use std::str::FromStr;
503
504 #[test]
505 fn test_e25519_dh() {
506 let kp_a = KeyPair::generate();
507 let kp_b = KeyPair::generate();
508
509 let output_a = SecretKey::from(kp_b.sk).ecdh(&kp_a.pk.into()).unwrap();
510 let output_b = SecretKey::from(kp_a.sk).ecdh(&kp_b.pk.into()).unwrap();
511
512 assert_eq!(output_a, output_b);
513 }
514
515 #[quickcheck]
516 fn prop_encode_decode(input: PublicKey) {
517 let encoded = input.to_string();
518 let decoded = PublicKey::from_str(&encoded).unwrap();
519
520 assert_eq!(input, decoded);
521 }
522
523 #[test]
524 fn test_encode_decode() {
525 let input = "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK";
526 let key = PublicKey::from_str(input).unwrap();
527
528 assert_eq!(key.to_string(), input);
529 }
530
531 #[quickcheck]
532 fn prop_key_equality(a: PublicKey, b: PublicKey) {
533 use std::collections::HashSet;
534
535 assert_ne!(a, b);
536
537 let mut hm = HashSet::new();
538
539 assert!(hm.insert(a));
540 assert!(hm.insert(b));
541 assert!(!hm.insert(a));
542 assert!(!hm.insert(b));
543 }
544}