1use ed25519_dalek::{Signer, Verifier};
4use hkdf::Hkdf;
5use hmac::{Hmac, Mac};
6use rand::rngs::OsRng;
7use sha2::Sha256;
8use x25519_dalek::{PublicKey as X25519PublicKey, StaticSecret};
9
10use crate::identity::Identity;
11
12type HmacSha256 = Hmac<Sha256>;
13
14#[derive(Clone)]
16pub struct SessionKey {
17 key: [u8; 32],
18}
19
20impl SessionKey {
21 pub fn as_bytes(&self) -> &[u8; 32] {
23 &self.key
24 }
25}
26
27pub struct HandshakeInit {
29 pub sender_pub: [u8; 32],
31 pub ephemeral_pub: [u8; 32],
33 pub nonce: [u8; 32],
35 pub signature: [u8; 64],
37}
38
39pub struct HandshakeResponse {
41 pub sender_pub: [u8; 32],
43 pub ephemeral_pub: [u8; 32],
45 pub nonce: [u8; 32],
47 pub signature: [u8; 64],
49}
50
51pub struct HandshakeConfirm {
53 pub hmac: [u8; 32],
55}
56
57pub struct Handshaker {
73 identity: HandshakeIdentity,
74 ephemeral_secret: Option<StaticSecret>,
75 ephemeral_pub: Option<[u8; 32]>,
76 session_key: Option<SessionKey>,
77 transcript: Vec<u8>,
78 mesh_handshake_key: Option<[u8; 32]>,
79}
80
81struct HandshakeIdentity {
83 signing_key_bytes: [u8; 32],
84 verifying_key_bytes: [u8; 32],
85}
86
87impl Handshaker {
88 pub fn new(identity: &Identity) -> Self {
90 Self {
91 identity: HandshakeIdentity {
92 signing_key_bytes: identity.signing_key().to_bytes(),
93 verifying_key_bytes: identity.public_key_bytes(),
94 },
95 ephemeral_secret: None,
96 ephemeral_pub: None,
97 session_key: None,
98 transcript: Vec::new(),
99 mesh_handshake_key: None,
100 }
101 }
102
103 pub fn with_mesh_handshake_key(mut self, key: [u8; 32]) -> Self {
108 self.mesh_handshake_key = Some(key);
109 self
110 }
111
112 fn ikm_with_mesh(&self, shared_secret: &[u8]) -> Vec<u8> {
117 let mut ikm = Vec::with_capacity(shared_secret.len() + 32);
118 ikm.extend_from_slice(shared_secret);
119 if let Some(mesh_key) = &self.mesh_handshake_key {
120 ikm.extend_from_slice(mesh_key);
121 }
122 ikm
123 }
124
125 pub fn initiate(&mut self) -> HandshakeInit {
127 let ephemeral_secret = StaticSecret::random_from_rng(OsRng);
128 let ephemeral_pub = X25519PublicKey::from(&ephemeral_secret);
129 let ephemeral_pub_bytes = ephemeral_pub.to_bytes();
130
131 let mut nonce = [0u8; 32];
132 rand::RngCore::fill_bytes(&mut OsRng, &mut nonce);
133
134 let mut msg = Vec::with_capacity(64);
136 msg.extend_from_slice(&ephemeral_pub_bytes);
137 msg.extend_from_slice(&nonce);
138
139 let signing_key = ed25519_dalek::SigningKey::from_bytes(&self.identity.signing_key_bytes);
140 let signature = signing_key.sign(&msg);
141
142 self.ephemeral_secret = Some(ephemeral_secret);
144 self.ephemeral_pub = Some(ephemeral_pub_bytes);
145
146 let init = HandshakeInit {
147 sender_pub: self.identity.verifying_key_bytes,
148 ephemeral_pub: ephemeral_pub_bytes,
149 nonce,
150 signature: signature.to_bytes(),
151 };
152
153 self.transcript.extend_from_slice(&init.sender_pub);
155 self.transcript.extend_from_slice(&init.ephemeral_pub);
156 self.transcript.extend_from_slice(&init.nonce);
157
158 init
159 }
160
161 pub fn respond(&mut self, init: &HandshakeInit) -> Result<HandshakeResponse, HandshakeError> {
163 let verifying_key = ed25519_dalek::VerifyingKey::from_bytes(&init.sender_pub)
165 .map_err(|_| HandshakeError::InvalidPublicKey)?;
166
167 let mut msg = Vec::with_capacity(64);
168 msg.extend_from_slice(&init.ephemeral_pub);
169 msg.extend_from_slice(&init.nonce);
170
171 let signature = ed25519_dalek::Signature::from_bytes(&init.signature);
172 verifying_key
173 .verify(&msg, &signature)
174 .map_err(|_| HandshakeError::InvalidSignature)?;
175
176 let ephemeral_secret = StaticSecret::random_from_rng(OsRng);
178 let ephemeral_pub = X25519PublicKey::from(&ephemeral_secret);
179 let ephemeral_pub_bytes = ephemeral_pub.to_bytes();
180
181 let mut nonce = [0u8; 32];
182 rand::RngCore::fill_bytes(&mut OsRng, &mut nonce);
183
184 let mut sign_msg = Vec::with_capacity(64);
186 sign_msg.extend_from_slice(&ephemeral_pub_bytes);
187 sign_msg.extend_from_slice(&nonce);
188
189 let signing_key = ed25519_dalek::SigningKey::from_bytes(&self.identity.signing_key_bytes);
190 let signature = signing_key.sign(&sign_msg);
191
192 let their_ephemeral = X25519PublicKey::from(init.ephemeral_pub);
194 let shared_secret = ephemeral_secret.diffie_hellman(&their_ephemeral);
195
196 self.transcript.extend_from_slice(&init.sender_pub);
198 self.transcript.extend_from_slice(&init.ephemeral_pub);
199 self.transcript.extend_from_slice(&init.nonce);
200 self.transcript
201 .extend_from_slice(&self.identity.verifying_key_bytes);
202 self.transcript.extend_from_slice(&ephemeral_pub_bytes);
203 self.transcript.extend_from_slice(&nonce);
204
205 let mut salt = Vec::with_capacity(64);
208 salt.extend_from_slice(&init.nonce);
209 salt.extend_from_slice(&nonce);
210
211 let ikm = self.ikm_with_mesh(shared_secret.as_bytes());
212 let hk = Hkdf::<Sha256>::new(Some(&salt), &ikm);
213 let mut key = [0u8; 32];
214 hk.expand(b"pim-session-v1", &mut key)
215 .expect("32 bytes is valid for HKDF-SHA256");
216
217 self.session_key = Some(SessionKey { key });
218 self.ephemeral_pub = Some(ephemeral_pub_bytes);
219
220 Ok(HandshakeResponse {
221 sender_pub: self.identity.verifying_key_bytes,
222 ephemeral_pub: ephemeral_pub_bytes,
223 nonce,
224 signature: signature.to_bytes(),
225 })
226 }
227
228 pub fn finalize_initiator(
230 &mut self,
231 response: &HandshakeResponse,
232 ) -> Result<(), HandshakeError> {
233 let verifying_key = ed25519_dalek::VerifyingKey::from_bytes(&response.sender_pub)
235 .map_err(|_| HandshakeError::InvalidPublicKey)?;
236
237 let mut msg = Vec::with_capacity(64);
238 msg.extend_from_slice(&response.ephemeral_pub);
239 msg.extend_from_slice(&response.nonce);
240
241 let signature = ed25519_dalek::Signature::from_bytes(&response.signature);
242 verifying_key
243 .verify(&msg, &signature)
244 .map_err(|_| HandshakeError::InvalidSignature)?;
245
246 let ephemeral_secret = self
248 .ephemeral_secret
249 .take()
250 .ok_or(HandshakeError::InvalidState)?;
251 let their_ephemeral = X25519PublicKey::from(response.ephemeral_pub);
252 let shared_secret = ephemeral_secret.diffie_hellman(&their_ephemeral);
253
254 self.transcript.extend_from_slice(&response.sender_pub);
256 self.transcript.extend_from_slice(&response.ephemeral_pub);
257 self.transcript.extend_from_slice(&response.nonce);
258
259 let init_nonce = &self.transcript[64..96];
263 let mut salt = Vec::with_capacity(64);
264 salt.extend_from_slice(init_nonce);
265 salt.extend_from_slice(&response.nonce);
266
267 let ikm = self.ikm_with_mesh(shared_secret.as_bytes());
268 let hk = Hkdf::<Sha256>::new(Some(&salt), &ikm);
269 let mut key = [0u8; 32];
270 hk.expand(b"pim-session-v1", &mut key)
271 .expect("32 bytes is valid for HKDF-SHA256");
272
273 self.session_key = Some(SessionKey { key });
274 Ok(())
275 }
276
277 pub fn make_confirm(&self) -> Result<HandshakeConfirm, HandshakeError> {
279 let session_key = self
280 .session_key
281 .as_ref()
282 .ok_or(HandshakeError::InvalidState)?;
283 let mut mac =
284 HmacSha256::new_from_slice(session_key.as_bytes()).expect("HMAC accepts any key size");
285 mac.update(&self.transcript);
286 let result = mac.finalize().into_bytes();
287 let mut hmac = [0u8; 32];
288 hmac.copy_from_slice(&result);
289 Ok(HandshakeConfirm { hmac })
290 }
291
292 pub fn verify_confirm(&self, confirm: &HandshakeConfirm) -> Result<(), HandshakeError> {
294 let session_key = self
295 .session_key
296 .as_ref()
297 .ok_or(HandshakeError::InvalidState)?;
298 let mut mac =
299 HmacSha256::new_from_slice(session_key.as_bytes()).expect("HMAC accepts any key size");
300 mac.update(&self.transcript);
301 mac.verify_slice(&confirm.hmac)
302 .map_err(|_| HandshakeError::ConfirmMismatch)
303 }
304
305 pub fn session_key(&self) -> Option<&SessionKey> {
307 self.session_key.as_ref()
308 }
309}
310
311#[derive(Debug, thiserror::Error)]
312pub enum HandshakeError {
314 #[error("invalid public key")]
316 InvalidPublicKey,
317 #[error("invalid signature")]
319 InvalidSignature,
320 #[error("handshake confirm mismatch")]
322 ConfirmMismatch,
323 #[error("invalid handshake state")]
325 InvalidState,
326}
327
328#[cfg(test)]
329mod tests;