1use std::sync::LazyLock;
24
25use libp2p_identity as identity;
26use rand::{Rng as _, SeedableRng};
27use snow::params::NoiseParams;
28use x25519_dalek::{x25519, X25519_BASEPOINT_BYTES};
29use zeroize::Zeroize;
30
31use crate::Error;
32
33pub(crate) const STATIC_KEY_DOMAIN: &str = "noise-libp2p-static-key:";
35
36pub(crate) static PARAMS_XX: LazyLock<NoiseParams> = LazyLock::new(|| {
37 "Noise_XX_25519_ChaChaPoly_SHA256"
38 .parse()
39 .expect("Invalid protocol name")
40});
41
42pub(crate) fn noise_params_into_builder<'b>(
43 params: NoiseParams,
44 prologue: &'b [u8],
45 private_key: &'b SecretKey,
46 remote_public_key: Option<&'b PublicKey>,
47) -> snow::Builder<'b> {
48 let mut builder = snow::Builder::with_resolver(params, Box::new(Resolver))
49 .prologue(prologue.as_ref())
50 .local_private_key(private_key.as_ref());
51
52 if let Some(remote_public_key) = remote_public_key {
53 builder = builder.remote_public_key(remote_public_key.as_ref());
54 }
55
56 builder
57}
58
59#[derive(Clone)]
61pub(crate) struct Keypair {
62 secret: SecretKey,
63 public: PublicKey,
64}
65
66#[derive(Clone)]
68pub(crate) struct AuthenticKeypair {
69 pub(crate) keypair: Keypair,
70 pub(crate) identity: KeypairIdentity,
71}
72
73#[derive(Clone)]
75pub(crate) struct KeypairIdentity {
76 pub(crate) public: identity::PublicKey,
78 pub(crate) signature: Vec<u8>,
80}
81
82impl Keypair {
83 pub(crate) fn secret(&self) -> &SecretKey {
85 &self.secret
86 }
87
88 pub(crate) fn into_authentic(
91 self,
92 id_keys: &identity::Keypair,
93 ) -> Result<AuthenticKeypair, Error> {
94 let sig = id_keys.sign(&[STATIC_KEY_DOMAIN.as_bytes(), self.public.as_ref()].concat())?;
95
96 let identity = KeypairIdentity {
97 public: id_keys.public(),
98 signature: sig,
99 };
100
101 Ok(AuthenticKeypair {
102 keypair: self,
103 identity,
104 })
105 }
106
107 pub(crate) fn empty() -> Self {
110 Keypair {
111 secret: SecretKey([0u8; 32]),
112 public: PublicKey([0u8; 32]),
113 }
114 }
115
116 pub(crate) fn new() -> Keypair {
118 let mut sk_bytes = [0u8; 32];
119 rand::thread_rng().fill(&mut sk_bytes);
120 let sk = SecretKey(sk_bytes); sk_bytes.zeroize();
122 Self::from(sk)
123 }
124}
125
126#[derive(Clone, Default)]
128pub(crate) struct SecretKey([u8; 32]);
129
130impl Drop for SecretKey {
131 fn drop(&mut self) {
132 self.0.zeroize()
133 }
134}
135
136impl AsRef<[u8]> for SecretKey {
137 fn as_ref(&self) -> &[u8] {
138 self.0.as_ref()
139 }
140}
141
142#[derive(Clone, PartialEq, Default)]
144pub(crate) struct PublicKey([u8; 32]);
145
146impl PublicKey {
147 pub(crate) fn from_slice(slice: &[u8]) -> Result<Self, Error> {
148 if slice.len() != 32 {
149 return Err(Error::InvalidLength);
150 }
151
152 let mut key = [0u8; 32];
153 key.copy_from_slice(slice);
154 Ok(PublicKey(key))
155 }
156}
157
158impl AsRef<[u8]> for PublicKey {
159 fn as_ref(&self) -> &[u8] {
160 self.0.as_ref()
161 }
162}
163
164struct Resolver;
169
170impl snow::resolvers::CryptoResolver for Resolver {
171 fn resolve_rng(&self) -> Option<Box<dyn snow::types::Random>> {
172 Some(Box::new(Rng(rand::rngs::StdRng::from_entropy())))
173 }
174
175 fn resolve_dh(&self, choice: &snow::params::DHChoice) -> Option<Box<dyn snow::types::Dh>> {
176 if let snow::params::DHChoice::Curve25519 = choice {
177 Some(Box::new(Keypair::empty()))
178 } else {
179 None
180 }
181 }
182
183 fn resolve_hash(
184 &self,
185 choice: &snow::params::HashChoice,
186 ) -> Option<Box<dyn snow::types::Hash>> {
187 #[cfg(target_arch = "wasm32")]
188 {
189 snow::resolvers::DefaultResolver.resolve_hash(choice)
190 }
191 #[cfg(not(target_arch = "wasm32"))]
192 {
193 snow::resolvers::RingResolver.resolve_hash(choice)
194 }
195 }
196
197 fn resolve_cipher(
198 &self,
199 choice: &snow::params::CipherChoice,
200 ) -> Option<Box<dyn snow::types::Cipher>> {
201 #[cfg(target_arch = "wasm32")]
202 {
203 snow::resolvers::DefaultResolver.resolve_cipher(choice)
204 }
205 #[cfg(not(target_arch = "wasm32"))]
206 {
207 snow::resolvers::RingResolver.resolve_cipher(choice)
208 }
209 }
210}
211
212struct Rng(rand::rngs::StdRng);
214
215impl rand::RngCore for Rng {
216 fn next_u32(&mut self) -> u32 {
217 self.0.next_u32()
218 }
219
220 fn next_u64(&mut self) -> u64 {
221 self.0.next_u64()
222 }
223
224 fn fill_bytes(&mut self, dest: &mut [u8]) {
225 self.0.fill_bytes(dest)
226 }
227
228 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
229 self.0.try_fill_bytes(dest)
230 }
231}
232
233impl rand::CryptoRng for Rng {}
234
235impl snow::types::Random for Rng {}
236
237impl Default for Keypair {
238 fn default() -> Self {
239 Self::new()
240 }
241}
242
243impl From<SecretKey> for Keypair {
245 fn from(secret: SecretKey) -> Keypair {
246 let public = PublicKey(x25519(secret.0, X25519_BASEPOINT_BYTES));
247 Keypair { secret, public }
248 }
249}
250
251#[doc(hidden)]
252impl snow::types::Dh for Keypair {
253 fn name(&self) -> &'static str {
254 "25519"
255 }
256 fn pub_len(&self) -> usize {
257 32
258 }
259 fn priv_len(&self) -> usize {
260 32
261 }
262 fn pubkey(&self) -> &[u8] {
263 self.public.as_ref()
264 }
265 fn privkey(&self) -> &[u8] {
266 self.secret.as_ref()
267 }
268
269 fn set(&mut self, sk: &[u8]) {
270 let mut secret = [0u8; 32];
271 secret.copy_from_slice(sk);
272 self.secret = SecretKey(secret); self.public = PublicKey(x25519(secret, X25519_BASEPOINT_BYTES));
274 secret.zeroize();
275 }
276
277 fn generate(&mut self, rng: &mut dyn snow::types::Random) {
278 let mut secret = [0u8; 32];
279 rng.fill_bytes(&mut secret);
280 self.secret = SecretKey(secret); self.public = PublicKey(x25519(secret, X25519_BASEPOINT_BYTES));
282 secret.zeroize();
283 }
284
285 fn dh(&self, pk: &[u8], shared_secret: &mut [u8]) -> Result<(), snow::Error> {
286 let mut p = [0; 32];
287 p.copy_from_slice(&pk[..32]);
288 let ss = x25519(self.secret.0, p);
289 shared_secret[..32].copy_from_slice(&ss[..]);
290 Ok(())
291 }
292}
293
294#[cfg(test)]
295mod tests {
296 use std::sync::LazyLock;
297
298 use super::*;
299
300 #[test]
301 fn handshake_hashes_disagree_if_prologue_differs() {
302 let alice = xx_builder(b"alice prologue").build_initiator().unwrap();
303 let bob = xx_builder(b"bob prologue").build_responder().unwrap();
304
305 let alice_handshake_hash = alice.get_handshake_hash();
306 let bob_handshake_hash = bob.get_handshake_hash();
307
308 assert_ne!(alice_handshake_hash, bob_handshake_hash)
309 }
310
311 #[test]
312 fn handshake_hashes_agree_if_prologue_is_the_same() {
313 let alice = xx_builder(b"shared knowledge").build_initiator().unwrap();
314 let bob = xx_builder(b"shared knowledge").build_responder().unwrap();
315
316 let alice_handshake_hash = alice.get_handshake_hash();
317 let bob_handshake_hash = bob.get_handshake_hash();
318
319 assert_eq!(alice_handshake_hash, bob_handshake_hash)
320 }
321
322 fn xx_builder(prologue: &'static [u8]) -> snow::Builder<'static> {
323 noise_params_into_builder(PARAMS_XX.clone(), prologue, TEST_KEY.secret(), None)
324 }
325
326 static TEST_KEY: LazyLock<Keypair> = LazyLock::new(Keypair::new);
328}