rust_cryptoauthlib/hw_impl/
mac.rs

1use std::cmp::min;
2use std::mem::MaybeUninit;
3
4use super::{AtcaDeviceType, AtcaStatus, AteccDevice, KeyType, MacParam, NonceTarget};
5
6use super::{
7    ATCA_AES_DATA_SIZE, ATCA_AES_KEY_SIZE, ATCA_ATECC_SLOTS_COUNT, ATCA_ATECC_TEMPKEY_KEYID,
8    ATCA_NONCE_SIZE, ATCA_SHA2_256_DIGEST_SIZE, SHA_MODE_TARGET_TEMPKEY,
9};
10
11use cryptoauthlib_sys::atca_aes_cmac_ctx_t;
12
13impl AteccDevice {
14    /// function that calculates the MAC code of the HMAC-SHA256 type
15    pub(crate) fn compute_mac_hmac_sha256(
16        &self,
17        mac_param: MacParam,
18        slot_id: u8,
19        data: &[u8],
20    ) -> Result<Vec<u8>, AtcaStatus> {
21        let mut mac_length: u8 = ATCA_SHA2_256_DIGEST_SIZE as u8;
22        if let Some(val) = &mac_param.mac_length {
23            mac_length = *val
24        };
25
26        let mac = self.common_mac_hmac_sha256(mac_param, slot_id, data, mac_length as usize)?;
27
28        Ok(mac)
29    } // AteccDevice::compute_mac_hmac_sha256()
30
31    /// function that verifies the MAC code of the HMAC-SHA256 type
32    pub(crate) fn verify_mac_hmac_sha256(
33        &self,
34        mac_param: MacParam,
35        slot_id: u8,
36        data: &[u8],
37    ) -> Result<bool, AtcaStatus> {
38        let mac_to_check: Vec<u8>;
39
40        if let Some(val) = mac_param.mac.clone() {
41            mac_to_check = val;
42        } else {
43            return Err(AtcaStatus::AtcaBadParam);
44        }
45
46        let mac = self.common_mac_hmac_sha256(mac_param, slot_id, data, mac_to_check.len())?;
47
48        Ok(mac == mac_to_check)
49    } // AteccDevice::verify_mac_hmac_sha256()
50
51    /// a helper function implementing common functionality for HMAC-SHA256
52    fn common_mac_hmac_sha256(
53        &self,
54        mac_param: MacParam,
55        slot_id: u8,
56        data: &[u8],
57        mac_length: usize,
58    ) -> Result<Vec<u8>, AtcaStatus> {
59        let result = self.common_mac_hmac(mac_param, slot_id);
60        if result != AtcaStatus::AtcaSuccess {
61            return Err(result);
62        }
63        let mut slot = slot_id as u16;
64        if slot_id == ATCA_ATECC_SLOTS_COUNT {
65            slot = ATCA_ATECC_TEMPKEY_KEYID;
66        }
67
68        let mut mac: [u8; ATCA_SHA2_256_DIGEST_SIZE] = [0x00; ATCA_SHA2_256_DIGEST_SIZE];
69
70        let result = AtcaStatus::from(unsafe {
71            let _guard = self
72                .api_mutex
73                .lock()
74                .expect("Could not lock atcab API mutex");
75            cryptoauthlib_sys::atcab_sha_hmac(
76                data.as_ptr(),
77                data.len() as u64,
78                slot,
79                mac.as_mut_ptr(),
80                SHA_MODE_TARGET_TEMPKEY,
81            )
82        });
83
84        match result {
85            AtcaStatus::AtcaSuccess => Ok({
86                let mut out_mac: Vec<u8> = vec![0x00; mac_length as usize];
87                out_mac.copy_from_slice(&mac[..mac_length as usize]);
88                out_mac
89            }),
90            _ => Err(result),
91        }
92    } // AteccDevice::common_mac_hmac_sha256()
93
94    /// function that calculates the MAC code of the CMAC type
95    pub(crate) fn compute_mac_cmac(
96        &self,
97        mac_param: MacParam,
98        slot_id: u8,
99        data: &[u8],
100    ) -> Result<Vec<u8>, AtcaStatus> {
101        let mut mac_length: u8 = ATCA_AES_DATA_SIZE as u8;
102        if let Some(val) = &mac_param.mac_length {
103            mac_length = *val
104        };
105
106        let mac = self.common_mac_cmac(mac_param, slot_id, data, mac_length as usize)?;
107
108        Ok(mac)
109    } // AteccDevice::compute_mac_cmac()
110
111    /// function that verifies the MAC code of the CMAC type
112    pub(crate) fn verify_mac_cmac(
113        &self,
114        mac_param: MacParam,
115        slot_id: u8,
116        data: &[u8],
117    ) -> Result<bool, AtcaStatus> {
118        let mac_to_check: Vec<u8>;
119
120        if let Some(val) = mac_param.mac.clone() {
121            mac_to_check = val;
122        } else {
123            return Err(AtcaStatus::AtcaBadParam);
124        }
125
126        let mac = self.common_mac_cmac(mac_param, slot_id, data, mac_to_check.len())?;
127
128        Ok(mac == mac_to_check)
129    } // AteccDevice::verify_mac_cmac()
130
131    /// a helper function implementing common functionality for CMAC
132    fn common_mac_cmac(
133        &self,
134        mac_param: MacParam,
135        slot_id: u8,
136        data: &[u8],
137        mac_length: usize,
138    ) -> Result<Vec<u8>, AtcaStatus> {
139        let result = self.common_mac(mac_param, slot_id);
140        if result != AtcaStatus::AtcaSuccess {
141            return Err(result);
142        }
143
144        let mut ctx = self.aes_cmac_init(slot_id)?;
145
146        let mut start_pos: usize = 0;
147        let mut shift: usize = min(data.len(), ATCA_AES_DATA_SIZE);
148        while shift > 0 {
149            let block = &data[start_pos..(start_pos + shift)];
150            ctx = self.aes_cmac_update(ctx, block)?;
151            start_pos += shift;
152            let remaining_bytes = data.len() - start_pos;
153            if remaining_bytes < ATCA_AES_DATA_SIZE {
154                shift = remaining_bytes
155            }
156        }
157
158        let mac = self.aes_cmac_finish(ctx, mac_length as u8)?;
159        Ok(mac)
160    } // AteccDevice::common_mac_cmac()
161
162    /// function that calculates the MAC code of the CBC-MAC type
163    /// read this: https://blog.cryptographyengineering.com/2013/02/15/why-i-hate-cbc-mac/ and don't use this mode directly
164    pub(crate) fn compute_mac_cbcmac(
165        &self,
166        mac_param: MacParam,
167        slot_id: u8,
168        data: &[u8],
169    ) -> Result<Vec<u8>, AtcaStatus> {
170        let mut mac_length: u8 = ATCA_AES_DATA_SIZE as u8;
171        if let Some(val) = &mac_param.mac_length {
172            mac_length = *val
173        };
174
175        let mac = self.common_mac_cbcmac(mac_param, slot_id, data, mac_length as usize)?;
176
177        Ok(mac)
178    } // AteccDevice::compute_mac_cbcmac()
179
180    /// function that verifies the MAC code of the CBC-MAC type
181    /// read this: https://blog.cryptographyengineering.com/2013/02/15/why-i-hate-cbc-mac/ and don't use this mode directly
182    pub(crate) fn verify_mac_cbcmac(
183        &self,
184        mac_param: MacParam,
185        slot_id: u8,
186        data: &[u8],
187    ) -> Result<bool, AtcaStatus> {
188        let mac_to_check: Vec<u8>;
189
190        if let Some(val) = mac_param.mac.clone() {
191            mac_to_check = val;
192        } else {
193            return Err(AtcaStatus::AtcaBadParam);
194        }
195
196        let mac = self.common_mac_cbcmac(mac_param, slot_id, data, mac_to_check.len())?;
197
198        Ok(mac == mac_to_check)
199    } // AteccDevice::verify_mac_cbcmac()
200
201    /// a helper function implementing common functionality for CBC-MAC
202    fn common_mac_cbcmac(
203        &self,
204        mac_param: MacParam,
205        slot_id: u8,
206        data: &[u8],
207        mac_length: usize,
208    ) -> Result<Vec<u8>, AtcaStatus> {
209        let result = self.common_mac(mac_param, slot_id);
210        if result != AtcaStatus::AtcaSuccess {
211            return Err(result);
212        }
213
214        let mut ctx = self.aes_cbcmac_init(slot_id);
215
216        let mut start_pos: usize = 0;
217        let mut shift: usize = min(data.len(), ATCA_AES_DATA_SIZE);
218        while shift > 0 {
219            let block = &data[start_pos..(start_pos + shift)];
220            ctx = self.aes_cbcmac_update(ctx, block)?;
221            start_pos += shift;
222            let remaining_bytes = data.len() - start_pos;
223            if remaining_bytes < ATCA_AES_DATA_SIZE {
224                shift = remaining_bytes
225            }
226        }
227
228        let mac = self.aes_cbcmac_finish(ctx, mac_length)?;
229        Ok(mac)
230    } // AteccDevice::common_mac_cbcmac()
231
232    /// Initialize a CMAC calculation using an AES-128 key in the device
233    fn aes_cmac_init(&self, slot_id: u8) -> Result<atca_aes_cmac_ctx_t, AtcaStatus> {
234        const BLOCK_IDX: u8 = 0;
235
236        let mut slot = slot_id as u16;
237        if slot_id == ATCA_ATECC_SLOTS_COUNT {
238            slot = ATCA_ATECC_TEMPKEY_KEYID;
239        }
240
241        let ctx: atca_aes_cmac_ctx_t = {
242            let ctx = MaybeUninit::<atca_aes_cmac_ctx_t>::zeroed();
243            unsafe { ctx.assume_init() }
244        };
245        let ctx_ptr = Box::into_raw(Box::new(ctx));
246
247        let result = AtcaStatus::from(unsafe {
248            let _guard = self
249                .api_mutex
250                .lock()
251                .expect("Could not lock atcab API mutex");
252            cryptoauthlib_sys::atcab_aes_cmac_init(ctx_ptr, slot, BLOCK_IDX)
253        });
254
255        match result {
256            AtcaStatus::AtcaSuccess => Ok({
257                let result = unsafe { *ctx_ptr };
258                unsafe { Box::from_raw(ctx_ptr) };
259                result
260            }),
261            _ => Err(result),
262        }
263    } // AteccDevice::aes_cmac_init()
264
265    /// Add data to an initialized CMAC calculation
266    fn aes_cmac_update(
267        &self,
268        ctx: atca_aes_cmac_ctx_t,
269        data: &[u8],
270    ) -> Result<atca_aes_cmac_ctx_t, AtcaStatus> {
271        if data.len() > ATCA_AES_DATA_SIZE {
272            return Err(AtcaStatus::AtcaInvalidSize);
273        }
274
275        let ctx_ptr = Box::into_raw(Box::new(ctx));
276
277        let result = AtcaStatus::from(unsafe {
278            let _guard = self
279                .api_mutex
280                .lock()
281                .expect("Could not lock atcab API mutex");
282            cryptoauthlib_sys::atcab_aes_cmac_update(ctx_ptr, data.as_ptr(), data.len() as u32)
283        });
284
285        let ctx = unsafe { *ctx_ptr };
286        unsafe { Box::from_raw(ctx_ptr) };
287
288        match result {
289            AtcaStatus::AtcaSuccess => Ok(ctx),
290            _ => Err(result),
291        }
292    } // AteccDevice::aes_cmac_update()
293
294    /// Finish a CMAC operation returning the CMAC value
295    fn aes_cmac_finish(
296        &self,
297        ctx: atca_aes_cmac_ctx_t,
298        mac_length: u8,
299    ) -> Result<Vec<u8>, AtcaStatus> {
300        let ctx_ptr = Box::into_raw(Box::new(ctx));
301        let mut mac: [u8; ATCA_AES_DATA_SIZE] = [0; ATCA_AES_DATA_SIZE];
302
303        let result = AtcaStatus::from(unsafe {
304            let _guard = self
305                .api_mutex
306                .lock()
307                .expect("Could not lock atcab API mutex");
308            cryptoauthlib_sys::atcab_aes_cmac_finish(ctx_ptr, mac.as_mut_ptr(), mac_length as u32)
309        });
310
311        unsafe { Box::from_raw(ctx_ptr) };
312
313        match result {
314            AtcaStatus::AtcaSuccess => Ok({
315                let mut out_mac: Vec<u8> = vec![0x00; mac_length as usize];
316                out_mac.copy_from_slice(&mac[..mac_length as usize]);
317                out_mac
318            }),
319            _ => Err(result),
320        }
321    } // AteccDevice::aes_cmac_finish()
322
323    /// Initialize context for AES CBC-MAC operation
324    pub(crate) fn aes_cbcmac_init(&self, slot_id: u8) -> atca_aes_cmac_ctx_t {
325        let mut slot = slot_id as u16;
326        if slot_id == ATCA_ATECC_SLOTS_COUNT {
327            slot = ATCA_ATECC_TEMPKEY_KEYID;
328        }
329
330        let mut ctx: atca_aes_cmac_ctx_t = {
331            let ctx = MaybeUninit::<atca_aes_cmac_ctx_t>::zeroed();
332            unsafe { ctx.assume_init() }
333        };
334
335        ctx.cbc_ctx.key_id = slot;
336        ctx.cbc_ctx.key_block = 0x00;
337
338        ctx
339    } // AteccDevice::aes_cbcmac_init()
340
341    /// Calculate AES CBC-MAC with key stored within ECC608A device.
342    /// aes_cbcmac_init() should be called before the first use of this function.
343    pub(crate) fn aes_cbcmac_update(
344        &self,
345        ctx: atca_aes_cmac_ctx_t,
346        data: &[u8],
347    ) -> Result<atca_aes_cmac_ctx_t, AtcaStatus> {
348        if data.is_empty() {
349            // Nothing to do
350            return Ok(ctx);
351        }
352
353        // Process full blocks of data with AES-CBC
354        let mut temp_ctx = ctx;
355        let mut idx: usize = 0;
356        let mut buffer: [u8; ATCA_AES_DATA_SIZE] = [0x00; ATCA_AES_DATA_SIZE];
357
358        for i in 0..(data.len() / ATCA_AES_DATA_SIZE) {
359            let start_pos = i * ATCA_AES_DATA_SIZE;
360            let end_pos = start_pos + ATCA_AES_DATA_SIZE;
361            idx += 1;
362
363            temp_ctx.cbc_ctx = self.aes_cbc_encrypt_block(
364                temp_ctx.cbc_ctx,
365                &data[start_pos..end_pos],
366                &mut buffer,
367            )?;
368        }
369
370        // Store incomplete block to context structure
371        let start_pos = idx * ATCA_AES_DATA_SIZE;
372        match start_pos < data.len() {
373            true => {
374                temp_ctx.block_size = (data.len() - start_pos) as u32;
375                temp_ctx.block[..(temp_ctx.block_size as usize)]
376                    .copy_from_slice(&data[start_pos..(start_pos + temp_ctx.block_size as usize)]);
377            }
378            false => temp_ctx.block_size = 0,
379        }
380
381        Ok(temp_ctx)
382    } // AteccDevice::aes_cbcmac_update()
383
384    /// Finish a CBC-MAC operation returning the CBC-MAC value. If the data
385    /// provided to the aes_cbcmac_update() function has incomplete
386    /// block this function will return an error code
387    pub(crate) fn aes_cbcmac_finish(
388        &self,
389        ctx: atca_aes_cmac_ctx_t,
390        tag_size: usize,
391    ) -> Result<Vec<u8>, AtcaStatus> {
392        let mut tag: Vec<u8> = vec![0x00; ATCA_AES_DATA_SIZE];
393        if tag_size > ATCA_AES_DATA_SIZE {
394            return Err(AtcaStatus::AtcaBadParam);
395        }
396
397        // Check for incomplete data block
398        if ctx.block_size != 0 {
399            return Err(AtcaStatus::AtcaInvalidSize); // Returns INVALID_SIZE if incomplete blocks are present
400        }
401
402        // All processing is already done, copying the mac to result buffer
403        tag[..tag_size].copy_from_slice(&ctx.cbc_ctx.ciphertext[..tag_size]);
404        tag.resize(tag_size, 0x00);
405        tag.shrink_to_fit();
406        Ok(tag)
407    } // AteccDevice::aes_cbcmac_finish()
408
409    /// auxiliary function checking input parameters common to CMAC and CBC-MAC modes
410    fn common_mac(&self, mac_param: MacParam, slot_id: u8) -> AtcaStatus {
411        const MIN_MAC_SIZE: usize = 1;
412        const MAX_MAC_SIZE: usize = ATCA_AES_DATA_SIZE;
413
414        if (slot_id > ATCA_ATECC_SLOTS_COUNT)
415            || ((slot_id < ATCA_ATECC_SLOTS_COUNT)
416                && (self.slots[slot_id as usize].config.key_type != KeyType::Aes))
417        {
418            return AtcaStatus::AtcaInvalidId;
419        }
420        if (ATCA_ATECC_SLOTS_COUNT == slot_id) && mac_param.key.is_none()
421            || (mac_param.mac_length.is_some() && mac_param.mac.is_some())
422        {
423            return AtcaStatus::AtcaBadParam;
424        }
425        if (mac_param.mac_length.is_some()
426            && ((mac_param.mac_length < Some(MIN_MAC_SIZE as u8))
427                || (mac_param.mac_length > Some(MAX_MAC_SIZE as u8))))
428            || (mac_param.mac.is_some()
429                && ((mac_param.mac.as_ref().unwrap().len() < MIN_MAC_SIZE)
430                    || (mac_param.mac.as_ref().unwrap().len() > MAX_MAC_SIZE)))
431            || (mac_param.key.is_some()
432                && (mac_param.key.as_ref().unwrap().len() != ATCA_AES_KEY_SIZE))
433        {
434            return AtcaStatus::AtcaInvalidSize;
435        }
436
437        if let Some(mut key) = mac_param.key {
438            key.resize(ATCA_NONCE_SIZE, 0x00);
439            let result = self.nonce(NonceTarget::TempKey, &key);
440            if AtcaStatus::AtcaSuccess != result {
441                return result;
442            }
443        }
444
445        AtcaStatus::AtcaSuccess
446    }
447
448    // auxiliary function checking input parameters for the HMAC-SHA256 mode
449    fn common_mac_hmac(&self, mac_param: MacParam, slot_id: u8) -> AtcaStatus {
450        const MIN_MAC_SIZE: usize = 1;
451        const MAX_MAC_SIZE: usize = ATCA_SHA2_256_DIGEST_SIZE;
452
453        if (slot_id > ATCA_ATECC_SLOTS_COUNT)
454            || ((slot_id < ATCA_ATECC_SLOTS_COUNT)
455                && (self.slots[slot_id as usize].config.key_type != KeyType::ShaOrText))
456        {
457            return AtcaStatus::AtcaInvalidId;
458        }
459        if (ATCA_ATECC_SLOTS_COUNT == slot_id)
460            && (mac_param.key.is_none() || (self.get_device_type() != AtcaDeviceType::ATECC608A))
461            || (mac_param.mac_length.is_some() && mac_param.mac.is_some())
462        {
463            return AtcaStatus::AtcaBadParam;
464        }
465        if (mac_param.mac_length.is_some()
466            && ((mac_param.mac_length < Some(MIN_MAC_SIZE as u8))
467                || (mac_param.mac_length > Some(MAX_MAC_SIZE as u8))))
468            || (mac_param.mac.is_some()
469                && ((mac_param.mac.as_ref().unwrap().len() < MIN_MAC_SIZE)
470                    || (mac_param.mac.as_ref().unwrap().len() > MAX_MAC_SIZE)))
471            || (mac_param.key.is_some()
472                && (mac_param.key.as_ref().unwrap().len() > ATCA_SHA2_256_DIGEST_SIZE))
473        {
474            return AtcaStatus::AtcaInvalidSize;
475        }
476
477        if let Some(mut key) = mac_param.key {
478            key.resize(ATCA_NONCE_SIZE, 0x00);
479            let result = self.nonce(NonceTarget::TempKey, &key);
480            if AtcaStatus::AtcaSuccess != result {
481                return result;
482            }
483        }
484
485        AtcaStatus::AtcaSuccess
486    }
487}