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 From<VerifyingKey> for PublicKey {
143    fn from(verifying_key: VerifyingKey) -> Self {
144        Self(verifying_key)
145    }
146}
147
148impl FromStr for PublicKey {
149    type Err = PublicKeyError;
150
151    /// Convert the TLD in a `&str` to a [PublicKey].
152    ///
153    /// # Examples
154    ///
155    /// - `o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy`
156    /// - `pk:o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy`
157    /// - `http://o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy`
158    /// - `https://o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy`
159    /// - `https://o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy/foo/bar`
160    /// - `https://foo.o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy.`
161    /// - `https://foo.o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy.#hash`
162    /// - `https://foo@bar.o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy.?q=v`
163    /// - `https://foo@bar.o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy.:8888?q=v`
164    /// - `https://yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no.o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy`
165    fn from_str(s: &str) -> Result<Self, Self::Err> {
166        let mut s = s;
167
168        if s.len() > 52 {
169            // Remove scheme
170            s = s.split_once(':').map(|tuple| tuple.1).unwrap_or(s);
171
172            if s.len() > 52 {
173                // Remove `//
174                s = s.strip_prefix("//").unwrap_or(s);
175
176                if s.len() > 52 {
177                    // Remove username
178                    s = s.split_once('@').map(|tuple| tuple.1).unwrap_or(s);
179
180                    if s.len() > 52 {
181                        // Remove port
182                        s = s.split_once(':').map(|tuple| tuple.0).unwrap_or(s);
183
184                        if s.len() > 52 {
185                            // Remove trailing path
186                            s = s.split_once('/').map(|tuple| tuple.0).unwrap_or(s);
187
188                            if s.len() > 52 {
189                                // Remove query
190                                s = s.split_once('?').map(|tuple| tuple.0).unwrap_or(s);
191
192                                if s.len() > 52 {
193                                    // Remove hash
194                                    s = s.split_once('#').map(|tuple| tuple.0).unwrap_or(s);
195
196                                    if s.len() > 52 {
197                                        if s.ends_with('.') {
198                                            // Remove trailing dot
199                                            s = s.trim_matches('.');
200                                        }
201
202                                        s = s.rsplit_once('.').map(|tuple| tuple.1).unwrap_or(s);
203                                    }
204                                }
205                            }
206                        }
207                    }
208                }
209            }
210        }
211
212        let bytes = if let Some(v) = base32::decode(base32::Alphabet::Z, s) {
213            Ok(v)
214        } else {
215            Err(PublicKeyError::InvalidPublicKeyEncoding)
216        }?;
217
218        bytes.as_slice().try_into()
219    }
220}
221
222impl TryFrom<&str> for PublicKey {
223    type Error = PublicKeyError;
224
225    fn try_from(s: &str) -> Result<PublicKey, PublicKeyError> {
226        PublicKey::from_str(s)
227    }
228}
229
230impl TryFrom<String> for PublicKey {
231    type Error = PublicKeyError;
232
233    fn try_from(s: String) -> Result<PublicKey, PublicKeyError> {
234        s.as_str().try_into()
235    }
236}
237
238impl TryFrom<&String> for PublicKey {
239    type Error = PublicKeyError;
240
241    fn try_from(s: &String) -> Result<PublicKey, PublicKeyError> {
242        s.try_into()
243    }
244}
245
246impl Display for PublicKey {
247    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
248        write!(
249            f,
250            "{}",
251            base32::encode(base32::Alphabet::Z, self.0.as_bytes())
252        )
253    }
254}
255
256impl Display for Keypair {
257    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
258        write!(f, "{}", self.public_key())
259    }
260}
261
262impl Debug for Keypair {
263    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264        write!(f, "Keypair({})", self.public_key())
265    }
266}
267
268impl Debug for PublicKey {
269    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
270        write!(f, "PublicKey({self})")
271    }
272}
273
274impl Serialize for PublicKey {
275    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
276    where
277        S: serde::Serializer,
278    {
279        let bytes = self.to_bytes();
280        bytes.serialize(serializer)
281    }
282}
283
284impl<'de> Deserialize<'de> for PublicKey {
285    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
286    where
287        D: serde::Deserializer<'de>,
288    {
289        let bytes: [u8; 32] = Deserialize::deserialize(deserializer)?;
290
291        (&bytes).try_into().map_err(serde::de::Error::custom)
292    }
293}
294
295#[derive(thiserror::Error, Debug, PartialEq, Eq)]
296/// Errors while trying to create a [PublicKey]
297pub enum PublicKeyError {
298    #[error("Invalid PublicKey length, expected 32 bytes but got: {0}")]
299    /// Invalid PublicKey length.
300    InvalidPublicKeyLength(usize),
301
302    #[error("Invalid Ed25519 publickey; Cannot decompress Edwards point")]
303    /// Cannot decompress Edwards point
304    InvalidEd25519PublicKey,
305
306    #[error("Invalid PublicKey encoding")]
307    /// Invalid PublicKey encoding
308    InvalidPublicKeyEncoding,
309}
310
311#[cfg(test)]
312mod tests {
313    use super::*;
314
315    #[test]
316    fn pkarr_key_generate() {
317        let key1 = Keypair::random();
318        let key2 = Keypair::from_secret_key(&key1.secret_key());
319
320        assert_eq!(key1.public_key(), key2.public_key())
321    }
322
323    #[test]
324    fn zbase32() {
325        let key1 = Keypair::random();
326        let _z32 = key1.public_key().to_string();
327
328        let key2 = Keypair::from_secret_key(&key1.secret_key());
329
330        assert_eq!(key1.public_key(), key2.public_key())
331    }
332
333    #[test]
334    fn sign_verify() {
335        let keypair = Keypair::random();
336
337        let message = b"Hello, world!";
338        let signature = keypair.sign(message);
339
340        assert!(keypair.verify(message, &signature).is_ok());
341
342        let public_key = keypair.public_key();
343        assert!(public_key.verify(message, &signature).is_ok());
344    }
345
346    #[test]
347    fn from_string() {
348        let str = "yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no";
349        let expected = [
350            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
351            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
352        ];
353
354        let public_key: PublicKey = str.try_into().unwrap();
355        assert_eq!(public_key.verifying_key().as_bytes(), &expected);
356    }
357
358    #[test]
359    fn to_uri() {
360        let bytes = [
361            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
362            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
363        ];
364        let expected = "pk:yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no";
365
366        let public_key: PublicKey = (&bytes).try_into().unwrap();
367
368        assert_eq!(public_key.to_uri_string(), expected);
369    }
370
371    #[test]
372    fn from_uri() {
373        let str = "pk:yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no";
374        let expected = [
375            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
376            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
377        ];
378
379        let public_key: PublicKey = str.try_into().unwrap();
380        assert_eq!(public_key.verifying_key().as_bytes(), &expected);
381    }
382
383    #[test]
384    fn from_uri_with_path() {
385        let str = "https://yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no///foo/bar";
386        let expected = [
387            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
388            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
389        ];
390
391        let public_key: PublicKey = str.try_into().unwrap();
392        assert_eq!(public_key.verifying_key().as_bytes(), &expected);
393    }
394
395    #[test]
396    fn from_uri_with_query() {
397        let str = "https://yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no?foo=bar";
398        let expected = [
399            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
400            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
401        ];
402
403        let public_key: PublicKey = str.try_into().unwrap();
404        assert_eq!(public_key.verifying_key().as_bytes(), &expected);
405    }
406
407    #[test]
408    fn from_uri_with_hash() {
409        let str = "https://yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no#foo";
410        let expected = [
411            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
412            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
413        ];
414
415        let public_key: PublicKey = str.try_into().unwrap();
416        assert_eq!(public_key.verifying_key().as_bytes(), &expected);
417    }
418
419    #[test]
420    fn from_uri_with_subdomain() {
421        let str = "https://foo.bar.yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no#foo";
422        let expected = [
423            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
424            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
425        ];
426
427        let public_key: PublicKey = str.try_into().unwrap();
428        assert_eq!(public_key.verifying_key().as_bytes(), &expected);
429    }
430
431    #[test]
432    fn from_uri_with_trailing_dot() {
433        let str = "https://foo.yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no.";
434        let expected = [
435            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
436            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
437        ];
438
439        let public_key: PublicKey = str.try_into().unwrap();
440        assert_eq!(public_key.verifying_key().as_bytes(), &expected);
441    }
442
443    #[test]
444    fn from_uri_with_username() {
445        let str = "https://foo@yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no#foo";
446        let expected = [
447            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
448            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
449        ];
450
451        let public_key: PublicKey = str.try_into().unwrap();
452        assert_eq!(public_key.verifying_key().as_bytes(), &expected);
453    }
454
455    #[test]
456    fn from_uri_with_port() {
457        let str = "https://yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no:8888";
458        let expected = [
459            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
460            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
461        ];
462
463        let public_key: PublicKey = str.try_into().unwrap();
464        assert_eq!(public_key.verifying_key().as_bytes(), &expected);
465    }
466
467    #[test]
468    fn from_uri_complex() {
469        let str =
470            "https://foo@bar.yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no.:8888?q=v&a=b#foo";
471        let expected = [
472            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
473            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
474        ];
475
476        let public_key: PublicKey = str.try_into().unwrap();
477        assert_eq!(public_key.verifying_key().as_bytes(), &expected);
478    }
479
480    #[test]
481    fn serde() {
482        let str = "yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no";
483        let expected = [
484            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
485            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
486        ];
487
488        let public_key: PublicKey = str.try_into().unwrap();
489
490        let bytes = postcard::to_allocvec(&public_key).unwrap();
491
492        assert_eq!(bytes, expected)
493    }
494
495    #[test]
496    fn from_uri_multiple_pkarr() {
497        // Should only catch the TLD.
498
499        let str =
500            "https://o4dksfbqk85ogzdb5osziw6befigbuxmuxkuxq8434q89uj56uyy.yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no";
501        let expected = [
502            1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254, 14,
503            207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
504        ];
505
506        let public_key: PublicKey = str.try_into().unwrap();
507        assert_eq!(public_key.verifying_key().as_bytes(), &expected);
508    }
509
510    #[cfg(all(not(target_family = "wasm"), feature = "tls"))]
511    #[test]
512    fn pkcs8() {
513        let str = "yg4gxe7z1r7mr6orids9fh95y7gxhdsxjqi6nngsxxtakqaxr5no";
514        let public_key: PublicKey = str.try_into().unwrap();
515
516        let der = public_key.to_public_key_der();
517
518        assert_eq!(
519            der.as_bytes(),
520            [
521                // Algorithm and other stuff.
522                48, 42, 48, 5, 6, 3, 43, 101, 112, 3, 33, 0, //
523                // Key
524                1, 180, 103, 163, 183, 145, 58, 178, 122, 4, 168, 237, 242, 243, 251, 7, 76, 254,
525                14, 207, 75, 171, 225, 8, 214, 123, 227, 133, 59, 15, 38, 197,
526            ]
527        )
528    }
529
530    #[cfg(all(not(target_family = "wasm"), feature = "tls"))]
531    #[test]
532    fn certificate() {
533        use rustls::SignatureAlgorithm;
534
535        let keypair = Keypair::from_secret_key(&[0; 32]);
536
537        let certified_key = keypair.to_rpk_certified_key();
538
539        assert_eq!(certified_key.key.algorithm(), SignatureAlgorithm::ED25519);
540
541        assert_eq!(
542            certified_key.end_entity_cert().unwrap().as_ref(),
543            [
544                48, 42, 48, 5, 6, 3, 43, 101, 112, 3, 33, 0, 59, 106, 39, 188, 206, 182, 164, 45,
545                98, 163, 168, 208, 42, 111, 13, 115, 101, 50, 21, 119, 29, 226, 67, 166, 58, 192,
546                72, 161, 139, 89, 218, 41,
547            ]
548        )
549    }
550
551    #[test]
552    fn invalid_key() {
553        let key = "c1bkg8tfsyy8wcedtmw4fwhdmm7bbzhgg3z58tf43m5ow8w9mbus";
554
555        assert_eq!(
556            PublicKey::try_from(key),
557            Err(PublicKeyError::InvalidEd25519PublicKey)
558        );
559    }
560}