liblmp: Add tests from Bluetooth SIG LMP TestSuite

Test: atest --host liblmp_tests
Change-Id: Ida61973bbed7735e43e38e373d3dc3b39cf24149
diff --git a/tools/rootcanal/lmp/Android.bp b/tools/rootcanal/lmp/Android.bp
index 574b5aa..3f941d2 100644
--- a/tools/rootcanal/lmp/Android.bp
+++ b/tools/rootcanal/lmp/Android.bp
@@ -41,3 +41,22 @@
         "lmp_packets.rs",
     ],
 }
+
+rust_test_host {
+    name: "liblmp_tests",
+    crate_name: "lmp",
+    srcs: [
+        "src/lib.rs",
+        ":LmpGeneratedPackets_rust",
+    ],
+    auto_gen_config: true,
+    edition: "2018",
+    proc_macros: ["libnum_derive", "libpaste"],
+    rustlibs: [
+        "libbt_packets",
+        "libbytes",
+        "libnum_traits",
+        "libthiserror",
+        "libpin_utils",
+    ],
+}
diff --git a/tools/rootcanal/lmp/src/procedure/secure_simple_pairing.rs b/tools/rootcanal/lmp/src/procedure/secure_simple_pairing.rs
index 945cbbf..469ceb2 100644
--- a/tools/rootcanal/lmp/src/procedure/secure_simple_pairing.rs
+++ b/tools/rootcanal/lmp/src/procedure/secure_simple_pairing.rs
@@ -618,3 +618,91 @@
 
     Ok(())
 }
