1use crate::Result;
7use crate::error::ProtocolError;
8use blvm_secp256k1::ellswift::{ellswift_create, ellswift_xdh};
9use chacha20poly1305::{
10 ChaCha20Poly1305, Key, Nonce,
11 aead::{Aead, KeyInit, Payload},
12};
13use getrandom::getrandom;
14use hkdf::Hkdf;
15use sha2::Sha256;
16use std::borrow::Cow;
17
18const REKEY_INTERVAL: u64 = 224;
20const MAINNET_MAGIC: [u8; 4] = [0xf9, 0xbe, 0xb4, 0xd9];
21pub const GARBAGE_MAX: usize = 4095;
23
24pub fn v1_version_prefix(magic: [u8; 4]) -> [u8; 16] {
26 let mut prefix = [0u8; 16];
27 prefix[..4].copy_from_slice(&magic);
28 prefix[4..].copy_from_slice(b"version\x00\x00\x00\x00\x00");
29 prefix
30}
31
32#[derive(Clone, Debug)]
34pub struct SessionKeys {
35 pub initiator_l: [u8; 32],
36 pub initiator_p: [u8; 32],
37 pub responder_l: [u8; 32],
38 pub responder_p: [u8; 32],
39 pub send_garbage_terminator: [u8; 16],
40 pub recv_garbage_terminator: [u8; 16],
41 pub session_id: [u8; 32],
42}
43
44pub struct V2Send {
46 send_l: FsChaCha20,
47 send_p: FsChaCha20Poly1305,
48}
49
50pub struct V2Recv {
52 recv_l: FsChaCha20,
53 recv_p: FsChaCha20Poly1305,
54}
55
56pub struct V2Transport {
58 send: V2Send,
59 recv: V2Recv,
60}
61
62pub struct V2Session {
64 transport: V2Transport,
65 pub session_id: [u8; 32],
66 pub send_garbage_terminator: [u8; 16],
67 pub recv_garbage_terminator: [u8; 16],
68}
69
70pub struct InitiatorPending {
72 private_key: [u8; 32],
73 ellswift: [u8; 64],
74 garbage: Vec<u8>,
75}
76
77pub struct ResponderPending {
79 ellswift: [u8; 64],
80 session: V2Session,
81 garbage: Vec<u8>,
82}
83
84pub enum V2Handshake {
86 Initiator {
87 private_key: [u8; 32],
88 ellswift: [u8; 64],
89 },
90 Responder {
91 private_key: [u8; 32],
92 initiator_ellswift: Option<[u8; 64]>,
93 },
94}
95
96struct FsChaCha20 {
97 key: [u8; 32],
98 block_counter: u32,
99 chunk_counter: u64,
100 keystream: Vec<u8>,
101}
102
103struct FsChaCha20Poly1305 {
104 key: [u8; 32],
105 packet_counter: u64,
106}
107
108fn chacha20_block(key: &[u8; 32], nonce: &[u8; 12], counter: u32) -> [u8; 64] {
109 fn quarter(s: &mut [u32; 16], a: usize, b: usize, c: usize, d: usize) {
110 s[a] = s[a].wrapping_add(s[b]);
111 s[d] ^= s[a];
112 s[d] = s[d].rotate_left(16);
113 s[c] = s[c].wrapping_add(s[d]);
114 s[b] ^= s[c];
115 s[b] = s[b].rotate_left(12);
116 s[a] = s[a].wrapping_add(s[b]);
117 s[d] ^= s[a];
118 s[d] = s[d].rotate_left(8);
119 s[c] = s[c].wrapping_add(s[d]);
120 s[b] ^= s[c];
121 s[b] = s[b].rotate_left(7);
122 }
123 let mut init = [0u32; 16];
124 init[0] = 0x6170_7865;
125 init[1] = 0x3320_646e;
126 init[2] = 0x7962_2d32;
127 init[3] = 0x6b20_6574;
128 for i in 0..8 {
129 init[4 + i] = u32::from_le_bytes(key[4 * i..4 * i + 4].try_into().unwrap());
130 }
131 init[12] = counter;
132 for i in 0..3 {
133 init[13 + i] = u32::from_le_bytes(nonce[4 * i..4 * i + 4].try_into().unwrap());
134 }
135 let mut state = init;
136 for _ in 0..10 {
137 quarter(&mut state, 0, 4, 8, 12);
138 quarter(&mut state, 1, 5, 9, 13);
139 quarter(&mut state, 2, 6, 10, 14);
140 quarter(&mut state, 3, 7, 11, 15);
141 quarter(&mut state, 0, 5, 10, 15);
142 quarter(&mut state, 1, 6, 11, 12);
143 quarter(&mut state, 2, 7, 8, 13);
144 quarter(&mut state, 3, 4, 9, 14);
145 }
146 let mut out = [0u8; 64];
147 for i in 0..16 {
148 let word = state[i].wrapping_add(init[i]);
149 out[4 * i..4 * i + 4].copy_from_slice(&word.to_le_bytes());
150 }
151 out
152}
153
154impl FsChaCha20 {
155 fn new(key: [u8; 32]) -> Self {
156 Self {
157 key,
158 block_counter: 0,
159 chunk_counter: 0,
160 keystream: Vec::new(),
161 }
162 }
163
164 fn keystream_bytes(&mut self, nbytes: usize) -> Vec<u8> {
165 while self.keystream.len() < nbytes {
166 let mut nonce = [0u8; 12];
167 nonce[4..12].copy_from_slice(&(self.chunk_counter / REKEY_INTERVAL).to_le_bytes());
168 self.keystream.extend_from_slice(&chacha20_block(
169 &self.key,
170 &nonce,
171 self.block_counter,
172 ));
173 self.block_counter += 1;
174 }
175 let out = self.keystream[..nbytes].to_vec();
176 self.keystream.drain(..nbytes);
177 out
178 }
179
180 fn crypt(&mut self, chunk: &[u8]) -> Vec<u8> {
181 let ks = self.keystream_bytes(chunk.len());
182 let out: Vec<u8> = chunk.iter().zip(ks).map(|(b, k)| b ^ k).collect();
183 if (self.chunk_counter + 1) % REKEY_INTERVAL == 0 {
184 let next = self.keystream_bytes(32);
185 self.key.copy_from_slice(&next);
186 self.block_counter = 0;
187 }
188 self.chunk_counter += 1;
189 out
190 }
191}
192
193impl FsChaCha20Poly1305 {
194 fn new(key: [u8; 32]) -> Self {
195 Self {
196 key,
197 packet_counter: 0,
198 }
199 }
200
201 fn crypt(&mut self, aad: &[u8], text: &[u8], decrypt: bool) -> Result<Vec<u8>> {
202 let mut nonce = [0u8; 12];
203 nonce[..4].copy_from_slice(&((self.packet_counter % REKEY_INTERVAL) as u32).to_le_bytes());
204 nonce[4..].copy_from_slice(&(self.packet_counter / REKEY_INTERVAL).to_le_bytes());
205 let cipher = ChaCha20Poly1305::new(Key::from_slice(&self.key));
206 let nonce_ref = Nonce::from_slice(&nonce);
207 let payload = Payload { msg: text, aad };
208 let ret = if decrypt {
209 cipher.decrypt(nonce_ref, payload)
210 } else {
211 cipher.encrypt(nonce_ref, payload)
212 }
213 .map_err(|_| protocol_err("packet authentication failed"))?;
214 if (self.packet_counter + 1) % REKEY_INTERVAL == 0 {
215 let mut rekey_nonce = nonce;
216 rekey_nonce[..4].copy_from_slice(&[0xff, 0xff, 0xff, 0xff]);
217 let rekey = ChaCha20Poly1305::new(Key::from_slice(&self.key));
218 let mixed = rekey
219 .encrypt(
220 Nonce::from_slice(&rekey_nonce),
221 Payload {
222 msg: &[0u8; 32],
223 aad: &[],
224 },
225 )
226 .map_err(|_| protocol_err("rekey failed"))?;
227 self.key.copy_from_slice(&mixed[..32]);
228 }
229 self.packet_counter += 1;
230 Ok(ret)
231 }
232}
233
234fn protocol_err(msg: &str) -> ProtocolError {
235 ProtocolError::Consensus(blvm_consensus::error::ConsensusError::Serialization(
236 Cow::Owned(msg.to_string()),
237 ))
238}
239
240fn hkdf_sha256(salt: &[u8], ikm: &[u8], info: &[u8], out: &mut [u8]) {
241 let hk = Hkdf::<Sha256>::new(Some(salt), ikm);
242 hk.expand(info, out)
243 .expect("HKDF expansion fits in 255 blocks");
244}
245
246pub fn session_keys(shared_secret: &[u8; 32], initiating: bool, magic: [u8; 4]) -> SessionKeys {
248 let mut salt = b"bitcoin_v2_shared_secret".to_vec();
249 salt.extend_from_slice(&magic);
250 let mut initiator_l = [0u8; 32];
251 let mut initiator_p = [0u8; 32];
252 let mut responder_l = [0u8; 32];
253 let mut responder_p = [0u8; 32];
254 let mut garbage = [0u8; 32];
255 let mut session_id = [0u8; 32];
256 hkdf_sha256(&salt, shared_secret, b"initiator_L", &mut initiator_l);
257 hkdf_sha256(&salt, shared_secret, b"initiator_P", &mut initiator_p);
258 hkdf_sha256(&salt, shared_secret, b"responder_L", &mut responder_l);
259 hkdf_sha256(&salt, shared_secret, b"responder_P", &mut responder_p);
260 hkdf_sha256(&salt, shared_secret, b"garbage_terminators", &mut garbage);
261 hkdf_sha256(&salt, shared_secret, b"session_id", &mut session_id);
262 let (send_garbage, recv_garbage) = if initiating {
263 (
264 garbage[..16].try_into().unwrap(),
265 garbage[16..].try_into().unwrap(),
266 )
267 } else {
268 (
269 garbage[16..].try_into().unwrap(),
270 garbage[..16].try_into().unwrap(),
271 )
272 };
273 SessionKeys {
274 initiator_l,
275 initiator_p,
276 responder_l,
277 responder_p,
278 send_garbage_terminator: send_garbage,
279 recv_garbage_terminator: recv_garbage,
280 session_id,
281 }
282}
283
284impl SessionKeys {
285 fn transport(self, initiating: bool) -> V2Transport {
286 let (send_l, send_p, recv_l, recv_p) = if initiating {
287 (
288 self.initiator_l,
289 self.initiator_p,
290 self.responder_l,
291 self.responder_p,
292 )
293 } else {
294 (
295 self.responder_l,
296 self.responder_p,
297 self.initiator_l,
298 self.initiator_p,
299 )
300 };
301 V2Transport {
302 send: V2Send {
303 send_l: FsChaCha20::new(send_l),
304 send_p: FsChaCha20Poly1305::new(send_p),
305 },
306 recv: V2Recv {
307 recv_l: FsChaCha20::new(recv_l),
308 recv_p: FsChaCha20Poly1305::new(recv_p),
309 },
310 }
311 }
312
313 fn into_session(self, initiating: bool) -> V2Session {
314 let session_id = self.session_id;
315 let send_garbage_terminator = self.send_garbage_terminator;
316 let recv_garbage_terminator = self.recv_garbage_terminator;
317 V2Session {
318 transport: self.transport(initiating),
319 session_id,
320 send_garbage_terminator,
321 recv_garbage_terminator,
322 }
323 }
324}
325
326impl V2Send {
327 pub fn encrypt(&mut self, plaintext: &[u8]) -> Result<Vec<u8>> {
329 self.encrypt_packet(plaintext, &[], false)
330 }
331
332 pub fn encrypt_packet(&mut self, contents: &[u8], aad: &[u8], ignore: bool) -> Result<Vec<u8>> {
334 if contents.len() > 0x00ff_ffff {
335 return Err(protocol_err("contents longer than 2^24-1"));
336 }
337 let mut plain = Vec::with_capacity(1 + contents.len());
338 plain.push(if ignore { 0x80 } else { 0 });
339 plain.extend_from_slice(contents);
340 let body = self.send_p.crypt(aad, &plain, false)?;
341 let len = (contents.len() as u32).to_le_bytes();
342 let enc_len = self.send_l.crypt(&len[..3]);
343 let mut packet = enc_len;
344 packet.extend_from_slice(&body);
345 Ok(packet)
346 }
347}
348
349impl V2Recv {
350 pub fn decrypt_length(&mut self, enc_len: &[u8]) -> Result<usize> {
352 if enc_len.len() != 3 {
353 return Err(protocol_err("length is 3 bytes"));
354 }
355 let len_bytes = self.recv_l.crypt(enc_len);
356 Ok(u32::from_le_bytes([len_bytes[0], len_bytes[1], len_bytes[2], 0]) as usize)
357 }
358
359 pub fn decrypt_body(&mut self, body: &[u8], aad: &[u8]) -> Result<(Vec<u8>, bool)> {
361 let plain = self.recv_p.crypt(aad, body, true)?;
362 if plain.is_empty() {
363 return Err(protocol_err("decrypted packet has no header"));
364 }
365 let ignore = plain[0] & 0x80 != 0;
366 Ok((plain[1..].to_vec(), ignore))
367 }
368
369 pub fn decrypt(&mut self, packet: &[u8]) -> Result<Vec<u8>> {
371 self.decrypt_packet(packet, &[])
372 .map(|(contents, _)| contents)
373 }
374
375 pub fn decrypt_packet(&mut self, packet: &[u8], aad: &[u8]) -> Result<(Vec<u8>, bool)> {
377 if packet.len() < 3 + 1 + 16 {
378 return Err(protocol_err("packet too short"));
379 }
380 let contents_len = self.decrypt_length(&packet[..3])?;
381 let body_len = 1 + contents_len + 16;
382 if packet.len() != 3 + body_len {
383 return Err(protocol_err("packet length does not match"));
384 }
385 let (contents, ignore) = self.decrypt_body(&packet[3..], aad)?;
386 if contents.len() != contents_len {
387 return Err(protocol_err("decrypted contents length mismatch"));
388 }
389 Ok((contents, ignore))
390 }
391}
392
393impl V2Transport {
394 pub fn new(send_key: [u8; 32], recv_key: [u8; 32]) -> Self {
396 Self {
397 send: V2Send {
398 send_l: FsChaCha20::new(send_key),
399 send_p: FsChaCha20Poly1305::new(send_key),
400 },
401 recv: V2Recv {
402 recv_l: FsChaCha20::new(recv_key),
403 recv_p: FsChaCha20Poly1305::new(recv_key),
404 },
405 }
406 }
407
408 pub fn encrypt(&mut self, plaintext: &[u8]) -> Result<Vec<u8>> {
410 self.send.encrypt(plaintext)
411 }
412
413 pub fn decrypt(&mut self, packet: &[u8]) -> Result<Vec<u8>> {
415 self.recv.decrypt(packet)
416 }
417
418 pub fn encrypt_packet(&mut self, contents: &[u8], aad: &[u8], ignore: bool) -> Result<Vec<u8>> {
420 self.send.encrypt_packet(contents, aad, ignore)
421 }
422
423 pub fn decrypt_packet(&mut self, packet: &[u8], aad: &[u8]) -> Result<(Vec<u8>, bool)> {
425 self.recv.decrypt_packet(packet, aad)
426 }
427
428 pub fn decrypt_length(&mut self, enc_len: &[u8]) -> Result<usize> {
430 self.recv.decrypt_length(enc_len)
431 }
432
433 pub fn decrypt_body(&mut self, body: &[u8], aad: &[u8]) -> Result<(Vec<u8>, bool)> {
435 self.recv.decrypt_body(body, aad)
436 }
437}
438
439impl V2Session {
440 pub fn encrypt(&mut self, plaintext: &[u8]) -> Result<Vec<u8>> {
442 self.transport.encrypt(plaintext)
443 }
444
445 pub fn decrypt(&mut self, packet: &[u8]) -> Result<Vec<u8>> {
447 self.transport.decrypt(packet)
448 }
449
450 pub fn encrypt_packet(&mut self, contents: &[u8], aad: &[u8], ignore: bool) -> Result<Vec<u8>> {
452 self.transport.encrypt_packet(contents, aad, ignore)
453 }
454
455 pub fn decrypt_packet(&mut self, packet: &[u8], aad: &[u8]) -> Result<(Vec<u8>, bool)> {
457 self.transport.decrypt_packet(packet, aad)
458 }
459
460 pub fn decrypt_length(&mut self, enc_len: &[u8]) -> Result<usize> {
462 self.transport.decrypt_length(enc_len)
463 }
464
465 pub fn decrypt_body(&mut self, body: &[u8], aad: &[u8]) -> Result<(Vec<u8>, bool)> {
467 self.transport.decrypt_body(body, aad)
468 }
469
470 pub fn split(self) -> (V2Send, V2Recv) {
472 (self.transport.send, self.transport.recv)
473 }
474}
475
476fn shared_secret(
477 ours: &[u8; 64],
478 theirs: &[u8; 64],
479 private_key: &[u8; 32],
480 initiating: bool,
481) -> Result<[u8; 32]> {
482 let secret = if initiating {
483 ellswift_xdh(ours, theirs, private_key, false)
484 } else {
485 ellswift_xdh(theirs, ours, private_key, true)
486 };
487 secret.ok_or_else(|| protocol_err("ElligatorSwift ECDH failed"))
488}
489
490fn transport_from_secret(secret: &[u8; 32], initiating: bool) -> V2Transport {
491 session_keys(secret, initiating, MAINNET_MAGIC).transport(initiating)
492}
493
494impl V2Handshake {
495 pub fn new_initiator() -> (Vec<u8>, Self) {
497 let mut key_bytes = [0u8; 32];
498 getrandom(&mut key_bytes).expect("Failed to generate random bytes");
499 let mut aux_rand = [0u8; 32];
500 getrandom(&mut aux_rand).expect("Failed to generate aux_rand");
501 let ellswift = ellswift_create(&key_bytes, Some(&aux_rand))
502 .expect("Failed to create ElligatorSwift encoding");
503 (
504 ellswift.to_vec(),
505 Self::Initiator {
506 private_key: key_bytes,
507 ellswift,
508 },
509 )
510 }
511
512 pub fn new_responder() -> Self {
514 let mut key_bytes = [0u8; 32];
515 getrandom(&mut key_bytes).expect("Failed to generate random bytes");
516 Self::Responder {
517 private_key: key_bytes,
518 initiator_ellswift: None,
519 }
520 }
521
522 pub fn process_initiator_message(
524 &mut self,
525 initiator_msg: &[u8],
526 ) -> Result<(Vec<u8>, V2Transport)> {
527 if initiator_msg.len() != 64 {
528 return Err(protocol_err("Invalid initiator message length"));
529 }
530 let mut initiator_ell64 = [0u8; 64];
531 initiator_ell64.copy_from_slice(initiator_msg);
532 let responder_private = match self {
533 Self::Responder { private_key, .. } => *private_key,
534 _ => return Err(protocol_err("Not a responder handshake")),
535 };
536 let mut aux_rand = [0u8; 32];
537 getrandom(&mut aux_rand).expect("Failed to generate aux_rand");
538 let responder_ell64 = ellswift_create(&responder_private, Some(&aux_rand))
539 .expect("Failed to create ElligatorSwift encoding");
540 let secret = shared_secret(
541 &responder_ell64,
542 &initiator_ell64,
543 &responder_private,
544 false,
545 )?;
546 if let Self::Responder {
547 initiator_ellswift, ..
548 } = self
549 {
550 *initiator_ellswift = Some(initiator_ell64);
551 }
552 Ok((
553 responder_ell64.to_vec(),
554 transport_from_secret(&secret, false),
555 ))
556 }
557
558 pub fn complete_handshake(self, responder_msg: &[u8]) -> Result<V2Transport> {
560 if responder_msg.len() != 64 {
561 return Err(protocol_err("Invalid responder message length"));
562 }
563 let mut responder_ell64 = [0u8; 64];
564 responder_ell64.copy_from_slice(responder_msg);
565 let (private_key, initiator_ell64) = match self {
566 Self::Initiator {
567 private_key,
568 ellswift,
569 } => (private_key, ellswift),
570 _ => return Err(protocol_err("Not an initiator handshake")),
571 };
572 let secret = shared_secret(&initiator_ell64, &responder_ell64, &private_key, true)?;
573 Ok(transport_from_secret(&secret, true))
574 }
575}
576
577fn fresh_ellswift() -> ([u8; 32], [u8; 64]) {
578 let mut key_bytes = [0u8; 32];
579 getrandom(&mut key_bytes).expect("Failed to generate random bytes");
580 let mut aux_rand = [0u8; 32];
581 getrandom(&mut aux_rand).expect("Failed to generate aux_rand");
582 let ellswift = ellswift_create(&key_bytes, Some(&aux_rand))
583 .expect("Failed to create ElligatorSwift encoding");
584 (key_bytes, ellswift)
585}
586
587fn garbage_ok(garbage: &[u8]) -> Result<()> {
588 if garbage.len() > GARBAGE_MAX {
589 return Err(protocol_err("garbage longer than 4095"));
590 }
591 Ok(())
592}
593
594impl InitiatorPending {
595 pub fn garbage(&self) -> &[u8] {
597 &self.garbage
598 }
599}
600
601pub fn begin_initiator(garbage: &[u8]) -> Result<(Vec<u8>, InitiatorPending)> {
603 garbage_ok(garbage)?;
604 let (private_key, ellswift) = fresh_ellswift();
605 let mut opening = ellswift.to_vec();
606 opening.extend_from_slice(garbage);
607 Ok((
608 opening,
609 InitiatorPending {
610 private_key,
611 ellswift,
612 garbage: garbage.to_vec(),
613 },
614 ))
615}
616
617pub fn responder_accept(
619 initiator_ellswift: &[u8],
620 garbage: &[u8],
621 magic: [u8; 4],
622) -> Result<ResponderPending> {
623 garbage_ok(garbage)?;
624 if initiator_ellswift.len() != 64 {
625 return Err(protocol_err("ellswift is 64 bytes"));
626 }
627 let mut initiator_ell64 = [0u8; 64];
628 initiator_ell64.copy_from_slice(initiator_ellswift);
629 let (private_key, ellswift) = fresh_ellswift();
630 let secret = shared_secret(&ellswift, &initiator_ell64, &private_key, false)?;
631 Ok(ResponderPending {
632 ellswift,
633 session: session_keys(&secret, false, magic).into_session(false),
634 garbage: garbage.to_vec(),
635 })
636}
637
638impl ResponderPending {
639 pub fn send_version(self, version: &[u8]) -> Result<(Vec<u8>, V2Session)> {
641 self.send_with_decoys(&[], version)
642 }
643
644 pub fn send_with_decoys(
647 mut self,
648 decoys: &[&[u8]],
649 version: &[u8],
650 ) -> Result<(Vec<u8>, V2Session)> {
651 let garbage = std::mem::take(&mut self.garbage);
652 let mut wire = self.ellswift.to_vec();
653 wire.extend_from_slice(&garbage);
654 wire.extend_from_slice(&self.session.send_garbage_terminator);
655 wire.extend_from_slice(&write_handshake_packets(
656 &mut self.session,
657 &garbage,
658 decoys,
659 version,
660 )?);
661 Ok((wire, self.session))
662 }
663}
664
665pub fn initiator_session(
667 pending: &InitiatorPending,
668 responder_ellswift: &[u8],
669 magic: [u8; 4],
670) -> Result<V2Session> {
671 if responder_ellswift.len() != 64 {
672 return Err(protocol_err("ellswift is 64 bytes"));
673 }
674 let mut responder_ell64 = [0u8; 64];
675 responder_ell64.copy_from_slice(responder_ellswift);
676 let secret = shared_secret(
677 &pending.ellswift,
678 &responder_ell64,
679 &pending.private_key,
680 true,
681 )?;
682 Ok(session_keys(&secret, true, magic).into_session(true))
683}
684
685pub fn append_terminator_and_version(
687 session: &mut V2Session,
688 garbage: &[u8],
689 version: &[u8],
690) -> Result<Vec<u8>> {
691 append_handshake(session, garbage, &[], version)
692}
693
694pub fn append_handshake(
697 session: &mut V2Session,
698 garbage: &[u8],
699 decoys: &[&[u8]],
700 version: &[u8],
701) -> Result<Vec<u8>> {
702 let mut tail = session.send_garbage_terminator.to_vec();
703 tail.extend_from_slice(&write_handshake_packets(session, garbage, decoys, version)?);
704 Ok(tail)
705}
706
707fn write_handshake_packets(
708 session: &mut V2Session,
709 garbage: &[u8],
710 decoys: &[&[u8]],
711 version: &[u8],
712) -> Result<Vec<u8>> {
713 let mut out = Vec::new();
714 let mut first = true;
715 for decoy in decoys {
716 let aad: &[u8] = if first { garbage } else { &[] };
717 first = false;
718 out.extend_from_slice(&session.encrypt_packet(decoy, aad, true)?);
719 }
720 let aad: &[u8] = if first { garbage } else { &[] };
721 out.extend_from_slice(&session.encrypt_packet(version, aad, false)?);
722 Ok(out)
723}
724
725pub fn take_version_packet(
727 session: &mut V2Session,
728 mut bytes: &[u8],
729 garbage: &[u8],
730) -> Result<Vec<u8>> {
731 let mut first = true;
732 loop {
733 if bytes.len() < 3 + 1 + 16 {
734 return Err(protocol_err("handshake ended before the version packet"));
735 }
736 let contents_len = session.decrypt_length(&bytes[..3])?;
737 let total = 3 + 1 + contents_len + 16;
738 if bytes.len() < total {
739 return Err(protocol_err("truncated handshake packet"));
740 }
741 let aad: &[u8] = if first { garbage } else { &[] };
742 first = false;
743 let (contents, ignore) = session.decrypt_body(&bytes[3..total], aad)?;
744 if contents.len() != contents_len {
745 return Err(protocol_err("decrypted contents length mismatch"));
746 }
747 bytes = &bytes[total..];
748 if !ignore {
749 if !bytes.is_empty() {
750 return Err(protocol_err("bytes follow the version packet"));
751 }
752 return Ok(contents);
753 }
754 }
755}
756
757#[cfg(test)]
758mod tests {
759 use super::*;
760
761 #[test]
762 fn test_v2_transport_encrypt_decrypt() {
763 let key = [0x42; 32];
764 let mut transport = V2Transport::new(key, key);
765 let plaintext = b"Hello, Bitcoin!";
766 let encrypted = transport.encrypt(plaintext).unwrap();
767 let decrypted = transport.decrypt(&encrypted).unwrap();
768 assert_eq!(plaintext, decrypted.as_slice());
769 }
770
771 #[test]
772 fn test_v2_transport_rekey_round_trip() {
773 let key = [0x11; 32];
774 let mut enc = V2Transport::new(key, key);
775 let mut dec = V2Transport::new(key, key);
776 for i in 0..300 {
777 let pt = format!("msg{i}").into_bytes();
778 let pkt = enc.encrypt(&pt).unwrap();
779 let out = dec.decrypt(&pkt).unwrap();
780 assert_eq!(pt, out, "round-trip failed at i={i}");
781 }
782 }
783
784 #[test]
785 fn test_elligator_swift_encode_decode() {
786 let seckey = [0x01u8; 32];
787 let ell = ellswift_create(&seckey, None).expect("valid key");
788 assert_eq!(ell.len(), 64);
789 let shared = ellswift_xdh(&ell, &ell, &seckey, false);
790 assert!(shared.is_some(), "XDH should not fail on valid inputs");
791 }
792
793 #[test]
794 fn packet_encoding_matches_the_reference_vectors() {
795 use blvm_secp256k1::ecdsa::pubkey_from_secret;
796 use blvm_secp256k1::ellswift::{ellswift_decode_x, ellswift_ecdh_xonly, ellswift_xdh};
797 use blvm_secp256k1::scalar::Scalar;
798
799 let csv = include_str!("../tests/data/bip324/packet_encoding_test_vectors.csv");
800 let mut rows = 0;
801 for line in csv.lines().skip(1) {
802 if line.is_empty() {
803 continue;
804 }
805 let col: Vec<&str> = line.split(',').collect();
806 assert_eq!(col.len(), 22, "vector row has {} columns", col.len());
807 let initiating = col[4] == "1";
808 let priv_ours = hex32(col[1]);
809 let ell_ours = hex64(col[2]);
810 let ell_theirs = hex64(col[3]);
811 let mut scalar = Scalar::zero();
812 assert!(!scalar.set_b32(&priv_ours));
813 let point = pubkey_from_secret(&scalar);
814 let mut x = point.x;
815 x.normalize();
816 let mut x_ours = [0u8; 32];
817 x.get_b32(&mut x_ours);
818 assert_eq!(hex::encode(x_ours), col[9]);
819 assert_eq!(hex::encode(ellswift_decode_x(&ell_ours)), col[9]);
820 assert_eq!(hex::encode(ellswift_decode_x(&ell_theirs)), col[10]);
821 let x_shared = ellswift_ecdh_xonly(&ell_theirs, &priv_ours).unwrap();
822 assert_eq!(hex::encode(x_shared), col[11]);
823 let secret = if initiating {
824 ellswift_xdh(&ell_ours, &ell_theirs, &priv_ours, false).unwrap()
825 } else {
826 ellswift_xdh(&ell_theirs, &ell_ours, &priv_ours, true).unwrap()
827 };
828 assert_eq!(hex::encode(secret), col[12]);
829 let keys = session_keys(&secret, initiating, MAINNET_MAGIC);
830 assert_eq!(hex::encode(keys.initiator_l), col[13]);
831 assert_eq!(hex::encode(keys.initiator_p), col[14]);
832 assert_eq!(hex::encode(keys.responder_l), col[15]);
833 assert_eq!(hex::encode(keys.responder_p), col[16]);
834 assert_eq!(hex::encode(keys.send_garbage_terminator), col[17]);
835 assert_eq!(hex::encode(keys.recv_garbage_terminator), col[18]);
836 assert_eq!(hex::encode(keys.session_id), col[19]);
837 let mut transport = keys.transport(initiating);
838 let mut peer = session_keys(&secret, !initiating, MAINNET_MAGIC).transport(!initiating);
839 let idx: usize = col[0].parse().unwrap();
840 for _ in 0..idx {
841 let empty = transport.encrypt_packet(&[], &[], false).unwrap();
842 let (body, empty_ignore) = peer.decrypt_packet(&empty, &[]).unwrap();
843 assert!(body.is_empty() && !empty_ignore);
844 }
845 let multiply: usize = col[6].parse().unwrap();
846 let mut contents = hex::decode(col[5]).unwrap();
847 if multiply != 1 {
848 contents = contents.repeat(multiply);
849 }
850 let aad = hex::decode(col[7]).unwrap();
851 let ignore = col[8] == "1";
852 let ciphertext = transport.encrypt_packet(&contents, &aad, ignore).unwrap();
853 let got = hex::encode(&ciphertext);
854 if !col[20].is_empty() {
855 assert_eq!(got, col[20]);
856 }
857 if !col[21].is_empty() {
858 assert!(
859 got.ends_with(col[21]),
860 "ciphertext suffix mismatch at idx {idx}"
861 );
862 }
863 let (opened, opened_ignore) = peer.decrypt_packet(&ciphertext, &aad).unwrap();
864 assert_eq!(opened, contents);
865 assert_eq!(opened_ignore, ignore);
866 rows += 1;
867 }
868 assert_eq!(rows, 7);
869 }
870
871 #[test]
872 fn garbage_terminator_and_version_complete_the_handshake() {
873 let regtest = [0xfa, 0xbf, 0xb5, 0xda];
874 for (ours, theirs) in [(b"" as &[u8], b"" as &[u8]), (b"ig", b"rg-garbage")] {
875 let (opening, pending) = begin_initiator(ours).unwrap();
876 assert_eq!(&opening[64..], ours);
877 let responder = responder_accept(&opening[..64], theirs, regtest).unwrap();
878 let (wire, mut responder_session) = responder.send_version(b"resp-version").unwrap();
879 let mut initiator = initiator_session(&pending, &wire[..64], regtest).unwrap();
880 assert_eq!(initiator.session_id, responder_session.session_id);
881 assert_ne!(initiator.session_id, [0u8; 32]);
882 assert_eq!(
883 initiator.send_garbage_terminator,
884 responder_session.recv_garbage_terminator
885 );
886 let rest = &wire[64..];
887 assert!(rest.starts_with(theirs));
888 let term = initiator.recv_garbage_terminator;
889 assert_eq!(&rest[theirs.len()..theirs.len() + 16], &term);
890 let packet = &rest[theirs.len() + 16..];
891 assert!(
892 initiator
893 .decrypt_packet(packet, b"not-the-garbage")
894 .is_err()
895 );
896 let mut initiator = initiator_session(&pending, &wire[..64], regtest).unwrap();
897 let (version, ignore) = initiator.decrypt_packet(packet, theirs).unwrap();
898 assert_eq!(version, b"resp-version");
899 assert!(!ignore);
900 let tail =
901 append_terminator_and_version(&mut initiator, ours, b"init-version").unwrap();
902 assert_eq!(&tail[..16], &responder_session.recv_garbage_terminator);
903 let (version, ignore) = responder_session.decrypt_packet(&tail[16..], ours).unwrap();
904 assert_eq!(version, b"init-version");
905 assert!(!ignore);
906 let next = initiator.encrypt(b"ping").unwrap();
907 assert_eq!(responder_session.decrypt(&next).unwrap(), b"ping");
908 }
909 }
910
911 #[test]
912 fn a_decoy_authenticates_the_garbage_and_the_version_follows() {
913 let regtest = [0xfa, 0xbf, 0xb5, 0xda];
914 let (opening, pending) = begin_initiator(b"ig").unwrap();
915 let responder = responder_accept(&opening[..64], b"rg", regtest).unwrap();
916 let (wire, mut responder_session) = responder
917 .send_with_decoys(&[b"\x00"], b"resp-version")
918 .unwrap();
919 let mut initiator = initiator_session(&pending, &wire[..64], regtest).unwrap();
920 let packets = &wire[64 + 2 + 16..];
921 let version = take_version_packet(&mut initiator, packets, b"rg").unwrap();
922 assert_eq!(version, b"resp-version");
923 let tail = append_handshake(&mut initiator, b"ig", &[b"\x00"], b"init-version").unwrap();
924 let version = take_version_packet(&mut responder_session, &tail[16..], b"ig").unwrap();
925 assert_eq!(version, b"init-version");
926 let next = initiator.encrypt(b"ping").unwrap();
927 assert_eq!(responder_session.decrypt(&next).unwrap(), b"ping");
928
929 let (opening, pending) = begin_initiator(b"ig").unwrap();
930 let responder = responder_accept(&opening[..64], b"rg", regtest).unwrap();
931 let (wire, _) = responder
932 .send_with_decoys(&[b"\x00"], b"resp-version")
933 .unwrap();
934 let mut wrong = initiator_session(&pending, &wire[..64], regtest).unwrap();
935 assert!(take_version_packet(&mut wrong, &wire[64 + 2 + 16..], b"no").is_err());
936 }
937
938 #[test]
939 fn garbage_longer_than_4095_is_rejected() {
940 assert!(begin_initiator(&vec![0u8; GARBAGE_MAX + 1]).is_err());
941 assert!(begin_initiator(&vec![0u8; GARBAGE_MAX]).is_ok());
942 }
943
944 #[test]
945 fn a_different_network_magic_does_not_open_the_packet() {
946 let regtest = [0xfa, 0xbf, 0xb5, 0xda];
947 let (opening, pending) = begin_initiator(b"ig").unwrap();
948 let responder = responder_accept(&opening[..64], b"rg", regtest).unwrap();
949 let (wire, _) = responder.send_version(b"resp-version").unwrap();
950 let mut wrong = initiator_session(&pending, &wire[..64], MAINNET_MAGIC).unwrap();
951 let packet = &wire[64 + 2 + 16..];
952 assert!(wrong.decrypt_packet(packet, b"rg").is_err());
953 }
954
955 #[test]
956 fn the_v1_prefix_is_magic_plus_the_version_command() {
957 let prefix = v1_version_prefix(MAINNET_MAGIC);
958 assert_eq!(&prefix[..4], &MAINNET_MAGIC);
959 assert_eq!(&prefix[4..], b"version\x00\x00\x00\x00\x00");
960 }
961
962 fn hex32(s: &str) -> [u8; 32] {
963 hex::decode(s).unwrap().try_into().unwrap()
964 }
965
966 fn hex64(s: &str) -> [u8; 64] {
967 hex::decode(s).unwrap().try_into().unwrap()
968 }
969}