ecc608_linux/
command.rs

1use crate::{
2    constants::{
3        ATCA_ECDH, ATCA_GENKEY, ATCA_INFO, ATCA_LOCK, ATCA_NONCE, ATCA_RANDOM, ATCA_READ,
4        ATCA_RSP_SIZE_MIN, ATCA_SIGN, ATCA_WRITE, CMD_STATUS_BYTE_COMM, CMD_STATUS_BYTE_ECC,
5        CMD_STATUS_BYTE_EXEC, CMD_STATUS_BYTE_PARSE, CMD_STATUS_BYTE_SELF_TEST,
6        CMD_STATUS_BYTE_SUCCESS, CMD_STATUS_BYTE_WATCHDOG,
7    },
8    Address, DataBuffer, Result, Zone,
9};
10use bitfield::bitfield;
11use bytes::{Buf, BufMut, Bytes, BytesMut};
12
13#[derive(Debug, PartialEq, Eq)]
14pub enum KeyType {
15    Public,
16    Private,
17}
18
19impl From<&KeyType> for u8 {
20    fn from(k: &KeyType) -> Self {
21        match k {
22            KeyType::Public => 0x00,
23            KeyType::Private => 0x04,
24        }
25    }
26}
27
28#[derive(Debug, PartialEq, Eq)]
29pub enum EccCommand {
30    Info,
31    GenKey { key_type: KeyType, slot: u8 },
32    Read { is_32: bool, address: Address },
33    Write { address: Address, data: Bytes },
34    Lock { zone: Zone },
35    Random,
36    Nonce { target: DataBuffer, data: Bytes },
37    Sign { source: DataBuffer, key_slot: u8 },
38    Ecdh { x: Bytes, y: Bytes, key_slot: u8 },
39}
40
41bitfield! {
42    #[derive(PartialEq)]
43    struct ReadWriteParam(u8);
44    impl Debug;
45    is_32, set_is_32: 7;
46    address_zone, set_address_zone: 1, 0;
47}
48
49impl From<ReadWriteParam> for u8 {
50    fn from(v: ReadWriteParam) -> Self {
51        v.0
52    }
53}
54
55bitfield! {
56    #[derive(PartialEq)]
57    struct NonceParam(u8);
58    impl Debug;
59    u8, target, set_target: 7, 6;
60    is_64, set_is_64: 5;
61    u8, mode, set_mode: 1, 0;
62}
63
64impl From<NonceParam> for u8 {
65    fn from(v: NonceParam) -> Self {
66        v.0
67    }
68}
69
70bitfield! {
71    #[derive(PartialEq)]
72    struct SignParam(u8);
73    impl Debug;
74    external, set_external: 7;
75    u8, source, set_source: 5, 5;
76}
77
78impl From<SignParam> for u8 {
79    fn from(v: SignParam) -> Self {
80        v.0
81    }
82}
83
84bitfield! {
85    #[derive(PartialEq, Eq)]
86    pub struct LockParam(u8);
87    impl Debug;
88    u8, zone, set_zone: 1, 0;
89    u8, slot, set_slot: 5, 2;
90    crc, set_crc: 7;
91}
92
93impl From<LockParam> for u8 {
94    fn from(v: LockParam) -> Self {
95        v.0
96    }
97}
98
99#[derive(Debug, PartialEq, Eq)]
100pub enum EccError {
101    /// Command was properly received but the length, command opcode, or
102    /// parameters are illegal regardless of the state (volatile and/or EEPROM
103    /// configuration) of the ECC. Changes in the value of the command bits
104    /// must be made before it is re-attempted.
105    ParseError,
106    /// A computation error occurred during ECC processing that caused the
107    /// result to be invalid. Retrying the command may result in a successful
108    /// execution.
109    Fault,
110    /// There was a self test error and the chip is in failure mode waiting for
111    /// the failure to be cleared.
112    SelfTestError,
113    /// Command was properly received but could not be executed by the device in
114    /// its current state. Changes in the device state or the value of the
115    /// command bits must be made before it is re-attempted.
116    ExecError,
117    /// Command was not properly received by AT88SHA204 and should be
118    /// re-transmitted by the I/O driver in the system. No attempt was made to
119    /// parse or execute the command.
120    CommsError,
121    /// There is insufficient time to execute the given command before the
122    /// watchdog timer will expire. The system must reset the watchdog timer by
123    /// entering the idle or sleep modes.
124    WatchDogError,
125    /// Crc in the message does not match the calculated Crc
126    CrcError,
127    /// Unknown or unhandled Ecc error
128    Unknown(u8),
129}
130
131#[derive(Debug, PartialEq, Eq)]
132pub enum EccResponse {
133    Error(EccError),
134    Data(Bytes),
135}
136
137macro_rules! put_cmd {
138    ($dest:ident, $cmd:ident, $param1:expr, $param2:expr) => {
139        $dest.put_u8($cmd);
140        $dest.put_u8($param1);
141        $dest.put_u16($param2);
142    };
143}
144
145impl EccCommand {
146    pub fn info() -> Self {
147        Self::Info
148    }
149
150    pub fn genkey(key_type: KeyType, slot: u8) -> Self {
151        Self::GenKey { key_type, slot }
152    }
153
154    pub fn read(is_32: bool, address: Address) -> Self {
155        Self::Read { is_32, address }
156    }
157
158    pub fn write(address: Address, data: &[u8]) -> Self {
159        Self::Write {
160            address,
161            data: Bytes::copy_from_slice(data),
162        }
163    }
164
165    pub fn lock(zone: Zone) -> Self {
166        Self::Lock { zone }
167    }
168
169    pub fn random() -> Self {
170        Self::Random
171    }
172
173    pub fn nonce(target: DataBuffer, data: Bytes) -> Self {
174        Self::Nonce { target, data }
175    }
176
177    pub fn sign(source: DataBuffer, key_slot: u8) -> Self {
178        Self::Sign { source, key_slot }
179    }
180
181    pub fn ecdh(x: Bytes, y: Bytes, key_slot: u8) -> Self {
182        Self::Ecdh { key_slot, x, y }
183    }
184
185    pub fn bytes_into(&self, bytes: &mut BytesMut) {
186        bytes.put_u8(0x00);
187        match self {
188            Self::Info => {
189                put_cmd!(bytes, ATCA_INFO, 0, 0);
190            }
191            Self::GenKey { key_type, slot } => {
192                put_cmd!(bytes, ATCA_GENKEY, u8::from(key_type), (*slot as u16) << 8);
193            }
194            Self::Read { is_32, address } => {
195                let mut param1 = ReadWriteParam(0);
196                param1.set_is_32(*is_32);
197                param1.set_address_zone(address.zone());
198                put_cmd!(bytes, ATCA_READ, u8::from(param1), u16::from(address));
199            }
200            Self::Write { address, data } => {
201                let mut param1 = ReadWriteParam(0);
202                param1.set_is_32(data.len() == 32);
203                param1.set_address_zone(address.zone());
204                put_cmd!(bytes, ATCA_WRITE, u8::from(param1), u16::from(address));
205                bytes.extend_from_slice(data);
206            }
207            Self::Lock { zone } => {
208                let mut param1 = LockParam(0);
209                param1.set_crc(true);
210                param1.set_zone(match zone {
211                    Zone::Config => 0x00,
212                    Zone::Data => 0x01,
213                });
214                put_cmd!(bytes, ATCA_LOCK, u8::from(param1), 0);
215            }
216            Self::Random => {
217                put_cmd!(bytes, ATCA_RANDOM, 0, 0);
218            }
219            Self::Nonce { target, data } => {
220                let mut param1 = NonceParam(0);
221                param1.set_mode(0x03); // pass-through only for now
222                param1.set_target(target.into());
223                param1.set_is_64(data.len() == 64);
224                put_cmd!(bytes, ATCA_NONCE, u8::from(param1), 0);
225                bytes.extend_from_slice(data)
226            }
227            Self::Sign { source, key_slot } => {
228                let mut param1 = SignParam(0);
229                param1.set_source(source.into());
230                param1.set_external(true);
231                put_cmd!(bytes, ATCA_SIGN, u8::from(param1), (*key_slot as u16) << 8);
232            }
233            Self::Ecdh { x, y, key_slot } => {
234                put_cmd!(bytes, ATCA_ECDH, 0, (*key_slot as u16) << 8);
235                bytes.extend_from_slice(x);
236                bytes.extend_from_slice(y)
237            }
238        }
239        bytes[1] = (bytes.len() + 1) as u8;
240        bytes.put_u16_le(crc(&bytes[1..]))
241    }
242}
243
244impl EccResponse {
245    pub fn from_bytes(buf: &[u8]) -> Result<Self> {
246        const RSM: u8 = ATCA_RSP_SIZE_MIN;
247        let resp = match buf {
248            [RSM, CMD_STATUS_BYTE_SUCCESS, ..] => Self::Data(Bytes::new()),
249            [RSM, CMD_STATUS_BYTE_PARSE, ..] => Self::Error(EccError::ParseError),
250            [RSM, CMD_STATUS_BYTE_ECC, ..] => Self::Error(EccError::Fault),
251            [RSM, CMD_STATUS_BYTE_SELF_TEST, ..] => Self::Error(EccError::SelfTestError),
252            [RSM, CMD_STATUS_BYTE_EXEC, ..] => Self::Error(EccError::ExecError),
253            [RSM, CMD_STATUS_BYTE_COMM, ..] => Self::Error(EccError::CommsError),
254            [RSM, CMD_STATUS_BYTE_WATCHDOG, ..] => Self::Error(EccError::WatchDogError),
255            [RSM, error, ..] => Self::Error(EccError::Unknown(*error)),
256            _ => {
257                let (buf, mut buf_crc) = buf.split_at(buf.len() - 2);
258                let expected = crc(buf);
259                let actual = buf_crc.get_u16_le();
260                if expected != actual {
261                    Self::Error(EccError::CrcError)
262                } else {
263                    Self::Data(Bytes::copy_from_slice(&buf[1..]))
264                }
265            }
266        };
267        Ok(resp)
268    }
269}
270
271impl EccError {
272    pub fn is_recoverable(&self) -> bool {
273        !matches!(self, Self::ParseError | Self::ExecError)
274    }
275}
276
277fn crc(src: &[u8]) -> u16 {
278    const POLYNOM: u16 = 0x8005;
279    let mut crc: u16 = 0x0000;
280    let mut data_bit;
281    let mut crc_bit;
282    for d in src {
283        for b in 0..8 {
284            if (d & 1 << b) == 0 {
285                data_bit = 0;
286            } else {
287                data_bit = 1;
288            }
289            crc_bit = crc >> 15 & 0xff;
290            crc <<= 1 & 0xffff;
291            if data_bit != crc_bit {
292                crc ^= POLYNOM;
293            }
294        }
295    }
296    crc
297}
298
299#[cfg(test)]
300mod tests {
301    use super::*;
302    use crate::constants::ATCA_CMD_SIZE_MAX;
303
304    #[test]
305    fn info() {
306        let packet = EccCommand::info();
307        let mut buf = BytesMut::with_capacity(ATCA_CMD_SIZE_MAX as usize);
308        buf.put_u8(0x03); // write i2c command flag
309        packet.bytes_into(&mut buf);
310        // assert encoding
311        assert_eq!(&[0x03, 0x07, 0x30, 0x00, 0x00, 0x00, 0x03, 0x5D], &buf[..])
312    }
313}