refactor(rust/trezor-thp): channel ids are allocated by application
What changed, and why it matters
This commit is a code refactor in Trezor's Rust THP (Trezor Host Protocol) library. It moves responsibility for assigning channel IDs from the internal library mux to the application using the library. The change also removes the requirement that credential stores/cloners implement the Clone trait, and adds a helper allocator for consecutive channel IDs. There is no direct evidence in the commit or supplied references that this fixes an active security vulnerability; it appears to be an architectural cleanup that may make misuse harder but also places more correctness burden on the application.
Treat as a normal refactor. Review downstream application code that now owns channel ID allocation to ensure it validates IDs, avoids collisions, and does not reuse IDs for active channels. No urgent patch action is indicated by the commit itself.
Security signals we found
Channel ID allocation moved from library to application, increasing application's responsibility for uniqueness/validity
New `ChannelIdAllocator` helper documents that callers must still check returned IDs for uniqueness
Removed `Clone` bound on credential traits, potentially avoiding accidental credential duplication but not a vulnerability fix per se
No mention of CVE, security bug, researcher attribution, or advisory in commit message or diff
Evidence from the diff
The patch refactors device::Mux and host::Mux in rust/trezor-thp so they no longer store credential stores/verifiers or generate channel IDs internally. Instead, channel_alloc() now takes channel_id and cred_verif/cred_store as caller-provided arguments. A new ChannelIdAllocator helper is introduced for the device side, using an AtomicU16 with wraparound. The CredentialStore and CredentialVerifier traits no longer require Clone. The commit updates examples and tests to pass IDs and credentials explicitly. The diff does not contain a security advisory, CVE, or attribution.
Changed components
rust/trezor-thp/src/channel/device.rsrust/trezor-thp/src/channel/host.rsrust/trezor-thp/src/channel/mod.rsrust/trezor-thp/src/channel/test.rsrust/trezor-thp/src/credential.rsrust/trezor-thp/examples/host-cli/client.rsrust/trezor-thp/examples/host-cli/main.rsrust/trezor-thp/examples/transport-level-ping.rsInspect captured patch +189 / −135
diff --git a/rust/trezor-thp/examples/host-cli/client.rs b/rust/trezor-thp/examples/host-cli/client.rs
index f5f4b5e0..26bb6472 100644
--- a/rust/trezor-thp/examples/host-cli/client.rs
+++ b/rust/trezor-thp/examples/host-cli/client.rs
@@ -2,10 +2,7 @@ use std::io::ErrorKind;
use std::net::{SocketAddr, UdpSocket};
use std::time::Duration;
-use trezor_thp::{
- Backend, ChannelIO, Error, channel::buffered::Buffered, channel::host::Mux,
- credential::CredentialStore,
-};
+use trezor_thp::{Backend, ChannelIO, Error, channel::buffered::Buffered, channel::host::Mux};
use protobuf::{Enum, Message};
@@ -23,12 +20,11 @@ pub struct Client<C> {
emu_addr: SocketAddr,
}
-impl<C, B> Client<Mux<C, B>>
+impl<B> Client<Mux<B>>
where
B: Backend,
- C: CredentialStore,
{
- pub fn open(emu_addr: SocketAddr, channel: Mux<C, B>) -> Self {
+ pub fn open(emu_addr: SocketAddr, channel: Mux<B>) -> Self {
let mut channel = Buffered::new(channel);
channel.set_packet_len(PACKET_LEN);
Client {
diff --git a/rust/trezor-thp/examples/host-cli/main.rs b/rust/trezor-thp/examples/host-cli/main.rs
index b6b1c181..a15e7264 100644
--- a/rust/trezor-thp/examples/host-cli/main.rs
+++ b/rust/trezor-thp/examples/host-cli/main.rs
@@ -31,10 +31,7 @@ impl Backend for RustCrypto {
type HostChannel = Channel<Host, RustCrypto>;
-fn do_allocation<C>(client: &mut Client<Mux<C, RustCrypto>>)
-where
- C: CredentialStore,
-{
+fn do_allocation(client: &mut Client<Mux<RustCrypto>>) {
client.call(0, &[]);
}
@@ -108,13 +105,13 @@ pub fn main() -> std::io::Result<()> {
env_logger::init_from_env(env_logger::Env::default().filter_or("RUST_LOG", "info"));
let cred_lookup = NullCredentialStore;
- let mut channel = Mux::<_, RustCrypto>::new(cred_lookup);
+ let mut channel = Mux::<RustCrypto>::new();
channel.request_channel(false);
let mut client = Client::open(get_address(), channel);
do_allocation(&mut client);
assert!(client.channel.channel_alloc_ready());
- let mut client = client.map(|c| c.complete().unwrap());
+ let mut client = client.map(|c| c.complete(cred_lookup).unwrap());
do_handshake(&mut client);
assert!(client.channel.handshake_done());
diff --git a/rust/trezor-thp/examples/transport-level-ping.rs b/rust/trezor-thp/examples/transport-level-ping.rs
index 2d116b49..dfc3709a 100644
--- a/rust/trezor-thp/examples/transport-level-ping.rs
+++ b/rust/trezor-thp/examples/transport-level-ping.rs
@@ -4,9 +4,7 @@ use std::net::{SocketAddr, UdpSocket};
use std::str::FromStr;
use std::time::Duration;
-use trezor_thp::{
- Backend, channel::buffered::ChannelExt, channel::host::Mux, credential::NullCredentialStore,
-};
+use trezor_thp::{Backend, channel::buffered::ChannelExt, channel::host::Mux};
struct RustCrypto;
@@ -31,7 +29,7 @@ pub fn main() -> std::io::Result<()> {
let socket = UdpSocket::bind("127.0.0.1:0")?;
let mut recvbuf = [0u8; PACKET_LEN];
- let mut mux = Mux::<_, RustCrypto>::new(NullCredentialStore).into_buffered();
+ let mut mux = Mux::<RustCrypto>::new().into_buffered();
mux.set_packet_len(PACKET_LEN);
for _i in 0..REPEAT {
diff --git a/rust/trezor-thp/src/channel/device.rs b/rust/trezor-thp/src/channel/device.rs
index cc3bee09..e19ddf62 100644
--- a/rust/trezor-thp/src/channel/device.rs
+++ b/rust/trezor-thp/src/channel/device.rs
@@ -16,7 +16,10 @@ use crate::{
util::prepare_zeroed,
};
-use core::marker::PhantomData;
+use core::{
+ marker::PhantomData,
+ sync::atomic::{AtomicU16, Ordering},
+};
// Must fit any of:
// - device_properties + overhead
@@ -34,12 +37,9 @@ const CODEC_V1_RESPONSE: &[u8] = b"?##\x00\x03\x00\x00\x00\x02\x08\x11";
/// Every packet interface on the device needs to have one Mux. Event loop should pass every
/// incoming packet to [`Mux::packet_in`] in order to determine what to do with it.
/// Single packet only. Does not keep track of opened channels.
-pub struct Mux<C, B> {
- // is_locked: bool,
- next_channel_id: u16,
- cred_verif: C,
+pub struct Mux<B> {
outgoing: heapless::Deque<MuxOutgoing, BROADCAST_OUTGOING_QUEUE_LEN>,
- new_channel: Option<(u16, Nonce)>,
+ new_channel: Option<Nonce>,
_phantom: PhantomData<B>,
}
@@ -59,17 +59,12 @@ impl MuxOutgoing {
}
}
-impl<C, B> Mux<C, B>
+impl<B> Mux<B>
where
- C: CredentialVerifier,
B: Backend,
{
- pub fn new(cred_verif: C) -> Self {
- // Use random starting id to avoid giving out the number of channels allocated since boot.
- let next_channel_id = random_channel_id::<B>();
+ pub const fn new() -> Self {
Self {
- next_channel_id,
- cred_verif,
outgoing: heapless::Deque::new(),
new_channel: None,
_phantom: PhantomData,
@@ -77,11 +72,21 @@ where
}
/// Create new [`ChannelOpen`] when channel allocation request is pending.
- pub fn channel_alloc(&mut self) -> Result<ChannelOpen<C, B>, Error> {
- let Some((channel_id, nonce)) = self.new_channel.take() else {
+ pub fn channel_alloc<C>(
+ &mut self,
+ channel_id: u16,
+ cred_verif: C,
+ ) -> Result<ChannelOpen<C, B>, Error>
+ where
+ C: CredentialVerifier,
+ {
+ if !channel_id_valid(channel_id) || channel_id == BROADCAST_CHANNEL_ID {
+ return Err(Error::unexpected_input());
+ }
+ let Some(nonce) = self.new_channel.take() else {
return Err(Error::not_ready());
};
- ChannelOpen::<C, B>::new(channel_id, nonce, self.cred_verif.clone())
+ ChannelOpen::<C, B>::new(channel_id, nonce, cred_verif)
}
/// Returns `true` if there is channel allocation request pending.
@@ -118,26 +123,21 @@ where
})
}
- fn handle_broadcast(&mut self, packet: &[u8]) -> Result<Option<u16>, Error> {
+ // Returns true if allocation request has been received.
+ fn handle_broadcast(&mut self, packet: &[u8]) -> Result<bool, Error> {
let (header, payload) = Reassembler::<Device>::single_inplace(packet)?;
match header {
Header::Ping if payload.len() == Nonce::LEN => {
- let (nonce, _rest) = Nonce::parse(payload).unwrap();
- self.enqueue(MuxOutgoing::Pong(nonce)).map(|_| None)
+ let (nonce, _rest) = Nonce::parse(payload)?;
+ self.enqueue(MuxOutgoing::Pong(nonce)).map(|_| false)
}
Header::ChannelAllocationRequest if payload.len() == Nonce::LEN => {
let (nonce, _rest) = Nonce::parse(payload)?;
- let channel_id = self.next_channel_id;
- self.next_channel_id += 1;
- if self.next_channel_id > MAX_CHANNEL_ID {
- log::debug!("Channel id max value reached, wrapping around.");
- self.next_channel_id = MIN_CHANNEL_ID;
- }
if self.new_channel.is_some() {
log::warn!("Dropping previous channel allocation request.");
}
- self.new_channel = Some((channel_id, nonce));
- Ok(Some(channel_id))
+ self.new_channel = Some(nonce);
+ Ok(true)
}
// No Header::TransportError for broadcast.
_ => {
@@ -172,17 +172,19 @@ where
};
PacketInResult::ignore(Error::malformed_data())
}
+}
- #[cfg(test)]
- pub(crate) fn set_next_channel_id(&mut self, channel_id: u16) {
- assert!(channel_id_valid(channel_id));
- self.next_channel_id = channel_id;
+impl<B> Default for Mux<B>
+where
+ B: Backend,
+{
+ fn default() -> Self {
+ Self::new()
}
}
-impl<C, B> ChannelIO for Mux<C, B>
+impl<B> ChannelIO for Mux<B>
where
- C: CredentialVerifier,
B: Backend,
{
fn packet_in(&mut self, packet_buffer: &[u8], _receive_buffer: &mut [u8]) -> PacketInResult {
@@ -200,11 +202,12 @@ where
if channel_id != BROADCAST_CHANNEL_ID {
return PacketInResult::route(channel_id);
}
- PacketInResult::from_result(self.handle_broadcast(packet_buffer).map(|r| {
- r.map_or_else(
- || PacketInResult::accept(false),
- PacketInResult::channel_allocation,
- )
+ PacketInResult::from_result(self.handle_broadcast(packet_buffer).map(|is_allocation| {
+ if is_allocation {
+ PacketInResult::channel_allocation()
+ } else {
+ PacketInResult::accept(false)
+ }
}))
}
@@ -543,14 +546,39 @@ where
}
}
-fn random_channel_id<B: Backend>() -> u16 {
- let mut bytes = [0u8, 0u8];
- for _i in 0..16 {
+/// Helper for assigning consecutive channel IDs.
+pub struct ChannelIdAllocator {
+ // Next value. Not guaranteed to be valid channel id, these are skipped
+ // in `ChannelIdAllocator::get()` until a valid one is found.
+ counter: AtomicU16,
+}
+
+impl ChannelIdAllocator {
+ /// Use random starting id to avoid giving out the number of channels allocated since boot.
+ pub fn new_random<B: Backend>() -> Self {
+ let mut bytes = [0u8, 0u8];
B::random_bytes(&mut bytes);
- let channel_id = u16::from_be_bytes(bytes);
- if channel_id_valid(channel_id) && channel_id != BROADCAST_CHANNEL_ID {
- return channel_id;
+ Self::new_from(u16::from_be_bytes(bytes))
+ }
+
+ /// Use fixed starting id, mainly useful for tests.
+ /// Please note [`ChannelIdAllocator::get()`] skips invalid values so the first result
+ /// is not necessarily the argument passed to this constructor.
+ pub const fn new_from(init_val: u16) -> Self {
+ Self {
+ counter: AtomicU16::new(init_val),
+ }
+ }
+
+ /// Get next ID. Wraps around to `MIN_CHANNEL_ID`.
+ /// If the caller has multiple channels it needs to check whether the returned
+ /// ID is not currently in use.
+ pub fn get(&self) -> u16 {
+ loop {
+ let channel_id = self.counter.fetch_add(1, Ordering::Relaxed);
+ if (MIN_CHANNEL_ID..=MAX_CHANNEL_ID).contains(&channel_id) {
+ return channel_id;
+ }
}
}
- panic!("Cannot generate random channel id.");
}
diff --git a/rust/trezor-thp/src/channel/host.rs b/rust/trezor-thp/src/channel/host.rs
index b945e091..4584ad16 100644
--- a/rust/trezor-thp/src/channel/host.rs
+++ b/rust/trezor-thp/src/channel/host.rs
@@ -56,24 +56,21 @@ enum PingState {
/// Handles broadcast channel messages, notably channel allocation requests.
/// Because host often only needs a single channel, you can throw away the Mux
/// after allocating one, if you don't need the keep-alive functionality.
-pub struct Mux<C, B> {
- cred_store: C,
+pub struct Mux<B> {
internal_buffer: heapless::Vec<u8, MAX_DEVICE_PROPERTIES_LEN>,
channel_allocation: AllocationState,
ping: PingState,
_phantom: PhantomData<B>,
}
-impl<C, B> Mux<C, B>
+impl<B> Mux<B>
where
- C: CredentialStore,
B: Backend,
{
- pub fn new(cred_store: C) -> Self {
+ pub fn new() -> Self {
let mut internal_buffer = heapless::Vec::new();
prepare_zeroed(&mut internal_buffer);
Self {
- cred_store,
internal_buffer,
channel_allocation: AllocationState::None,
ping: PingState::None,
@@ -98,7 +95,10 @@ where
}
/// Create new [`ChannelOpen`] after channel allocation response was received.
- pub fn channel_alloc(&mut self) -> Result<ChannelOpen<C, B>, Error> {
+ pub fn channel_alloc<C>(&mut self, cred_store: C) -> Result<ChannelOpen<C, B>, Error>
+ where
+ C: CredentialStore,
+ {
let AllocationState::ReceivedId {
try_to_unlock,
channel_id,
@@ -106,12 +106,7 @@ where
else {
return Err(Error::not_ready());
};
- let ch = ChannelOpen::new(
- channel_id,
- self.cred_store.clone(),
- &self.internal_buffer,
- try_to_unlock,
- )?;
+ let ch = ChannelOpen::new(channel_id, cred_store, &self.internal_buffer, try_to_unlock)?;
self.channel_allocation = AllocationState::None;
prepare_zeroed(&mut self.internal_buffer);
Ok(ch)
@@ -124,8 +119,11 @@ where
/// Same as [`Mux::channel_alloc`] but destroys the [`Mux`],
/// like `complete()` does for other types.
- pub fn complete(mut self) -> Result<ChannelOpen<C, B>, Error> {
- self.channel_alloc()
+ pub fn complete<C>(mut self, cred_store: C) -> Result<ChannelOpen<C, B>, Error>
+ where
+ C: CredentialStore,
+ {
+ self.channel_alloc(cred_store)
}
fn handle_broadcast(&mut self, packet: &[u8]) -> Result<PacketInResult, Error> {
@@ -216,13 +214,21 @@ where
channel_id,
};
log::debug!("Got channel id {}.", channel_id);
- Ok(PacketInResult::channel_allocation(channel_id))
+ Ok(PacketInResult::channel_allocation())
}
}
-impl<C, B> ChannelIO for Mux<C, B>
+impl<B> Default for Mux<B>
+where
+ B: Backend,
+{
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl<B> ChannelIO for Mux<B>
where
- C: CredentialStore,
B: Backend,
{
fn packet_in(&mut self, packet_buffer: &[u8], _receive_buffer: &mut [u8]) -> PacketInResult {
diff --git a/rust/trezor-thp/src/channel/mod.rs b/rust/trezor-thp/src/channel/mod.rs
index e4b928d4..6c4ff33b 100644
--- a/rust/trezor-thp/src/channel/mod.rs
+++ b/rust/trezor-thp/src/channel/mod.rs
@@ -324,11 +324,9 @@ pub enum PacketInResult {
/// [`Mux::channel_alloc`] to create new channel object. Only [`device::Mux`] and [`host::Mux`]
/// return this variant. There is no queue, do it before processing the next packet.
///
- /// Please note that the library does not keep track of allocated ids, that is left to
- /// the application. By creating a lot of new channels an attacker can obtain an id that
- /// was issued earlier. If there is existing channel with such id it should be destroyed.
- /// (This is only relevant on the device side.)
- ChannelAllocation { channel_id: u16 },
+ /// On the device side, application is responsible for allocating unique ids. It can use
+ /// [`device::ChannelIdAllocator`] but still must check the result for uniqueness.
+ ChannelAllocation,
/// Call [`device::ChannelOpen::set_static_key`] or [`device::ChannelOpen::send_device_locked`].
/// Only [`device::ChannelOpen`] returns this variant.
HandshakeKeyRequired { try_to_unlock: bool },
@@ -380,8 +378,8 @@ impl PacketInResult {
Self::Failed { error }
}
- const fn channel_allocation(channel_id: u16) -> Self {
- Self::ChannelAllocation { channel_id }
+ const fn channel_allocation() -> Self {
+ Self::ChannelAllocation
}
const fn pong() -> Self {
@@ -416,7 +414,7 @@ impl PacketInResult {
}
pub const fn got_channel(&self) -> bool {
- matches!(self, Self::ChannelAllocation { .. })
+ matches!(self, Self::ChannelAllocation)
}
pub const fn got_pong(&self) -> bool {
diff --git a/rust/trezor-thp/src/channel/test.rs b/rust/trezor-thp/src/channel/test.rs
index 44281349..bf8e0dcf 100644
--- a/rust/trezor-thp/src/channel/test.rs
+++ b/rust/trezor-thp/src/channel/test.rs
@@ -258,13 +258,16 @@ impl Direction {
fn test_open() -> Result<()> {
setup();
- let (mut hm, mut dm) = create_mux();
+ let (mut hm, mut dm, cids) = create_mux();
// channel allocation
hm.request_channel(false);
take_turns(&mut hm, &mut dm)?;
- let mut d = dm.channel_alloc()?.with_key(DEVICE_KEY).into_buffered();
+ let mut d = dm
+ .channel_alloc(cids.get(), TestCredentialVerifier)?
+ .with_key(DEVICE_KEY)
+ .into_buffered();
take_turns(&mut hm, &mut d)?;
- let mut h = hm.channel_alloc()?.into_buffered();
+ let mut h = hm.channel_alloc(NullCredentialStore)?.into_buffered();
// handshake
assert!(!h.handshake_done());
@@ -293,13 +296,15 @@ fn test_open() -> Result<()> {
fn test_device_locked() -> Result<()> {
setup();
- let (mut hm, mut dm) = create_mux();
+ let (mut hm, mut dm, cids) = create_mux();
// channel allocation
hm.request_channel(false);
take_turns(&mut hm, &mut dm)?;
- let mut d = dm.channel_alloc()?.into_buffered();
+ let mut d = dm
+ .channel_alloc(cids.get(), TestCredentialVerifier)?
+ .into_buffered();
take_turns(&mut hm, &mut d)?;
- let mut h = hm.channel_alloc()?.into_buffered();
+ let mut h = hm.channel_alloc(NullCredentialStore)?.into_buffered();
// handshake
assert!(!h.handshake_done());
@@ -318,14 +323,16 @@ fn test_device_locked() -> Result<()> {
}
fn create_mux() -> (
- Buffered<host::Mux<NullCredentialStore, RustCrypto>>,
- Buffered<device::Mux<TestCredentialVerifier, RustCrypto>>,
+ Buffered<host::Mux<RustCrypto>>,
+ Buffered<device::Mux<RustCrypto>>,
+ device::ChannelIdAllocator,
) {
- let mut hm = host::Mux::<_, RustCrypto>::new(NullCredentialStore).into_buffered();
+ let mut hm = host::Mux::<RustCrypto>::new().into_buffered();
hm.set_packet_len(DEFAULT_PACKET_LEN);
- let mut dm = device::Mux::<_, RustCrypto>::new(TestCredentialVerifier).into_buffered();
+ let mut dm = device::Mux::<RustCrypto>::new().into_buffered();
dm.set_packet_len(DEFAULT_PACKET_LEN);
- (hm, dm)
+ let cids = device::ChannelIdAllocator::new_random::<RustCrypto>();
+ (hm, dm, cids)
}
fn open_channel(
@@ -334,14 +341,17 @@ fn open_channel(
Buffered<Channel<Host, RustCrypto>>,
Buffered<Channel<Device, RustCrypto>>,
)> {
- let (mut hm, mut dm) = create_mux();
+ let (mut hm, mut dm, cids) = create_mux();
hm.set_packet_len(packet_len);
dm.set_packet_len(packet_len);
hm.request_channel(false);
take_turns(&mut hm, &mut dm)?;
- let mut d = dm.channel_alloc()?.with_key(DEVICE_KEY).into_buffered();
+ let mut d = dm
+ .channel_alloc(cids.get(), TestCredentialVerifier)?
+ .with_key(DEVICE_KEY)
+ .into_buffered();
take_turns(&mut d, &mut hm)?;
- let mut h = hm.channel_alloc()?.into_buffered();
+ let mut h = hm.channel_alloc(NullCredentialStore)?.into_buffered();
take_turns(&mut h, &mut d)?;
let h = h.map(|h| h.complete())?;
let d = d.map(|d| d.unwrap().complete())?;
@@ -383,21 +393,25 @@ fn test_packet_length(packet_len: usize) -> Result<()> {
fn test_one_device_multiple_hosts() -> Result<()> {
const NHOSTS: usize = 4;
setup();
- let mut dm = device::Mux::<_, RustCrypto>::new(TestCredentialVerifier).into_buffered();
+ let mut dm = device::Mux::<RustCrypto>::new().into_buffered();
dm.set_packet_len(DEFAULT_PACKET_LEN);
+ let cids = device::ChannelIdAllocator::new_from(42);
let mut device_chans = Vec::<Buffered<Channel<Device, RustCrypto>>>::new();
let mut host_chans = Vec::<Buffered<Channel<Host, RustCrypto>>>::new();
// open channels
for _i in 0..NHOSTS {
- let mut hm = host::Mux::<_, RustCrypto>::new(NullCredentialStore).into_buffered();
+ let mut hm = host::Mux::<RustCrypto>::new().into_buffered();
hm.set_packet_len(DEFAULT_PACKET_LEN);
hm.request_channel(false);
take_turns(&mut hm, &mut dm)?;
- let mut d = dm.channel_alloc()?.with_key(DEVICE_KEY).into_buffered();
+ let mut d = dm
+ .channel_alloc(cids.get(), TestCredentialVerifier)?
+ .with_key(DEVICE_KEY)
+ .into_buffered();
take_turns(&mut d, &mut hm)?;
- let mut h = hm.channel_alloc()?.into_buffered();
+ let mut h = hm.channel_alloc(NullCredentialStore)?.into_buffered();
take_turns(&mut h, &mut d)?;
let h = h.map(|h| h.complete())?;
let d = d.map(|d| d.unwrap().complete())?;
@@ -457,7 +471,7 @@ fn lose_nth(dir: Direction, i: usize) -> impl FnMut(Direction, &mut VecDeque<Pac
fn test_packet_loss_alloc() -> Result<()> {
setup();
- let (mut hm, mut dm) = create_mux();
+ let (mut hm, mut dm, cids) = create_mux();
// channel allocation request lost
hm.request_channel(false);
take_turns_mutate(&mut hm, &mut dm, lose_nth(HostToDevice, 0))?;
@@ -466,16 +480,20 @@ fn test_packet_loss_alloc() -> Result<()> {
// channel allocation response lost
hm.request_channel(false);
take_turns(&mut hm, &mut dm)?;
- let mut d = dm.channel_alloc()?.into_buffered();
+ let mut d = dm
+ .channel_alloc(cids.get(), TestCredentialVerifier)?
+ .into_buffered();
take_turns_mutate(&mut hm, &mut d, lose_nth(DeviceToHost, 0))?;
assert!(!hm.channel_alloc_ready());
// successful allocation
hm.request_channel(false);
take_turns(&mut hm, &mut dm)?;
- let mut d = dm.channel_alloc()?.into_buffered();
+ let mut d = dm
+ .channel_alloc(cids.get(), TestCredentialVerifier)?
+ .into_buffered();
take_turns(&mut hm, &mut d)?;
- let mut _h = hm.channel_alloc()?.into_buffered();
+ let mut _h = hm.channel_alloc(NullCredentialStore)?.into_buffered();
Ok(())
}
@@ -492,13 +510,16 @@ fn test_packet_loss_alloc() -> Result<()> {
fn test_packet_loss_handshake(dir: Direction, lost_index: usize) -> Result<()> {
setup();
- let (mut hm, mut dm) = create_mux();
+ let (mut hm, mut dm, cids) = create_mux();
// channel allocation
hm.request_channel(false);
take_turns(&mut hm, &mut dm)?;
- let mut d = dm.channel_alloc()?.with_key(DEVICE_KEY).into_buffered();
+ let mut d = dm
+ .channel_alloc(cids.get(), TestCredentialVerifier)?
+ .with_key(DEVICE_KEY)
+ .into_buffered();
take_turns(&mut hm, &mut d)?;
- let mut h = hm.channel_alloc()?.into_buffered();
+ let mut h = hm.channel_alloc(NullCredentialStore)?.into_buffered();
// handshake
take_turns_mutate(&mut h, &mut d, lose_nth(dir, lost_index))?;
@@ -554,7 +575,7 @@ fn damage_nth(
fn test_packet_damage_alloc(byte_index: usize) -> Result<()> {
setup();
- let (mut hm, mut dm) = create_mux();
+ let (mut hm, mut dm, cids) = create_mux();
// channel allocation request lost
hm.request_channel(false);
take_turns_mutate(&mut hm, &mut dm, damage_nth(HostToDevice, 0, byte_index))?;
@@ -563,16 +584,20 @@ fn test_packet_damage_alloc(byte_index: usize) -> Result<()> {
// channel allocation response lost
hm.request_channel(false);
take_turns(&mut hm, &mut dm)?;
- let mut d = dm.channel_alloc()?.into_buffered();
+ let mut d = dm
+ .channel_alloc(cids.get(), TestCredentialVerifier)?
+ .into_buffered();
take_turns_mutate(&mut hm, &mut d, damage_nth(DeviceToHost, 0, byte_index))?;
assert!(!hm.channel_alloc_ready());
// successful allocation
hm.request_channel(false);
take_turns(&mut hm, &mut dm)?;
- let mut d = dm.channel_alloc()?.into_buffered();
+ let mut d = dm
+ .channel_alloc(cids.get(), TestCredentialVerifier)?
+ .into_buffered();
take_turns(&mut hm, &mut d)?;
- let mut _h = hm.channel_alloc()?.into_buffered();
+ let mut _h = hm.channel_alloc(NullCredentialStore)?.into_buffered();
Ok(())
}
@@ -600,13 +625,16 @@ fn test_packet_damage_handshake(
return Ok(());
}
- let (mut hm, mut dm) = create_mux();
+ let (mut hm, mut dm, cids) = create_mux();
// channel allocation
hm.request_channel(false);
take_turns(&mut hm, &mut dm)?;
- let mut d = dm.channel_alloc()?.with_key(DEVICE_KEY).into_buffered();
+ let mut d = dm
+ .channel_alloc(cids.get(), TestCredentialVerifier)?
+ .with_key(DEVICE_KEY)
+ .into_buffered();
take_turns(&mut hm, &mut d)?;
- let mut h = hm.channel_alloc()?.into_buffered();
+ let mut h = hm.channel_alloc(NullCredentialStore)?.into_buffered();
// handshake
take_turns_mutate(&mut h, &mut d, damage_nth(dir, packet_index, byte_index))?;
@@ -669,7 +697,7 @@ fn test_codec_v1() -> Result<()> {
let v1_cont = hex::decode(v1_cont).unwrap();
// broadcast handling
- let (mut hm, mut dm) = create_mux();
+ let (mut hm, mut dm, _cids) = create_mux();
// device::Mux shoud respond
let pir = dm.packet_in(&v1_init);
assert!(matches!(
@@ -712,7 +740,7 @@ fn test_codec_v1() -> Result<()> {
fn test_ping() -> Result<()> {
setup();
- let (mut hm, mut dm) = create_mux();
+ let (mut hm, mut dm, _cids) = create_mux();
// host->device ping
hm.ping();
let ping_packet = hm.packet_out()?;
@@ -775,7 +803,7 @@ fn test_invalid_channel_id() -> Result<()> {
res
}
- let (mut hm, mut dm) = create_mux();
+ let (mut hm, mut dm, _cids) = create_mux();
// muxes return Route(cid) for valid non-broadcast channel
let pir = dm.packet_in(&make_packet(66));
assert_eq!(pir, PacketInResult::Route { channel_id: 66 });
@@ -887,25 +915,28 @@ fn test_channel_id_wraparound() -> Result<()> {
setup();
fn alloc_test(
- hm: &mut Buffered<host::Mux<NullCredentialStore, RustCrypto>>,
- dm: &mut Buffered<device::Mux<TestCredentialVerifier, RustCrypto>>,
+ hm: &mut Buffered<host::Mux<RustCrypto>>,
+ dm: &mut Buffered<device::Mux<RustCrypto>>,
+ cids: &device::ChannelIdAllocator,
expected_id: u16,
) -> Result<()> {
hm.request_channel(false);
take_turns(hm, dm)?;
- let mut d = dm.channel_alloc()?.into_buffered();
+ let mut d = dm
+ .channel_alloc(cids.get(), TestCredentialVerifier)?
+ .into_buffered();
take_turns(hm, &mut d)?;
- let h = hm.channel_alloc()?.into_buffered();
+ let h = hm.channel_alloc(NullCredentialStore)?.into_buffered();
assert_eq!(h.channel_id(), expected_id);
Ok(())
}
- let (mut hm, mut dm) = create_mux();
- dm.set_next_channel_id(MAX_CHANNEL_ID - 1);
- alloc_test(&mut hm, &mut dm, MAX_CHANNEL_ID - 1)?;
- alloc_test(&mut hm, &mut dm, MAX_CHANNEL_ID)?;
- alloc_test(&mut hm, &mut dm, MIN_CHANNEL_ID)?;
- alloc_test(&mut hm, &mut dm, MIN_CHANNEL_ID + 1)?;
+ let (mut hm, mut dm, _) = create_mux();
+ let cids = device::ChannelIdAllocator::new_from(MAX_CHANNEL_ID - 1);
+ alloc_test(&mut hm, &mut dm, &cids, MAX_CHANNEL_ID - 1)?;
+ alloc_test(&mut hm, &mut dm, &cids, MAX_CHANNEL_ID)?;
+ alloc_test(&mut hm, &mut dm, &cids, MIN_CHANNEL_ID)?;
+ alloc_test(&mut hm, &mut dm, &cids, MIN_CHANNEL_ID + 1)?;
Ok(())
}
diff --git a/rust/trezor-thp/src/credential.rs b/rust/trezor-thp/src/credential.rs
index c6b6fc76..f13ad5ea 100644
--- a/rust/trezor-thp/src/credential.rs
+++ b/rust/trezor-thp/src/credential.rs
@@ -7,7 +7,7 @@ pub struct FoundCredential<'a> {
/// Host-side credential store.
/// Basically a set of (`remote_static_pubkey`, `local_static_privkey`, `auth_credential`).
-pub trait CredentialStore: Clone {
+pub trait CredentialStore {
/// Find a record such that `remote_static_pubkey` satisfies
/// `masked_static_pubkey == X25519(SHA-256(remote_static_pubkey || ephemeral_pubkey), remote_static_pubkey)`.
/// If found, write `local_static_privkey` and `auth_credential` to `dest` and return the written subslices.
@@ -35,7 +35,7 @@ impl CredentialStore for NullCredentialStore {
}
/// Device-side credential handling.
-pub trait CredentialVerifier: Clone {
+pub trait CredentialVerifier {
/// Validate given protobuf-encoded credential.
fn verify(&self, remote_static_pubkey: &[u8], credential: &[u8]) -> PairingState;
Why this scored 28/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.