Skip to main content

osdp/command/
piv.rs

1//! PIV-related commands: `osdp_PIVDATA` (`0xA3`), `osdp_GENAUTH` (`0xA4`),
2//! `osdp_CRAUTH` (`0xA5`).
3//!
4//! # Spec: §6.23 (PIVDATA), §6.24 (GENAUTH), §6.25 (CRAUTH)
5//!
6//! These commands typically use the multi-part envelope (Annex E examples)
7//! when their payload exceeds a single packet's RX size.
8
9use crate::error::Error;
10use alloc::vec::Vec;
11
12/// `osdp_PIVDATA` body.
13///
14/// Format follows Annex F of the spec; we treat the body as a typed selector
15/// plus opaque payload for forward-compatibility with PIV variants.
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct PivData {
18    /// PIV object tag (e.g. CHUID, CCC, PHOTO).
19    pub object_id: [u8; 3],
20    /// Element ID within the object.
21    pub element_id: u8,
22    /// Data offset (when fragmented).
23    pub offset: u16,
24    /// Trailing payload (often empty for read requests).
25    pub data: Vec<u8>,
26}
27
28impl PivData {
29    /// Encode.
30    pub fn encode(&self) -> Result<Vec<u8>, Error> {
31        let mut out = Vec::with_capacity(6 + self.data.len());
32        out.extend_from_slice(&self.object_id);
33        out.push(self.element_id);
34        out.extend_from_slice(&self.offset.to_le_bytes());
35        out.extend_from_slice(&self.data);
36        Ok(out)
37    }
38
39    /// Decode.
40    pub fn decode(data: &[u8]) -> Result<Self, Error> {
41        if data.len() < 6 {
42            return Err(Error::MalformedPayload {
43                code: 0xA3,
44                reason: "PIVDATA requires at least 6 bytes",
45            });
46        }
47        let mut object_id = [0u8; 3];
48        object_id.copy_from_slice(&data[..3]);
49        Ok(Self {
50            object_id,
51            element_id: data[3],
52            offset: u16::from_le_bytes([data[4], data[5]]),
53            data: data[6..].to_vec(),
54        })
55    }
56}
57
58/// `osdp_GENAUTH` body. Generic authenticate sub-command for PIV.
59#[derive(Debug, Clone, PartialEq, Eq)]
60pub struct GenAuth {
61    /// Algorithm reference (PIV §3.2.4).
62    pub algorithm: u8,
63    /// Key reference.
64    pub key_ref: u8,
65    /// Encoded TLV authentication template.
66    pub auth_template: Vec<u8>,
67}
68
69impl GenAuth {
70    /// Encode.
71    pub fn encode(&self) -> Result<Vec<u8>, Error> {
72        let mut out = Vec::with_capacity(2 + self.auth_template.len());
73        out.push(self.algorithm);
74        out.push(self.key_ref);
75        out.extend_from_slice(&self.auth_template);
76        Ok(out)
77    }
78
79    /// Decode.
80    pub fn decode(data: &[u8]) -> Result<Self, Error> {
81        if data.len() < 2 {
82            return Err(Error::MalformedPayload {
83                code: 0xA4,
84                reason: "GENAUTH requires at least 2 bytes",
85            });
86        }
87        Ok(Self {
88            algorithm: data[0],
89            key_ref: data[1],
90            auth_template: data[2..].to_vec(),
91        })
92    }
93}
94
95/// `osdp_CRAUTH` body. Crypto challenge.
96#[derive(Debug, Clone, PartialEq, Eq)]
97pub struct CrAuth {
98    /// Challenge nonce (typically 16 bytes).
99    pub challenge: Vec<u8>,
100}
101
102impl CrAuth {
103    /// Encode.
104    pub fn encode(&self) -> Result<Vec<u8>, Error> {
105        Ok(self.challenge.clone())
106    }
107
108    /// Decode.
109    pub fn decode(data: &[u8]) -> Result<Self, Error> {
110        Ok(Self {
111            challenge: data.to_vec(),
112        })
113    }
114}