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 ed25519_dalek::{SigningKey, VerifyingKey};
14use nested_enum_utils::common_fields;
15use rand_core::CryptoRng;
16use serde::{Deserialize, Serialize};
17use snafu::{Backtrace, Snafu};
18
19/// A public key.
20///
21/// The key itself is stored as the `CompressedEdwards` y coordinate of the public key
22/// It is verified to decompress into a valid key when created.
23#[derive(Clone, Copy, PartialEq, Eq)]
24#[repr(transparent)]
25pub struct PublicKey(CompressedEdwardsY);
26
27impl Borrow<[u8; 32]> for PublicKey {
28    fn borrow(&self) -> &[u8; 32] {
29        self.as_bytes()
30    }
31}
32
33impl Deref for PublicKey {
34    type Target = [u8; 32];
35
36    fn deref(&self) -> &Self::Target {
37        self.as_bytes()
38    }
39}
40
41impl PartialOrd for PublicKey {
42    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
43        Some(self.cmp(other))
44    }
45}
46
47impl Ord for PublicKey {
48    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
49        self.0.as_bytes().cmp(other.0.as_bytes())
50    }
51}
52
53/// The identifier for an endpoint in the (iroh) network.
54///
55/// Each endpoint in iroh has a unique identifier created as a cryptographic key.  This can be
56/// used to globally identify an endpoint.  Since it is also a cryptographic key it is also the
57/// mechanism by which all traffic is always encrypted for a specific endpoint only.
58///
59/// This is equivalent to [`PublicKey`].  By convention we will (or should) use `PublicKey`
60/// as type name when performing cryptographic operations, but use `EndpointId` when referencing
61/// an endpoint.  E.g.:
62///
63/// - `encrypt(key: PublicKey)`
64/// - `send_to(endpoint: EndpointId)`
65pub type EndpointId = PublicKey;
66
67impl Hash for PublicKey {
68    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
69        self.0.hash(state);
70    }
71}
72
73impl Serialize for PublicKey {
74    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
75    where
76        S: serde::Serializer,
77    {
78        if serializer.is_human_readable() {
79            serializer.serialize_str(&self.to_string())
80        } else {
81            self.0.as_bytes().serialize(serializer)
82        }
83    }
84}
85
86impl<'de> Deserialize<'de> for PublicKey {
87    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
88    where
89        D: serde::Deserializer<'de>,
90    {
91        if deserializer.is_human_readable() {
92            let s = String::deserialize(deserializer)?;
93            Self::from_str(&s).map_err(serde::de::Error::custom)
94        } else {
95            let data: [u8; 32] = serde::Deserialize::deserialize(deserializer)?;
96            Self::try_from(data.as_ref()).map_err(serde::de::Error::custom)
97        }
98    }
99}
100
101impl PublicKey {
102    /// The length of an ed25519 `PublicKey`, in bytes.
103    pub const LENGTH: usize = ed25519_dalek::PUBLIC_KEY_LENGTH;
104
105    /// Get this public key as a byte array.
106    pub fn as_bytes(&self) -> &[u8; 32] {
107        self.0.as_bytes()
108    }
109
110    /// Construct a `PublicKey` from a slice of bytes.
111    ///
112    /// # Warning
113    ///
114    /// This will return a [`SignatureError`] if the bytes passed into this method do not represent
115    /// a valid `ed25519_dalek` curve point. Will never fail for bytes return from [`Self::as_bytes`].
116    /// See [`VerifyingKey::from_bytes`] for details.
117    pub fn from_bytes(bytes: &[u8; 32]) -> Result<Self, KeyParsingError> {
118        let key = VerifyingKey::from_bytes(bytes)?;
119        let y = CompressedEdwardsY(key.to_bytes());
120        Ok(Self(y))
121    }
122
123    /// Verify a signature on a message with this secret key's public key.
124    ///
125    /// # Return
126    ///
127    /// Returns `Ok(())` if the signature is valid, and `Err` otherwise.
128    pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> {
129        self.as_verifying_key()
130            .verify_strict(message, &signature.0)
131            .map_err(|_| SignatureSnafu.build())
132    }
133
134    /// Convert to a hex string limited to the first 5 bytes for a friendly string
135    /// representation of the key.
136    pub fn fmt_short(&self) -> impl Display + 'static {
137        PublicKeyShort(
138            self.0.as_bytes()[0..5]
139                .try_into()
140                .expect("slice with incorrect length"),
141        )
142    }
143
144    /// Needed for internal conversions, not part of the stable API.
145    #[doc(hidden)]
146    pub fn as_verifying_key(&self) -> VerifyingKey {
147        VerifyingKey::from_bytes(self.0.as_bytes()).expect("already verified")
148    }
149
150    /// Needed for internal conversions, not part of the stable API.
151    #[doc(hidden)]
152    pub fn from_verifying_key(key: VerifyingKey) -> Self {
153        Self(CompressedEdwardsY(key.to_bytes()))
154    }
155}
156
157struct PublicKeyShort([u8; 5]);
158
159impl Display for PublicKeyShort {
160    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161        data_encoding::HEXLOWER.encode_write(&self.0, f)
162    }
163}
164
165impl TryFrom<&[u8]> for PublicKey {
166    type Error = KeyParsingError;
167
168    #[inline]
169    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
170        let vk = VerifyingKey::try_from(bytes)?;
171        Ok(Self(CompressedEdwardsY(vk.to_bytes())))
172    }
173}
174
175impl TryFrom<&[u8; 32]> for PublicKey {
176    type Error = KeyParsingError;
177
178    #[inline]
179    fn try_from(bytes: &[u8; 32]) -> Result<Self, Self::Error> {
180        Self::from_bytes(bytes)
181    }
182}
183
184impl AsRef<[u8]> for PublicKey {
185    fn as_ref(&self) -> &[u8] {
186        self.as_bytes()
187    }
188}
189
190impl Debug for PublicKey {
191    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
192        write!(
193            f,
194            "PublicKey({})",
195            data_encoding::HEXLOWER.encode(self.as_bytes())
196        )
197    }
198}
199
200impl Display for PublicKey {
201    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202        write!(f, "{}", data_encoding::HEXLOWER.encode(self.as_bytes()))
203    }
204}
205
206/// Error when deserialising a [`PublicKey`] or a [`SecretKey`].
207#[common_fields({
208    backtrace: Option<Backtrace>,
209    #[snafu(implicit)]
210    span_trace: n0_snafu::SpanTrace,
211})]
212#[derive(Snafu, Debug)]
213#[allow(missing_docs)]
214#[snafu(visibility(pub(crate)))]
215pub enum KeyParsingError {
216    /// Error when decoding.
217    #[snafu(transparent)]
218    Decode { source: data_encoding::DecodeError },
219    /// Error when decoding the public key.
220    #[snafu(transparent)]
221    Key {
222        source: ed25519_dalek::SignatureError,
223    },
224    /// The encoded information had the wrong length.
225    #[snafu(display("invalid length"))]
226    DecodeInvalidLength {},
227}
228
229/// Deserialises the [`PublicKey`] from it's base32 encoding.
230///
231/// [`Display`] is capable of serialising this format.
232impl FromStr for PublicKey {
233    type Err = KeyParsingError;
234
235    fn from_str(s: &str) -> Result<Self, Self::Err> {
236        let bytes = decode_base32_hex(s)?;
237
238        Self::from_bytes(&bytes)
239    }
240}
241
242/// A secret key.
243#[derive(Clone, zeroize::ZeroizeOnDrop)]
244pub struct SecretKey(SigningKey);
245
246impl Debug for SecretKey {
247    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
248        write!(f, "SecretKey(..)")
249    }
250}
251
252impl FromStr for SecretKey {
253    type Err = KeyParsingError;
254
255    fn from_str(s: &str) -> Result<Self, Self::Err> {
256        let bytes = decode_base32_hex(s)?;
257        Ok(SecretKey::from(bytes))
258    }
259}
260
261impl Serialize for SecretKey {
262    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
263    where
264        S: serde::Serializer,
265    {
266        self.0.serialize(serializer)
267    }
268}
269
270impl<'de> Deserialize<'de> for SecretKey {
271    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
272    where
273        D: serde::Deserializer<'de>,
274    {
275        let secret = SigningKey::deserialize(deserializer)?;
276        Ok(Self(secret))
277    }
278}
279
280impl SecretKey {
281    /// The public key of this [`SecretKey`].
282    pub fn public(&self) -> PublicKey {
283        let key = self.0.verifying_key().to_bytes();
284        PublicKey(CompressedEdwardsY(key))
285    }
286
287    /// Generate a new [`SecretKey`] with a randomness generator.
288    ///
289    /// ```rust
290    /// // use the OsRng option for OS depedndent most secure RNG.
291    /// let _key = iroh_base::SecretKey::generate(&mut rand::rng());
292    /// ```
293    pub fn generate<R: CryptoRng + ?Sized>(csprng: &mut R) -> Self {
294        let secret = SigningKey::generate(csprng);
295        Self(secret)
296    }
297
298    /// Sign the given message and return a digital signature
299    pub fn sign(&self, msg: &[u8]) -> Signature {
300        use ed25519_dalek::Signer;
301
302        let sig = self.0.sign(msg);
303        Signature(sig)
304    }
305
306    /// Convert this to the bytes representing the secret part.
307    /// The public part can always be recovered.
308    pub fn to_bytes(&self) -> [u8; 32] {
309        self.0.to_bytes()
310    }
311
312    /// Create a secret key from its byte representation.
313    pub fn from_bytes(bytes: &[u8; 32]) -> Self {
314        let secret = SigningKey::from_bytes(bytes);
315        Self(secret)
316    }
317
318    /// Needed for internal conversions, not part of the stable API.
319    #[doc(hidden)]
320    pub fn as_signing_key(&self) -> &SigningKey {
321        &self.0
322    }
323}
324
325impl From<[u8; 32]> for SecretKey {
326    fn from(value: [u8; 32]) -> Self {
327        Self::from_bytes(&value)
328    }
329}
330
331impl TryFrom<&[u8]> for SecretKey {
332    type Error = KeyParsingError;
333
334    #[inline]
335    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
336        let secret = SigningKey::try_from(bytes)?;
337        Ok(Self(secret))
338    }
339}
340
341/// Ed25519 signature.
342#[derive(Copy, Clone, Eq, PartialEq)]
343pub struct Signature(ed25519_dalek::Signature);
344
345impl Debug for Signature {
346    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
347        write!(f, "{:?}", self.0)
348    }
349}
350
351impl Display for Signature {
352    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
353        write!(f, "{}", self.0)
354    }
355}
356
357impl Signature {
358    /// The length of an ed25519 `Signature`, in bytes.
359    pub const LENGTH: usize = ed25519_dalek::Signature::BYTE_SIZE;
360
361    /// Return the inner byte array.
362    pub fn to_bytes(&self) -> [u8; Self::LENGTH] {
363        self.0.to_bytes()
364    }
365
366    /// Parse an Ed25519 signature from a byte slice.
367    pub fn from_bytes(bytes: &[u8; Self::LENGTH]) -> Self {
368        Self(ed25519_dalek::Signature::from_bytes(bytes))
369    }
370}
371
372/// Verification of a signature failed.
373#[derive(Debug, Snafu)]
374#[snafu(display("Invalid signature"))]
375pub struct SignatureError;
376
377fn decode_base32_hex(s: &str) -> Result<[u8; 32], KeyParsingError> {
378    let mut bytes = [0u8; 32];
379
380    let res = if s.len() == PublicKey::LENGTH * 2 {
381        // hex
382        data_encoding::HEXLOWER.decode_mut(s.as_bytes(), &mut bytes)
383    } else {
384        let input = s.to_ascii_uppercase();
385        let input = input.as_bytes();
386        if data_encoding::BASE32_NOPAD.decode_len(input.len())? != bytes.len() {
387            return Err(DecodeInvalidLengthSnafu.build());
388        }
389        data_encoding::BASE32_NOPAD.decode_mut(input, &mut bytes)
390    };
391    match res {
392        Ok(len) => {
393            if len != PublicKey::LENGTH {
394                return Err(DecodeInvalidLengthSnafu.build());
395            }
396        }
397        Err(partial) => return Err(partial.error.into()),
398    }
399    Ok(bytes)
400}
401
402#[cfg(test)]
403mod tests {
404    use data_encoding::HEXLOWER;
405    use rand::SeedableRng;
406
407    use super::*;
408
409    #[test]
410    fn test_public_key_postcard() {
411        let public_key =
412            PublicKey::from_str("ae58ff8833241ac82d6ff7611046ed67b5072d142c588d0063e942d9a75502b6")
413                .unwrap();
414        let bytes = postcard::to_stdvec(&public_key).unwrap();
415        let expected = HEXLOWER
416            .decode(b"ae58ff8833241ac82d6ff7611046ed67b5072d142c588d0063e942d9a75502b6")
417            .unwrap();
418        assert_eq!(bytes, expected);
419    }
420
421    #[test]
422    fn public_key_postcard() {
423        let key = PublicKey::from_bytes(&[0; 32]).unwrap();
424        let bytes = postcard::to_stdvec(&key).unwrap();
425        let key2: PublicKey = postcard::from_bytes(&bytes).unwrap();
426        assert_eq!(key, key2);
427    }
428
429    #[test]
430    fn public_key_json() {
431        let key = PublicKey::from_bytes(&[0; 32]).unwrap();
432        let bytes = serde_json::to_string(&key).unwrap();
433        let key2: PublicKey = serde_json::from_str(&bytes).unwrap();
434        assert_eq!(key, key2);
435    }
436
437    #[test]
438    fn test_from_str() {
439        let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(0u64);
440        let key = SecretKey::generate(&mut rng);
441        assert_eq!(
442            SecretKey::from_str(&HEXLOWER.encode(&key.to_bytes()))
443                .unwrap()
444                .to_bytes(),
445            key.to_bytes()
446        );
447
448        assert_eq!(
449            PublicKey::from_str(&key.public().to_string()).unwrap(),
450            key.public()
451        );
452    }
453
454    #[test]
455    fn test_regression_parse_endpoint_id_panic() {
456        let not_a_endpoint_id = "foobarbaz";
457        assert!(PublicKey::from_str(not_a_endpoint_id).is_err());
458    }
459}