ctaphid_types/
packet.rs

1// Copyright (C) 2021 Robin Krahl <robin.krahl@ireas.org>
2// SPDX-License-Identifier: Apache-2.0 or MIT
3
4use core::convert;
5
6use crate::{
7    channel::Channel,
8    command::Command,
9    error::{ParseError, SerializationError},
10    util::{Parser, Serializer},
11};
12
13/// A CTAPHID packet.
14///
15/// See [§ 11.2.4 of the CTAP specification][spec].
16///
17/// [spec]: https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#usb-message-and-packet-structure
18#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
19pub enum Packet<T: AsRef<[u8]>> {
20    /// A CTAPHID initialization packet.
21    Initialization(InitializationPacket<T>),
22    /// A CTAPHID continuation packet.
23    Continuation(ContinuationPacket<T>),
24}
25
26impl<T: AsRef<[u8]>> Packet<T> {
27    /// Returns the channel for this packet.
28    pub fn channel(&self) -> Channel {
29        match self {
30            Self::Initialization(packet) => packet.channel,
31            Self::Continuation(packet) => packet.channel,
32        }
33    }
34
35    /// Returns the type of this packet.
36    pub fn packet_type(&self) -> PacketType {
37        match self {
38            Self::Initialization(_) => PacketType::Initialization,
39            Self::Continuation(_) => PacketType::Continuation,
40        }
41    }
42
43    /// Serializes this packet to the given buffer.
44    pub fn serialize(&self, buffer: &mut [u8]) -> Result<usize, SerializationError> {
45        match self {
46            Self::Initialization(packet) => packet.serialize(buffer),
47            Self::Continuation(packet) => packet.serialize(buffer),
48        }
49    }
50}
51
52impl<T: AsRef<[u8]>> From<ContinuationPacket<T>> for Packet<T> {
53    fn from(packet: ContinuationPacket<T>) -> Self {
54        Self::Continuation(packet)
55    }
56}
57
58impl<T: AsRef<[u8]>> From<InitializationPacket<T>> for Packet<T> {
59    fn from(packet: InitializationPacket<T>) -> Self {
60        Self::Initialization(packet)
61    }
62}
63
64impl<'a, T: AsRef<[u8]> + convert::TryFrom<&'a [u8]>> convert::TryFrom<&'a [u8]> for Packet<T> {
65    type Error = ParseError;
66
67    fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
68        if data.len() < 5 {
69            return Err(ParseError::NotEnoughData);
70        }
71        let command_or_sequence = data[4];
72
73        if command_or_sequence.leading_ones() > 0 {
74            InitializationPacket::try_from(data).map(From::from)
75        } else {
76            ContinuationPacket::try_from(data).map(From::from)
77        }
78    }
79}
80
81/// The type of a CTAPHID [`Packet`][].
82///
83/// See [§ 11.2.4 of the CTAP specification][spec].
84///
85/// [spec]: https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#usb-message-and-packet-structure
86#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
87pub enum PacketType {
88    /// A CTAPHID initialization packet ([`Packet::Initialization`][], [`InitializationPacket`][]).
89    Initialization,
90    /// A CTAPHID continuation packet ([`Packet::Continuation`][], [`ContinuationPacket`][]).
91    Continuation,
92}
93
94/// The size of the header of an initialization packet in bytes.
95///
96/// See [§ 11.2.4 of the CTAP specification][spec].
97///
98/// [spec]: https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#usb-message-and-packet-structure
99pub const INITIALIZATION_HEADER_SIZE: usize = 7;
100
101/// A CTAPHID initialization packet.
102///
103/// See [§ 11.2.4 of the CTAP specification][spec].
104///
105/// [spec]: https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#usb-message-and-packet-structure
106#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
107pub struct InitializationPacket<T: AsRef<[u8]>> {
108    /// The channel this packet is sent or received on.
109    pub channel: Channel,
110    /// The CTAPHID command.
111    pub command: Command,
112    /// The length of the payload data.
113    pub length: u16,
114    /// The payload data.
115    pub data: T,
116}
117
118impl<T: AsRef<[u8]>> InitializationPacket<T> {
119    /// Serializes this packet to the given buffer.
120    pub fn serialize(&self, buffer: &mut [u8]) -> Result<usize, SerializationError> {
121        let mut serializer = Serializer::new(buffer);
122        serializer.push_slice(&self.channel.to_bytes())?;
123        serializer.push_slice(&[u8::from(self.command) | 0b1000_0000])?;
124        serializer.push_slice(&self.length.to_be_bytes())?;
125        serializer.push_slice(self.data.as_ref())?;
126        Ok(serializer.bytes_written())
127    }
128}
129
130impl<T: AsRef<[u8]>> convert::TryFrom<Packet<T>> for InitializationPacket<T> {
131    type Error = ParseError;
132
133    fn try_from(packet: Packet<T>) -> Result<Self, Self::Error> {
134        if let Packet::Initialization(packet) = packet {
135            Ok(packet)
136        } else {
137            Err(ParseError::InvalidPacketType {
138                expected: PacketType::Initialization,
139                actual: packet.packet_type(),
140            })
141        }
142    }
143}
144
145impl<'a, T: AsRef<[u8]> + convert::TryFrom<&'a [u8]>> convert::TryFrom<&'a [u8]>
146    for InitializationPacket<T>
147{
148    type Error = ParseError;
149
150    fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
151        let mut parser = Parser::new(data);
152        let channel = parser.take_into()?;
153        let command = parser.take()?;
154        let length = parser.take_array()?;
155        let rest = parser.into_rest();
156
157        if command.leading_ones() == 0 {
158            return Err(ParseError::InvalidPacketType {
159                expected: PacketType::Initialization,
160                actual: PacketType::Continuation,
161            });
162        }
163
164        Ok(InitializationPacket {
165            channel,
166            command: Command::from(command & 0b0111_1111),
167            length: u16::from_be_bytes(length),
168            data: T::try_from(rest).map_err(|_| ParseError::BufferCreationFailed)?,
169        })
170    }
171}
172
173/// The size of the header of a continuation packet in bytes.
174///
175/// See [§ 11.2.4 of the CTAP specification][spec].
176///
177/// [spec]: https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#usb-message-and-packet-structure
178pub const CONTINUATION_HEADER_SIZE: usize = 5;
179
180/// A CTAPHID continuation packet.
181///
182/// See [§ 11.2.4 of the CTAP specification][spec].
183///
184/// [spec]: https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#usb-message-and-packet-structure
185#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
186pub struct ContinuationPacket<T: AsRef<[u8]>> {
187    /// The channel this packet is sent or received on.
188    pub channel: Channel,
189    /// The sequence number of this packet.
190    pub sequence: u8,
191    /// The payload data.
192    pub data: T,
193}
194
195impl<T: AsRef<[u8]>> ContinuationPacket<T> {
196    /// Serializes this packet to the given buffer.
197    pub fn serialize(&self, buffer: &mut [u8]) -> Result<usize, SerializationError> {
198        let mut serializer = Serializer::new(buffer);
199        serializer.push_slice(&self.channel.to_bytes())?;
200        serializer.push_slice(&[self.sequence])?;
201        serializer.push_slice(self.data.as_ref())?;
202        Ok(serializer.bytes_written())
203    }
204}
205
206impl<T: AsRef<[u8]>> convert::TryFrom<Packet<T>> for ContinuationPacket<T> {
207    type Error = ParseError;
208
209    fn try_from(packet: Packet<T>) -> Result<Self, Self::Error> {
210        if let Packet::Continuation(packet) = packet {
211            Ok(packet)
212        } else {
213            Err(ParseError::InvalidPacketType {
214                expected: PacketType::Continuation,
215                actual: packet.packet_type(),
216            })
217        }
218    }
219}
220
221impl<'a, T: AsRef<[u8]> + convert::TryFrom<&'a [u8]>> convert::TryFrom<&'a [u8]>
222    for ContinuationPacket<T>
223{
224    type Error = ParseError;
225
226    fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
227        let mut parser = Parser::new(data);
228        let channel = parser.take_into()?;
229        let sequence = parser.take()?;
230        let rest = parser.into_rest();
231
232        if sequence.leading_ones() > 0 {
233            return Err(ParseError::InvalidPacketType {
234                expected: PacketType::Continuation,
235                actual: PacketType::Initialization,
236            });
237        }
238
239        Ok(ContinuationPacket {
240            channel,
241            sequence,
242            data: T::try_from(rest).map_err(|_| ParseError::BufferCreationFailed)?,
243        })
244    }
245}
246
247#[cfg(test)]
248mod test {
249    use std::convert::TryFrom;
250
251    use quickcheck::{Arbitrary, TestResult};
252
253    use super::{ContinuationPacket, InitializationPacket, Packet, PacketType};
254
255    impl Arbitrary for Packet<Vec<u8>> {
256        fn arbitrary(g: &mut quickcheck::Gen) -> Self {
257            match PacketType::arbitrary(g) {
258                PacketType::Initialization => Packet::Initialization(Arbitrary::arbitrary(g)),
259                PacketType::Continuation => Packet::Continuation(Arbitrary::arbitrary(g)),
260            }
261        }
262
263        fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
264            match self {
265                Packet::Initialization(packet) => Box::new(packet.shrink().map(From::from)),
266                Packet::Continuation(packet) => Box::new(packet.shrink().map(From::from)),
267            }
268        }
269    }
270
271    impl Arbitrary for PacketType {
272        fn arbitrary(g: &mut quickcheck::Gen) -> Self {
273            *g.choose(&[PacketType::Initialization, PacketType::Continuation])
274                .unwrap()
275        }
276    }
277
278    impl Arbitrary for InitializationPacket<Vec<u8>> {
279        fn arbitrary(g: &mut quickcheck::Gen) -> Self {
280            Self {
281                channel: Arbitrary::arbitrary(g),
282                command: Arbitrary::arbitrary(g),
283                length: Arbitrary::arbitrary(g),
284                data: Arbitrary::arbitrary(g),
285            }
286        }
287
288        fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
289            let channel = self.channel;
290            let command = self.command;
291            let length = self.length;
292            Box::new(self.data.shrink().map(move |data| Self {
293                channel,
294                command,
295                length,
296                data,
297            }))
298        }
299    }
300
301    impl Arbitrary for ContinuationPacket<Vec<u8>> {
302        fn arbitrary(g: &mut quickcheck::Gen) -> Self {
303            Self {
304                channel: Arbitrary::arbitrary(g),
305                sequence: Arbitrary::arbitrary(g),
306                data: Arbitrary::arbitrary(g),
307            }
308        }
309
310        fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
311            let channel = self.channel;
312            let sequence = self.sequence;
313            Box::new(self.data.shrink().map(move |data| Self {
314                channel,
315                sequence,
316                data,
317            }))
318        }
319    }
320
321    quickcheck::quickcheck! {
322        fn parse_init(data: Vec<u8>) -> bool {
323            let _ = InitializationPacket::<Vec<u8>>::try_from(data.as_slice());
324            true
325        }
326    }
327
328    quickcheck::quickcheck! {
329        fn serialize_init(packet: InitializationPacket<Vec<u8>>) -> bool {
330            let mut buffer = vec![0; packet.data.len() + 10];
331            let _ = packet.serialize(&mut buffer);
332            true
333        }
334    }
335
336    quickcheck::quickcheck! {
337        fn serialize_parse_init(packet: InitializationPacket<Vec<u8>>) -> TestResult {
338            if usize::from(packet.length) < packet.data.len() {
339                return TestResult::discard();
340            }
341            if u8::from(packet.command).leading_ones() > 0 {
342                return TestResult::discard();
343            }
344            let mut buffer = vec![0; packet.data.len() + 10];
345            let n = packet.serialize(&mut buffer).unwrap();
346            let parsed = InitializationPacket::try_from(&buffer[..n]).unwrap();
347            TestResult::from_bool(packet == parsed)
348        }
349    }
350
351    quickcheck::quickcheck! {
352        fn parse_cont(data: Vec<u8>) -> bool {
353            let _ = ContinuationPacket::<Vec<u8>>::try_from(data.as_slice());
354            true
355        }
356    }
357
358    quickcheck::quickcheck! {
359        fn serialize_cont(packet: ContinuationPacket<Vec<u8>>) -> bool {
360            let mut buffer = vec![0; packet.data.len() + 10];
361            let _ = packet.serialize(&mut buffer);
362            true
363        }
364    }
365
366    quickcheck::quickcheck! {
367        fn serialize_parse_cont(packet: ContinuationPacket<Vec<u8>>) -> TestResult {
368            if u8::from(packet.sequence).leading_ones() > 0 {
369                return TestResult::discard();
370            }
371            let mut buffer = vec![0; packet.data.len() + 10];
372            let n = packet.serialize(&mut buffer).unwrap();
373            let parsed = ContinuationPacket::try_from(&buffer[..n]).unwrap();
374            TestResult::from_bool(packet == parsed)
375        }
376    }
377}