Skip to main content

kevy_noise/
handshake.rs

1//! The IK handshake: `<- s ... -> e, es, s, ss  <- e, ee, se`.
2
3use kevy_crypto::{ct_eq, x25519};
4
5use crate::symmetric::SymmetricState;
6use crate::{Error, Transport};
7
8const PROTOCOL: &[u8] = b"Noise_IK_25519_ChaChaPoly_BLAKE2s";
9const DHLEN: usize = 32;
10const TAG: usize = 16;
11
12/// An X25519 key pair.
13///
14/// ```
15/// use kevy_noise::Keypair;
16///
17/// let kp = Keypair::from_secret([7; 32]);
18/// assert_eq!(kp.public(), Keypair::from_secret([7; 32]).public());
19/// ```
20#[derive(Clone)]
21pub struct Keypair {
22    secret: [u8; 32],
23    public: [u8; 32],
24}
25
26// the secret never reaches a log line
27impl core::fmt::Debug for Keypair {
28    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
29        f.debug_struct("Keypair").field("public", &self.public).finish_non_exhaustive()
30    }
31}
32
33impl Keypair {
34    /// Derive the public key of `secret`.
35    ///
36    /// ```
37    /// let kp = kevy_noise::Keypair::from_secret([1; 32]);
38    /// assert_ne!(kp.public(), [0; 32]);
39    /// ```
40    pub fn from_secret(secret: [u8; 32]) -> Self {
41        Keypair { public: x25519::x25519(&secret, &x25519::BASEPOINT), secret }
42    }
43
44    /// The public key, X25519(secret, 9): what the peer is given.
45    ///
46    /// ```
47    /// let kp = kevy_noise::Keypair::from_secret([1; 32]);
48    /// assert_ne!(kp.public(), [0; 32]);
49    /// ```
50    pub fn public(&self) -> [u8; 32] {
51        self.public
52    }
53}
54
55/// X25519, refusing an all-zero result (a peer that sent a point of small
56/// order).
57fn dh(secret: &[u8; 32], public: &[u8; 32]) -> Result<[u8; 32], Error> {
58    let shared = x25519::x25519(secret, public);
59    if ct_eq(&shared, &[0; 32]) {
60        return Err(Error::LowOrderPoint);
61    }
62    Ok(shared)
63}
64
65fn take<'a>(msg: &mut &'a [u8], n: usize) -> Result<&'a [u8], Error> {
66    if msg.len() < n {
67        return Err(Error::Truncated);
68    }
69    let (head, rest) = msg.split_at(n);
70    *msg = rest;
71    Ok(head)
72}
73
74fn key32(b: &[u8]) -> [u8; 32] {
75    let mut k = [0u8; 32];
76    k.copy_from_slice(b);
77    k
78}
79
80/// The initiator after sending the first message, waiting for the reply.
81///
82/// ```
83/// use kevy_noise::{Initiator, Keypair};
84///
85/// let server = Keypair::from_secret([1; 32]);
86/// let (first, _waiting) =
87///     Initiator::start(&Keypair::from_secret([2; 32]), &server.public(), Keypair::from_secret([3; 32]), b"", b"")
88///         .unwrap();
89/// assert_eq!(first.len(), 32 + 48 + 16);
90/// ```
91pub struct Initiator {
92    ss: SymmetricState,
93    s: Keypair,
94    e: Keypair,
95}
96
97// public keys only: the handshake state holds chaining keys
98impl core::fmt::Debug for Initiator {
99    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
100        f.debug_struct("Initiator").field("local_static", &self.s.public()).finish_non_exhaustive()
101    }
102}
103
104impl Initiator {
105    /// Write the first handshake message to a responder whose static
106    /// public key is `remote_static`. `ephemeral` must be fresh for every
107    /// handshake.
108    ///
109    /// ```
110    /// use kevy_noise::{Initiator, Keypair, Responder};
111    ///
112    /// let server = Keypair::from_secret([1; 32]);
113    /// let client = Keypair::from_secret([2; 32]);
114    /// let (msg1, init) =
115    ///     Initiator::start(&client, &server.public(), Keypair::from_secret([3; 32]), b"p", b"hi").unwrap();
116    /// let (payload, resp) = Responder::accept(&server, Keypair::from_secret([4; 32]), b"p", &msg1).unwrap();
117    /// assert_eq!(payload, b"hi");
118    /// assert_eq!(resp.remote_static(), client.public());
119    /// let (msg2, mut server_t) = resp.finish(b"").unwrap();
120    /// let (_, mut client_t) = init.finish(&msg2).unwrap();
121    /// let sealed = client_t.seal(b"ping").unwrap();
122    /// assert_eq!(server_t.open(&sealed).unwrap(), b"ping");
123    /// ```
124    pub fn start(
125        local: &Keypair,
126        remote_static: &[u8; 32],
127        ephemeral: Keypair,
128        prologue: &[u8],
129        payload: &[u8],
130    ) -> Result<(Vec<u8>, Initiator), Error> {
131        let mut ss = SymmetricState::new(PROTOCOL);
132        ss.mix_hash(prologue);
133        ss.mix_hash(remote_static);
134        let mut msg = ephemeral.public.to_vec();
135        ss.mix_hash(&ephemeral.public);
136        ss.mix_key(&dh(&ephemeral.secret, remote_static)?);
137        msg.extend(ss.encrypt_and_hash(&local.public)?);
138        ss.mix_key(&dh(&local.secret, remote_static)?);
139        msg.extend(ss.encrypt_and_hash(payload)?);
140        Ok((msg, Initiator { ss, s: local.clone(), e: ephemeral }))
141    }
142
143    /// Read the responder's reply: its payload, and the transport.
144    ///
145    /// ```
146    /// use kevy_noise::{Error, Initiator, Keypair};
147    ///
148    /// let server = Keypair::from_secret([1; 32]);
149    /// let (_, init) =
150    ///     Initiator::start(&Keypair::from_secret([2; 32]), &server.public(), Keypair::from_secret([3; 32]), b"", b"")
151    ///         .unwrap();
152    /// assert_eq!(init.finish(&[0; 10]).err(), Some(Error::Truncated));
153    /// ```
154    pub fn finish(mut self, reply: &[u8]) -> Result<(Vec<u8>, Transport), Error> {
155        let mut rest = reply;
156        let re = key32(take(&mut rest, DHLEN)?);
157        self.ss.mix_hash(&re);
158        self.ss.mix_key(&dh(&self.e.secret, &re)?);
159        self.ss.mix_key(&dh(&self.s.secret, &re)?);
160        let payload = self.ss.decrypt_and_hash(rest)?;
161        let (send, recv, hash) = self.ss.split();
162        Ok((payload, Transport::new(send, recv, hash)))
163    }
164}
165
166/// The responder after reading the first message, before replying.
167///
168/// ```
169/// use kevy_noise::{Initiator, Keypair, Responder};
170///
171/// let (server, client) = (Keypair::from_secret([1; 32]), Keypair::from_secret([2; 32]));
172/// let (m1, _) = Initiator::start(&client, &server.public(), Keypair::from_secret([3; 32]), b"", b"").unwrap();
173/// let (_, responder) = Responder::accept(&server, Keypair::from_secret([4; 32]), b"", &m1).unwrap();
174/// let (reply, _) = responder.finish(b"").unwrap();
175/// assert_eq!(reply.len(), 32 + 16);
176/// ```
177pub struct Responder {
178    ss: SymmetricState,
179    e: Keypair,
180    re: [u8; 32],
181    rs: [u8; 32],
182}
183
184impl core::fmt::Debug for Responder {
185    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
186        f.debug_struct("Responder").field("remote_static", &self.rs).finish_non_exhaustive()
187    }
188}
189
190impl Responder {
191    /// Read an initiator's first message: its payload, and the state to
192    /// reply from. `ephemeral` must be fresh for every handshake.
193    ///
194    /// ```
195    /// use kevy_noise::{Error, Keypair, Responder};
196    ///
197    /// let server = Keypair::from_secret([1; 32]);
198    /// let r = Responder::accept(&server, Keypair::from_secret([4; 32]), b"", &[0; 95]);
199    /// assert!(matches!(r, Err(Error::Truncated | Error::Decrypt | Error::LowOrderPoint)));
200    /// ```
201    pub fn accept(
202        local: &Keypair,
203        ephemeral: Keypair,
204        prologue: &[u8],
205        first: &[u8],
206    ) -> Result<(Vec<u8>, Responder), Error> {
207        let mut ss = SymmetricState::new(PROTOCOL);
208        ss.mix_hash(prologue);
209        ss.mix_hash(&local.public);
210        let mut rest = first;
211        let re = key32(take(&mut rest, DHLEN)?);
212        ss.mix_hash(&re);
213        ss.mix_key(&dh(&local.secret, &re)?);
214        let rs = key32(&ss.decrypt_and_hash(take(&mut rest, DHLEN + TAG)?)?);
215        ss.mix_key(&dh(&local.secret, &rs)?);
216        let payload = ss.decrypt_and_hash(rest)?;
217        Ok((payload, Responder { ss, e: ephemeral, re, rs }))
218    }
219
220    /// The initiator's static public key, authenticated by the handshake so
221    /// far: decide here whether to let it in.
222    ///
223    /// ```
224    /// use kevy_noise::{Initiator, Keypair, Responder};
225    ///
226    /// let (server, client) = (Keypair::from_secret([1; 32]), Keypair::from_secret([2; 32]));
227    /// let (m1, _) = Initiator::start(&client, &server.public(), Keypair::from_secret([3; 32]), b"", b"").unwrap();
228    /// let (_, r) = Responder::accept(&server, Keypair::from_secret([4; 32]), b"", &m1).unwrap();
229    /// assert_eq!(r.remote_static(), client.public());
230    /// ```
231    pub fn remote_static(&self) -> [u8; 32] {
232        self.rs
233    }
234
235    /// Write the reply carrying `payload`, and return the transport.
236    ///
237    /// ```
238    /// use kevy_noise::{Initiator, Keypair, Responder};
239    ///
240    /// let (server, client) = (Keypair::from_secret([1; 32]), Keypair::from_secret([2; 32]));
241    /// let (m1, init) = Initiator::start(&client, &server.public(), Keypair::from_secret([3; 32]), b"", b"").unwrap();
242    /// let (_, r) = Responder::accept(&server, Keypair::from_secret([4; 32]), b"", &m1).unwrap();
243    /// let (m2, _) = r.finish(b"welcome").unwrap();
244    /// assert_eq!(init.finish(&m2).unwrap().0, b"welcome");
245    /// ```
246    pub fn finish(mut self, payload: &[u8]) -> Result<(Vec<u8>, Transport), Error> {
247        let mut msg = self.e.public.to_vec();
248        self.ss.mix_hash(&self.e.public);
249        self.ss.mix_key(&dh(&self.e.secret, &self.re)?);
250        self.ss.mix_key(&dh(&self.e.secret, &self.rs)?);
251        msg.extend(self.ss.encrypt_and_hash(payload)?);
252        let (recv, send, hash) = self.ss.split();
253        Ok((msg, Transport::new(send, recv, hash)))
254    }
255}