Skip to main content

iroh_base/
key.rs

1//! Cryptographic key handling for `iroh`.
2
3use std::{
4    borrow::Borrow,
5    cmp::{Ord, PartialOrd},
6    fmt::{self, Debug, Display},
7    hash::Hash,
8    ops::Deref,
9    str::FromStr,
10};
11
12use curve25519_dalek::edwards::CompressedEdwardsY;
13use data_encoding::Encoding;
14use data_encoding_macro::new_encoding;
15use ed25519_dalek::{SigningKey, VerifyingKey};
16use n0_error::{e, ensure, stack_error};
17use serde::{Deserialize, Serialize, de, ser};
18
19/// z-base-32 encoding as used by [pkarr](https://pkarr.org) for endpoint id domain names.
20const Z_BASE_32: Encoding = new_encoding! {
21    symbols: "ybndrfg8ejkmcpqxot1uwisza345h769",
22};
23
24/// A public key.
25///
26/// The key itself is stored as the `CompressedEdwards` y-coordinate of the public key.
27/// It is verified to decompress into a valid key when created.
28#[derive(Clone, Copy, PartialEq, Eq)]
29#[repr(transparent)]
30pub struct PublicKey(CompressedEdwardsY);
31
32impl Borrow<[u8; 32]> for PublicKey {
33    fn borrow(&self) -> &[u8; 32] {
34        self.as_bytes()
35    }
36}
37
38impl Deref for PublicKey {
39    type Target = [u8; 32];
40
41    fn deref(&self) -> &Self::Target {
42        self.as_bytes()
43    }
44}
45
46impl PartialOrd for PublicKey {
47    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
48        Some(self.cmp(other))
49    }
50}
51
52impl Ord for PublicKey {
53    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
54        self.0.as_bytes().cmp(other.0.as_bytes())
55    }
56}
57
58/// The identifier for an endpoint in the (iroh) network.
59///
60/// Each endpoint in iroh has a unique identifier created as a cryptographic key.  This can be
61/// used to globally identify an endpoint.  Since it is also a cryptographic key it is also the
62/// mechanism by which all traffic is always encrypted for a specific endpoint only.
63///
64/// This is equivalent to [`PublicKey`].  By convention we will (or should) use `PublicKey`
65/// as type name when performing cryptographic operations, but use `EndpointId` when referencing
66/// an endpoint.  E.g.:
67///
68/// - `encrypt(key: PublicKey)`
69/// - `send_to(endpoint: EndpointId)`
70pub type EndpointId = PublicKey;
71
72impl Hash for PublicKey {
73    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
74        self.0.hash(state);
75    }
76}
77
78impl Serialize for PublicKey {
79    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
80    where
81        S: serde::Serializer,
82    {
83        if serializer.is_human_readable() {
84            serializer.serialize_str(&self.to_string())
85        } else {
86            self.0.as_bytes().serialize(serializer)
87        }
88    }
89}
90
91impl<'de> Deserialize<'de> for PublicKey {
92    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
93    where
94        D: serde::Deserializer<'de>,
95    {
96        if deserializer.is_human_readable() {
97            let s = String::deserialize(deserializer)?;
98            Self::from_str(&s).map_err(serde::de::Error::custom)
99        } else {
100            let data: [u8; 32] = serde::Deserialize::deserialize(deserializer)?;
101            Self::try_from(data.as_ref()).map_err(serde::de::Error::custom)
102        }
103    }
104}
105
106impl PublicKey {
107    /// The length of an ed25519 `PublicKey`, in bytes.
108    pub const LENGTH: usize = ed25519_dalek::PUBLIC_KEY_LENGTH;
109
110    /// Get this public key as a byte array.
111    pub fn as_bytes(&self) -> &[u8; 32] {
112        self.0.as_bytes()
113    }
114
115    /// Construct a `PublicKey` from a slice of bytes.
116    ///
117    /// # Warning
118    ///
119    /// This will return a [`SignatureError`] if the bytes passed into this method do not represent
120    /// a valid `ed25519_dalek` curve point. Will never fail for bytes return from [`Self::as_bytes`].
121    /// See [`VerifyingKey::from_bytes`] for details.
122    pub fn from_bytes(bytes: &[u8; 32]) -> Result<Self, KeyParsingError> {
123        let key =
124            VerifyingKey::from_bytes(bytes).map_err(|_| e!(KeyParsingError::InvalidKeyData))?;
125        let y = CompressedEdwardsY(key.to_bytes());
126        Ok(Self(y))
127    }
128
129    /// Verify a signature on a message with this public key.
130    ///
131    /// # Return
132    ///
133    /// Returns `Ok(())` if the signature is valid, and `Err` otherwise.
134    pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> {
135        self.as_verifying_key()
136            .verify_strict(message, &signature.0)
137            .map_err(|_| SignatureError::new())
138    }
139
140    /// Convert to a hex string limited to the first 5 bytes for a friendly string
141    /// representation of the key.
142    pub fn fmt_short(&self) -> impl Display + Copy + 'static {
143        PublicKeyShort(
144            self.0.as_bytes()[0..5]
145                .try_into()
146                .expect("slice with incorrect length"),
147        )
148    }
149
150    /// Needed for internal conversions, not part of the stable API.
151    #[doc(hidden)]
152    pub fn as_verifying_key(&self) -> VerifyingKey {
153        VerifyingKey::from_bytes(self.0.as_bytes()).expect("already verified")
154    }
155
156    /// Needed for internal conversions, not part of the stable API.
157    #[doc(hidden)]
158    pub fn from_verifying_key(key: VerifyingKey) -> Self {
159        Self(CompressedEdwardsY(key.to_bytes()))
160    }
161
162    /// Encodes this key in [z-base-32](https://philzimmermann.com/docs/human-oriented-base-32-encoding.txt),
163    /// the encoding used by [pkarr](https://pkarr.org) domain names.
164    pub fn to_z32(&self) -> String {
165        Z_BASE_32.encode(self.as_bytes())
166    }
167
168    /// Parses a key from its [z-base-32](https://philzimmermann.com/docs/human-oriented-base-32-encoding.txt) encoding.
169    pub fn from_z32(s: &str) -> Result<Self, KeyParsingError> {
170        let bytes = Z_BASE_32
171            .decode(s.as_bytes())
172            .map_err(|_| e!(KeyParsingError::FailedToDecodeBase32))?;
173        Self::try_from(bytes.as_slice())
174    }
175}
176
177#[derive(Copy, Clone)]
178struct PublicKeyShort([u8; 5]);
179
180impl Display for PublicKeyShort {
181    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
182        data_encoding::HEXLOWER.encode_write(&self.0, f)
183    }
184}
185
186impl TryFrom<&[u8]> for PublicKey {
187    type Error = KeyParsingError;
188
189    #[inline]
190    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
191        let vk = VerifyingKey::try_from(bytes).map_err(|_| e!(KeyParsingError::InvalidKeyData))?;
192        Ok(Self(CompressedEdwardsY(vk.to_bytes())))
193    }
194}
195
196impl TryFrom<&[u8; 32]> for PublicKey {
197    type Error = KeyParsingError;
198
199    #[inline]
200    fn try_from(bytes: &[u8; 32]) -> Result<Self, Self::Error> {
201        Self::from_bytes(bytes)
202    }
203}
204
205impl AsRef<[u8]> for PublicKey {
206    fn as_ref(&self) -> &[u8] {
207        self.as_bytes()
208    }
209}
210
211impl Debug for PublicKey {
212    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
213        write!(
214            f,
215            "PublicKey({})",
216            data_encoding::HEXLOWER.encode(self.as_bytes())
217        )
218    }
219}
220
221impl Display for PublicKey {
222    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
223        write!(f, "{}", data_encoding::HEXLOWER.encode(self.as_bytes()))
224    }
225}
226
227/// Error returned when parsing a [`PublicKey`] or a [`SecretKey`].
228#[stack_error(derive, add_meta, from_sources, std_sources)]
229#[allow(missing_docs)]
230#[non_exhaustive]
231pub enum KeyParsingError {
232    /// The input string could not be decoded as hex.
233    #[error("failed to decode hex string")]
234    FailedToDecodeHex,
235    /// The input string could not be decoded as base32.
236    #[error("failed to decode base32 string")]
237    FailedToDecodeBase32,
238    /// The input has invalid length.
239    #[error("invalid length")]
240    InvalidLength,
241    /// The decoded data is not a valid Ed25519 public key.
242    #[error("data is not a valid public key")]
243    InvalidKeyData,
244}
245
246/// Parses a [`PublicKey`] from its hex or base32 encoding.
247///
248/// [`Display`] produces the hex encoding.
249impl FromStr for PublicKey {
250    type Err = KeyParsingError;
251
252    fn from_str(s: &str) -> Result<Self, Self::Err> {
253        let bytes = decode_base32_hex(s)?;
254
255        Self::from_bytes(&bytes)
256    }
257}
258
259/// A secret key.
260#[derive(Clone, zeroize::ZeroizeOnDrop)]
261pub struct SecretKey(SigningKey);
262
263impl Debug for SecretKey {
264    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
265        write!(f, "SecretKey(..)")
266    }
267}
268
269impl FromStr for SecretKey {
270    type Err = KeyParsingError;
271
272    fn from_str(s: &str) -> Result<Self, Self::Err> {
273        let bytes = decode_base32_hex(s)?;
274        Ok(SecretKey::from(bytes))
275    }
276}
277
278impl Serialize for SecretKey {
279    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
280    where
281        S: serde::Serializer,
282    {
283        self.0.serialize(serializer)
284    }
285}
286
287impl<'de> Deserialize<'de> for SecretKey {
288    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
289    where
290        D: serde::Deserializer<'de>,
291    {
292        let secret = SigningKey::deserialize(deserializer)?;
293        Ok(Self(secret))
294    }
295}
296
297impl SecretKey {
298    /// The public key of this [`SecretKey`].
299    pub fn public(&self) -> PublicKey {
300        let key = self.0.verifying_key().to_bytes();
301        PublicKey(CompressedEdwardsY(key))
302    }
303
304    /// Generate a new [`SecretKey`] with a randomness generator.
305    ///
306    /// This uses the default random number generator from the `rand` crate.
307    /// If you want to customize how the randomness is generated, use
308    /// [`Self::from_bytes`] instead and generate the 32 bytes yourself:
309    ///
310    /// ```rust
311    /// # use iroh_base::SecretKey;
312    /// # use rand::RngExt;
313    /// // Create a random number generator.
314    /// let mut rng = rand::rng();
315    /// // Use it to generate the 32 bytes that make up a secret key.
316    /// let secret_key = SecretKey::from_bytes(&rng.random());
317    /// ```
318    pub fn generate() -> Self {
319        Self::from_bytes(&rand::random())
320    }
321
322    /// Sign the given message and return a digital signature
323    pub fn sign(&self, msg: &[u8]) -> Signature {
324        use ed25519_dalek::Signer;
325
326        let sig = self.0.sign(msg);
327        Signature(sig)
328    }
329
330    /// Convert this to the bytes representing the secret part.
331    /// The public part can always be recovered.
332    pub fn to_bytes(&self) -> [u8; 32] {
333        self.0.to_bytes()
334    }
335
336    /// Create a secret key from its byte representation.
337    pub fn from_bytes(bytes: &[u8; 32]) -> Self {
338        let secret = SigningKey::from_bytes(bytes);
339        Self(secret)
340    }
341
342    /// Needed for internal conversions, not part of the stable API.
343    #[doc(hidden)]
344    pub fn as_signing_key(&self) -> &SigningKey {
345        &self.0
346    }
347}
348
349impl From<[u8; 32]> for SecretKey {
350    fn from(value: [u8; 32]) -> Self {
351        Self::from_bytes(&value)
352    }
353}
354
355impl From<&[u8; 32]> for SecretKey {
356    fn from(value: &[u8; 32]) -> Self {
357        Self::from_bytes(value)
358    }
359}
360
361impl TryFrom<&[u8]> for SecretKey {
362    type Error = KeyParsingError;
363
364    #[inline]
365    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
366        let bytes: [u8; 32] = bytes
367            .try_into()
368            .map_err(|_| e!(KeyParsingError::InvalidLength))?;
369        let secret = SigningKey::from_bytes(&bytes);
370        Ok(Self(secret))
371    }
372}
373
374/// Ed25519 signature.
375#[derive(Copy, Clone, Eq, PartialEq)]
376pub struct Signature(ed25519_dalek::Signature);
377
378impl Serialize for Signature {
379    fn serialize<S: ser::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
380        use ser::SerializeTuple;
381
382        let mut seq = serializer.serialize_tuple(Signature::LENGTH)?;
383
384        for byte in self.to_bytes() {
385            seq.serialize_element(&byte)?;
386        }
387
388        seq.end()
389    }
390}
391
392// serde lacks support for deserializing arrays larger than 32-bytes
393// see: <https://github.com/serde-rs/serde/issues/631>
394impl<'de> Deserialize<'de> for Signature {
395    fn deserialize<D: de::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
396        struct ByteArrayVisitor;
397
398        impl<'de> de::Visitor<'de> for ByteArrayVisitor {
399            type Value = [u8; Signature::LENGTH];
400
401            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
402                formatter.write_str("bytestring of length 64")
403            }
404
405            fn visit_seq<A>(self, mut seq: A) -> Result<[u8; Signature::LENGTH], A::Error>
406            where
407                A: de::SeqAccess<'de>,
408            {
409                use de::Error;
410                let mut arr = [0u8; Signature::LENGTH];
411
412                for (i, byte) in arr.iter_mut().enumerate() {
413                    *byte = seq
414                        .next_element()?
415                        .ok_or_else(|| Error::invalid_length(i, &self))?;
416                }
417
418                Ok(arr)
419            }
420        }
421
422        deserializer
423            .deserialize_tuple(Signature::LENGTH, ByteArrayVisitor)
424            .map(|b| Signature::from_bytes(&b))
425    }
426}
427
428impl Debug for Signature {
429    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
430        write!(f, "{:?}", self.0)
431    }
432}
433
434impl Display for Signature {
435    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
436        write!(f, "{}", self.0)
437    }
438}
439
440/// Error generated when failed to parse an ED25519 signature.
441#[stack_error(derive, add_meta)]
442#[error("Could not parse ed25519 signature")]
443pub struct SignatureParsingError;
444
445impl TryFrom<&[u8]> for Signature {
446    type Error = SignatureParsingError;
447
448    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
449        let signature =
450            ed25519_dalek::Signature::from_slice(bytes).map_err(|_| e!(SignatureParsingError))?;
451        Ok(Self(signature))
452    }
453}
454
455impl Signature {
456    /// The length of an ed25519 `Signature`, in bytes.
457    pub const LENGTH: usize = ed25519_dalek::Signature::BYTE_SIZE;
458
459    /// Return the inner byte array.
460    pub fn to_bytes(&self) -> [u8; Self::LENGTH] {
461        self.0.to_bytes()
462    }
463
464    /// Parse an Ed25519 signature from a byte slice.
465    pub fn from_bytes(bytes: &[u8; Self::LENGTH]) -> Self {
466        Self(ed25519_dalek::Signature::from_bytes(bytes))
467    }
468}
469
470/// Verification of a signature failed.
471#[stack_error(derive, add_meta)]
472#[error("Invalid signature")]
473pub struct SignatureError {}
474
475fn decode_base32_hex(s: &str) -> Result<[u8; 32], KeyParsingError> {
476    let mut bytes = [0u8; 32];
477
478    let len = if s.len() == PublicKey::LENGTH * 2 {
479        // hex
480        data_encoding::HEXLOWER
481            .decode_mut(s.as_bytes(), &mut bytes)
482            .map_err(|_| e!(KeyParsingError::FailedToDecodeHex))?
483    } else {
484        let input = s.to_ascii_uppercase();
485        let input = input.as_bytes();
486        ensure!(
487            data_encoding::BASE32_NOPAD.decode_len(input.len()) == Ok(bytes.len()),
488            KeyParsingError::InvalidLength
489        );
490        data_encoding::BASE32_NOPAD
491            .decode_mut(input, &mut bytes)
492            .map_err(|_| e!(KeyParsingError::FailedToDecodeBase32))?
493    };
494    ensure!(len == PublicKey::LENGTH, KeyParsingError::InvalidLength);
495    Ok(bytes)
496}
497
498#[cfg(test)]
499mod tests {
500    use data_encoding::HEXLOWER;
501    use rand::{RngExt, SeedableRng};
502
503    use super::*;
504
505    #[test]
506    fn test_public_key_postcard() {
507        let public_key =
508            PublicKey::from_str("ae58ff8833241ac82d6ff7611046ed67b5072d142c588d0063e942d9a75502b6")
509                .unwrap();
510        let bytes = postcard::to_stdvec(&public_key).unwrap();
511        let expected = HEXLOWER
512            .decode(b"ae58ff8833241ac82d6ff7611046ed67b5072d142c588d0063e942d9a75502b6")
513            .unwrap();
514        assert_eq!(bytes, expected);
515    }
516
517    #[test]
518    fn public_key_postcard() {
519        let key = PublicKey::from_bytes(&[0; 32]).unwrap();
520        let bytes = postcard::to_stdvec(&key).unwrap();
521        let key2: PublicKey = postcard::from_bytes(&bytes).unwrap();
522        assert_eq!(key, key2);
523    }
524
525    #[test]
526    fn public_key_json() {
527        let key = PublicKey::from_bytes(&[0; 32]).unwrap();
528        let bytes = serde_json::to_string(&key).unwrap();
529        let key2: PublicKey = serde_json::from_str(&bytes).unwrap();
530        assert_eq!(key, key2);
531    }
532
533    #[test]
534    fn test_from_str() {
535        let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(0u64);
536        let key = SecretKey::from_bytes(&rng.random());
537        assert_eq!(
538            SecretKey::from_str(&HEXLOWER.encode(&key.to_bytes()))
539                .unwrap()
540                .to_bytes(),
541            key.to_bytes()
542        );
543
544        assert_eq!(
545            PublicKey::from_str(&key.public().to_string()).unwrap(),
546            key.public()
547        );
548    }
549
550    #[test]
551    fn test_regression_parse_endpoint_id_panic() {
552        let not_a_endpoint_id = "foobarbaz";
553        assert!(PublicKey::from_str(not_a_endpoint_id).is_err());
554    }
555
556    #[test]
557    fn signature_postcard() {
558        let key = SecretKey::generate();
559        let signature = key.sign(b"hello world");
560        let bytes = postcard::to_stdvec(&signature).unwrap();
561        let signature2: Signature = postcard::from_bytes(&bytes).unwrap();
562        assert_eq!(signature, signature2);
563    }
564}