pkarr/
keys.rs

1//! Utility structs for Ed25519 keys.
2
3use ed25519_dalek::{
4    SecretKey, Signature, SignatureError, Signer, SigningKey, Verifier, VerifyingKey,
5};
6use std::{
7    fmt::{self, Debug, Display, Formatter},
8    hash::Hash,
9    str::FromStr,
10};
11
12use serde::{Deserialize, Serialize};
13
14#[derive(Clone, PartialEq, Eq)]
15/// Ed25519 keypair to sign dns [Packet](crate::SignedPacket)s.
16pub struct Keypair(pub(crate) SigningKey);
17
18impl Keypair {
19    /// Generates a new random `Keypair` using the operating system's CSPRNG.
20    pub fn random() -> Keypair {
21        let mut bytes = [0u8; 32];
22
23        getrandom::getrandom(&mut bytes).expect("getrandom failed");
24
25        let signing_key: SigningKey = SigningKey::from_bytes(&bytes);
26
27        Keypair(signing_key)
28    }
29
30    /// Creates a `Keypair` from a given `SecretKey`.
31    pub fn from_secret_key(secret_key: &SecretKey) -> Keypair {
32        Keypair(SigningKey::from_bytes(secret_key))
33    }
34
35    /// Signs a message with the private key of this `Keypair`.
36    pub fn sign(&self, message: &[u8]) -> Signature {
37        self.0.sign(message)
38    }
39
40    /// Verifies a message against a given signature using this `Keypair`.
41    pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> {
42        self.0.verify(message, signature)
43    }
44
45    /// Returns the secret part of this `Keypair`.
46    pub fn secret_key(&self) -> SecretKey {
47        self.0.to_bytes()
48    }
49
50    /// Returns the [PublicKey] of this `Keypair`.
51    pub fn public_key(&self) -> PublicKey {
52        PublicKey(self.0.verifying_key())
53    }
54
55    /// Converts the public key of this `Keypair` to a z-base32 encoded string.
56    pub fn to_z32(&self) -> String {
57        self.public_key().to_string()
58    }
59
60    /// Converts the public key of this `Keypair` to a URI string.
61    pub fn to_uri_string(&self) -> String {
62        self.public_key().to_uri_string()
63    }
64}
65
66/// Ed25519 public key to verify a signature over dns [Packet](crate::SignedPacket)s.
67///
68/// It can formatted to and parsed from a z-base32 string.
69#[derive(Clone, Eq, PartialEq, Hash)]
70pub struct PublicKey(pub(crate) VerifyingKey);
71
72impl PublicKey {
73    /// Format the public key as z-base32 string.
74    pub fn to_z32(&self) -> String {
75        self.to_string()
76    }
77
78    /// Format the public key as `pk:` URI string.
79    pub fn to_uri_string(&self) -> String {
80        format!("pk:{}", self)
81    }
82
83    /// Verify a signature over a message.
84    pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> {
85        self.0.verify(message, signature)
86    }
87
88    /// Return a reference to the underlying [VerifyingKey]
89    pub fn verifying_key(&self) -> &VerifyingKey {
90        &self.0
91    }
92
93    /// Return a the underlying [u8; 32] bytes.
94    pub fn to_bytes(&self) -> [u8; 32] {
95        self.0.to_bytes()
96    }
97
98    /// Return a reference to the underlying [u8; 32] bytes.
99    pub fn as_bytes(&self) -> &[u8; 32] {
100        self.0.as_bytes()
101    }
102}
103
104impl AsRef<Keypair> for Keypair {
105    fn as_ref(&self) -> &Keypair {
106        self
107    }
108}
109
110impl AsRef<PublicKey> for PublicKey {
111    fn as_ref(&self) -> &PublicKey {
112        self
113    }
114}
115
116impl TryFrom<&[u8]> for PublicKey {
117    type Error = PublicKeyError;
118
119    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
120        let bytes_32: &[u8; 32] = bytes
121            .try_into()
122            .map_err(|_| PublicKeyError::InvalidPublicKeyLength(bytes.len()))?;
123
124        Ok(Self(
125            VerifyingKey::from_bytes(bytes_32)
126                .map_err(|_| PublicKeyError::InvalidEd25519PublicKey)?,
127        ))
128    }
129}
130
131impl TryFrom<&[u8; 32]> for PublicKey {
132    type Error = PublicKeyError;
133
134    fn try_from(public: &[u8; 32]) -> Result<Self, Self::Error> {
135        Ok(Self(
136            VerifyingKey::from_bytes(public)
137                .map_err(|_| PublicKeyError::InvalidEd25519PublicKey)?,
138        ))
139    }
140}
141
142impl FromStr for PublicKey {
143    type Err = PublicKeyError;
144
145    /// Convert the TLD in a `&str` to a [PublicKey].
146    ///
147    /// # Examples
148    ///
149    /// - `o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy`
150    /// - `pk:o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy`
151    /// - `http://o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy`
152    /// - `https://o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy`
153    /// - `https://o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy/foo/bar`
154    /// - `https://foo.o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy.`
155    /// - `https://foo.o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy.#hash`
156    /// - `https://foo@bar.o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy.?q=v`
157    /// - `https://foo@bar.o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy.:8888?q=v`
158    /// - `https://yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no.o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy`
159    fn from_str(s: &str) -> Result<Self, Self::Err> {
160        let mut s = s;
161
162        if s.len() > 52 {
163            // Remove scheme
164            s = s.split_once(':').map(|tuple| tuple.1).unwrap_or(s);
165
166            if s.len() > 52 {
167                // Remove `//
168                s = s.strip_prefix("//").unwrap_or(s);
169
170                if s.len() > 52 {
171                    // Remove username
172                    s = s.split_once('@').map(|tuple| tuple.1).unwrap_or(s);
173
174                    if s.len() > 52 {
175                        // Remove port
176                        s = s.split_once(':').map(|tuple| tuple.0).unwrap_or(s);
177
178                        if s.len() > 52 {
179                            // Remove trailing path
180                            s = s.split_once('/').map(|tuple| tuple.0).unwrap_or(s);
181
182                            if s.len() > 52 {
183                                // Remove query
184                                s = s.split_once('?').map(|tuple| tuple.0).unwrap_or(s);
185
186                                if s.len() > 52 {
187                                    // Remove hash
188                                    s = s.split_once('#').map(|tuple| tuple.0).unwrap_or(s);
189
190                                    if s.len() > 52 {
191                                        if s.ends_with('.') {
192                                            // Remove trailing dot
193                                            s = s.trim_matches('.');
194                                        }
195
196                                        s = s.rsplit_once('.').map(|tuple| tuple.1).unwrap_or(s);
197                                    }
198                                }
199                            }
200                        }
201                    }
202                }
203            }
204        }
205
206        let bytes = if let Some(v) = base32::decode(base32::Alphabet::Z, s) {
207            Ok(v)
208        } else {
209            Err(PublicKeyError::InvalidPublicKeyEncoding)
210        }?;
211
212        bytes.as_slice().try_into()
213    }
214}
215
216impl TryFrom<&str> for PublicKey {
217    type Error = PublicKeyError;
218
219    fn try_from(s: &str) -> Result<PublicKey, PublicKeyError> {
220        PublicKey::from_str(s)
221    }
222}
223
224impl TryFrom<String> for PublicKey {
225    type Error = PublicKeyError;
226
227    fn try_from(s: String) -> Result<PublicKey, PublicKeyError> {
228        s.as_str().try_into()
229    }
230}
231
232impl TryFrom<&String> for PublicKey {
233    type Error = PublicKeyError;
234
235    fn try_from(s: &String) -> Result<PublicKey, PublicKeyError> {
236        s.try_into()
237    }
238}
239
240impl Display for PublicKey {
241    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
242        write!(
243            f,
244            "{}",
245            base32::encode(base32::Alphabet::Z, self.0.as_bytes())
246        )
247    }
248}
249
250impl Display for Keypair {
251    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
252        write!(f, "{}", self.public_key())
253    }
254}
255
256impl Debug for Keypair {
257    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258        write!(f, "Keypair({})", self.public_key())
259    }
260}
261
262impl Debug for PublicKey {
263    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264        write!(f, "PublicKey({})", self)
265    }
266}
267
268impl Serialize for PublicKey {
269    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
270    where
271        S: serde::Serializer,
272    {
273        let bytes = self.to_bytes();
274        bytes.serialize(serializer)
275    }
276}
277
278impl<'de> Deserialize<'de> for PublicKey {
279    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
280    where
281        D: serde::Deserializer<'de>,
282    {
283        let bytes: [u8; 32] = Deserialize::deserialize(deserializer)?;
284
285        (&bytes).try_into().map_err(serde::de::Error::custom)
286    }
287}
288
289#[derive(thiserror::Error, Debug, PartialEq, Eq)]
290/// Errors while trying to create a [PublicKey]
291pub enum PublicKeyError {
292    #[error("Invalid PublicKey length, expected 32 bytes but got: {0}")]
293    /// Invalid PublicKey length.
294    InvalidPublicKeyLength(usize),
295
296    #[error("Invalid Ed25519 publickey; Cannot decompress Edwards point")]
297    /// Cannot decompress Edwards point
298    InvalidEd25519PublicKey,
299
300    #[error("Invalid PublicKey encoding")]
301    /// Invalid PublicKey encoding
302    InvalidPublicKeyEncoding,
303}
304
305#[cfg(test)]
306mod tests {
307    use super::*;
308
309    #[test]
310    fn pkarr_key_generate() {
311        let key1 = Keypair::random();
312        let key2 = Keypair::from_secret_key(&key1.secret_key());
313
314        assert_eq!(key1.public_key(), key2.public_key())
315    }
316
317    #[test]
318    fn zbase32() {
319        let key1 = Keypair::random();
320        let _z32 = key1.public_key().to_string();
321
322        let key2 = Keypair::from_secret_key(&key1.secret_key());
323
324        assert_eq!(key1.public_key(), key2.public_key())
325    }
326
327    #[test]
328    fn sign_verify() {
329        let keypair = Keypair::random();
330
331        let message = b"Hello, world!";
332        let signature = keypair.sign(message);
333
334        assert!(keypair.verify(message, &signature).is_ok());
335
336        let public_key = keypair.public_key();
337        assert!(public_key.verify(message, &signature).is_ok());
338    }
339
340    #[test]
341    fn from_string() {
342        let str = "yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no";
343        let expected = [
344            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
345            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
346        ];
347
348        let public_key: PublicKey = str.try_into().unwrap();
349        assert_eq!(public_key.verifying_key().as_bytes(), &expected);
350    }
351
352    #[test]
353    fn to_uri() {
354        let bytes = [
355            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
356            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
357        ];
358        let expected = "pk:yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no";
359
360        let public_key: PublicKey = (&bytes).try_into().unwrap();
361
362        assert_eq!(public_key.to_uri_string(), expected);
363    }
364
365    #[test]
366    fn from_uri() {
367        let str = "pk:yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no";
368        let expected = [
369            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
370            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
371        ];
372
373        let public_key: PublicKey = str.try_into().unwrap();
374        assert_eq!(public_key.verifying_key().as_bytes(), &expected);
375    }
376
377    #[test]
378    fn from_uri_with_path() {
379        let str = "https://yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no///foo/bar";
380        let expected = [
381            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
382            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
383        ];
384
385        let public_key: PublicKey = str.try_into().unwrap();
386        assert_eq!(public_key.verifying_key().as_bytes(), &expected);
387    }
388
389    #[test]
390    fn from_uri_with_query() {
391        let str = "https://yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no?foo=bar";
392        let expected = [
393            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
394            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
395        ];
396
397        let public_key: PublicKey = str.try_into().unwrap();
398        assert_eq!(public_key.verifying_key().as_bytes(), &expected);
399    }
400
401    #[test]
402    fn from_uri_with_hash() {
403        let str = "https://yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no#foo";
404        let expected = [
405            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
406            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
407        ];
408
409        let public_key: PublicKey = str.try_into().unwrap();
410        assert_eq!(public_key.verifying_key().as_bytes(), &expected);
411    }
412
413    #[test]
414    fn from_uri_with_subdomain() {
415        let str = "https://foo.bar.yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no#foo";
416        let expected = [
417            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
418            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
419        ];
420
421        let public_key: PublicKey = str.try_into().unwrap();
422        assert_eq!(public_key.verifying_key().as_bytes(), &expected);
423    }
424
425    #[test]
426    fn from_uri_with_trailing_dot() {
427        let str = "https://foo.yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no.";
428        let expected = [
429            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
430            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
431        ];
432
433        let public_key: PublicKey = str.try_into().unwrap();
434        assert_eq!(public_key.verifying_key().as_bytes(), &expected);
435    }
436
437    #[test]
438    fn from_uri_with_username() {
439        let str = "https://foo@yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no#foo";
440        let expected = [
441            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
442            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
443        ];
444
445        let public_key: PublicKey = str.try_into().unwrap();
446        assert_eq!(public_key.verifying_key().as_bytes(), &expected);
447    }
448
449    #[test]
450    fn from_uri_with_port() {
451        let str = "https://yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no:8888";
452        let expected = [
453            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
454            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
455        ];
456
457        let public_key: PublicKey = str.try_into().unwrap();
458        assert_eq!(public_key.verifying_key().as_bytes(), &expected);
459    }
460
461    #[test]
462    fn from_uri_complex() {
463        let str =
464            "https://foo@bar.yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no.:8888?q=v&a=b#foo";
465        let expected = [
466            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
467            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
468        ];
469
470        let public_key: PublicKey = str.try_into().unwrap();
471        assert_eq!(public_key.verifying_key().as_bytes(), &expected);
472    }
473
474    #[test]
475    fn serde() {
476        let str = "yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no";
477        let expected = [
478            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
479            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
480        ];
481
482        let public_key: PublicKey = str.try_into().unwrap();
483
484        let bytes = postcard::to_allocvec(&public_key).unwrap();
485
486        assert_eq!(bytes, expected)
487    }
488
489    #[test]
490    fn from_uri_multiple_pkarr() {
491        // Should only catch the TLD.
492
493        let str =
494            "https://o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy.yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no";
495        let expected = [
496            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
497            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
498        ];
499
500        let public_key: PublicKey = str.try_into().unwrap();
501        assert_eq!(public_key.verifying_key().as_bytes(), &expected);
502    }
503
504    #[cfg(all(not(target_family = "wasm"), feature = "tls"))]
505    #[test]
506    fn pkcs8() {
507        let str = "yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no";
508        let public_key: PublicKey = str.try_into().unwrap();
509
510        let der = public_key.to_public_key_der();
511
512        assert_eq!(
513            der.as_bytes(),
514            [
515                // Algorithm and other stuff.
516                48, 42, 48, 5, 6, 3, 43, 101, 112, 3, 33, 0, //
517                // Key
518                1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254,
519                14, 207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
520            ]
521        )
522    }
523
524    #[cfg(all(not(target_family = "wasm"), feature = "tls"))]
525    #[test]
526    fn certificate() {
527        use rustls::SignatureAlgorithm;
528
529        let keypair = Keypair::from_secret_key(&[0; 32]);
530
531        let certified_key = keypair.to_rpk_certified_key();
532
533        assert_eq!(certified_key.key.algorithm(), SignatureAlgorithm::ED25519);
534
535        assert_eq!(
536            certified_key.end_entity_cert().unwrap().as_ref(),
537            [
538                48, 42, 48, 5, 6, 3, 43, 101, 112, 3, 33, 0, 59, 106, 39, 188, 206, 182, 164, 45,
539                98, 163, 168, 208, 42, 111, 13, 115, 101, 50, 21, 119, 29, 226, 67, 166, 58, 192,
540                72, 161, 139, 89, 218, 41,
541            ]
542        )
543    }
544
545    #[test]
546    fn invalid_key() {
547        let key = "c1bkg8tfsyy8wcedtmw4fwhdmm7bbzhgg3z58tf43m5ow8w9mbus";
548
549        assert_eq!(
550            PublicKey::try_from(key),
551            Err(PublicKeyError::InvalidEd25519PublicKey)
552        );
553    }
554}