+
+#[cfg(test)]
+mod tests {
+    use crate::procedure::Context;
+    use crate::test::{sequence, TestContext};
+    // simple pairing is part of authentication procedure
+    use super::super::authentication::initiate;
+    use super::super::authentication::respond;
+
+    #[test]
+    fn initiate_size() {
+        let context = crate::test::TestContext::new();
+        let procedure = super::initiate(&context);
+
+        fn assert_max_size<T>(_value: T, limit: usize) {
+            let type_name = std::any::type_name::<T>();
+            let size = std::mem::size_of::<T>();
+            println!("Size of {}: {}", type_name, size);
+            assert!(size < limit)
+        }
+
+        assert_max_size(procedure, 250);
+    }
+
+    #[test]
+    fn numeric_comparaison_initiator_success() {
+        let context = TestContext::new();
+        let procedure = initiate;
+
+        include!("../../test/SP/BV-06-C.in");
+    }
+
+    #[test]
+    fn numeric_comparaison_responder_success() {
+        let context = TestContext::new();
+        let procedure = respond;
+
+        include!("../../test/SP/BV-07-C.in");
+    }
+
+    #[test]
+    fn numeric_comparaison_initiator_failure_on_initiating_side() {
+        let context = TestContext::new();
+        let procedure = initiate;
+
+        include!("../../test/SP/BV-08-C.in");
+    }
+
+    #[test]
+    fn numeric_comparaison_responder_failure_on_initiating_side() {
+        let context = TestContext::new();
+        let procedure = respond;
+
+        include!("../../test/SP/BV-09-C.in");
+    }
+
+    #[test]
+    fn numeric_comparaison_initiator_failure_on_responding_side() {
+        let context = TestContext::new();
+        let procedure = initiate;
+
+        include!("../../test/SP/BV-10-C.in");
+    }
+
+    #[test]
+    fn numeric_comparaison_responder_failure_on_responding_side() {
+        let context = TestContext::new();
+        let procedure = respond;
+
+        include!("../../test/SP/BV-11-C.in");
+    }
+
+    #[test]
+    fn passkey_entry_initiator_success() {
+        let context = TestContext::new();
+        let procedure = initiate;
+
+        include!("../../test/SP/BV-12-C.in");
+    }
+
+    #[test]
+    fn passkey_entry_responder_success() {
+        let context = TestContext::new();
+        let procedure = respond;
+
+        include!("../../test/SP/BV-13-C.in");
+    }
+}
diff --git a/tools/rootcanal/lmp/src/test/context.rs b/tools/rootcanal/lmp/src/test/context.rs
new file mode 100644
index 0000000..f790bf7
--- /dev/null
+++ b/tools/rootcanal/lmp/src/test/context.rs
@@ -0,0 +1,87 @@
+use std::cell::RefCell;
+use std::collections::VecDeque;
+use std::convert::{TryFrom, TryInto};
+use std::future::Future;
+use std::pin::Pin;
+use std::task::{self, Poll};
+
+use crate::packets::{hci, lmp};
+
+use crate::procedure::Context;
+
+#[derive(Default)]
+pub struct TestContext {
+    pub in_lmp_packets: RefCell<VecDeque<lmp::PacketPacket>>,
+    pub out_lmp_packets: RefCell<VecDeque<lmp::PacketPacket>>,
+    pub hci_events: RefCell<VecDeque<hci::EventPacket>>,
+    pub hci_commands: RefCell<VecDeque<hci::CommandPacket>>,
+}
+
+impl TestContext {
+    pub fn new() -> Self {
+        Default::default()
+    }
+}
+
+impl Context for TestContext {
+    fn poll_hci_command<C: TryFrom<hci::CommandPacket>>(&self) -> Poll<C> {
+        let command =
+            self.hci_commands.borrow().front().and_then(|command| command.clone().try_into().ok());
+
+        if let Some(command) = command {
+            self.hci_commands.borrow_mut().pop_front();
+            Poll::Ready(command)
+        } else {
+            Poll::Pending
+        }
+    }
+
+    fn poll_lmp_packet<P: TryFrom<lmp::PacketPacket>>(&self) -> Poll<P> {
+        let packet =
+            self.in_lmp_packets.borrow().front().and_then(|packet| packet.clone().try_into().ok());
+
+        if let Some(packet) = packet {
+            self.in_lmp_packets.borrow_mut().pop_front();
+            Poll::Ready(packet)
+        } else {
+            Poll::Pending
+        }
+    }
+
+    fn send_hci_event<E: Into<hci::EventPacket>>(&self, event: E) {
+        self.hci_events.borrow_mut().push_back(event.into());
+    }
+
+    fn send_lmp_packet<P: Into<lmp::PacketPacket>>(&self, packet: P) {
+        self.out_lmp_packets.borrow_mut().push_back(packet.into());
+    }
+
+    fn peer_address(&self) -> hci::Address {
+        hci::Address { bytes: [0; 6] }
+    }
+
+    fn peer_handle(&self) -> u16 {
+        0x42
+    }
+
+    fn peer_extended_features(&self, features_page: u8) -> Option<u64> {
+        if features_page == 1 {
+            Some(1)
+        } else {
+            Some(0)
+        }
+    }
+
+    fn extended_features(&self, features_page: u8) -> u64 {
+        if features_page == 1 {
+            1
+        } else {
+            0
+        }
+    }
+}
+
+pub fn poll(future: Pin<&mut impl Future<Output = ()>>) -> Poll<()> {
+    let waker = crate::future::noop_waker();
+    future.poll(&mut task::Context::from_waker(&waker))
+}
diff --git a/tools/rootcanal/lmp/src/test/mod.rs b/tools/rootcanal/lmp/src/test/mod.rs
new file mode 100644
index 0000000..ad790a1
--- /dev/null
+++ b/tools/rootcanal/lmp/src/test/mod.rs
@@ -0,0 +1,5 @@
+mod context;
+mod sequence;
+
+pub(crate) use context::{poll, TestContext};
+pub(crate) use sequence::{sequence, sequence_body};
diff --git a/tools/rootcanal/lmp/src/test/sequence.rs b/tools/rootcanal/lmp/src/test/sequence.rs
new file mode 100644
index 0000000..b9df64e
--- /dev/null
+++ b/tools/rootcanal/lmp/src/test/sequence.rs
@@ -0,0 +1,115 @@
+macro_rules! sequence_body {
+        ($ctx:ident, ) => { None };
+        ($ctx:ident, Lower Tester -> IUT: $packet:ident {
+            $($name:ident: $value:expr),* $(,)?
+        } $($tail:tt)*) => {{
+            use crate::packets::lmp::*;
+
+            let builder = paste! {
+                [<$packet Builder>] {
+                    $($name: $value),*
+                }
+            };
+            $ctx.0.in_lmp_packets.borrow_mut().push_back(builder.build().into());
+
+            let poll = crate::test::poll($ctx.1.as_mut());
+
+            assert!($ctx.0.in_lmp_packets.borrow().is_empty(), "{} was not consumed by procedure", stringify!($packet));
+
+            println!("Lower Tester -> IUT: {}", stringify!($packet));
+
+            sequence_body!($ctx, $($tail)*).or(Some(poll))
+        }};
+        ($ctx:ident, Upper Tester -> IUT: $packet:ident {
+            $($name:ident: $value:expr),* $(,)?
+        } $($tail:tt)*) => {{
+            use crate::packets::hci::*;
+
+            let builder = paste! {
+                [<$packet Builder>] {
+                    $($name: $value),*
+                }
+            };
+            $ctx.0.hci_commands.borrow_mut().push_back(builder.build().into());
+
+            let poll = crate::test::poll($ctx.1.as_mut());
+
+            assert!($ctx.0.hci_commands.borrow().is_empty(), "{} was not consumed by procedure", stringify!($packet));
+
+            println!("Upper Tester -> IUT: {}", stringify!($packet));
+
+            sequence_body!($ctx, $($tail)*).or(Some(poll))
+        }};
+        ($ctx:ident, IUT -> Upper Tester: $packet:ident {
+            $($name:ident: $expected_value:expr),* $(,)?
+        } $($tail:tt)*) => {{
+            use crate::packets::hci::*;
+
+            paste! {
+                let packet: [<$packet Packet>] = $ctx.0.hci_events.borrow_mut().pop_front().expect("No hci packet").try_into().unwrap();
+            }
+
+            $(
+                let value = paste! { packet.[<get_ $name>]() };
+                assert_eq!(value.clone(), $expected_value);
+            )*
+
+            println!("IUT -> Upper Tester: {}", stringify!($packet));
+
+            sequence_body!($ctx, $($tail)*)
+        }};
+        ($ctx:ident, IUT -> Lower Tester: $packet:ident {
+            $($name:ident: $expected_value:expr),* $(,)?
+        } $($tail:tt)*) => {{
+            use crate::packets::lmp::*;
+
+            paste! {
+                let packet: [<$packet Packet>] = $ctx.0.out_lmp_packets.borrow_mut().pop_front().expect("No lmp packet").try_into().unwrap();
+            }
+
+            $(
+                let value = paste! { packet.[<get_ $name>]() };
+                assert_eq!(value.clone(), $expected_value);
+            )*
+
+            println!("IUT -> Lower Tester: {}", stringify!($packet));
+
+            sequence_body!($ctx, $($tail)*)
+        }};
+        ($ctx:ident, repeat $number:literal times {
+            $($inner:tt)*
+        } $($tail:tt)*) => {{
+            println!("repeat {}", $number);
+            for _ in 0..$number {
+                sequence_body!($ctx, $($inner)*);
+            }
+            println!("endrepeat");
+
+            sequence_body!($ctx, $($tail)*)
+        }};
+    }
+
+macro_rules! sequence {
+        ($procedure_fn:path, $context:path, $($tail:tt)*) => ({
+            use paste::paste;
+            use std::convert::TryInto;
+
+            let procedure = $procedure_fn(&$context);
+
+            use crate::future::pin;
+            pin!(procedure);
+
+            let mut ctx = (&$context, procedure);
+            use crate::test::sequence_body;
+            let last_poll = sequence_body!(ctx, $($tail)*).unwrap();
+
+            assert!(last_poll.is_ready());
+            assert!($context.in_lmp_packets.borrow().is_empty());
+            assert!($context.out_lmp_packets.borrow().is_empty());
+            assert!($context.hci_commands.borrow().is_empty());
+            assert!($context.hci_events.borrow().is_empty());
+        });
+    }
+
+pub(crate) use sequence;
+pub(crate) use sequence_body;
diff --git a/tools/rootcanal/lmp/test/SP/BV-06-C.in b/tools/rootcanal/lmp/test/SP/BV-06-C.in
new file mode 100644
index 0000000..dfe68a9
--- /dev/null
+++ b/tools/rootcanal/lmp/test/SP/BV-06-C.in
@@ -0,0 +1,163 @@
+sequence! { procedure, context,
+    // ACL Connection Established
+    Upper Tester -> IUT: AuthenticationRequested {
+        connection_handle: context.peer_handle()
+    }
+    IUT -> Upper Tester: AuthenticationRequestedStatus {
+       num_hci_command_packets: 1,
+       status: ErrorCode::Success,
+    }
+    IUT -> Upper Tester: LinkKeyRequest {
+        bd_addr: context.peer_address(),
+    }
+    Upper Tester -> IUT: LinkKeyRequestNegativeReply {
+        bd_addr: context.peer_address(),
+    }
+    IUT -> Upper Tester: LinkKeyRequestNegativeReplyComplete {
+       num_hci_command_packets: 1,
+       status: ErrorCode::Success,
+       bd_addr: context.peer_address(),
+    }
+    IUT -> Upper Tester: IoCapabilityRequest {
+        bd_addr: context.peer_address(),
+    }
+    Upper Tester -> IUT: IoCapabilityRequestReply {
+        bd_addr: context.peer_address(),
+        io_capability: IoCapability::DisplayYesNo,
+        oob_present: OobDataPresent::NotPresent,
+        authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection,
+    }
+    IUT -> Upper Tester: IoCapabilityRequestReplyComplete {
+        num_hci_command_packets: 1,
+        status: ErrorCode::Success,
+        bd_addr: context.peer_address(),
+    }
+    IUT -> Lower Tester: IoCapabilityReq {
+        transaction_id: 0,
+        io_capabilities: 0x01,
+        oob_authentication_data: 0x00,
+        authentication_requirement: 0x01,
+    }
+    Lower Tester -> IUT: IoCapabilityRes {
+        transaction_id: 0,
+        io_capabilities: 0x01,
+        oob_authentication_data: 0x00,
+        authentication_requirement: 0x01,
+    }
+    IUT -> Upper Tester: IoCapabilityResponse {
+        bd_addr: context.peer_address(),
+        io_capability: IoCapability::DisplayYesNo,
+        oob_data_present: OobDataPresent::NotPresent,
+        authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection,
+    }
+    // Public Key Exchange
+    IUT -> Lower Tester: EncapsulatedHeader {
+        transaction_id: 0,
+        major_type: 1,
+        minor_type: 1,
+        payload_length: 48,
+    }
+    Lower Tester -> IUT: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::EncapsulatedHeader,
+    }
+    repeat 3 times {
+        IUT -> Lower Tester: EncapsulatedPayload {
+            transaction_id: 0,
+            data: [0; 16],
+        }
+        Lower Tester -> IUT: Accepted {
+            transaction_id: 0,
+            accepted_opcode: Opcode::EncapsulatedPayload,
+        }
+    }
+    Lower Tester -> IUT: EncapsulatedHeader {
+        transaction_id: 0,
+        major_type: 1,
+        minor_type: 1,
+        payload_length: 48,
+    }
+    IUT -> Lower Tester: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::EncapsulatedHeader,
+    }
+    repeat 3 times {
+        Lower Tester -> IUT: EncapsulatedPayload {
+            transaction_id: 0,
+            data: [0; 16],
+        }
+        IUT -> Lower Tester: Accepted {
+            transaction_id: 0,
+            accepted_opcode: Opcode::EncapsulatedPayload,
+        }
+    }
+    // Authentication Stage 1: Numeric Comparaison Protocol
+    Lower Tester -> IUT: SimplePairingConfirm {
+        transaction_id: 0,
+        commitment_value: [0; 16],
+    }
+    IUT -> Lower Tester: SimplePairingNumber {
+        transaction_id: 0,
+        nonce: [0; 16],
+    }
+    Lower Tester -> IUT: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::SimplePairingNumber,
+    }
+    Lower Tester -> IUT: SimplePairingNumber {
+        transaction_id: 0,
+        nonce: [0; 16],
+    }
+    IUT -> Upper Tester: UserConfirmationRequest { bd_addr: context.peer_address(), numeric_value: 0 }
+    IUT -> Lower Tester: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::SimplePairingNumber,
+    }
+    Upper Tester -> IUT: UserConfirmationRequestReply { bd_addr: context.peer_address() }
+    IUT -> Upper Tester: UserConfirmationRequestReplyComplete {
+        num_hci_command_packets: 1,
+        status: ErrorCode::Success,
+        bd_addr: context.peer_address(),
+    }
+    // Authentication Stage 2
+    IUT -> Lower Tester: DhkeyCheck {
+        transaction_id: 0,
+        confirmation_value: [0; 16],
+    }
+    Lower Tester -> IUT: Accepted { transaction_id: 0, accepted_opcode: Opcode::DhkeyCheck }
+    Lower Tester -> IUT: DhkeyCheck {
+        transaction_id: 0,
+        confirmation_value: [0; 16],
+    }
+    IUT -> Lower Tester: Accepted { transaction_id: 0, accepted_opcode: Opcode::DhkeyCheck }
+    IUT -> Upper Tester: SimplePairingComplete {
+        status: ErrorCode::Success,
+        bd_addr: context.peer_address(),
+    }
+    // Link Key Calculation
+    IUT -> Lower Tester: AuRand {
+        transaction_id: 0,
+        random_number: [0; 16],
+    }
+    Lower Tester -> IUT: Sres {
+        transaction_id: 0,
+        authentication_rsp: [0; 4],
+    }
+    Lower Tester -> IUT: AuRand {
+        transaction_id: 0,
+        random_number: [0; 16],
+    }
+    IUT -> Lower Tester: Sres {
+        transaction_id: 0,
+        authentication_rsp: [0; 4],
+    }
+    IUT -> Upper Tester: LinkKeyNotification {
+        bd_addr: context.peer_address(),
+        key_type: KeyType::AuthenticatedP192,
+        link_key: [0; 16],
+    }
+    IUT -> Upper Tester: AuthenticationComplete {
+        status: ErrorCode::Success,
+        connection_handle: context.peer_handle(),
+    }
+}
diff --git a/tools/rootcanal/lmp/test/SP/BV-07-C.in b/tools/rootcanal/lmp/test/SP/BV-07-C.in
new file mode 100644
index 0000000..62913ae
--- /dev/null
+++ b/tools/rootcanal/lmp/test/SP/BV-07-C.in
@@ -0,0 +1,141 @@
+sequence! { procedure, context,
+    // ACL Connection Established
+    Lower Tester -> IUT: IoCapabilityReq {
+        transaction_id: 0,
+        io_capabilities: 0x01,
+        oob_authentication_data: 0x00,
+        authentication_requirement: 0x01,
+    }
+    IUT -> Upper Tester: IoCapabilityResponse {
+        bd_addr: context.peer_address(),
+        io_capability: IoCapability::DisplayYesNo,
+        oob_data_present: OobDataPresent::NotPresent,
+        authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection,
+    }
+    IUT -> Upper Tester: IoCapabilityRequest {
+        bd_addr: context.peer_address(),
+    }
+    Upper Tester -> IUT: IoCapabilityRequestReply {
+        bd_addr: context.peer_address(),
+        io_capability: IoCapability::DisplayYesNo,
+        oob_present: OobDataPresent::NotPresent,
+        authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection,
+    }
+    IUT -> Upper Tester: IoCapabilityRequestReplyComplete {
+        num_hci_command_packets: 1,
+        status: ErrorCode::Success,
+        bd_addr: context.peer_address(),
+    }
+    IUT -> Lower Tester: IoCapabilityRes {
+        transaction_id: 0,
+        io_capabilities: 0x01,
+        oob_authentication_data: 0x00,
+        authentication_requirement: 0x01,
+    }
+    // Public Key Exchange
+    Lower Tester -> IUT: EncapsulatedHeader {
+        transaction_id: 0,
+        major_type: 1,
+        minor_type: 1,
+        payload_length: 48,
+    }
+    IUT -> Lower Tester: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::EncapsulatedHeader,
+    }
+    repeat 3 times {
+        Lower Tester -> IUT: EncapsulatedPayload {
+            transaction_id: 0,
+            data: [0; 16],
+        }
+        IUT -> Lower Tester: Accepted {
+            transaction_id: 0,
+            accepted_opcode: Opcode::EncapsulatedPayload,
+        }
+    }
+    IUT -> Lower Tester: EncapsulatedHeader {
+        transaction_id: 0,
+        major_type: 1,
+        minor_type: 1,
+        payload_length: 48,
+    }
+    Lower Tester -> IUT: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::EncapsulatedHeader,
+    }
+    repeat 3 times {
+        IUT -> Lower Tester: EncapsulatedPayload {
+            transaction_id: 0,
+            data: [0; 16],
+        }
+        Lower Tester -> IUT: Accepted {
+            transaction_id: 0,
+            accepted_opcode: Opcode::EncapsulatedPayload,
+        }
+    }
+    // Authentication Stage 1: Numeric Comparaison Protocol
+    IUT -> Lower Tester: SimplePairingConfirm {
+        transaction_id: 0,
+        commitment_value: [0; 16],
+    }
+    Lower Tester -> IUT: SimplePairingNumber {
+        transaction_id: 0,
+        nonce: [0; 16],
+    }
+    IUT -> Lower Tester: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::SimplePairingNumber,
+    }
+    IUT -> Lower Tester: SimplePairingNumber {
+        transaction_id: 0,
+        nonce: [0; 16],
+    }
+    Lower Tester -> IUT: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::SimplePairingNumber,
+    }
+    IUT -> Upper Tester: UserConfirmationRequest { bd_addr: context.peer_address(), numeric_value: 0 }
+    Upper Tester -> IUT: UserConfirmationRequestReply { bd_addr: context.peer_address() }
+    IUT -> Upper Tester: UserConfirmationRequestReplyComplete {
+        num_hci_command_packets: 1,
+        status: ErrorCode::Success,
+        bd_addr: context.peer_address(),
+    }
+    // Authentication Stage 2
+    Lower Tester -> IUT: DhkeyCheck {
+        transaction_id: 0,
+        confirmation_value: [0; 16],
+    }
+    IUT -> Lower Tester: Accepted { transaction_id: 0, accepted_opcode: Opcode::DhkeyCheck }
+    IUT -> Lower Tester: DhkeyCheck {
+        transaction_id: 0,
+        confirmation_value: [0; 16],
+    }
+    Lower Tester -> IUT: Accepted { transaction_id: 0, accepted_opcode: Opcode::DhkeyCheck }
+    IUT -> Upper Tester: SimplePairingComplete {
+        status: ErrorCode::Success,
+        bd_addr: context.peer_address(),
+    }
+    // Link Key Calculation
+    Lower Tester -> IUT: AuRand {
+        transaction_id: 0,
+        random_number: [0; 16],
+    }
+    IUT -> Lower Tester: Sres {
+        transaction_id: 0,
+        authentication_rsp: [0; 4],
+    }
+    IUT -> Lower Tester: AuRand {
+        transaction_id: 0,
+        random_number: [0; 16],
+    }
+    Lower Tester -> IUT: Sres {
+        transaction_id: 0,
+        authentication_rsp: [0; 4],
+    }
+    IUT -> Upper Tester: LinkKeyNotification {
+        bd_addr: context.peer_address(),
+        key_type: KeyType::AuthenticatedP192,
+        link_key: [0; 16],
+    }
+}
diff --git a/tools/rootcanal/lmp/test/SP/BV-08-C.in b/tools/rootcanal/lmp/test/SP/BV-08-C.in
new file mode 100644
index 0000000..2b5fbed
--- /dev/null
+++ b/tools/rootcanal/lmp/test/SP/BV-08-C.in
@@ -0,0 +1,133 @@
+sequence! { procedure, context,
+    // ACL Connection Established
+    Upper Tester -> IUT: AuthenticationRequested {
+        connection_handle: context.peer_handle()
+    }
+    IUT -> Upper Tester: AuthenticationRequestedStatus {
+       num_hci_command_packets: 1,
+       status: ErrorCode::Success,
+    }
+    IUT -> Upper Tester: LinkKeyRequest {
+        bd_addr: context.peer_address(),
+    }
+    Upper Tester -> IUT: LinkKeyRequestNegativeReply {
+        bd_addr: context.peer_address(),
+    }
+    IUT -> Upper Tester: LinkKeyRequestNegativeReplyComplete {
+       num_hci_command_packets: 1,
+       status: ErrorCode::Success,
+       bd_addr: context.peer_address(),
+    }
+    IUT -> Upper Tester: IoCapabilityRequest {
+        bd_addr: context.peer_address(),
+    }
+    Upper Tester -> IUT: IoCapabilityRequestReply {
+        bd_addr: context.peer_address(),
+        io_capability: IoCapability::DisplayYesNo,
+        oob_present: OobDataPresent::NotPresent,
+        authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection,
+    }
+    IUT -> Upper Tester: IoCapabilityRequestReplyComplete {
+        num_hci_command_packets: 1,
+        status: ErrorCode::Success,
+        bd_addr: context.peer_address(),
+    }
+    IUT -> Lower Tester: IoCapabilityReq {
+        transaction_id: 0,
+        io_capabilities: 0x01,
+        oob_authentication_data: 0x00,
+        authentication_requirement: 0x01,
+    }
+    Lower Tester -> IUT: IoCapabilityRes {
+        transaction_id: 0,
+        io_capabilities: 0x01,
+        oob_authentication_data: 0x00,
+        authentication_requirement: 0x01,
+    }
+    IUT -> Upper Tester: IoCapabilityResponse {
+        bd_addr: context.peer_address(),
+        io_capability: IoCapability::DisplayYesNo,
+        oob_data_present: OobDataPresent::NotPresent,
+        authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection,
+    }
+    // Public Key Exchange
+    IUT -> Lower Tester: EncapsulatedHeader {
+        transaction_id: 0,
+        major_type: 1,
+        minor_type: 1,
+        payload_length: 48,
+    }
+    Lower Tester -> IUT: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::EncapsulatedHeader,
+    }
+    repeat 3 times {
+        IUT -> Lower Tester: EncapsulatedPayload {
+            transaction_id: 0,
+            data: [0; 16],
+        }
+        Lower Tester -> IUT: Accepted {
+            transaction_id: 0,
+            accepted_opcode: Opcode::EncapsulatedPayload,
+        }
+    }
+    Lower Tester -> IUT: EncapsulatedHeader {
+        transaction_id: 0,
+        major_type: 1,
+        minor_type: 1,
+        payload_length: 48,
+    }
+    IUT -> Lower Tester: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::EncapsulatedHeader,
+    }
+    repeat 3 times {
+        Lower Tester -> IUT: EncapsulatedPayload {
+            transaction_id: 0,
+            data: [0; 16],
+        }
+        IUT -> Lower Tester: Accepted {
+            transaction_id: 0,
+            accepted_opcode: Opcode::EncapsulatedPayload,
+        }
+    }
+    // Authentication Stage 1: Numeric Comparaison Protocol
+    Lower Tester -> IUT: SimplePairingConfirm {
+        transaction_id: 0,
+        commitment_value: [0; 16],
+    }
+    IUT -> Lower Tester: SimplePairingNumber {
+        transaction_id: 0,
+        nonce: [0; 16],
+    }
+    Lower Tester -> IUT: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::SimplePairingNumber,
+    }
+    Lower Tester -> IUT: SimplePairingNumber {
+        transaction_id: 0,
+        nonce: [0; 16],
+    }
+    IUT -> Upper Tester: UserConfirmationRequest { bd_addr: context.peer_address(), numeric_value: 0 }
+    IUT -> Lower Tester: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::SimplePairingNumber,
+    }
+    Upper Tester -> IUT: UserConfirmationRequestNegativeReply { bd_addr: context.peer_address() }
+    IUT -> Upper Tester: UserConfirmationRequestNegativeReplyComplete {
+        num_hci_command_packets: 1,
+        status: ErrorCode::Success,
+        bd_addr: context.peer_address(),
+    }
+    IUT -> Lower Tester: NumericComparaisonFailed {
+        transaction_id: 0,
+    }
+    IUT -> Upper Tester: SimplePairingComplete {
+        status: ErrorCode::AuthenticationFailure,
+        bd_addr: context.peer_address(),
+    }
+    IUT -> Upper Tester: AuthenticationComplete {
+        status: ErrorCode::AuthenticationFailure,
+        connection_handle: context.peer_handle(),
+    }
+}
diff --git a/tools/rootcanal/lmp/test/SP/BV-09-C.in b/tools/rootcanal/lmp/test/SP/BV-09-C.in
new file mode 100644
index 0000000..d0d9a3b
--- /dev/null
+++ b/tools/rootcanal/lmp/test/SP/BV-09-C.in
@@ -0,0 +1,111 @@
+sequence! { procedure, context,
+    // ACL Connection Established
+    Lower Tester -> IUT: IoCapabilityReq {
+        transaction_id: 0,
+        io_capabilities: 0x01,
+        oob_authentication_data: 0x00,
+        authentication_requirement: 0x01,
+    }
+    IUT -> Upper Tester: IoCapabilityResponse {
+        bd_addr: context.peer_address(),
+        io_capability: IoCapability::DisplayYesNo,
+        oob_data_present: OobDataPresent::NotPresent,
+        authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection,
+    }
+    IUT -> Upper Tester: IoCapabilityRequest {
+        bd_addr: context.peer_address(),
+    }
+    Upper Tester -> IUT: IoCapabilityRequestReply {
+        bd_addr: context.peer_address(),
+        io_capability: IoCapability::DisplayYesNo,
+        oob_present: OobDataPresent::NotPresent,
+        authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection,
+    }
+    IUT -> Upper Tester: IoCapabilityRequestReplyComplete {
+        num_hci_command_packets: 1,
+        status: ErrorCode::Success,
+        bd_addr: context.peer_address(),
+    }
+    IUT -> Lower Tester: IoCapabilityRes {
+        transaction_id: 0,
+        io_capabilities: 0x01,
+        oob_authentication_data: 0x00,
+        authentication_requirement: 0x01,
+    }
+    // Public Key Exchange
+    Lower Tester -> IUT: EncapsulatedHeader {
+        transaction_id: 0,
+        major_type: 1,
+        minor_type: 1,
+        payload_length: 48,
+    }
+    IUT -> Lower Tester: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::EncapsulatedHeader,
+    }
+    repeat 3 times {
+        Lower Tester -> IUT: EncapsulatedPayload {
+            transaction_id: 0,
+            data: [0; 16],
+        }
+        IUT -> Lower Tester: Accepted {
+            transaction_id: 0,
+            accepted_opcode: Opcode::EncapsulatedPayload,
+        }
+    }
+    IUT -> Lower Tester: EncapsulatedHeader {
+        transaction_id: 0,
+        major_type: 1,
+        minor_type: 1,
+        payload_length: 48,
+    }
+    Lower Tester -> IUT: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::EncapsulatedHeader,
+    }
+    repeat 3 times {
+        IUT -> Lower Tester: EncapsulatedPayload {
+            transaction_id: 0,
+            data: [0; 16],
+        }
+        Lower Tester -> IUT: Accepted {
+            transaction_id: 0,
+            accepted_opcode: Opcode::EncapsulatedPayload,
+        }
+    }
+    // Authentication Stage 1: Numeric Comparaison Protocol
+    IUT -> Lower Tester: SimplePairingConfirm {
+        transaction_id: 0,
+        commitment_value: [0; 16],
+    }
+    Lower Tester -> IUT: SimplePairingNumber {
+        transaction_id: 0,
+        nonce: [0; 16],
+    }
+    IUT -> Lower Tester: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::SimplePairingNumber,
+    }
+    IUT -> Lower Tester: SimplePairingNumber {
+        transaction_id: 0,
+        nonce: [0; 16],
+    }
+    Lower Tester -> IUT: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::SimplePairingNumber,
+    }
+    IUT -> Upper Tester: UserConfirmationRequest { bd_addr: context.peer_address(), numeric_value: 0 }
+    Upper Tester -> IUT: UserConfirmationRequestReply { bd_addr: context.peer_address() }
+    IUT -> Upper Tester: UserConfirmationRequestReplyComplete {
+        num_hci_command_packets: 1,
+        status: ErrorCode::Success,
+        bd_addr: context.peer_address(),
+    }
+    Lower Tester -> IUT: NumericComparaisonFailed {
+        transaction_id: 0,
+    }
+    IUT -> Upper Tester: SimplePairingComplete {
+        status: ErrorCode::AuthenticationFailure,
+        bd_addr: context.peer_address(),
+    }
+}
diff --git a/tools/rootcanal/lmp/test/SP/BV-10-C.in b/tools/rootcanal/lmp/test/SP/BV-10-C.in
new file mode 100644
index 0000000..9d4938f
--- /dev/null
+++ b/tools/rootcanal/lmp/test/SP/BV-10-C.in
@@ -0,0 +1,135 @@
+sequence! { procedure, context,
+    // ACL Connection Established
+    Upper Tester -> IUT: AuthenticationRequested {
+        connection_handle: context.peer_handle()
+    }
+    IUT -> Upper Tester: AuthenticationRequestedStatus {
+       num_hci_command_packets: 1,
+       status: ErrorCode::Success,
+    }
+    IUT -> Upper Tester: LinkKeyRequest {
+        bd_addr: context.peer_address(),
+    }
+    Upper Tester -> IUT: LinkKeyRequestNegativeReply {
+        bd_addr: context.peer_address(),
+    }
+    IUT -> Upper Tester: LinkKeyRequestNegativeReplyComplete {
+       num_hci_command_packets: 1,
+       status: ErrorCode::Success,
+       bd_addr: context.peer_address(),
+    }
+    IUT -> Upper Tester: IoCapabilityRequest {
+        bd_addr: context.peer_address(),
+    }
+    Upper Tester -> IUT: IoCapabilityRequestReply {
+        bd_addr: context.peer_address(),
+        io_capability: IoCapability::DisplayYesNo,
+        oob_present: OobDataPresent::NotPresent,
+        authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection,
+    }
+    IUT -> Upper Tester: IoCapabilityRequestReplyComplete {
+        num_hci_command_packets: 1,
+        status: ErrorCode::Success,
+        bd_addr: context.peer_address(),
+    }
+    IUT -> Lower Tester: IoCapabilityReq {
+        transaction_id: 0,
+        io_capabilities: 0x01,
+        oob_authentication_data: 0x00,
+        authentication_requirement: 0x01,
+    }
+    Lower Tester -> IUT: IoCapabilityRes {
+        transaction_id: 0,
+        io_capabilities: 0x01,
+        oob_authentication_data: 0x00,
+        authentication_requirement: 0x01,
+    }
+    IUT -> Upper Tester: IoCapabilityResponse {
+        bd_addr: context.peer_address(),
+        io_capability: IoCapability::DisplayYesNo,
+        oob_data_present: OobDataPresent::NotPresent,
+        authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection,
+    }
+    // Public Key Exchange
+    IUT -> Lower Tester: EncapsulatedHeader {
+        transaction_id: 0,
+        major_type: 1,
+        minor_type: 1,
+        payload_length: 48,
+    }
+    Lower Tester -> IUT: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::EncapsulatedHeader,
+    }
+    repeat 3 times {
+        IUT -> Lower Tester: EncapsulatedPayload {
+            transaction_id: 0,
+            data: [0; 16],
+        }
+        Lower Tester -> IUT: Accepted {
+            transaction_id: 0,
+            accepted_opcode: Opcode::EncapsulatedPayload,
+        }
+    }
+    Lower Tester -> IUT: EncapsulatedHeader {
+        transaction_id: 0,
+        major_type: 1,
+        minor_type: 1,
+        payload_length: 48,
+    }
+    IUT -> Lower Tester: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::EncapsulatedHeader,
+    }
+    repeat 3 times {
+        Lower Tester -> IUT: EncapsulatedPayload {
+            transaction_id: 0,
+            data: [0; 16],
+        }
+        IUT -> Lower Tester: Accepted {
+            transaction_id: 0,
+            accepted_opcode: Opcode::EncapsulatedPayload,
+        }
+    }
+    // Authentication Stage 1: Numeric Comparaison Protocol
+    Lower Tester -> IUT: SimplePairingConfirm {
+        transaction_id: 0,
+        commitment_value: [0; 16],
+    }
+    IUT -> Lower Tester: SimplePairingNumber {
+        transaction_id: 0,
+        nonce: [0; 16],
+    }
+    Lower Tester -> IUT: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::SimplePairingNumber,
+    }
+    Lower Tester -> IUT: SimplePairingNumber {
+        transaction_id: 0,
+        nonce: [0; 16],
+    }
+    IUT -> Upper Tester: UserConfirmationRequest { bd_addr: context.peer_address(), numeric_value: 0 }
+    IUT -> Lower Tester: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::SimplePairingNumber,
+    }
+    Upper Tester -> IUT: UserConfirmationRequestReply { bd_addr: context.peer_address() }
+    IUT -> Upper Tester: UserConfirmationRequestReplyComplete {
+        num_hci_command_packets: 1,
+        status: ErrorCode::Success,
+        bd_addr: context.peer_address(),
+    }
+    IUT -> Lower Tester: DhkeyCheck {
+        transaction_id: 0,
+        confirmation_value: [0; 16],
+    }
+    Lower Tester -> IUT: NotAccepted { transaction_id: 0, not_accepted_opcode: Opcode::DhkeyCheck, error_code: 0x05 }
+    IUT -> Upper Tester: SimplePairingComplete {
+        status: ErrorCode::AuthenticationFailure,
+        bd_addr: context.peer_address(),
+    }
+    IUT -> Upper Tester: AuthenticationComplete {
+        status: ErrorCode::AuthenticationFailure,
+        connection_handle: context.peer_handle(),
+    }
+}
diff --git a/tools/rootcanal/lmp/test/SP/BV-11-C.in b/tools/rootcanal/lmp/test/SP/BV-11-C.in
new file mode 100644
index 0000000..fbd98d2
--- /dev/null
+++ b/tools/rootcanal/lmp/test/SP/BV-11-C.in
@@ -0,0 +1,113 @@
+sequence! { procedure, context,
+    // ACL Connection Established
+    Lower Tester -> IUT: IoCapabilityReq {
+        transaction_id: 0,
+        io_capabilities: 0x01,
+        oob_authentication_data: 0x00,
+        authentication_requirement: 0x01,
+    }
+    IUT -> Upper Tester: IoCapabilityResponse {
+        bd_addr: context.peer_address(),
+        io_capability: IoCapability::DisplayYesNo,
+        oob_data_present: OobDataPresent::NotPresent,
+        authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection,
+    }
+    IUT -> Upper Tester: IoCapabilityRequest {
+        bd_addr: context.peer_address(),
+    }
+    Upper Tester -> IUT: IoCapabilityRequestReply {
+        bd_addr: context.peer_address(),
+        io_capability: IoCapability::DisplayYesNo,
+        oob_present: OobDataPresent::NotPresent,
+        authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection,
+    }
+    IUT -> Upper Tester: IoCapabilityRequestReplyComplete {
+        num_hci_command_packets: 1,
+        status: ErrorCode::Success,
+        bd_addr: context.peer_address(),
+    }
+    IUT -> Lower Tester: IoCapabilityRes {
+        transaction_id: 0,
+        io_capabilities: 0x01,
+        oob_authentication_data: 0x00,
+        authentication_requirement: 0x01,
+    }
+    // Public Key Exchange
+    Lower Tester -> IUT: EncapsulatedHeader {
+        transaction_id: 0,
+        major_type: 1,
+        minor_type: 1,
+        payload_length: 48,
+    }
+    IUT -> Lower Tester: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::EncapsulatedHeader,
+    }
+    repeat 3 times {
+        Lower Tester -> IUT: EncapsulatedPayload {
+            transaction_id: 0,
+            data: [0; 16],
+        }
+        IUT -> Lower Tester: Accepted {
+            transaction_id: 0,
+            accepted_opcode: Opcode::EncapsulatedPayload,
+        }
+    }
+    IUT -> Lower Tester: EncapsulatedHeader {
+        transaction_id: 0,
+        major_type: 1,
+        minor_type: 1,
+        payload_length: 48,
+    }
+    Lower Tester -> IUT: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::EncapsulatedHeader,
+    }
+    repeat 3 times {
+        IUT -> Lower Tester: EncapsulatedPayload {
+            transaction_id: 0,
+            data: [0; 16],
+        }
+        Lower Tester -> IUT: Accepted {
+            transaction_id: 0,
+            accepted_opcode: Opcode::EncapsulatedPayload,
+        }
+    }
+    // Authentication Stage 1: Numeric Comparaison Protocol
+    IUT -> Lower Tester: SimplePairingConfirm {
+        transaction_id: 0,
+        commitment_value: [0; 16],
+    }
+    Lower Tester -> IUT: SimplePairingNumber {
+        transaction_id: 0,
+        nonce: [0; 16],
+    }
+    IUT -> Lower Tester: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::SimplePairingNumber,
+    }
+    IUT -> Lower Tester: SimplePairingNumber {
+        transaction_id: 0,
+        nonce: [0; 16],
+    }
+    Lower Tester -> IUT: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::SimplePairingNumber,
+    }
+    IUT -> Upper Tester: UserConfirmationRequest { bd_addr: context.peer_address(), numeric_value: 0 }
+    Upper Tester -> IUT: UserConfirmationRequestNegativeReply { bd_addr: context.peer_address() }
+    IUT -> Upper Tester: UserConfirmationRequestNegativeReplyComplete {
+        num_hci_command_packets: 1,
+        status: ErrorCode::Success,
+        bd_addr: context.peer_address(),
+    }
+    Lower Tester -> IUT: DhkeyCheck {
+        transaction_id: 0,
+        confirmation_value: [0; 16],
+    }
+    IUT -> Lower Tester: NotAccepted { transaction_id: 0, not_accepted_opcode: Opcode::DhkeyCheck }
+    IUT -> Upper Tester: SimplePairingComplete {
+        status: ErrorCode::AuthenticationFailure,
+        bd_addr: context.peer_address(),
+    }
+}
diff --git a/tools/rootcanal/lmp/test/SP/BV-12-C.in b/tools/rootcanal/lmp/test/SP/BV-12-C.in
new file mode 100644
index 0000000..3d9eb6e
--- /dev/null
+++ b/tools/rootcanal/lmp/test/SP/BV-12-C.in
@@ -0,0 +1,174 @@
+sequence! { procedure, context,
+    // ACL Connection Established
+    Upper Tester -> IUT: AuthenticationRequested {
+        connection_handle: context.peer_handle()
+    }
+    IUT -> Upper Tester: AuthenticationRequestedStatus {
+       num_hci_command_packets: 1,
+       status: ErrorCode::Success,
+    }
+    IUT -> Upper Tester: LinkKeyRequest {
+        bd_addr: context.peer_address(),
+    }
+    Upper Tester -> IUT: LinkKeyRequestNegativeReply {
+        bd_addr: context.peer_address(),
+    }
+    IUT -> Upper Tester: LinkKeyRequestNegativeReplyComplete {
+       num_hci_command_packets: 1,
+       status: ErrorCode::Success,
+       bd_addr: context.peer_address(),
+    }
+    IUT -> Upper Tester: IoCapabilityRequest {
+        bd_addr: context.peer_address(),
+    }
+    Upper Tester -> IUT: IoCapabilityRequestReply {
+        bd_addr: context.peer_address(),
+        io_capability: IoCapability::KeyboardOnly,
+        oob_present: OobDataPresent::NotPresent,
+        authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection,
+    }
+    IUT -> Upper Tester: IoCapabilityRequestReplyComplete {
+        num_hci_command_packets: 1,
+        status: ErrorCode::Success,
+        bd_addr: context.peer_address(),
+    }
+    IUT -> Lower Tester: IoCapabilityReq {
+        transaction_id: 0,
+        io_capabilities: 0x02,
+        oob_authentication_data: 0x00,
+        authentication_requirement: 0x01,
+    }
+    Lower Tester -> IUT: IoCapabilityRes {
+        transaction_id: 0,
+        io_capabilities: 0x00,
+        oob_authentication_data: 0x00,
+        authentication_requirement: 0x01,
+    }
+    IUT -> Upper Tester: IoCapabilityResponse {
+        bd_addr: context.peer_address(),
+        io_capability: IoCapability::DisplayOnly,
+        oob_data_present: OobDataPresent::NotPresent,
+        authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection,
+    }
+    // Public Key Exchange
+    IUT -> Lower Tester: EncapsulatedHeader {
+        transaction_id: 0,
+        major_type: 1,
+        minor_type: 1,
+        payload_length: 48,
+    }
+    Lower Tester -> IUT: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::EncapsulatedHeader,
+    }
+    repeat 3 times {
+        IUT -> Lower Tester: EncapsulatedPayload {
+            transaction_id: 0,
+            data: [0; 16],
+        }
+        Lower Tester -> IUT: Accepted {
+            transaction_id: 0,
+            accepted_opcode: Opcode::EncapsulatedPayload,
+        }
+    }
+    Lower Tester -> IUT: EncapsulatedHeader {
+        transaction_id: 0,
+        major_type: 1,
+        minor_type: 1,
+        payload_length: 48,
+    }
+    IUT -> Lower Tester: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::EncapsulatedHeader,
+    }
+    repeat 3 times {
+        Lower Tester -> IUT: EncapsulatedPayload {
+            transaction_id: 0,
+            data: [0; 16],
+        }
+        IUT -> Lower Tester: Accepted {
+            transaction_id: 0,
+            accepted_opcode: Opcode::EncapsulatedPayload,
+        }
+    }
+    // Authentication Stage 1: Passkey Entry Protocol
+    IUT -> Upper Tester: UserPasskeyRequest {
+        bd_addr: context.peer_address(),
+    }
+    Upper Tester -> IUT: UserPasskeyRequestReply {
+        bd_addr: context.peer_address(),
+        numeric_value: 0,
+    }
+    IUT -> Upper Tester: UserPasskeyRequestReplyComplete {
+        num_hci_command_packets: 1,
+        status: ErrorCode::Success,
+        bd_addr: context.peer_address(),
+    }
+    repeat 20 times {
+        IUT -> Lower Tester: SimplePairingConfirm {
+            transaction_id: 0,
+            commitment_value: [0; 16],
+        }
+        Lower Tester -> IUT: SimplePairingConfirm {
+            transaction_id: 0,
+            commitment_value: [0; 16],
+        }
+        IUT -> Lower Tester: SimplePairingNumber {
+            transaction_id: 0,
+            nonce: [0; 16],
+        }
+        Lower Tester -> IUT: Accepted {
+            transaction_id: 0,
+            accepted_opcode: Opcode::SimplePairingNumber,
+        }
+        Lower Tester -> IUT: SimplePairingNumber {
+            transaction_id: 0,
+            nonce: [0; 16],
+        }
+        IUT -> Lower Tester: Accepted {
+            transaction_id: 0,
+            accepted_opcode: Opcode::SimplePairingNumber,
+        }
+    }
+    // Authentication Stage 2
+    IUT -> Lower Tester: DhkeyCheck {
+        transaction_id: 0,
+        confirmation_value: [0; 16],
+    }
+    Lower Tester -> IUT: Accepted { transaction_id: 0, accepted_opcode: Opcode::DhkeyCheck }
+    Lower Tester -> IUT: DhkeyCheck {
+        transaction_id: 0,
+        confirmation_value: [0; 16],
+    }
+    IUT -> Lower Tester: Accepted { transaction_id: 0, accepted_opcode: Opcode::DhkeyCheck }
+    IUT -> Upper Tester: SimplePairingComplete {
+        status: ErrorCode::Success,
+        bd_addr: context.peer_address(),
+    }
+    // Link Key Calculation
+    IUT -> Lower Tester: AuRand {
+        transaction_id: 0,
+        random_number: [0; 16],
+    }
+    Lower Tester -> IUT: Sres {
+        transaction_id: 0,
+        authentication_rsp: [0; 4],
+    }
+    Lower Tester -> IUT: AuRand {
+        transaction_id: 0,
+        random_number: [0; 16],
+    }
+    IUT -> Lower Tester: Sres {
+        transaction_id: 0,
+        authentication_rsp: [0; 4],
+    }
+    IUT -> Upper Tester: LinkKeyNotification {
+        bd_addr: context.peer_address(),
+        key_type: KeyType::AuthenticatedP192,
+        link_key: [0; 16],
+    }
+    IUT -> Upper Tester: AuthenticationComplete {
+        status: ErrorCode::Success,
+        connection_handle: context.peer_handle(),
+    }
+}
diff --git a/tools/rootcanal/lmp/test/SP/BV-13-C.in b/tools/rootcanal/lmp/test/SP/BV-13-C.in
new file mode 100644
index 0000000..1449c60
--- /dev/null
+++ b/tools/rootcanal/lmp/test/SP/BV-13-C.in
@@ -0,0 +1,141 @@
+sequence! { procedure, context,
+    // ACL Connection Established
+    Lower Tester -> IUT: IoCapabilityReq {
+        transaction_id: 0,
+        io_capabilities: 0x02,
+        oob_authentication_data: 0x00,
+        authentication_requirement: 0x01,
+    }
+    IUT -> Upper Tester: IoCapabilityResponse {
+        bd_addr: context.peer_address(),
+        io_capability: IoCapability::KeyboardOnly,
+        oob_data_present: OobDataPresent::NotPresent,
+        authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection,
+    }
+    IUT -> Upper Tester: IoCapabilityRequest {
+        bd_addr: context.peer_address(),
+    }
+    Upper Tester -> IUT: IoCapabilityRequestReply {
+        bd_addr: context.peer_address(),
+        io_capability: IoCapability::DisplayOnly,
+        oob_present: OobDataPresent::NotPresent,
+        authentication_requirements: AuthenticationRequirements::NoBondingMitmProtection,
+    }
+    IUT -> Upper Tester: IoCapabilityRequestReplyComplete {
+        num_hci_command_packets: 1,
+        status: ErrorCode::Success,
+        bd_addr: context.peer_address(),
+    }
+    IUT -> Lower Tester: IoCapabilityRes {
+        transaction_id: 0,
+        io_capabilities: 0x00,
+        oob_authentication_data: 0x00,
+        authentication_requirement: 0x01,
+    }
+    // Public Key Exchange
+    Lower Tester -> IUT: EncapsulatedHeader {
+        transaction_id: 0,
+        major_type: 1,
+        minor_type: 1,
+        payload_length: 48,
+    }
+    IUT -> Lower Tester: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::EncapsulatedHeader,
+    }
+    repeat 3 times {
+        Lower Tester -> IUT: EncapsulatedPayload {
+            transaction_id: 0,
+            data: [0; 16],
+        }
+        IUT -> Lower Tester: Accepted {
+            transaction_id: 0,
+            accepted_opcode: Opcode::EncapsulatedPayload,
+        }
+    }
+    IUT -> Lower Tester: EncapsulatedHeader {
+        transaction_id: 0,
+        major_type: 1,
+        minor_type: 1,
+        payload_length: 48,
+    }
+    Lower Tester -> IUT: Accepted {
+        transaction_id: 0,
+        accepted_opcode: Opcode::EncapsulatedHeader,
+    }
+    repeat 3 times {
+        IUT -> Lower Tester: EncapsulatedPayload {
+            transaction_id: 0,
+            data: [0; 16],
+        }
+        Lower Tester -> IUT: Accepted {
+            transaction_id: 0,
+            accepted_opcode: Opcode::EncapsulatedPayload,
+        }
+    }
+    // Authentication Stage 1: Numeric Comparaison Protocol
+    IUT -> Upper Tester: UserPasskeyNotification { bd_addr: context.peer_address(), passkey: 0 }
+    repeat 20 times {
+        Lower Tester -> IUT: SimplePairingConfirm {
+            transaction_id: 0,
+            commitment_value: [0; 16],
+        }
+        IUT -> Lower Tester: SimplePairingConfirm {
+            transaction_id: 0,
+            commitment_value: [0; 16],
+        }
+        Lower Tester -> IUT: SimplePairingNumber {
+            transaction_id: 0,
+            nonce: [0; 16],
+        }
+        IUT -> Lower Tester: Accepted {
+            transaction_id: 0,
+            accepted_opcode: Opcode::SimplePairingNumber,
+        }
+        IUT -> Lower Tester: SimplePairingNumber {
+            transaction_id: 0,
+            nonce: [0; 16],
+        }
+        Lower Tester -> IUT: Accepted {
+            transaction_id: 0,
+            accepted_opcode: Opcode::SimplePairingNumber,
+        }
+    }
+    // Authentication Stage 2
+    Lower Tester -> IUT: DhkeyCheck {
+        transaction_id: 0,
+        confirmation_value: [0; 16],
+    }
+    IUT -> Lower Tester: Accepted { transaction_id: 0, accepted_opcode: Opcode::DhkeyCheck }
+    IUT -> Lower Tester: DhkeyCheck {
+        transaction_id: 0,
+        confirmation_value: [0; 16],
+    }
+    Lower Tester -> IUT: Accepted { transaction_id: 0, accepted_opcode: Opcode::DhkeyCheck }
+    IUT -> Upper Tester: SimplePairingComplete {
+        status: ErrorCode::Success,
+        bd_addr: context.peer_address(),
+    }
+    // Link Key Calculation
+    Lower Tester -> IUT: AuRand {
+        transaction_id: 0,
+        random_number: [0; 16],
+    }
+    IUT -> Lower Tester: Sres {
+        transaction_id: 0,
+        authentication_rsp: [0; 4],
+    }
+    IUT -> Lower Tester: AuRand {
+        transaction_id: 0,
+        random_number: [0; 16],
+    }
+    Lower Tester -> IUT: Sres {
+        transaction_id: 0,
+        authentication_rsp: [0; 4],
+    }
+    IUT -> Upper Tester: LinkKeyNotification {
+        bd_addr: context.peer_address(),
+        key_type: KeyType::AuthenticatedP192,
+        link_key: [0; 16],
+    }
+}