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}