commonware_stream/public_key/
x25519.rs

1//! Operations over x25519 keys.
2
3use bytes::{Buf, BufMut};
4use commonware_codec::{Error as CodecError, FixedSize, Read, ReadExt, Write};
5use rand::{CryptoRng, Rng};
6use x25519_dalek::{EphemeralSecret, PublicKey as X25519PublicKey};
7
8/// x25519 Public Key.
9#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
10pub struct PublicKey {
11    inner: X25519PublicKey,
12}
13
14impl PublicKey {
15    /// Derive a public key from a secret key.
16    pub fn from_secret(secret: &EphemeralSecret) -> Self {
17        PublicKey {
18            inner: X25519PublicKey::from(secret),
19        }
20    }
21
22    /// Parse a public key from a byte array.
23    pub fn from_bytes(array: [u8; 32]) -> Self {
24        PublicKey {
25            inner: X25519PublicKey::from(array),
26        }
27    }
28}
29
30impl AsRef<x25519_dalek::PublicKey> for PublicKey {
31    fn as_ref(&self) -> &x25519_dalek::PublicKey {
32        &self.inner
33    }
34}
35
36impl Write for PublicKey {
37    fn write(&self, buf: &mut impl BufMut) {
38        self.inner.as_bytes().write(buf);
39    }
40}
41
42impl Read for PublicKey {
43    type Cfg = ();
44
45    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
46        let public_key = <[u8; Self::SIZE]>::read(buf)?;
47        Ok(PublicKey {
48            inner: X25519PublicKey::from(public_key),
49        })
50    }
51}
52
53impl FixedSize for PublicKey {
54    const SIZE: usize = 32;
55}
56
57/// Generate a new ephemeral secret for X25519 key exchange.
58///
59/// This creates a fresh ephemeral secret key that should be used for a single
60/// key exchange and then discarded. The ephemeral nature provides forward secrecy.
61pub fn new<R: Rng + CryptoRng>(rng: &mut R) -> EphemeralSecret {
62    EphemeralSecret::random_from_rng(rng)
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68    use bytes::Bytes;
69    use commonware_codec::{DecodeExt, Encode as _};
70    use commonware_runtime::{deterministic, Runner};
71
72    #[test]
73    fn test_codec() {
74        let executor = deterministic::Runner::default();
75        executor.start(|mut context| async move {
76            // Create a random public key
77            let mut buf = [0u8; PublicKey::SIZE];
78            context.fill(&mut buf);
79            let original = PublicKey {
80                inner: X25519PublicKey::from(buf),
81            };
82            // Encode and decode the public key
83            let encoded = original.encode();
84            assert_eq!(encoded.len(), PublicKey::SIZE);
85            let decoded = PublicKey::decode(encoded).unwrap();
86            assert_eq!(original, decoded);
87        });
88    }
89
90    #[test]
91    fn test_decode_invalid() {
92        // Create a Bytes object that is too short
93        let invalid_bytes = Bytes::from(vec![1, 2, 3]); // Length 3 instead of 32
94        let result = PublicKey::decode(invalid_bytes);
95        assert!(result.is_err());
96
97        // Create Bytes object that's too long
98        let too_long_bytes = Bytes::from(vec![0u8; 33]); // Length 33
99        let result = PublicKey::decode(too_long_bytes);
100        assert!(result.is_err());
101    }
102}