1use bt_hci::param::ConnHandle;
2use embassy_time::Instant;
3use rand_core::{CryptoRng, RngCore};
4
5use self::util::PairingMethod;
6use crate::connection::{ConnectionEvent, SecurityLevel};
7use crate::security_manager::types::{BondingFlag, Command, PairingFeatures};
8use crate::security_manager::TxPacket;
9use crate::{Address, BondInformation, Error, IoCapabilities, LongTermKey, PacketPool};
10
11#[derive(Debug)]
12#[cfg_attr(feature = "defmt", derive(defmt::Format))]
13struct PairingData {
14 local_address: Address,
15 peer_address: Address,
16 local_features: PairingFeatures,
17 peer_features: PairingFeatures,
18 pairing_method: PairingMethod,
19 timeout_at: Instant,
20 bond_information: Option<BondInformation>,
21}
22
23impl PairingData {
24 fn new(
25 local_address: Address,
26 peer_address: Address,
27 local_io: IoCapabilities,
28 timeout: embassy_time::Duration,
29 ) -> PairingData {
30 PairingData {
31 local_address,
32 peer_address,
33 local_features: PairingFeatures {
34 io_capabilities: local_io,
35 ..Default::default()
36 },
37 peer_features: PairingFeatures::default(),
38 pairing_method: PairingMethod::JustWorks,
39 timeout_at: Instant::now() + timeout,
40 bond_information: None,
41 }
42 }
43
44 fn want_bonding(&self) -> bool {
45 matches!(self.local_features.security_properties.bond(), BondingFlag::Bonding)
46 && matches!(self.peer_features.security_properties.bond(), BondingFlag::Bonding)
47 }
48}
49
50pub mod central;
51#[cfg(feature = "legacy-pairing")]
52pub mod legacy_central;
53#[cfg(feature = "legacy-pairing")]
54pub mod legacy_peripheral;
55pub mod peripheral;
56mod util;
57
58pub(super) enum Input<'a> {
59 Command(Command, &'a [u8]),
60 Event(Event),
61}
62
63pub trait PairingOps<P: PacketPool> {
64 fn try_send_packet(&mut self, packet: TxPacket<P>) -> Result<(), Error>;
65 fn find_bond(&self) -> Option<BondInformation>;
66 fn try_enable_bonded_encryption(&mut self) -> Result<Option<BondInformation>, Error>;
67 fn try_enable_encryption(
68 &mut self,
69 ltk: &LongTermKey,
70 security_level: SecurityLevel,
71 is_bonded: bool,
72 #[cfg(feature = "legacy-pairing")] ediv: u16,
73 #[cfg(feature = "legacy-pairing")] rand: [u8; 8],
74 #[cfg(feature = "legacy-pairing")] encryption_key_len: u8,
75 ) -> Result<BondInformation, Error>;
76 fn try_update_bond_information(&mut self, bond: &BondInformation) -> Result<(), Error>;
77 fn connection_handle(&mut self) -> ConnHandle;
78 fn try_send_connection_event(&mut self, event: ConnectionEvent) -> Result<(), Error>;
79 fn bonding_flag(&self) -> BondingFlag;
80 fn oob_available(&self) -> bool;
82 fn secret_key(&self) -> &crate::security_manager::crypto::SecretKey;
84 fn public_key(&self) -> &crate::security_manager::crypto::PublicKey;
86 fn local_irk(&self) -> [u8; 16];
89 fn local_identity_address(&self) -> Result<Address, Error>;
92}
93
94#[derive(Debug)]
95#[cfg_attr(feature = "defmt", derive(defmt::Format))]
96enum State {
97 Central(central::Pairing),
98 Peripheral(peripheral::Pairing),
99 #[cfg(feature = "legacy-pairing")]
100 LegacyCentral(legacy_central::Pairing),
101 #[cfg(feature = "legacy-pairing")]
102 LegacyPeripheral(legacy_peripheral::Pairing),
103}
104
105#[derive(Debug)]
106#[cfg_attr(feature = "defmt", derive(defmt::Format))]
107pub struct Pairing {
108 pairing_data: PairingData,
109 state: State,
110}
111
112impl Pairing {
113 pub(crate) fn is_central(&self) -> bool {
114 match &self.state {
115 State::Central(_) => true,
116 #[cfg(feature = "legacy-pairing")]
117 State::LegacyCentral(_) => true,
118 _ => false,
119 }
120 }
121
122 #[cfg(feature = "legacy-pairing")]
123 pub(crate) fn is_lesc_central(&self) -> bool {
124 matches!(&self.state, State::Central(_))
125 }
126
127 #[cfg(feature = "legacy-pairing")]
128 pub(crate) fn is_lesc_peripheral(&self) -> bool {
129 matches!(&self.state, State::Peripheral(_))
130 }
131
132 pub(crate) fn result(&self) -> Option<Result<(), Error>> {
133 match &self.state {
134 State::Central(c) => c.result(),
135 State::Peripheral(p) => p.result(),
136 #[cfg(feature = "legacy-pairing")]
137 State::LegacyCentral(c) => c.result(),
138 #[cfg(feature = "legacy-pairing")]
139 State::LegacyPeripheral(p) => p.result(),
140 }
141 }
142
143 pub(crate) fn handle_l2cap_command<P: PacketPool, OPS: PairingOps<P>, RNG: CryptoRng + RngCore>(
144 &mut self,
145 command: Command,
146 payload: &[u8],
147 ops: &mut OPS,
148 rng: &mut RNG,
149 ) -> Result<(), Error> {
150 self.handle_input(Input::Command(command, payload), ops, rng)
151 }
152
153 pub(crate) fn handle_event<P: PacketPool, OPS: PairingOps<P>, RNG: CryptoRng + RngCore>(
154 &mut self,
155 event: Event,
156 ops: &mut OPS,
157 rng: &mut RNG,
158 ) -> Result<(), Error> {
159 self.handle_input(Input::Event(event), ops, rng)
160 }
161
162 fn handle_input<P: PacketPool, OPS: PairingOps<P>, RNG: CryptoRng + RngCore>(
163 &mut self,
164 input: Input<'_>,
165 ops: &mut OPS,
166 rng: &mut RNG,
167 ) -> Result<(), Error> {
168 match &mut self.state {
169 State::Central(central) => central.handle_input(input, &mut self.pairing_data, ops, rng),
170 State::Peripheral(peripheral) => peripheral.handle_input(input, &mut self.pairing_data, ops, rng),
171 #[cfg(feature = "legacy-pairing")]
172 State::LegacyCentral(central) => central.handle_input(input, &mut self.pairing_data, ops, rng),
173 #[cfg(feature = "legacy-pairing")]
174 State::LegacyPeripheral(peripheral) => peripheral.handle_input(input, &mut self.pairing_data, ops, rng),
175 }
176 }
177
178 pub(crate) fn security_level(&self) -> SecurityLevel {
179 let is_encrypted = match &self.state {
180 State::Central(c) => c.is_encrypted(),
181 State::Peripheral(p) => p.is_encrypted(),
182 #[cfg(feature = "legacy-pairing")]
183 State::LegacyCentral(c) => c.is_encrypted(),
184 #[cfg(feature = "legacy-pairing")]
185 State::LegacyPeripheral(p) => p.is_encrypted(),
186 };
187 if is_encrypted {
188 self.pairing_data
189 .bond_information
190 .as_ref()
191 .map(|x| x.security_level)
192 .unwrap_or(SecurityLevel::NoEncryption)
193 } else {
194 SecurityLevel::NoEncryption
195 }
196 }
197
198 pub(crate) fn bond_information(&self) -> Option<&BondInformation> {
199 self.pairing_data.bond_information.as_ref()
200 }
201
202 pub(crate) fn new_central(local_address: Address, peer_address: Address, local_io: IoCapabilities) -> Pairing {
203 Pairing {
204 pairing_data: PairingData::new(
205 local_address,
206 peer_address,
207 local_io,
208 crate::security_manager::constants::TIMEOUT_DISABLE,
209 ),
210 state: State::Central(central::Pairing::new_idle()),
211 }
212 }
213
214 pub(crate) fn initiate_central<P: PacketPool, OPS: PairingOps<P>>(
215 local_address: Address,
216 peer_address: Address,
217 ops: &mut OPS,
218 local_io: IoCapabilities,
219 user_initiated: bool,
220 ) -> Result<Self, Error> {
221 let mut pairing_data = PairingData::new(
222 local_address,
223 peer_address,
224 local_io,
225 crate::security_manager::constants::TIMEOUT_DISABLE,
226 );
227 let state = central::Pairing::initiate(&mut pairing_data, ops, user_initiated)?;
228 pairing_data.timeout_at = Instant::now() + crate::security_manager::constants::TIMEOUT;
229 Ok(Pairing {
230 pairing_data,
231 state: State::Central(state),
232 })
233 }
234
235 pub(crate) fn new_peripheral(local_address: Address, peer_address: Address, local_io: IoCapabilities) -> Pairing {
236 Pairing {
237 pairing_data: PairingData::new(
238 local_address,
239 peer_address,
240 local_io,
241 crate::security_manager::constants::TIMEOUT,
242 ),
243 state: State::Peripheral(peripheral::Pairing::new()),
244 }
245 }
246
247 #[cfg(feature = "legacy-pairing")]
248 pub(crate) fn new_legacy_peripheral(
249 local_address: Address,
250 peer_address: Address,
251 local_io: IoCapabilities,
252 ) -> Pairing {
253 Pairing {
254 pairing_data: PairingData::new(
255 local_address,
256 peer_address,
257 local_io,
258 crate::security_manager::constants::TIMEOUT,
259 ),
260 state: State::LegacyPeripheral(legacy_peripheral::Pairing::new()),
261 }
262 }
263
264 pub(crate) fn initiate_peripheral<P: PacketPool, OPS: PairingOps<P>>(
265 local_address: Address,
266 peer_address: Address,
267 ops: &mut OPS,
268 local_io: IoCapabilities,
269 user_initiated: bool,
270 ) -> Result<Self, Error> {
271 let pairing_data = PairingData::new(
272 local_address,
273 peer_address,
274 local_io,
275 crate::security_manager::constants::TIMEOUT,
276 );
277 let state = peripheral::Pairing::initiate(&pairing_data, ops, user_initiated)?;
278 Ok(Pairing {
279 pairing_data,
280 state: State::Peripheral(state),
281 })
282 }
283
284 #[cfg(feature = "legacy-pairing")]
286 pub(crate) fn switch_to_legacy_central(self) -> Result<Pairing, Error> {
287 use crate::codec::Encode;
288
289 let Pairing { pairing_data, state } = self;
290 match state {
291 State::Central(_) => {
292 let mut preq = [0u8; 7];
293 preq[0] = u8::from(Command::PairingRequest);
294 pairing_data.local_features.encode(&mut preq[1..]).unwrap();
295 Ok(Pairing {
296 pairing_data,
297 state: State::LegacyCentral(legacy_central::Pairing::from_lesc_switch(preq)),
298 })
299 }
300 _ => Err(Error::InvalidState),
301 }
302 }
303
304 #[cfg(feature = "legacy-pairing")]
306 pub(crate) fn switch_to_legacy_peripheral(self) -> Result<Pairing, Error> {
307 let Pairing { pairing_data, state } = self;
308 match state {
309 State::Peripheral(_) => Ok(Pairing {
310 pairing_data,
311 state: State::LegacyPeripheral(legacy_peripheral::Pairing::new()),
312 }),
313 _ => Err(Error::InvalidState),
314 }
315 }
316
317 pub(crate) fn is_waiting_bonded_encryption(&self) -> bool {
318 match &self.state {
319 State::Central(c) => c.is_waiting_bonded_encryption(),
320 _ => false,
321 }
322 }
323
324 pub(crate) fn peer_address(&self) -> Address {
325 self.pairing_data.peer_address
326 }
327
328 pub(crate) fn timeout_at(&self) -> Instant {
329 if self.result().is_some() {
330 Instant::now() + crate::security_manager::constants::TIMEOUT_DISABLE
331 } else {
332 self.pairing_data.timeout_at
333 }
334 }
335
336 pub(crate) fn reset_timeout(&mut self) {
337 self.pairing_data.timeout_at = Instant::now() + crate::security_manager::constants::TIMEOUT;
338 }
339
340 pub(crate) fn mark_timeout(&mut self) {
341 match &mut self.state {
342 State::Central(c) => c.mark_timeout(),
343 State::Peripheral(p) => p.mark_timeout(),
344 #[cfg(feature = "legacy-pairing")]
345 State::LegacyCentral(c) => c.mark_timeout(),
346 #[cfg(feature = "legacy-pairing")]
347 State::LegacyPeripheral(p) => p.mark_timeout(),
348 }
349 }
350}
351
352#[derive(Clone, Copy, Debug)]
357#[cfg_attr(feature = "defmt", derive(defmt::Format))]
358pub struct OobData {
359 pub random: [u8; 16],
361 pub confirm: [u8; 16],
363}
364
365pub enum Event {
366 LinkEncryptedResult(bool),
367 PassKeyConfirm,
368 PassKeyCancel,
369 PassKeyInput(u32),
370 OobDataReceived { local: OobData, peer: OobData },
371}
372
373#[cfg(test)]
374mod tests {
375 use rand_chacha::{ChaCha12Core, ChaCha12Rng};
376 use rand_core::SeedableRng;
377
378 use super::*;
379 use crate::{Identity, Packet};
380
381 #[derive(Debug)]
382 pub(crate) struct TestPacket(pub(crate) heapless::Vec<u8, 128>);
383
384 impl AsRef<[u8]> for TestPacket {
385 fn as_ref(&self) -> &[u8] {
386 self.0.as_slice()
387 }
388 }
389
390 impl AsMut<[u8]> for TestPacket {
391 fn as_mut(&mut self) -> &mut [u8] {
392 self.0.as_mut_slice()
393 }
394 }
395
396 impl Packet for TestPacket {}
397
398 #[derive(Debug)]
399 pub(crate) struct HeaplessPool;
400
401 impl PacketPool for HeaplessPool {
402 type Packet = TestPacket;
403 const MTU: usize = 128;
404
405 fn allocate() -> Option<Self::Packet> {
406 let mut ret = TestPacket(heapless::Vec::new());
407 ret.0.resize(Self::MTU, 0).unwrap();
408 Some(ret)
409 }
410
411 fn capacity() -> usize {
412 isize::MAX as usize
413 }
414 }
415
416 pub(crate) struct TestOps<const N: usize> {
417 pub(crate) sent_packets: heapless::Vec<TxPacket<HeaplessPool>, N>,
418 pub(crate) encryptions: heapless::Vec<LongTermKey, 10>,
419 pub(crate) connection_events: heapless::Vec<ConnectionEvent, 10>,
420 pub(crate) bond_information: Option<BondInformation>,
421 pub(crate) bondable: bool,
422 pub(crate) oob_available: bool,
423 pub(crate) secret_key: crate::security_manager::crypto::SecretKey,
424 pub(crate) public_key: crate::security_manager::crypto::PublicKey,
425 }
426
427 impl<const N: usize> TestOps<N> {
428 pub(crate) fn new(seed: u64) -> Self {
429 let mut rng: ChaCha12Rng = ChaCha12Core::seed_from_u64(seed).into();
430 let secret_key = crate::security_manager::crypto::SecretKey::new(&mut rng);
431 let public_key = secret_key.public_key();
432 Self {
433 sent_packets: heapless::Vec::new(),
434 encryptions: heapless::Vec::new(),
435 connection_events: heapless::Vec::new(),
436 bond_information: None,
437 bondable: false,
438 oob_available: false,
439 secret_key,
440 public_key,
441 }
442 }
443 }
444
445 impl<const N: usize> PairingOps<HeaplessPool> for TestOps<N> {
446 fn try_send_packet(&mut self, packet: TxPacket<HeaplessPool>) -> Result<(), Error> {
447 self.sent_packets.push(packet).map_err(|_| Error::OutOfMemory)
448 }
449
450 fn try_enable_encryption(
451 &mut self,
452 ltk: &LongTermKey,
453 security_level: SecurityLevel,
454 is_bonded: bool,
455 #[cfg(feature = "legacy-pairing")] ediv: u16,
456 #[cfg(feature = "legacy-pairing")] rand: [u8; 8],
457 #[cfg(feature = "legacy-pairing")] encryption_key_len: u8,
458 ) -> Result<BondInformation, Error> {
459 self.encryptions.push(ltk.clone()).unwrap();
460 Ok(BondInformation {
461 security_level,
462 identity: Identity::default(),
463 ltk: ltk.clone(),
464 is_bonded,
465 #[cfg(feature = "legacy-pairing")]
466 ediv,
467 #[cfg(feature = "legacy-pairing")]
468 rand,
469 #[cfg(feature = "legacy-pairing")]
470 encryption_key_len,
471 })
472 }
473
474 fn find_bond(&self) -> Option<BondInformation> {
475 self.bond_information.clone()
476 }
477
478 fn try_enable_bonded_encryption(&mut self) -> Result<Option<BondInformation>, Error> {
479 if let Some(bond) = &self.bond_information {
480 self.encryptions.push(bond.ltk.clone()).unwrap();
481 Ok(Some(bond.clone()))
482 } else {
483 Ok(None)
484 }
485 }
486
487 fn try_update_bond_information(&mut self, bond: &BondInformation) -> Result<(), Error> {
488 Ok(())
489 }
490
491 fn connection_handle(&mut self) -> ConnHandle {
492 ConnHandle::new(2)
493 }
494
495 fn try_send_connection_event(&mut self, event: ConnectionEvent) -> Result<(), Error> {
496 self.connection_events.push(event).unwrap();
497 Ok(())
498 }
499
500 fn bonding_flag(&self) -> BondingFlag {
501 if self.bondable {
502 BondingFlag::Bonding
503 } else {
504 BondingFlag::NoBonding
505 }
506 }
507
508 fn oob_available(&self) -> bool {
509 self.oob_available
510 }
511
512 fn secret_key(&self) -> &crate::security_manager::crypto::SecretKey {
513 &self.secret_key
514 }
515
516 fn public_key(&self) -> &crate::security_manager::crypto::PublicKey {
517 &self.public_key
518 }
519
520 fn local_irk(&self) -> [u8; 16] {
521 [0; 16]
522 }
523
524 fn local_identity_address(&self) -> Result<Address, Error> {
525 Ok(Address::random([0xff, 0x8f, 0x08, 0x05, 0xe4, 0xff]))
526 }
527 }
528
529 #[test]
530 fn just_works() {
531 let peripheral = Address::random([0xff, 1, 2, 3, 4, 5]);
532 let central = Address::random([0xff, 2, 2, 3, 4, 5]);
533
534 let mut peripheral_ops = TestOps::<10>::new(0xDEAD);
535 let mut central_ops = TestOps::<10>::new(0xBEEF);
536
537 let mut peripheral_pairing = Pairing::new_peripheral(peripheral, central, IoCapabilities::NoInputNoOutput);
538 let mut central_pairing = Pairing::initiate_central(
539 central,
540 peripheral,
541 &mut central_ops,
542 IoCapabilities::NoInputNoOutput,
543 true,
544 )
545 .unwrap();
546
547 let mut num_central_data_sent = 0;
548 let mut num_peripheral_data_sent = 0;
549 let mut rng: ChaCha12Rng = ChaCha12Core::seed_from_u64(1).into();
550 transmit_packets(
551 &mut peripheral_ops,
552 &mut central_ops,
553 &mut rng,
554 &mut peripheral_pairing,
555 &mut central_pairing,
556 &mut num_central_data_sent,
557 &mut num_peripheral_data_sent,
558 );
559
560 assert_eq!(central_ops.encryptions[0], peripheral_ops.encryptions[0]);
561 central_pairing
562 .handle_event(Event::LinkEncryptedResult(true), &mut central_ops, &mut rng)
563 .unwrap();
564 peripheral_pairing
565 .handle_event(Event::LinkEncryptedResult(true), &mut peripheral_ops, &mut rng)
566 .unwrap();
567
568 assert!(matches!(
569 central_ops.connection_events[0],
570 ConnectionEvent::PairingComplete {
571 security_level: SecurityLevel::Encrypted,
572 bond: None
573 }
574 ));
575 assert!(matches!(
576 peripheral_ops.connection_events[0],
577 ConnectionEvent::PairingComplete {
578 security_level: SecurityLevel::Encrypted,
579 bond: None
580 }
581 ));
582 assert_eq!(central_pairing.security_level(), SecurityLevel::Encrypted);
583 assert_eq!(peripheral_pairing.security_level(), SecurityLevel::Encrypted);
584 }
585
586 #[test]
587 fn numeric_compare() {
588 let peripheral = Address::random([0xff, 1, 2, 3, 4, 5]);
589 let central = Address::random([0xff, 2, 2, 3, 4, 5]);
590
591 let mut peripheral_ops = TestOps::<10>::new(0xDEAD);
592 let mut central_ops = TestOps::<10>::new(0xBEEF);
593
594 let mut peripheral_pairing = Pairing::new_peripheral(peripheral, central, IoCapabilities::DisplayYesNo);
595 let mut central_pairing = Pairing::initiate_central(
596 central,
597 peripheral,
598 &mut central_ops,
599 IoCapabilities::DisplayYesNo,
600 true,
601 )
602 .unwrap();
603
604 let mut num_central_data_sent = 0;
605 let mut num_peripheral_data_sent = 0;
606 let mut rng: ChaCha12Rng = ChaCha12Core::seed_from_u64(1).into();
607 transmit_packets(
608 &mut peripheral_ops,
609 &mut central_ops,
610 &mut rng,
611 &mut peripheral_pairing,
612 &mut central_pairing,
613 &mut num_central_data_sent,
614 &mut num_peripheral_data_sent,
615 );
616
617 let (central_numeric, peripheral_numeric) = {
618 let central = match ¢ral_ops.connection_events[0] {
619 ConnectionEvent::PassKeyConfirm(n) => n,
620 _ => panic!("Unexpected connection event"),
621 };
622
623 let peripheral = match &peripheral_ops.connection_events[0] {
624 ConnectionEvent::PassKeyConfirm(n) => n,
625 _ => panic!("Unexpected connection event"),
626 };
627
628 (*central, *peripheral)
629 };
630
631 assert_eq!(central_numeric, peripheral_numeric);
632 central_pairing
633 .handle_event(Event::PassKeyConfirm, &mut central_ops, &mut rng)
634 .unwrap();
635 peripheral_pairing
636 .handle_event(Event::PassKeyConfirm, &mut peripheral_ops, &mut rng)
637 .unwrap();
638
639 transmit_packets(
640 &mut peripheral_ops,
641 &mut central_ops,
642 &mut rng,
643 &mut peripheral_pairing,
644 &mut central_pairing,
645 &mut num_central_data_sent,
646 &mut num_peripheral_data_sent,
647 );
648
649 assert_eq!(central_ops.encryptions[0], peripheral_ops.encryptions[0]);
650 central_pairing
651 .handle_event(Event::LinkEncryptedResult(true), &mut central_ops, &mut rng)
652 .unwrap();
653 peripheral_pairing
654 .handle_event(Event::LinkEncryptedResult(true), &mut peripheral_ops, &mut rng)
655 .unwrap();
656
657 assert!(matches!(
658 central_ops.connection_events[1],
659 ConnectionEvent::PairingComplete {
660 security_level: SecurityLevel::EncryptedAuthenticated,
661 bond: None
662 }
663 ));
664 assert!(matches!(
665 peripheral_ops.connection_events[1],
666 ConnectionEvent::PairingComplete {
667 security_level: SecurityLevel::EncryptedAuthenticated,
668 bond: None
669 }
670 ));
671 assert_eq!(central_pairing.security_level(), SecurityLevel::EncryptedAuthenticated);
672 assert_eq!(
673 peripheral_pairing.security_level(),
674 SecurityLevel::EncryptedAuthenticated
675 );
676 }
677
678 #[test]
679 fn pass_key_entry_keyboard_only() {
680 let peripheral = Address::random([0xff, 1, 2, 3, 4, 5]);
681 let central = Address::random([0xff, 2, 2, 3, 4, 5]);
682
683 let mut peripheral_ops = TestOps::<80>::new(0xDEAD);
684 let mut central_ops = TestOps::<80>::new(0xBEEF);
685
686 let mut peripheral_pairing = Pairing::new_peripheral(peripheral, central, IoCapabilities::KeyboardOnly);
687 let mut central_pairing = Pairing::initiate_central(
688 central,
689 peripheral,
690 &mut central_ops,
691 IoCapabilities::KeyboardOnly,
692 true,
693 )
694 .unwrap();
695
696 let mut num_central_data_sent = 0;
697 let mut num_peripheral_data_sent = 0;
698 let mut rng: ChaCha12Rng = ChaCha12Core::seed_from_u64(1).into();
699 transmit_packets(
700 &mut peripheral_ops,
701 &mut central_ops,
702 &mut rng,
703 &mut peripheral_pairing,
704 &mut central_pairing,
705 &mut num_central_data_sent,
706 &mut num_peripheral_data_sent,
707 );
708
709 assert!(matches!(
710 central_ops.connection_events[0],
711 ConnectionEvent::PassKeyInput
712 ));
713 assert!(matches!(
714 peripheral_ops.connection_events[0],
715 ConnectionEvent::PassKeyInput
716 ));
717
718 central_pairing
719 .handle_event(Event::PassKeyInput(123456), &mut central_ops, &mut rng)
720 .unwrap();
721 peripheral_pairing
722 .handle_event(Event::PassKeyInput(123456), &mut peripheral_ops, &mut rng)
723 .unwrap();
724
725 transmit_packets(
726 &mut peripheral_ops,
727 &mut central_ops,
728 &mut rng,
729 &mut peripheral_pairing,
730 &mut central_pairing,
731 &mut num_central_data_sent,
732 &mut num_peripheral_data_sent,
733 );
734
735 assert_eq!(central_ops.encryptions[0], peripheral_ops.encryptions[0]);
736 central_pairing
737 .handle_event(Event::LinkEncryptedResult(true), &mut central_ops, &mut rng)
738 .unwrap();
739 peripheral_pairing
740 .handle_event(Event::LinkEncryptedResult(true), &mut peripheral_ops, &mut rng)
741 .unwrap();
742
743 assert!(matches!(
744 central_ops.connection_events[1],
745 ConnectionEvent::PairingComplete {
746 security_level: SecurityLevel::EncryptedAuthenticated,
747 bond: None
748 }
749 ));
750 assert!(matches!(
751 peripheral_ops.connection_events[1],
752 ConnectionEvent::PairingComplete {
753 security_level: SecurityLevel::EncryptedAuthenticated,
754 bond: None
755 }
756 ));
757 assert_eq!(central_pairing.security_level(), SecurityLevel::EncryptedAuthenticated);
758 assert_eq!(
759 peripheral_pairing.security_level(),
760 SecurityLevel::EncryptedAuthenticated
761 );
762 }
763
764 #[test]
765 fn pass_key_entry_peripheral_display() {
766 let peripheral = Address::random([0xff, 1, 2, 3, 4, 5]);
767 let central = Address::random([0xff, 2, 2, 3, 4, 5]);
768
769 let mut peripheral_ops = TestOps::<80>::new(0xDEAD);
770 let mut central_ops = TestOps::<80>::new(0xBEEF);
771
772 let mut peripheral_pairing = Pairing::new_peripheral(peripheral, central, IoCapabilities::DisplayOnly);
773 let mut central_pairing = Pairing::initiate_central(
774 central,
775 peripheral,
776 &mut central_ops,
777 IoCapabilities::KeyboardOnly,
778 true,
779 )
780 .unwrap();
781
782 let mut num_central_data_sent = 0;
783 let mut num_peripheral_data_sent = 0;
784 let mut rng: ChaCha12Rng = ChaCha12Core::seed_from_u64(1).into();
785 transmit_packets(
786 &mut peripheral_ops,
787 &mut central_ops,
788 &mut rng,
789 &mut peripheral_pairing,
790 &mut central_pairing,
791 &mut num_central_data_sent,
792 &mut num_peripheral_data_sent,
793 );
794
795 let pass_key = match &peripheral_ops.connection_events[0] {
796 ConnectionEvent::PassKeyDisplay(pk) => *pk,
797 _ => panic!("Unexpected connection event"),
798 };
799
800 assert!(matches!(
801 central_ops.connection_events[0],
802 ConnectionEvent::PassKeyInput
803 ));
804
805 central_pairing
806 .handle_event(Event::PassKeyInput(pass_key.value()), &mut central_ops, &mut rng)
807 .unwrap();
808
809 transmit_packets(
810 &mut peripheral_ops,
811 &mut central_ops,
812 &mut rng,
813 &mut peripheral_pairing,
814 &mut central_pairing,
815 &mut num_central_data_sent,
816 &mut num_peripheral_data_sent,
817 );
818
819 assert_eq!(central_ops.encryptions[0], peripheral_ops.encryptions[0]);
820 central_pairing
821 .handle_event(Event::LinkEncryptedResult(true), &mut central_ops, &mut rng)
822 .unwrap();
823 peripheral_pairing
824 .handle_event(Event::LinkEncryptedResult(true), &mut peripheral_ops, &mut rng)
825 .unwrap();
826
827 assert!(matches!(
828 central_ops.connection_events[1],
829 ConnectionEvent::PairingComplete {
830 security_level: SecurityLevel::EncryptedAuthenticated,
831 bond: None
832 }
833 ));
834 assert!(matches!(
835 peripheral_ops.connection_events[1],
836 ConnectionEvent::PairingComplete {
837 security_level: SecurityLevel::EncryptedAuthenticated,
838 bond: None
839 }
840 ));
841 assert_eq!(central_pairing.security_level(), SecurityLevel::EncryptedAuthenticated);
842 assert_eq!(
843 peripheral_pairing.security_level(),
844 SecurityLevel::EncryptedAuthenticated
845 );
846 }
847
848 #[test]
849 fn pass_key_entry_central_display() {
850 let peripheral = Address::random([0xff, 1, 2, 3, 4, 5]);
851 let central = Address::random([0xff, 2, 2, 3, 4, 5]);
852
853 let mut peripheral_ops = TestOps::<80>::new(0xDEAD);
854 let mut central_ops = TestOps::<80>::new(0xBEEF);
855
856 let mut peripheral_pairing = Pairing::new_peripheral(peripheral, central, IoCapabilities::KeyboardOnly);
857 let mut central_pairing =
858 Pairing::initiate_central(central, peripheral, &mut central_ops, IoCapabilities::DisplayOnly, true)
859 .unwrap();
860
861 let mut num_central_data_sent = 0;
862 let mut num_peripheral_data_sent = 0;
863 let mut rng: ChaCha12Rng = ChaCha12Core::seed_from_u64(1).into();
864 transmit_packets(
865 &mut peripheral_ops,
866 &mut central_ops,
867 &mut rng,
868 &mut peripheral_pairing,
869 &mut central_pairing,
870 &mut num_central_data_sent,
871 &mut num_peripheral_data_sent,
872 );
873
874 let pass_key = match ¢ral_ops.connection_events[0] {
875 ConnectionEvent::PassKeyDisplay(pk) => *pk,
876 _ => panic!("Unexpected connection event"),
877 };
878
879 assert!(matches!(
880 peripheral_ops.connection_events[0],
881 ConnectionEvent::PassKeyInput
882 ));
883
884 peripheral_pairing
885 .handle_event(Event::PassKeyInput(pass_key.value()), &mut peripheral_ops, &mut rng)
886 .unwrap();
887
888 transmit_packets(
889 &mut peripheral_ops,
890 &mut central_ops,
891 &mut rng,
892 &mut peripheral_pairing,
893 &mut central_pairing,
894 &mut num_central_data_sent,
895 &mut num_peripheral_data_sent,
896 );
897
898 assert_eq!(central_ops.encryptions[0], peripheral_ops.encryptions[0]);
899 central_pairing
900 .handle_event(Event::LinkEncryptedResult(true), &mut central_ops, &mut rng)
901 .unwrap();
902 peripheral_pairing
903 .handle_event(Event::LinkEncryptedResult(true), &mut peripheral_ops, &mut rng)
904 .unwrap();
905
906 assert!(matches!(
907 central_ops.connection_events[1],
908 ConnectionEvent::PairingComplete {
909 security_level: SecurityLevel::EncryptedAuthenticated,
910 bond: None
911 }
912 ));
913 assert!(matches!(
914 peripheral_ops.connection_events[1],
915 ConnectionEvent::PairingComplete {
916 security_level: SecurityLevel::EncryptedAuthenticated,
917 bond: None
918 }
919 ));
920 assert_eq!(central_pairing.security_level(), SecurityLevel::EncryptedAuthenticated);
921 assert_eq!(
922 peripheral_pairing.security_level(),
923 SecurityLevel::EncryptedAuthenticated
924 );
925 }
926
927 #[test]
928 fn bondable_just_works() {
929 let peripheral = Address::random([0xff, 1, 2, 3, 4, 5]);
930 let central = Address::random([0xff, 2, 2, 3, 4, 5]);
931
932 let mut peripheral_ops = TestOps::<80>::new(0xDEAD);
933 let mut central_ops = TestOps::<80>::new(0xBEEF);
934 peripheral_ops.bondable = true;
935 central_ops.bondable = true;
936
937 let mut peripheral_pairing = Pairing::new_peripheral(peripheral, central, IoCapabilities::NoInputNoOutput);
938 let mut central_pairing = Pairing::initiate_central(
939 central,
940 peripheral,
941 &mut central_ops,
942 IoCapabilities::NoInputNoOutput,
943 true,
944 )
945 .unwrap();
946
947 let mut num_central_data_sent = 0;
948 let mut num_peripheral_data_sent = 0;
949 let mut rng: ChaCha12Rng = ChaCha12Core::seed_from_u64(1).into();
950 transmit_packets(
951 &mut peripheral_ops,
952 &mut central_ops,
953 &mut rng,
954 &mut peripheral_pairing,
955 &mut central_pairing,
956 &mut num_central_data_sent,
957 &mut num_peripheral_data_sent,
958 );
959
960 assert_eq!(central_ops.encryptions[0], peripheral_ops.encryptions[0]);
961 central_pairing
962 .handle_event(Event::LinkEncryptedResult(true), &mut central_ops, &mut rng)
963 .unwrap();
964 peripheral_pairing
965 .handle_event(Event::LinkEncryptedResult(true), &mut peripheral_ops, &mut rng)
966 .unwrap();
967
968 transmit_packets(
970 &mut peripheral_ops,
971 &mut central_ops,
972 &mut rng,
973 &mut peripheral_pairing,
974 &mut central_pairing,
975 &mut num_central_data_sent,
976 &mut num_peripheral_data_sent,
977 );
978
979 assert!(matches!(
980 central_ops.connection_events[0],
981 ConnectionEvent::PairingComplete {
982 security_level: SecurityLevel::Encrypted,
983 bond: Some(BondInformation {
984 is_bonded: true,
985 security_level: SecurityLevel::Encrypted,
986 ..
987 })
988 }
989 ));
990 assert!(matches!(
991 peripheral_ops.connection_events[0],
992 ConnectionEvent::PairingComplete {
993 security_level: SecurityLevel::Encrypted,
994 bond: Some(BondInformation {
995 is_bonded: true,
996 security_level: SecurityLevel::Encrypted,
997 ..
998 })
999 }
1000 ));
1001 assert_eq!(central_pairing.security_level(), SecurityLevel::Encrypted);
1002 assert_eq!(peripheral_pairing.security_level(), SecurityLevel::Encrypted);
1003 }
1004
1005 #[test]
1006 fn bonded_central_initiates() {
1007 let peripheral = Address::random([0xff, 1, 2, 3, 4, 5]);
1008 let central = Address::random([0xff, 2, 2, 3, 4, 5]);
1009
1010 let mut peripheral_ops = TestOps::<80>::new(0xDEAD);
1011 let mut central_ops = TestOps::<80>::new(0xBEEF);
1012 central_ops.bond_information = Some(BondInformation {
1013 security_level: SecurityLevel::EncryptedAuthenticated,
1014 is_bonded: true,
1015 ltk: LongTermKey(1),
1016 identity: peripheral.into(),
1017 #[cfg(feature = "legacy-pairing")]
1018 ediv: 0,
1019 #[cfg(feature = "legacy-pairing")]
1020 rand: [0; 8],
1021 #[cfg(feature = "legacy-pairing")]
1022 encryption_key_len: 16,
1023 });
1024
1025 peripheral_ops.bond_information = Some(BondInformation {
1026 security_level: SecurityLevel::EncryptedAuthenticated,
1027 is_bonded: true,
1028 ltk: LongTermKey(1),
1029 identity: central.into(),
1030 #[cfg(feature = "legacy-pairing")]
1031 ediv: 0,
1032 #[cfg(feature = "legacy-pairing")]
1033 rand: [0; 8],
1034 #[cfg(feature = "legacy-pairing")]
1035 encryption_key_len: 16,
1036 });
1037
1038 let mut rng: ChaCha12Rng = ChaCha12Core::seed_from_u64(1).into();
1039
1040 let mut peripheral_pairing = Pairing::new_peripheral(peripheral, central, IoCapabilities::NoInputNoOutput);
1041 let mut central_pairing = Pairing::initiate_central(
1042 central,
1043 peripheral,
1044 &mut central_ops,
1045 IoCapabilities::NoInputNoOutput,
1046 true,
1047 )
1048 .unwrap();
1049 assert_eq!(central_ops.sent_packets.len(), 0);
1050 assert_eq!(peripheral_ops.sent_packets.len(), 0);
1051 assert_eq!(central_ops.encryptions.len(), 1);
1052 assert_eq!(central_ops.encryptions[0], LongTermKey(1));
1053
1054 central_pairing
1055 .handle_event(Event::LinkEncryptedResult(true), &mut central_ops, &mut rng)
1056 .unwrap();
1057 peripheral_pairing
1058 .handle_event(Event::LinkEncryptedResult(true), &mut peripheral_ops, &mut rng)
1059 .unwrap();
1060
1061 assert!(matches!(
1062 central_ops.connection_events[0],
1063 ConnectionEvent::PairingComplete {
1064 security_level: SecurityLevel::EncryptedAuthenticated,
1065 bond: None
1066 }
1067 ));
1068 assert!(matches!(
1069 peripheral_ops.connection_events[0],
1070 ConnectionEvent::PairingComplete {
1071 security_level: SecurityLevel::EncryptedAuthenticated,
1072 bond: None
1073 }
1074 ));
1075 assert_eq!(central_ops.connection_events.len(), 1);
1076 assert_eq!(peripheral_ops.connection_events.len(), 1);
1077 assert_eq!(central_pairing.security_level(), SecurityLevel::EncryptedAuthenticated);
1078 assert_eq!(
1079 peripheral_pairing.security_level(),
1080 SecurityLevel::EncryptedAuthenticated
1081 );
1082 }
1083
1084 #[test]
1085 fn bonded_peripheral_initiates() {
1086 let peripheral = Address::random([0xff, 1, 2, 3, 4, 5]);
1087 let central = Address::random([0xff, 2, 2, 3, 4, 5]);
1088
1089 let mut peripheral_ops = TestOps::<80>::new(0xDEAD);
1090 let mut central_ops = TestOps::<80>::new(0xBEEF);
1091 central_ops.bond_information = Some(BondInformation {
1092 security_level: SecurityLevel::EncryptedAuthenticated,
1093 is_bonded: true,
1094 ltk: LongTermKey(1),
1095 identity: peripheral.into(),
1096 #[cfg(feature = "legacy-pairing")]
1097 ediv: 0,
1098 #[cfg(feature = "legacy-pairing")]
1099 rand: [0; 8],
1100 #[cfg(feature = "legacy-pairing")]
1101 encryption_key_len: 16,
1102 });
1103
1104 peripheral_ops.bond_information = Some(BondInformation {
1105 security_level: SecurityLevel::EncryptedAuthenticated,
1106 is_bonded: true,
1107 ltk: LongTermKey(1),
1108 identity: central.into(),
1109 #[cfg(feature = "legacy-pairing")]
1110 ediv: 0,
1111 #[cfg(feature = "legacy-pairing")]
1112 rand: [0; 8],
1113 #[cfg(feature = "legacy-pairing")]
1114 encryption_key_len: 16,
1115 });
1116
1117 let mut rng: ChaCha12Rng = ChaCha12Core::seed_from_u64(1).into();
1118
1119 let mut peripheral_pairing = Pairing::initiate_peripheral(
1120 peripheral,
1121 central,
1122 &mut peripheral_ops,
1123 IoCapabilities::NoInputNoOutput,
1124 false,
1125 )
1126 .unwrap();
1127 let mut central_pairing = Pairing::new_central(central, peripheral, IoCapabilities::NoInputNoOutput);
1128
1129 let mut num_central_data_sent = 0;
1130 let mut num_peripheral_data_sent = 0;
1131 transmit_packets(
1132 &mut peripheral_ops,
1133 &mut central_ops,
1134 &mut rng,
1135 &mut peripheral_pairing,
1136 &mut central_pairing,
1137 &mut num_central_data_sent,
1138 &mut num_peripheral_data_sent,
1139 );
1140
1141 assert_eq!(central_ops.sent_packets.len(), 0);
1142 assert_eq!(peripheral_ops.sent_packets.len(), 1);
1143 assert_eq!(central_ops.encryptions.len(), 1);
1144 assert_eq!(central_ops.encryptions[0], LongTermKey(1));
1145
1146 central_pairing
1147 .handle_event(Event::LinkEncryptedResult(true), &mut central_ops, &mut rng)
1148 .unwrap();
1149 peripheral_pairing
1150 .handle_event(Event::LinkEncryptedResult(true), &mut peripheral_ops, &mut rng)
1151 .unwrap();
1152
1153 assert!(matches!(
1154 central_ops.connection_events[0],
1155 ConnectionEvent::PairingComplete {
1156 security_level: SecurityLevel::EncryptedAuthenticated,
1157 bond: None
1158 }
1159 ));
1160 assert!(matches!(
1161 peripheral_ops.connection_events[0],
1162 ConnectionEvent::PairingComplete {
1163 security_level: SecurityLevel::EncryptedAuthenticated,
1164 bond: None
1165 }
1166 ));
1167 assert_eq!(central_ops.connection_events.len(), 1);
1168 assert_eq!(peripheral_ops.connection_events.len(), 1);
1169 assert_eq!(central_pairing.security_level(), SecurityLevel::EncryptedAuthenticated);
1170 assert_eq!(
1171 peripheral_pairing.security_level(),
1172 SecurityLevel::EncryptedAuthenticated
1173 );
1174 }
1175
1176 fn transmit_packets<const N: usize>(
1177 peripheral_ops: &mut TestOps<N>,
1178 central_ops: &mut TestOps<N>,
1179 rng: &mut ChaCha12Rng,
1180 peripheral_pairing: &mut Pairing,
1181 central_pairing: &mut Pairing,
1182 num_central_data_sent: &mut usize,
1183 num_peripheral_data_sent: &mut usize,
1184 ) {
1185 let mut loop_count = 0;
1186 loop {
1187 let saved_num_central_data_sent = *num_central_data_sent;
1188 let saved_num_peripheral_data_sent = *num_peripheral_data_sent;
1189
1190 while *num_central_data_sent < central_ops.sent_packets.len() {
1191 peripheral_pairing
1192 .handle_l2cap_command(
1193 central_ops.sent_packets[*num_central_data_sent].command,
1194 central_ops.sent_packets[*num_central_data_sent].payload(),
1195 peripheral_ops,
1196 rng,
1197 )
1198 .unwrap();
1199 *num_central_data_sent += 1;
1200 }
1201
1202 while *num_peripheral_data_sent < peripheral_ops.sent_packets.len() {
1203 central_pairing
1204 .handle_l2cap_command(
1205 peripheral_ops.sent_packets[*num_peripheral_data_sent].command,
1206 peripheral_ops.sent_packets[*num_peripheral_data_sent].payload(),
1207 central_ops,
1208 rng,
1209 )
1210 .unwrap();
1211 *num_peripheral_data_sent += 1;
1212 }
1213
1214 if saved_num_central_data_sent == *num_central_data_sent
1215 && saved_num_peripheral_data_sent == *num_peripheral_data_sent
1216 {
1217 break;
1218 }
1219
1220 loop_count += 1;
1221 if loop_count > 10000 {
1222 panic!("Too many loops");
1223 }
1224 }
1225 }
1226
1227 #[test]
1228 fn oob_lesc_bilateral() {
1229 use crate::security_manager::crypto::Nonce;
1230
1231 let peripheral_addr = Address::random([0xff, 1, 2, 3, 4, 5]);
1232 let central_addr = Address::random([0xff, 2, 2, 3, 4, 5]);
1233
1234 let mut peripheral_ops = TestOps::<10>::new(0xDEAD);
1235 let mut central_ops = TestOps::<10>::new(0xBEEF);
1236 peripheral_ops.oob_available = true;
1237 central_ops.oob_available = true;
1238
1239 let central_oob = {
1241 let mut rng: ChaCha12Rng = ChaCha12Core::seed_from_u64(100).into();
1242 let r = Nonce::new(&mut rng);
1243 let c = r.f4(central_ops.public_key.x(), central_ops.public_key.x(), 0);
1244 OobData {
1245 random: r.0.to_le_bytes(),
1246 confirm: c.0.to_le_bytes(),
1247 }
1248 };
1249 let peripheral_oob = {
1250 let mut rng: ChaCha12Rng = ChaCha12Core::seed_from_u64(200).into();
1251 let r = Nonce::new(&mut rng);
1252 let c = r.f4(peripheral_ops.public_key.x(), peripheral_ops.public_key.x(), 0);
1253 OobData {
1254 random: r.0.to_le_bytes(),
1255 confirm: c.0.to_le_bytes(),
1256 }
1257 };
1258
1259 let mut peripheral_pairing =
1260 Pairing::new_peripheral(peripheral_addr, central_addr, IoCapabilities::NoInputNoOutput);
1261 let mut central_pairing = Pairing::initiate_central(
1262 central_addr,
1263 peripheral_addr,
1264 &mut central_ops,
1265 IoCapabilities::NoInputNoOutput,
1266 true,
1267 )
1268 .unwrap();
1269
1270 let mut num_central_data_sent = 0;
1271 let mut num_peripheral_data_sent = 0;
1272 let mut rng: ChaCha12Rng = ChaCha12Core::seed_from_u64(1).into();
1273
1274 transmit_packets(
1276 &mut peripheral_ops,
1277 &mut central_ops,
1278 &mut rng,
1279 &mut peripheral_pairing,
1280 &mut central_pairing,
1281 &mut num_central_data_sent,
1282 &mut num_peripheral_data_sent,
1283 );
1284
1285 assert!(peripheral_ops
1287 .connection_events
1288 .iter()
1289 .any(|e| matches!(e, ConnectionEvent::OobRequest)));
1290 assert!(central_ops
1291 .connection_events
1292 .iter()
1293 .any(|e| matches!(e, ConnectionEvent::OobRequest)));
1294
1295 central_pairing
1298 .handle_event(
1299 Event::OobDataReceived {
1300 local: central_oob.clone(),
1301 peer: peripheral_oob.clone(),
1302 },
1303 &mut central_ops,
1304 &mut rng,
1305 )
1306 .unwrap();
1307 peripheral_pairing
1309 .handle_event(
1310 Event::OobDataReceived {
1311 local: peripheral_oob,
1312 peer: central_oob,
1313 },
1314 &mut peripheral_ops,
1315 &mut rng,
1316 )
1317 .unwrap();
1318
1319 transmit_packets(
1321 &mut peripheral_ops,
1322 &mut central_ops,
1323 &mut rng,
1324 &mut peripheral_pairing,
1325 &mut central_pairing,
1326 &mut num_central_data_sent,
1327 &mut num_peripheral_data_sent,
1328 );
1329
1330 assert_eq!(central_ops.encryptions[0], peripheral_ops.encryptions[0]);
1332
1333 central_pairing
1335 .handle_event(Event::LinkEncryptedResult(true), &mut central_ops, &mut rng)
1336 .unwrap();
1337 peripheral_pairing
1338 .handle_event(Event::LinkEncryptedResult(true), &mut peripheral_ops, &mut rng)
1339 .unwrap();
1340
1341 assert!(central_ops.connection_events.iter().any(|e| matches!(
1343 e,
1344 ConnectionEvent::PairingComplete {
1345 security_level: SecurityLevel::EncryptedAuthenticated,
1346 ..
1347 }
1348 )));
1349 assert!(peripheral_ops.connection_events.iter().any(|e| matches!(
1350 e,
1351 ConnectionEvent::PairingComplete {
1352 security_level: SecurityLevel::EncryptedAuthenticated,
1353 ..
1354 }
1355 )));
1356 }
1357
1358 #[test]
1362 fn oob_lesc_central_only() {
1363 use crate::security_manager::crypto::Nonce;
1364
1365 let peripheral_addr = Address::random([0xff, 1, 2, 3, 4, 5]);
1366 let central_addr = Address::random([0xff, 2, 2, 3, 4, 5]);
1367
1368 let mut peripheral_ops = TestOps::<10>::new(0xDEAD);
1369 let mut central_ops = TestOps::<10>::new(0xBEEF);
1370 peripheral_ops.oob_available = false;
1372 central_ops.oob_available = true;
1373
1374 let central_oob = {
1376 let mut rng: ChaCha12Rng = ChaCha12Core::seed_from_u64(100).into();
1377 let r = Nonce::new(&mut rng);
1378 let c = r.f4(central_ops.public_key.x(), central_ops.public_key.x(), 0);
1379 OobData {
1380 random: r.0.to_le_bytes(),
1381 confirm: c.0.to_le_bytes(),
1382 }
1383 };
1384 let peripheral_oob = {
1386 let mut rng: ChaCha12Rng = ChaCha12Core::seed_from_u64(200).into();
1387 let r = Nonce::new(&mut rng);
1388 let c = r.f4(peripheral_ops.public_key.x(), peripheral_ops.public_key.x(), 0);
1389 OobData {
1390 random: r.0.to_le_bytes(),
1391 confirm: c.0.to_le_bytes(),
1392 }
1393 };
1394 let no_oob = OobData {
1395 random: [0; 16],
1396 confirm: [0; 16],
1397 };
1398
1399 let mut peripheral_pairing =
1400 Pairing::new_peripheral(peripheral_addr, central_addr, IoCapabilities::NoInputNoOutput);
1401 let mut central_pairing = Pairing::initiate_central(
1402 central_addr,
1403 peripheral_addr,
1404 &mut central_ops,
1405 IoCapabilities::NoInputNoOutput,
1406 true,
1407 )
1408 .unwrap();
1409
1410 let mut num_central_data_sent = 0;
1411 let mut num_peripheral_data_sent = 0;
1412 let mut rng: ChaCha12Rng = ChaCha12Core::seed_from_u64(1).into();
1413
1414 transmit_packets(
1415 &mut peripheral_ops,
1416 &mut central_ops,
1417 &mut rng,
1418 &mut peripheral_pairing,
1419 &mut central_pairing,
1420 &mut num_central_data_sent,
1421 &mut num_peripheral_data_sent,
1422 );
1423
1424 central_pairing
1426 .handle_event(
1427 Event::OobDataReceived {
1428 local: central_oob,
1429 peer: peripheral_oob,
1430 },
1431 &mut central_ops,
1432 &mut rng,
1433 )
1434 .unwrap();
1435 peripheral_pairing
1437 .handle_event(
1438 Event::OobDataReceived {
1439 local: peripheral_oob,
1440 peer: no_oob,
1441 },
1442 &mut peripheral_ops,
1443 &mut rng,
1444 )
1445 .unwrap();
1446
1447 transmit_packets(
1448 &mut peripheral_ops,
1449 &mut central_ops,
1450 &mut rng,
1451 &mut peripheral_pairing,
1452 &mut central_pairing,
1453 &mut num_central_data_sent,
1454 &mut num_peripheral_data_sent,
1455 );
1456
1457 assert_eq!(central_ops.encryptions[0], peripheral_ops.encryptions[0]);
1458
1459 central_pairing
1460 .handle_event(Event::LinkEncryptedResult(true), &mut central_ops, &mut rng)
1461 .unwrap();
1462 peripheral_pairing
1463 .handle_event(Event::LinkEncryptedResult(true), &mut peripheral_ops, &mut rng)
1464 .unwrap();
1465
1466 assert!(central_ops.connection_events.iter().any(|e| matches!(
1467 e,
1468 ConnectionEvent::PairingComplete {
1469 security_level: SecurityLevel::EncryptedAuthenticated,
1470 ..
1471 }
1472 )));
1473 assert!(peripheral_ops.connection_events.iter().any(|e| matches!(
1474 e,
1475 ConnectionEvent::PairingComplete {
1476 security_level: SecurityLevel::EncryptedAuthenticated,
1477 ..
1478 }
1479 )));
1480 }
1481
1482 #[test]
1484 fn oob_lesc_peripheral_only() {
1485 use crate::security_manager::crypto::Nonce;
1486
1487 let peripheral_addr = Address::random([0xff, 1, 2, 3, 4, 5]);
1488 let central_addr = Address::random([0xff, 2, 2, 3, 4, 5]);
1489
1490 let mut peripheral_ops = TestOps::<10>::new(0xDEAD);
1491 let mut central_ops = TestOps::<10>::new(0xBEEF);
1492 peripheral_ops.oob_available = true;
1494 central_ops.oob_available = false;
1495
1496 let peripheral_oob = {
1498 let mut rng: ChaCha12Rng = ChaCha12Core::seed_from_u64(200).into();
1499 let r = Nonce::new(&mut rng);
1500 let c = r.f4(peripheral_ops.public_key.x(), peripheral_ops.public_key.x(), 0);
1501 OobData {
1502 random: r.0.to_le_bytes(),
1503 confirm: c.0.to_le_bytes(),
1504 }
1505 };
1506 let central_oob = {
1508 let mut rng: ChaCha12Rng = ChaCha12Core::seed_from_u64(100).into();
1509 let r = Nonce::new(&mut rng);
1510 let c = r.f4(central_ops.public_key.x(), central_ops.public_key.x(), 0);
1511 OobData {
1512 random: r.0.to_le_bytes(),
1513 confirm: c.0.to_le_bytes(),
1514 }
1515 };
1516 let no_oob = OobData {
1517 random: [0; 16],
1518 confirm: [0; 16],
1519 };
1520
1521 let mut peripheral_pairing =
1522 Pairing::new_peripheral(peripheral_addr, central_addr, IoCapabilities::NoInputNoOutput);
1523 let mut central_pairing = Pairing::initiate_central(
1524 central_addr,
1525 peripheral_addr,
1526 &mut central_ops,
1527 IoCapabilities::NoInputNoOutput,
1528 true,
1529 )
1530 .unwrap();
1531
1532 let mut num_central_data_sent = 0;
1533 let mut num_peripheral_data_sent = 0;
1534 let mut rng: ChaCha12Rng = ChaCha12Core::seed_from_u64(1).into();
1535
1536 transmit_packets(
1537 &mut peripheral_ops,
1538 &mut central_ops,
1539 &mut rng,
1540 &mut peripheral_pairing,
1541 &mut central_pairing,
1542 &mut num_central_data_sent,
1543 &mut num_peripheral_data_sent,
1544 );
1545
1546 central_pairing
1548 .handle_event(
1549 Event::OobDataReceived {
1550 local: central_oob,
1551 peer: no_oob,
1552 },
1553 &mut central_ops,
1554 &mut rng,
1555 )
1556 .unwrap();
1557 peripheral_pairing
1559 .handle_event(
1560 Event::OobDataReceived {
1561 local: peripheral_oob,
1562 peer: central_oob,
1563 },
1564 &mut peripheral_ops,
1565 &mut rng,
1566 )
1567 .unwrap();
1568
1569 transmit_packets(
1570 &mut peripheral_ops,
1571 &mut central_ops,
1572 &mut rng,
1573 &mut peripheral_pairing,
1574 &mut central_pairing,
1575 &mut num_central_data_sent,
1576 &mut num_peripheral_data_sent,
1577 );
1578
1579 assert_eq!(central_ops.encryptions[0], peripheral_ops.encryptions[0]);
1580
1581 central_pairing
1582 .handle_event(Event::LinkEncryptedResult(true), &mut central_ops, &mut rng)
1583 .unwrap();
1584 peripheral_pairing
1585 .handle_event(Event::LinkEncryptedResult(true), &mut peripheral_ops, &mut rng)
1586 .unwrap();
1587
1588 assert!(central_ops.connection_events.iter().any(|e| matches!(
1589 e,
1590 ConnectionEvent::PairingComplete {
1591 security_level: SecurityLevel::EncryptedAuthenticated,
1592 ..
1593 }
1594 )));
1595 assert!(peripheral_ops.connection_events.iter().any(|e| matches!(
1596 e,
1597 ConnectionEvent::PairingComplete {
1598 security_level: SecurityLevel::EncryptedAuthenticated,
1599 ..
1600 }
1601 )));
1602 }
1603
1604 #[test]
1605 fn oob_lesc_confirm_mismatch() {
1606 use crate::security_manager::crypto::Nonce;
1607
1608 let peripheral_addr = Address::random([0xff, 1, 2, 3, 4, 5]);
1609 let central_addr = Address::random([0xff, 2, 2, 3, 4, 5]);
1610
1611 let mut peripheral_ops = TestOps::<10>::new(0xDEAD);
1612 let mut central_ops = TestOps::<10>::new(0xBEEF);
1613 peripheral_ops.oob_available = true;
1614 central_ops.oob_available = true;
1615
1616 let central_oob = {
1618 let mut rng: ChaCha12Rng = ChaCha12Core::seed_from_u64(100).into();
1619 let r = Nonce::new(&mut rng);
1620 let c = r.f4(central_ops.public_key.x(), central_ops.public_key.x(), 0);
1621 OobData {
1622 random: r.0.to_le_bytes(),
1623 confirm: c.0.to_le_bytes(),
1624 }
1625 };
1626
1627 let peripheral_oob = OobData {
1629 random: [1; 16],
1630 confirm: [2; 16], };
1632
1633 let mut peripheral_pairing =
1634 Pairing::new_peripheral(peripheral_addr, central_addr, IoCapabilities::NoInputNoOutput);
1635 let mut central_pairing = Pairing::initiate_central(
1636 central_addr,
1637 peripheral_addr,
1638 &mut central_ops,
1639 IoCapabilities::NoInputNoOutput,
1640 true,
1641 )
1642 .unwrap();
1643
1644 let mut num_central_data_sent = 0;
1645 let mut num_peripheral_data_sent = 0;
1646 let mut rng: ChaCha12Rng = ChaCha12Core::seed_from_u64(1).into();
1647
1648 transmit_packets(
1649 &mut peripheral_ops,
1650 &mut central_ops,
1651 &mut rng,
1652 &mut peripheral_pairing,
1653 &mut central_pairing,
1654 &mut num_central_data_sent,
1655 &mut num_peripheral_data_sent,
1656 );
1657
1658 let result = central_pairing.handle_event(
1660 Event::OobDataReceived {
1661 local: central_oob,
1662 peer: peripheral_oob,
1663 },
1664 &mut central_ops,
1665 &mut rng,
1666 );
1667
1668 assert!(matches!(
1669 result,
1670 Err(Error::Security(crate::security_manager::Reason::ConfirmValueFailed))
1671 ));
1672 }
1673}