Skip to main content

osdp/reply/
ccrypt.rs

1//! `osdp_CCRYPT` (`0x76`) — client cryptogram (PD response to `osdp_CHLNG`).
2//!
3//! # Spec: §7.16, Annex D.4
4//!
5//! Body: `cUID (8) || RND.B (8) || ClientCryptogram (16)`.
6
7use crate::error::Error;
8use crate::payload_util::require_exact_len;
9use alloc::vec::Vec;
10
11/// `osdp_CCRYPT` body.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct CCrypt {
14    /// PD client identifier (8 bytes).
15    pub cuid: [u8; 8],
16    /// `RND.B` from the PD.
17    pub rnd_b: [u8; 8],
18    /// Client cryptogram = AES_S-ENC( RND.A || RND.B ).
19    pub client_cryptogram: [u8; 16],
20}
21
22impl CCrypt {
23    /// Encode.
24    pub fn encode(&self) -> Result<Vec<u8>, Error> {
25        let mut out = Vec::with_capacity(32);
26        out.extend_from_slice(&self.cuid);
27        out.extend_from_slice(&self.rnd_b);
28        out.extend_from_slice(&self.client_cryptogram);
29        Ok(out)
30    }
31
32    /// Decode.
33    pub fn decode(data: &[u8]) -> Result<Self, Error> {
34        require_exact_len(data, 32, 0x76)?;
35        let mut cuid = [0u8; 8];
36        cuid.copy_from_slice(&data[..8]);
37        let mut rnd_b = [0u8; 8];
38        rnd_b.copy_from_slice(&data[8..16]);
39        let mut client_cryptogram = [0u8; 16];
40        client_cryptogram.copy_from_slice(&data[16..32]);
41        Ok(Self {
42            cuid,
43            rnd_b,
44            client_cryptogram,
45        })
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52
53    #[test]
54    fn roundtrip() {
55        let body = CCrypt {
56            cuid: [0xAA; 8],
57            rnd_b: [0xBB; 8],
58            client_cryptogram: [0xCC; 16],
59        };
60        let bytes = body.encode().unwrap();
61        assert_eq!(bytes.len(), 32);
62        assert_eq!(&bytes[..8], &[0xAA; 8]);
63        assert_eq!(&bytes[8..16], &[0xBB; 8]);
64        assert_eq!(&bytes[16..32], &[0xCC; 16]);
65        assert_eq!(CCrypt::decode(&bytes).unwrap(), body);
66    }
67
68    #[test]
69    fn decode_rejects_wrong_length() {
70        assert!(matches!(
71            CCrypt::decode(&[0; 31]),
72            Err(Error::PayloadLength { code: 0x76, .. })
73        ));
74    }
75}