rust_cryptoauthlib/hw_impl/
mod.rs

1use std::cell::RefCell;
2use std::cmp::min;
3use std::collections::HashMap;
4use std::convert::{From, TryFrom};
5use std::mem::discriminant;
6use std::ptr;
7use std::sync::Mutex;
8
9#[cfg(test)]
10use cryptoauthlib_sys::atca_aes_cbc_ctx_t;
11#[cfg(test)]
12use cryptoauthlib_sys::atca_aes_ctr_ctx_t;
13
14use super::{
15    AeadAlgorithm, AeadParam, AtcaAesCcmCtx, AtcaDeviceType, AtcaIfaceCfg, AtcaIfaceCfgPtrWrapper,
16    AtcaIfaceType, AtcaSlot, AtcaSlotCapacity, AtcaStatus, AteccDeviceTrait, ChipOptions,
17    CipherAlgorithm, CipherOperation, CipherParam, EccKeyAttr, EcdhParams, EcdhResult, EcdhSource,
18    EcdhTarget, FeedbackMode, HkdfMsgLoc, InfoCmdType, KdfAlgorithm, KdfParams, KdfPrfKeyLen,
19    KdfPrfTargetLen, KdfResult, KdfSource, KdfTarget, KeyType, MacAlgorithm, MacParam, NonceTarget,
20    OutputProtectionState, ReadKey, SignMode, SlotConfig, VerifyMode, WriteConfig,
21};
22use super::{
23    ATCA_AES_DATA_SIZE, ATCA_AES_GCM_IV_STD_LENGTH, ATCA_AES_KEY_SIZE,
24    ATCA_ATECC_CONFIG_BUFFER_SIZE, ATCA_ATECC_MIN_SLOT_IDX_FOR_PUB_KEY, ATCA_ATECC_PRIV_KEY_SIZE,
25    ATCA_ATECC_PUB_KEY_SIZE, ATCA_ATECC_SLOTS_COUNT, ATCA_ATECC_TEMPKEY_KEYID,
26    ATCA_ATSHA_CONFIG_BUFFER_SIZE, ATCA_BLOCK_SIZE, ATCA_ECDH_KEY_SIZE, ATCA_KDF_MAX_MSG_SIZE,
27    ATCA_KEY_SIZE, ATCA_LOCK_ZONE_CONFIG, ATCA_LOCK_ZONE_DATA, ATCA_NONCE_NUMIN_SIZE,
28    ATCA_NONCE_SIZE, ATCA_RANDOM_BUFFER_SIZE, ATCA_SERIAL_NUM_SIZE, ATCA_SHA2_256_DIGEST_SIZE,
29    ATCA_SIG_SIZE, ATCA_ZONE_DATA, SHA_MODE_TARGET_TEMPKEY,
30};
31
32mod aes_ccm;
33mod aes_cipher;
34mod aes_gcm;
35mod c2rust;
36mod ecdh;
37mod kdf;
38mod mac;
39mod provisioning;
40mod rust2c;
41
42struct AteccResourceManager {
43    ref_counter: u8,
44}
45
46lazy_static! {
47    static ref ATECC_RESOURCE_MANAGER: Mutex<AteccResourceManager> =
48        Mutex::new(AteccResourceManager { ref_counter: 0 });
49}
50
51impl AteccResourceManager {
52    // Aquire an acceptance to create an ATECC instance
53    fn acquire(&mut self) -> bool {
54        if self.ref_counter == 0 {
55            self.ref_counter = 1;
56            true
57        } else {
58            false
59        }
60    }
61
62    // Release a reservation of an ATECC instance
63    fn release(&mut self) -> bool {
64        if self.ref_counter == 1 {
65            self.ref_counter = 0;
66            true
67        } else {
68            false
69        }
70    }
71}
72
73/// An ATECC cryptochip context holder.
74#[derive(Debug)]
75pub struct AteccDevice {
76    /// Interface configuration to be stored on a heap to avoid side effects of
77    /// Rust and C interoperability
78    iface_cfg_ptr: AtcaIfaceCfgPtrWrapper,
79    /// A mutex to ensure a mutual access from different threads to an ATECC instance
80    api_mutex: Mutex<()>,
81    serial_number: [u8; ATCA_SERIAL_NUM_SIZE],
82    config_zone_locked: bool,
83    data_zone_locked: bool,
84    chip_options: ChipOptions,
85    access_keys: Mutex<RefCell<HashMap<u8, [u8; ATCA_KEY_SIZE]>>>,
86    slots: Vec<AtcaSlot>,
87}
88
89impl Default for AteccDevice {
90    fn default() -> AteccDevice {
91        AteccDevice {
92            iface_cfg_ptr: AtcaIfaceCfgPtrWrapper {
93                ptr: std::ptr::null_mut(),
94            },
95            api_mutex: Mutex::new(()),
96            serial_number: [0; ATCA_SERIAL_NUM_SIZE],
97            config_zone_locked: false,
98            data_zone_locked: false,
99            chip_options: Default::default(),
100            access_keys: Mutex::new(RefCell::new(HashMap::new())),
101            slots: Vec::new(),
102        }
103    }
104}
105
106impl AteccDeviceTrait for AteccDevice {
107    /// Request ATECC to generate a vector of random bytes
108    /// Trait implementation
109    fn random(&self, rand_out: &mut Vec<u8>) -> AtcaStatus {
110        self.random(rand_out)
111    } // AteccDevice::random()
112
113    /// Request ATECC to compute a message hash (SHA256)
114    /// Trait implementation
115    fn sha(&self, message: Vec<u8>, digest: &mut Vec<u8>) -> AtcaStatus {
116        self.sha(message, digest)
117    } // AteccDevice::sha()
118
119    /// Execute a Nonce command in pass-through mode to load one of the
120    /// device's internal buffers with a fixed value.
121    /// For the ATECC608A, available targets are TempKey (32 or 64 bytes), Message
122    /// Digest Buffer (32 or 64 bytes), or the Alternate Key Buffer (32 bytes). For
123    /// all other devices, only TempKey (32 bytes) is available.
124    /// Trait implementation
125    fn nonce(&self, target: NonceTarget, data: &[u8]) -> AtcaStatus {
126        self.nonce(target, data)
127    } // AteccDevice::nonce()
128
129    /// Execute a Nonce command to generate a random nonce combining a host
130    /// nonce and a device random number.
131    /// Trait implementation
132    fn nonce_rand(&self, host_nonce: &[u8], rand_out: &mut Vec<u8>) -> AtcaStatus {
133        self.nonce_rand(host_nonce, rand_out)
134    } // AteccDevice::nonce_rand()
135
136    /// Request ATECC to generate a cryptographic key
137    /// Trait implementation
138    fn gen_key(&self, key_type: KeyType, slot_id: u8) -> AtcaStatus {
139        self.gen_key(key_type, slot_id)
140    } // AteccDevice::gen_key()
141
142    /// Request ATECC to import a cryptographic key
143    /// Trait implementation
144    fn import_key(&self, key_type: KeyType, key_data: &[u8], slot_id: u8) -> AtcaStatus {
145        self.import_key(key_type, key_data, slot_id)
146    } // AteccDevice::import_key()
147
148    /// Request ATECC to export a cryptographic key
149    /// Trait implementation
150    fn export_key(&self, key_type: KeyType, key_data: &mut Vec<u8>, slot_id: u8) -> AtcaStatus {
151        self.export_key(key_type, key_data, slot_id)
152    } // AteccDevice::export_key()
153
154    /// Depending on the socket configuration, this function calculates
155    /// public key based on an existing private key in the socket
156    /// or exports the public key directly
157    /// Trait implementation
158    fn get_public_key(&self, slot_id: u8, public_key: &mut Vec<u8>) -> AtcaStatus {
159        self.get_public_key(slot_id, public_key)
160    } // AteccDevice::get_public_key()
161
162    /// Request ATECC to generate an ECDSA signature
163    /// Trait implementation
164    fn sign_hash(&self, mode: SignMode, slot_id: u8, signature: &mut Vec<u8>) -> AtcaStatus {
165        self.sign_hash(mode, slot_id, signature)
166    } // AteccDevice::sign_hash()
167
168    /// Request ATECC to verify ECDSA signature
169    /// Trait implementation
170    fn verify_hash(
171        &self,
172        mode: VerifyMode,
173        hash: &[u8],
174        signature: &[u8],
175    ) -> Result<bool, AtcaStatus> {
176        self.verify_hash(mode, hash, signature)
177    } // AteccDevice::verify_hash()
178
179    /// Data encryption function in AES unauthenticated cipher alhorithms modes
180    /// Trait implementation
181    fn cipher_encrypt(
182        &self,
183        algorithm: CipherAlgorithm,
184        slot_id: u8,
185        data: &mut Vec<u8>,
186    ) -> AtcaStatus {
187        self.cipher_encrypt(algorithm, slot_id, data)
188    } // AteccDevice::cipher_encrypt()
189
190    /// Data decryption function in AES unauthenticated cipher alhorithms modes
191    /// Trait implementation
192    fn cipher_decrypt(
193        &self,
194        algorithm: CipherAlgorithm,
195        slot_id: u8,
196        data: &mut Vec<u8>,
197    ) -> AtcaStatus {
198        self.cipher_decrypt(algorithm, slot_id, data)
199    } // AteccDevice::cipher_decrypt()
200
201    /// Data encryption function in AES AEAD (authenticated encryption with associated data) modes
202    /// Trait implementation
203    fn aead_encrypt(
204        &self,
205        algorithm: AeadAlgorithm,
206        slot_id: u8,
207        data: &mut Vec<u8>,
208    ) -> Result<Vec<u8>, AtcaStatus> {
209        self.aead_encrypt(algorithm, slot_id, data)
210    } // AteccDevice::aead_encrypt()
211
212    /// Data decryption function in AES AEAD (authenticated encryption with associated data) modes
213    /// Trait implementation
214    fn aead_decrypt(
215        &self,
216        algorithm: AeadAlgorithm,
217        slot_id: u8,
218        data: &mut Vec<u8>,
219    ) -> Result<bool, AtcaStatus> {
220        self.aead_decrypt(algorithm, slot_id, data)
221    } // AteccDevice::aead_decrypt()
222
223    /// A function that calculates the MAC (Message Authentication Code) value for a message
224    /// Trait implementation
225    fn mac_compute(
226        &self,
227        algorithm: MacAlgorithm,
228        slot_id: u8,
229        data: &[u8],
230    ) -> Result<Vec<u8>, AtcaStatus> {
231        self.mac_compute(algorithm, slot_id, data)
232    } // AteccDevice::mac_compute()
233
234    /// A function that verifies the value of MAC (Message Authentication Code) for a message
235    /// Trait implementation
236    fn mac_verify(
237        &self,
238        algorithm: MacAlgorithm,
239        slot_id: u8,
240        data: &[u8],
241    ) -> Result<bool, AtcaStatus> {
242        self.mac_verify(algorithm, slot_id, data)
243    } // AteccDevice::mac_verify()
244
245    /// KDF command function, which derives a new key in PRF, AES, or HKDF modes
246    /// (only relevant for the ATECC608x chip)
247    /// Trait implementation
248    fn kdf(
249        &self,
250        algorithm: KdfAlgorithm,
251        parameters: KdfParams,
252        message: Option<&[u8]>,
253        message_length: usize,
254    ) -> Result<KdfResult, AtcaStatus> {
255        self.kdf(algorithm, parameters, message, message_length)
256    } // AteccDevice::kdf()
257
258    /// Function for generating premaster secret key using ECDH
259    /// Trait implementation
260    fn ecdh(
261        &self,
262        parameters: EcdhParams,
263        peer_public_key: &[u8],
264    ) -> Result<EcdhResult, AtcaStatus> {
265        self.ecdh(parameters, peer_public_key)
266    } // AteccDevice::ecdh()
267
268    /// Execute this command prevents future modifications of the Configuration zone.
269    /// This command fails if the designated area is already locked.
270    /// Trait implementation
271    fn lock_config_zone(&self) -> AtcaStatus {
272        self.lock_config_zone()
273    } // AteccDevice::lock_config_zone()
274
275    /// Execute this command prevents future modifications of the Data and OTP zones.
276    /// This command fails if the designated area is already locked.
277    /// Trait implementation
278    fn lock_data_zone(&self) -> AtcaStatus {
279        self.lock_data_zone()
280    } // AteccDevice::lock_data_zone()
281
282    /// Lock an individual slot in the data zone on an ATECC device. Not available for ATSHA devices.
283    /// Slot must be configured to be slot lockable slots[slot_idx].config.lockable = true.
284    /// This command fails if the designated area is already locked.
285    /// Trait implementation
286    fn lock_slot(&self, slot_id: u8) -> AtcaStatus {
287        self.lock_slot(slot_id)
288    } // AteccDevice::lock_slot()
289
290    /// Function for uploading configuration to the chip.
291    /// First 16 bytes of data are skipped as they are not writable. LockValue and LockConfig
292    /// are also skipped and can only be changed via the Lock command.
293    /// This command may fail if UserExtra and/or Selector bytes have already been set to non-zero values.
294    /// Trait implementation
295    fn load_config_into_chip(&self, config: &[u8]) -> AtcaStatus {
296        self.load_config_into_chip(config)
297    } // AteccDevice::load_config_into_chip()
298
299    /// Request ATECC to return own device type
300    /// Trait implementation
301    fn get_device_type(&self) -> AtcaDeviceType {
302        self.get_device_type()
303    } // AteccDevice::get_device_type()
304
305    /// Request ATECC to check if its configuration is locked.
306    /// If true, a chip can be used for cryptographic operations
307    /// Trait implementation
308    fn is_configuration_locked(&self) -> bool {
309        self.config_zone_locked
310    } // AteccDevice::is_configuration_locked()
311
312    /// Request ATECC to check if its Data Zone is locked.
313    /// If true, a chip can be used for cryptographic operations
314    /// Trait implementation
315    fn is_data_zone_locked(&self) -> bool {
316        self.data_zone_locked
317    } // AteccDevice::is_data_zone_locked()
318
319    /// Returns a structure containing configuration data read from ATECC
320    /// during initialization of the AteccDevice object.
321    /// Trait implementation
322    fn get_config(&self, atca_slots: &mut Vec<AtcaSlot>) -> AtcaStatus {
323        self.get_config(atca_slots)
324    } // AteccDevice::get_config()
325
326    /// Command accesses some static or dynamic information from the ATECC chip
327    /// Trait implementation
328    fn info_cmd(&self, command: InfoCmdType) -> Result<Vec<u8>, AtcaStatus> {
329        self.info_cmd(command)
330    } // AteccDevice::info_cmd()
331
332    /// A function that adds an access key for securely reading or writing data
333    /// that is located in a specific slot on the ATECCx08 chip.
334    /// Data is not written to the ATECCx08 chip, but to the AteccDevice structure.
335    /// Trait implementation
336    fn add_access_key(&self, slot_id: u8, access_key: &[u8]) -> AtcaStatus {
337        self.add_access_key(slot_id, access_key)
338    } // AteccDevice::add_access_key()
339
340    /// A function that deletes all access keys for secure read or write operations
341    /// performed by the ATECCx08 chip
342    /// Trait implementation
343    fn flush_access_keys(&self) -> AtcaStatus {
344        self.flush_access_keys()
345    } // AteccDevice::flush_access_keys()
346
347    /// Get serial number of the ATECC device
348    /// Trait implementation
349    fn get_serial_number(&self) -> [u8; ATCA_SERIAL_NUM_SIZE] {
350        self.serial_number
351    } // AteccDevice::get_serial_number()
352
353    /// Checks if the chip supports AES encryption
354    /// (only relevant for the ATECC608x chip)
355    /// Trait implementation
356    fn is_aes_enabled(&self) -> bool {
357        self.chip_options.aes_enabled
358    } // AteccDevice::is_aes_enabled()
359
360    /// Checks if the chip supports AES for KDF operations
361    /// (only relevant for the ATECC608x chip)
362    /// Trait implementation
363    fn is_kdf_aes_enabled(&self) -> bool {
364        self.chip_options.kdf_aes_enabled
365    } // AteccDevice::is_kdf_aes_enabled()
366
367    /// Checks if the special KDF Initialization Vector function is enabled
368    /// (only relevant for the ATECC608x chip)
369    /// Trait implementation
370    fn is_kdf_iv_enabled(&self) -> bool {
371        self.chip_options.kdf_iv_enabled
372    } // AteccDevice::is_kdf_iv_enabled()
373
374    /// Checks whether transmission between chip and host is to be encrypted
375    /// (IO encryption is only possible for ATECC608x chip)
376    /// Trait implementation
377    fn is_io_protection_key_enabled(&self) -> bool {
378        self.chip_options.io_key_enabled
379    } // AteccDevice::is_io_protection_key_enabled()
380
381    ///
382    /// (only relevant for the ATECC608x chip)
383    /// Trait implementation
384    fn get_ecdh_output_protection_state(&self) -> OutputProtectionState {
385        self.chip_options.ecdh_output_protection
386    } // AteccDevice::get_ecdh_output_protection_state()
387
388    ///
389    /// (only relevant for the ATECC608x chip)
390    /// Trait implementation
391    fn get_kdf_output_protection_state(&self) -> OutputProtectionState {
392        self.chip_options.kdf_output_protection
393    } // AteccDevice::get_kdf_output_protection_state()
394
395    /// wakeup the CryptoAuth device
396    /// Trait implementation
397    fn wakeup(&self) -> AtcaStatus {
398        self.wakeup()
399    } // AteccDevice::wakeup()
400
401    /// invoke sleep on the CryptoAuth device
402    /// Trait implementation
403    fn sleep(&self) -> AtcaStatus {
404        self.sleep()
405    } // AteccDevice::sleep()
406
407    /// ATECC device instance destructor
408    /// Trait implementation
409    fn release(&self) -> AtcaStatus {
410        self.release()
411    } // AteccDevice::release()
412
413    //--------------------------------------------------
414    //
415    // Functions available only during testing
416    //
417    //--------------------------------------------------
418
419    /// A generic function that reads data from the chip
420    /// Trait implementation
421    #[cfg(test)]
422    fn read_zone(&self, zone: u8, slot: u16, block: u8, offset: u8, data: &mut [u8]) -> AtcaStatus {
423        self.read_zone(zone, slot, block, offset, data)
424    } // AteccDevice::read_zone()
425
426    /// Request ATECC to read and return own configuration zone.
427    /// Note: this function returns raw data, function get_config(..) implements a more
428    /// structured return.
429    /// Trait implementation
430    #[cfg(test)]
431    fn read_config_zone(&self, config_data: &mut Vec<u8>) -> AtcaStatus {
432        self.read_config_zone(config_data)
433    } // AteccDevice::read_config_zone()
434
435    /// Compare internal config zone contents vs. config_data.
436    /// Diagnostic function.
437    /// Trait implementation
438    #[cfg(test)]
439    fn cmp_config_zone(&self, config_data: &mut [u8]) -> Result<bool, AtcaStatus> {
440        self.cmp_config_zone(config_data)
441    } // AteccDevice::cmp_config_zone()
442
443    /// A function that takes an encryption key for securely reading or writing data
444    /// that is located in a specific slot on an ATECCx08 chip.
445    /// Data is not taken directly from the ATECCx08 chip, but from the AteccDevice structure
446    /// Trait implementation
447    #[cfg(test)]
448    fn get_access_key(&self, slot_id: u8, key: &mut Vec<u8>) -> AtcaStatus {
449        self.get_access_key(slot_id, key)
450    } // AteccDevice::get_access_key()
451
452    /// Perform an AES-128 encrypt operation with a key in the device
453    /// Trait implementation
454    #[cfg(test)]
455    fn aes_encrypt_block(
456        &self,
457        key_id: u16,
458        key_block: u8,
459        input: &[u8],
460    ) -> Result<[u8; ATCA_AES_DATA_SIZE], AtcaStatus> {
461        self.aes_encrypt_block(key_id, key_block, input)
462    } // AteccDevice::aes_encrypt_block()
463
464    /// Perform an AES-128 decrypt operation with a key in the device
465    /// Trait implementation
466    #[cfg(test)]
467    fn aes_decrypt_block(
468        &self,
469        key_id: u16,
470        key_block: u8,
471        input: &[u8],
472    ) -> Result<[u8; ATCA_AES_DATA_SIZE], AtcaStatus> {
473        self.aes_decrypt_block(key_id, key_block, input)
474    } // AteccDevice::aes_decrypt_block()
475
476    /// Initialize context for AES CTR operation with an existing IV
477    /// Trait implementation
478    #[cfg(test)]
479    fn aes_ctr_init(
480        &self,
481        slot_id: u8,
482        counter_size: u8,
483        iv: &[u8],
484    ) -> Result<atca_aes_ctr_ctx_t, AtcaStatus> {
485        self.aes_ctr_init(slot_id, counter_size, iv)
486    } // AteccDevice::aes_ctr_init()
487
488    /// Increments AES CTR counter value
489    /// Trait implementation
490    #[cfg(test)]
491    fn aes_ctr_increment(&self, ctx: atca_aes_ctr_ctx_t) -> Result<atca_aes_ctr_ctx_t, AtcaStatus> {
492        self.aes_ctr_increment(ctx)
493    } // AteccDevice::aes_ctr_increment()
494
495    /// Initialize context for AES CBC operation.
496    /// Trait implementation
497    #[cfg(test)]
498    fn aes_cbc_init(&self, slot_id: u8, iv: &[u8]) -> Result<atca_aes_cbc_ctx_t, AtcaStatus> {
499        self.aes_cbc_init(slot_id, iv)
500    } // AteccDevice::aes_cbc_init()
501
502    /// A helper function that returns number of blocks and bytes of data
503    /// available for a given socket
504    /// Trait implementation
505    #[cfg(test)]
506    fn get_slot_capacity(&self, slot_id: u8) -> AtcaSlotCapacity {
507        self.get_slot_capacity(slot_id)
508    } // AteccDevice::get_slot_capacity()
509}
510
511/// Implementation of CryptoAuth Library API Rust wrapper calls
512impl AteccDevice {
513    /// ATECC device instance constructor
514    pub fn new(r_iface_cfg: AtcaIfaceCfg) -> Result<AteccDevice, String> {
515        if !ATECC_RESOURCE_MANAGER.lock().unwrap().acquire() {
516            return Err(AtcaStatus::AtcaAllocFailure.to_string());
517        }
518        let iface_cfg = Box::new(
519            match cryptoauthlib_sys::ATCAIfaceCfg::try_from(r_iface_cfg) {
520                Ok(x) => x,
521                Err(()) => {
522                    ATECC_RESOURCE_MANAGER.lock().unwrap().release();
523                    return Err(AtcaStatus::AtcaBadParam.to_string());
524                }
525            },
526        );
527        let mut atecc_device = AteccDevice::default();
528
529        let iface_cfg_raw_ptr: *mut cryptoauthlib_sys::ATCAIfaceCfg = Box::into_raw(iface_cfg);
530        // From now on iface_cfg is consumed and iface_cfg_ptr must be stored to be released
531        // when no longer needed.
532
533        let result = AtcaStatus::from(unsafe {
534            let _guard = atecc_device
535                .api_mutex
536                .lock()
537                .expect("Could not lock atcab API mutex");
538            cryptoauthlib_sys::atcab_init(iface_cfg_raw_ptr)
539        });
540
541        atecc_device.iface_cfg_ptr = match result {
542            AtcaStatus::AtcaSuccess => AtcaIfaceCfgPtrWrapper {
543                ptr: iface_cfg_raw_ptr,
544            },
545            _ => {
546                // Here init failed so no need to call a proper release
547                ATECC_RESOURCE_MANAGER.lock().unwrap().release();
548                unsafe { Box::from_raw(iface_cfg_raw_ptr) };
549                return Err(result.to_string());
550            }
551        };
552
553        // atecc_device.api_mutex is already initialized
554        // from now on it is safe to call atecc_device.release();
555
556        let mut config_data = Vec::new();
557        let result = atecc_device.get_config_from_chip(&mut config_data);
558        if result != AtcaStatus::AtcaSuccess {
559            atecc_device.release();
560            return Err(result.to_string());
561        }
562
563        atecc_device.serial_number = atecc_device.get_serial_number(&config_data);
564
565        atecc_device.slots = {
566            let mut atca_slots = Vec::new();
567            atcab_get_slots_config_from_config_data(&config_data, &mut atca_slots);
568            atca_slots
569        };
570
571        atecc_device.config_zone_locked =
572            atecc_device.is_locked(&config_data, ATCA_LOCK_ZONE_CONFIG);
573
574        atecc_device.data_zone_locked = atecc_device.is_locked(&config_data, ATCA_LOCK_ZONE_DATA);
575
576        atecc_device.get_chip_options_data_from_chip_config_data(&config_data);
577
578        let chip_type = atecc_device.get_device_type();
579        let err_str = "\n\n\u{001b}[1m\u{001b}[33mcheck if 'device_type' is correct in \
580        'config.toml' file, because chip on the bus seems to be";
581        if atecc_device.chip_options.aes_enabled && (chip_type != AtcaDeviceType::ATECC608A) {
582            atecc_device.release();
583            return Err(format!(
584                "{} type ATECC608x,\nand you have chosen \u{001b}[31m{}\u{001b}[33m !\u{001b}[0m\n\n",
585                err_str,
586                chip_type
587            ));
588        }
589        if !atecc_device.chip_options.aes_enabled && (chip_type == AtcaDeviceType::ATECC608A) {
590            atecc_device.release();
591            return Err(format!(
592                "{} of a different type than the \u{001b}[31mATECC608x\u{001b}[33m you selected !\u{001b}[0m\n\n",
593                err_str
594            ));
595        }
596
597        Ok(atecc_device)
598    } // AteccDevice::new()
599
600    /// Request ATECC to generate a vector of random bytes
601    fn random(&self, rand_out: &mut Vec<u8>) -> AtcaStatus {
602        if self.check_that_configuration_is_not_locked(false) {
603            return AtcaStatus::AtcaNotLocked;
604        }
605        rand_out.resize(ATCA_RANDOM_BUFFER_SIZE, 0);
606        AtcaStatus::from(unsafe {
607            let _guard = self
608                .api_mutex
609                .lock()
610                .expect("Could not lock atcab API mutex");
611            cryptoauthlib_sys::atcab_random(rand_out.as_mut_ptr())
612        })
613    } // AteccDevice::random()
614
615    /// Request ATECC to compute a message hash (SHA256)
616    fn sha(&self, message: Vec<u8>, digest: &mut Vec<u8>) -> AtcaStatus {
617        if self.check_that_configuration_is_not_locked(false) {
618            return AtcaStatus::AtcaNotLocked;
619        }
620        let length: u16 = match u16::try_from(message.len()) {
621            Ok(val) => val,
622            Err(_) => return AtcaStatus::AtcaBadParam,
623        };
624
625        digest.resize(ATCA_SHA2_256_DIGEST_SIZE, 0);
626
627        AtcaStatus::from(unsafe {
628            let _guard = self
629                .api_mutex
630                .lock()
631                .expect("Could not lock atcab API mutex");
632            cryptoauthlib_sys::atcab_sha(length, message.as_ptr(), digest.as_mut_ptr())
633        })
634    } // AteccDevice::sha()
635
636    /// Execute a Nonce command in pass-through mode to load one of the
637    /// device's internal buffers with a fixed value.
638    /// For the ATECC608A, available targets are TempKey (32 or 64 bytes), Message
639    /// Digest Buffer (32 or 64 bytes), or the Alternate Key Buffer (32 bytes). For
640    /// all other devices, only TempKey (32 bytes) is available.
641    fn nonce(&self, target: NonceTarget, data: &[u8]) -> AtcaStatus {
642        if (self.get_device_type() != AtcaDeviceType::ATECC608A) && (target != NonceTarget::TempKey)
643        {
644            return AtcaStatus::AtcaBadParam;
645        }
646        let dev_type_608: bool = AtcaDeviceType::ATECC608A == self.get_device_type();
647        let alt_key_buff: bool = NonceTarget::AltKeyBuf == target;
648        let no_len_32: bool = data.len() != ATCA_NONCE_SIZE;
649        let no_len_64: bool = data.len() != (2 * ATCA_NONCE_SIZE);
650
651        if !dev_type_608 && alt_key_buff
652            || alt_key_buff && no_len_32
653            || !dev_type_608 && !no_len_64
654            || no_len_32 && no_len_64
655            || !no_len_32 && !no_len_64
656        {
657            return AtcaStatus::AtcaInvalidSize;
658        }
659        AtcaStatus::from(unsafe {
660            let _guard = self
661                .api_mutex
662                .lock()
663                .expect("Could not lock atcab API mutex");
664            cryptoauthlib_sys::atcab_nonce_load(target as u8, data.as_ptr(), data.len() as u16)
665        })
666    } // AteccDevice::nonce()
667
668    /// Execute a Nonce command to generate a random nonce combining a host
669    /// nonce and a device random number.
670    fn nonce_rand(&self, host_nonce: &[u8], rand_out: &mut Vec<u8>) -> AtcaStatus {
671        if host_nonce.len() != ATCA_NONCE_NUMIN_SIZE {
672            return AtcaStatus::AtcaInvalidSize;
673        }
674
675        rand_out.resize(ATCA_RANDOM_BUFFER_SIZE, 0);
676
677        AtcaStatus::from(unsafe {
678            let _guard = self
679                .api_mutex
680                .lock()
681                .expect("Could not lock atcab API mutex");
682            cryptoauthlib_sys::atcab_nonce_rand(host_nonce.as_ptr(), rand_out.as_mut_ptr())
683        })
684    } // AteccDevice::nonce_rand()
685
686    /// Request ATECC to generate a cryptographic key
687    fn gen_key(&self, key_type: KeyType, slot_id: u8) -> AtcaStatus {
688        if self.check_that_configuration_is_not_locked(false) {
689            return AtcaStatus::AtcaNotLocked;
690        }
691
692        if let Err(err) = self.encryption_key_setup_parameters_check(key_type, slot_id) {
693            return err;
694        }
695
696        let slot = match slot_id {
697            ATCA_ATECC_SLOTS_COUNT => ATCA_ATECC_TEMPKEY_KEYID,
698            _ => slot_id as u16,
699        };
700
701        match key_type {
702            KeyType::P256EccKey => {
703                if (slot_id < ATCA_ATECC_SLOTS_COUNT)
704                    && !self.slots[slot_id as usize].config.is_secret
705                {
706                    return AtcaStatus::AtcaBadParam;
707                }
708                AtcaStatus::from(unsafe {
709                    let _guard = self
710                        .api_mutex
711                        .lock()
712                        .expect("Could not lock atcab API mutex");
713                    cryptoauthlib_sys::atcab_genkey(slot, ptr::null_mut() as *mut u8)
714                })
715            }
716            KeyType::Aes => {
717                const BLOCK_IDX: u8 = 0;
718
719                let mut key: Vec<u8> = Vec::with_capacity(ATCA_RANDOM_BUFFER_SIZE);
720                let result = self.random(&mut key);
721                if AtcaStatus::AtcaSuccess != result {
722                    return result;
723                }
724
725                for idx in &mut key[ATCA_AES_KEY_SIZE..] {
726                    *idx = 0x00
727                }
728
729                match slot {
730                    ATCA_ATECC_TEMPKEY_KEYID => AtcaStatus::AtcaUnimplemented, // TODO
731                    _ => self.write_slot(slot, BLOCK_IDX, &key),
732                }
733            }
734            _ => AtcaStatus::AtcaBadParam,
735        }
736    } // AteccDevice::gen_key()
737
738    /// Request ATECC to import a cryptographic key
739    fn import_key(&self, key_type: KeyType, key_data: &[u8], slot_id: u8) -> AtcaStatus {
740        if self.check_that_configuration_is_not_locked(true) {
741            return AtcaStatus::AtcaNotLocked;
742        }
743        if let Err(err) = self.encryption_key_setup_parameters_check(key_type, slot_id) {
744            return err;
745        }
746
747        let slot = match slot_id {
748            ATCA_ATECC_SLOTS_COUNT => ATCA_ATECC_TEMPKEY_KEYID,
749            _ => slot_id as u16,
750        };
751
752        match key_type {
753            KeyType::P256EccKey => self.write_p256_ecc_key_to_slot(key_data, slot),
754            KeyType::Aes => self.write_aes_key_to_slot(key_data, slot),
755            KeyType::ShaOrText => self.write_sha_or_text_key_to_slot(key_data, slot),
756            _ => AtcaStatus::AtcaBadParam,
757        }
758    } // AteccDevice::import_key()
759
760    /// Request ATECC to export a cryptographic key.
761    /// For key type 'ShaOrText', the amount of data returned is as large as
762    /// size of the given buffer 'key_data', but when this size is greater than
763    /// maximum amount of data that can be hold by slot, this function will return an error.
764    /// For other types of keys, the amount of data returned corresponds to size of a given key.
765    fn export_key(&self, key_type: KeyType, key_data: &mut Vec<u8>, slot_id: u8) -> AtcaStatus {
766        if self.check_that_configuration_is_not_locked(true) {
767            return AtcaStatus::AtcaNotLocked;
768        }
769        if slot_id > ATCA_ATECC_SLOTS_COUNT {
770            return AtcaStatus::AtcaInvalidId;
771        };
772        match key_type {
773            KeyType::P256EccKey => self.get_public_key(slot_id, key_data),
774            KeyType::Aes => self.read_aes_key_from_slot(slot_id, key_data),
775            KeyType::ShaOrText => self.read_sha_or_text_key_from_slot(slot_id, key_data),
776            _ => AtcaStatus::AtcaBadParam,
777        }
778    } // AteccDevice::export_key()
779
780    /// Depending on the socket configuration, this function calculates
781    /// public key based on an existing private key in the socket
782    /// or exports the public key directly
783    fn get_public_key(&self, slot_id: u8, public_key: &mut Vec<u8>) -> AtcaStatus {
784        if self.check_that_configuration_is_not_locked(true) {
785            return AtcaStatus::AtcaNotLocked;
786        }
787
788        public_key.resize(ATCA_ATECC_PUB_KEY_SIZE, 0);
789
790        if slot_id == ATCA_ATECC_SLOTS_COUNT {
791            return AtcaStatus::from(unsafe {
792                let _guard = self
793                    .api_mutex
794                    .lock()
795                    .expect("Could not lock atcab API mutex");
796                cryptoauthlib_sys::atcab_get_pubkey(
797                    ATCA_ATECC_TEMPKEY_KEYID,
798                    public_key.as_mut_ptr(),
799                )
800            });
801        }
802
803        if self.slots[slot_id as usize].config.key_type != KeyType::P256EccKey {
804            return AtcaStatus::AtcaBadParam;
805        }
806
807        if self.slots[slot_id as usize].config.is_secret {
808            if self.slots[slot_id as usize].config.pub_info
809                && self.slots[slot_id as usize].config.ecc_key_attr.is_private
810            {
811                AtcaStatus::from(unsafe {
812                    let _guard = self
813                        .api_mutex
814                        .lock()
815                        .expect("Could not lock atcab API mutex");
816                    cryptoauthlib_sys::atcab_get_pubkey(slot_id as u16, public_key.as_mut_ptr())
817                })
818            } else if self.slots[slot_id as usize].config.read_key.encrypt_read {
819                if slot_id < ATCA_ATECC_MIN_SLOT_IDX_FOR_PUB_KEY {
820                    AtcaStatus::AtcaInvalidId
821                } else {
822                    // TODO encrypt read
823                    // Question is whether someone will store public key in a slot that requires encrypted access?
824
825                    AtcaStatus::AtcaUnimplemented
826                }
827            } else {
828                AtcaStatus::AtcaBadParam
829            }
830        } else if self.slots[slot_id as usize].config.write_config == WriteConfig::Always {
831            if slot_id < ATCA_ATECC_MIN_SLOT_IDX_FOR_PUB_KEY {
832                AtcaStatus::AtcaInvalidId
833            } else {
834                AtcaStatus::from(unsafe {
835                    let _guard = self
836                        .api_mutex
837                        .lock()
838                        .expect("Could not lock atcab API mutex");
839                    cryptoauthlib_sys::atcab_read_pubkey(slot_id as u16, public_key.as_mut_ptr())
840                })
841            }
842        } else {
843            AtcaStatus::AtcaBadParam
844        }
845    } // AteccDevice::get_public_key()
846
847    /// Request ATECC to generate an ECDSA signature
848    fn sign_hash(&self, mode: SignMode, slot_id: u8, signature: &mut Vec<u8>) -> AtcaStatus {
849        if self.check_that_configuration_is_not_locked(true) {
850            return AtcaStatus::AtcaNotLocked;
851        }
852        if slot_id >= ATCA_ATECC_SLOTS_COUNT {
853            return AtcaStatus::AtcaInvalidId;
854        }
855        signature.resize(ATCA_SIG_SIZE, 0);
856        match mode {
857            // Executes Sign command, to sign a 32-byte external message using the
858            // private key in the specified slot. The message to be signed
859            // will be loaded into the Message Digest Buffer to the
860            // ATECC608A device or TempKey for other devices.
861            SignMode::External(hash) => AtcaStatus::from(unsafe {
862                let _guard = self
863                    .api_mutex
864                    .lock()
865                    .expect("Could not lock atcab API mutex");
866                cryptoauthlib_sys::atcab_sign(slot_id as u16, hash.as_ptr(), signature.as_mut_ptr())
867            }),
868            _ => AtcaStatus::AtcaUnimplemented,
869        }
870    } // AteccDevice::sign_hash()
871
872    /// Request ATECC to verify ECDSA signature
873    fn verify_hash(
874        &self,
875        mode: VerifyMode,
876        hash: &[u8],
877        signature: &[u8],
878    ) -> Result<bool, AtcaStatus> {
879        if self.check_that_configuration_is_not_locked(true) {
880            return Err(AtcaStatus::AtcaNotLocked);
881        }
882        if (signature.len() != ATCA_SIG_SIZE) || (hash.len() != ATCA_SHA2_256_DIGEST_SIZE) {
883            return Err(AtcaStatus::AtcaInvalidSize);
884        };
885        let mut is_verified: bool = false;
886        let result: AtcaStatus;
887
888        match mode {
889            // Executes the Verify command, which verifies a signature (ECDSA
890            // verify operation) with a public key stored in the device. The
891            // message to be signed will be loaded into the Message Digest Buffer
892            // to the ATECC608A device or TempKey for other devices.
893            VerifyMode::Internal(slot_number) => {
894                if slot_number >= ATCA_ATECC_SLOTS_COUNT {
895                    return Err(AtcaStatus::AtcaInvalidId);
896                }
897                result = AtcaStatus::from(unsafe {
898                    let _guard = self
899                        .api_mutex
900                        .lock()
901                        .expect("Could not lock atcab API mutex");
902                    cryptoauthlib_sys::atcab_verify_stored(
903                        hash.as_ptr(),
904                        signature.as_ptr(),
905                        slot_number as u16,
906                        &mut is_verified,
907                    )
908                })
909            }
910            // Executes the Verify command, which verifies a signature (ECDSA
911            // verify operation) with all components (message, signature, and
912            // public key) supplied. The message to be signed will be loaded into
913            // the Message Digest Buffer to the ATECC608A device or TempKey for
914            // other devices.
915            VerifyMode::External(public_key) => {
916                if public_key.len() != ATCA_ATECC_PUB_KEY_SIZE {
917                    return Err(AtcaStatus::AtcaInvalidId);
918                }
919                result = AtcaStatus::from(unsafe {
920                    let _guard = self
921                        .api_mutex
922                        .lock()
923                        .expect("Could not lock atcab API mutex");
924                    cryptoauthlib_sys::atcab_verify_extern(
925                        hash.as_ptr(),
926                        signature.as_ptr(),
927                        public_key.as_ptr(),
928                        &mut is_verified,
929                    )
930                })
931            }
932            _ => return Err(AtcaStatus::AtcaUnimplemented),
933        }
934
935        match result {
936            AtcaStatus::AtcaSuccess => Ok(is_verified),
937            _ => Err(result),
938        }
939    } // AteccDevice::verify_hash()
940
941    /// Data encryption function in AES unauthenticated cipher alhorithms modes
942    fn cipher_encrypt(
943        &self,
944        algorithm: CipherAlgorithm,
945        slot_id: u8,
946        data: &mut Vec<u8>,
947    ) -> AtcaStatus {
948        if self.check_that_configuration_is_not_locked(true) {
949            return AtcaStatus::AtcaNotLocked;
950        }
951        if !self.is_aes_enabled() {
952            // If chip does not support AES hardware encryption, the operation cannot be performed
953            return AtcaStatus::AtcaBadParam;
954        }
955
956        match algorithm {
957            CipherAlgorithm::Ctr(cipher_param) => self.cipher_aes_ctr(cipher_param, slot_id, data),
958            CipherAlgorithm::Cfb(cipher_param) => {
959                self.cipher_aes_cfb(cipher_param, slot_id, data, CipherOperation::Encrypt)
960            }
961            CipherAlgorithm::Ofb(cipher_param) => {
962                self.cipher_aes_ofb(cipher_param, slot_id, data, CipherOperation::Encrypt)
963            }
964            CipherAlgorithm::Ecb(cipher_param) => {
965                self.cipher_aes_ecb(cipher_param, slot_id, data, CipherOperation::Encrypt)
966            }
967            CipherAlgorithm::Cbc(cipher_param) => {
968                self.cipher_aes_cbc(cipher_param, slot_id, data, CipherOperation::Encrypt)
969            }
970            CipherAlgorithm::CbcPkcs7(cipher_param) => {
971                self.cipher_aes_cbc_pkcs7(cipher_param, slot_id, data, CipherOperation::Encrypt)
972            }
973            _ => AtcaStatus::AtcaUnimplemented,
974        }
975    } // AteccDevice::cipher_encrypt()
976
977    /// Data decryption function in AES unauthenticated cipher alhorithms modes
978    fn cipher_decrypt(
979        &self,
980        algorithm: CipherAlgorithm,
981        slot_id: u8,
982        data: &mut Vec<u8>,
983    ) -> AtcaStatus {
984        if self.check_that_configuration_is_not_locked(true) {
985            return AtcaStatus::AtcaNotLocked;
986        }
987        if !self.is_aes_enabled() {
988            // If chip does not support AES hardware encryption, the operation cannot be performed
989            return AtcaStatus::AtcaBadParam;
990        }
991
992        match algorithm {
993            CipherAlgorithm::Ctr(cipher_param) => self.cipher_aes_ctr(cipher_param, slot_id, data),
994            CipherAlgorithm::Cfb(cipher_param) => {
995                self.cipher_aes_cfb(cipher_param, slot_id, data, CipherOperation::Decrypt)
996            }
997            CipherAlgorithm::Ofb(cipher_param) => {
998                self.cipher_aes_ofb(cipher_param, slot_id, data, CipherOperation::Decrypt)
999            }
1000            CipherAlgorithm::Ecb(cipher_param) => {
1001                self.cipher_aes_ecb(cipher_param, slot_id, data, CipherOperation::Decrypt)
1002            }
1003            CipherAlgorithm::Cbc(cipher_param) => {
1004                self.cipher_aes_cbc(cipher_param, slot_id, data, CipherOperation::Decrypt)
1005            }
1006            CipherAlgorithm::CbcPkcs7(cipher_param) => {
1007                self.cipher_aes_cbc_pkcs7(cipher_param, slot_id, data, CipherOperation::Decrypt)
1008            }
1009            _ => AtcaStatus::AtcaUnimplemented,
1010        }
1011    } // AteccDevice::cipher_decrypt()
1012
1013    /// Data encryption function in AES AEAD (authenticated encryption with associated data) modes
1014    fn aead_encrypt(
1015        &self,
1016        algorithm: AeadAlgorithm,
1017        slot_id: u8,
1018        data: &mut Vec<u8>,
1019    ) -> Result<Vec<u8>, AtcaStatus> {
1020        if self.check_that_configuration_is_not_locked(true) {
1021            return Err(AtcaStatus::AtcaNotLocked);
1022        }
1023        if !self.is_aes_enabled() {
1024            // If chip does not support AES hardware encryption, the operation cannot be performed
1025            return Err(AtcaStatus::AtcaBadParam);
1026        }
1027
1028        match algorithm {
1029            AeadAlgorithm::Ccm(aead_param) => self.encrypt_aes_ccm(aead_param, slot_id, data),
1030            AeadAlgorithm::Gcm(aead_param) => self.encrypt_aes_gcm(aead_param, slot_id, data),
1031        }
1032    } // AteccDevice::aead_encrypt()
1033
1034    /// Data decryption function in AES AEAD (authenticated encryption with associated data) modes
1035    fn aead_decrypt(
1036        &self,
1037        algorithm: AeadAlgorithm,
1038        slot_id: u8,
1039        data: &mut Vec<u8>,
1040    ) -> Result<bool, AtcaStatus> {
1041        if self.check_that_configuration_is_not_locked(true) {
1042            return Err(AtcaStatus::AtcaNotLocked);
1043        }
1044        if !self.is_aes_enabled() {
1045            // If chip does not support AES hardware encryption, the operation cannot be performed
1046            return Err(AtcaStatus::AtcaBadParam);
1047        }
1048
1049        match algorithm {
1050            AeadAlgorithm::Ccm(aead_param) => self.decrypt_aes_ccm(aead_param, slot_id, data),
1051            AeadAlgorithm::Gcm(aead_param) => self.decrypt_aes_gcm(aead_param, slot_id, data),
1052        }
1053    } // AteccDevice::aead_decrypt()
1054
1055    /// A function that calculates the MAC (Message Authentication Code) value for a message
1056    fn mac_compute(
1057        &self,
1058        algorithm: MacAlgorithm,
1059        slot_id: u8,
1060        data: &[u8],
1061    ) -> Result<Vec<u8>, AtcaStatus> {
1062        if self.check_that_configuration_is_not_locked(true) {
1063            return Err(AtcaStatus::AtcaNotLocked);
1064        }
1065        if (discriminant(&algorithm) != discriminant(&MacAlgorithm::HmacSha256(Default::default())))
1066            && !self.is_aes_enabled()
1067        {
1068            // If chip does not support AES hardware encryption, an operation other than HmacSha256 cannot be performed
1069            return Err(AtcaStatus::AtcaBadParam);
1070        }
1071
1072        match algorithm {
1073            MacAlgorithm::HmacSha256(mac_param) => {
1074                self.compute_mac_hmac_sha256(mac_param, slot_id, data)
1075            }
1076            MacAlgorithm::Cbcmac(mac_param) => self.compute_mac_cbcmac(mac_param, slot_id, data),
1077            MacAlgorithm::Cmac(mac_param) => self.compute_mac_cmac(mac_param, slot_id, data),
1078        }
1079    } // AteccDevice::mac_compute()
1080
1081    /// A function that verifies the value of MAC (Message Authentication Code) for a message
1082    fn mac_verify(
1083        &self,
1084        algorithm: MacAlgorithm,
1085        slot_id: u8,
1086        data: &[u8],
1087    ) -> Result<bool, AtcaStatus> {
1088        if self.check_that_configuration_is_not_locked(true) {
1089            return Err(AtcaStatus::AtcaNotLocked);
1090        }
1091        if (discriminant(&algorithm) != discriminant(&MacAlgorithm::HmacSha256(Default::default())))
1092            && !self.is_aes_enabled()
1093        {
1094            // If chip does not support AES hardware encryption, an operation other than HmacSha256 cannot be performed
1095            return Err(AtcaStatus::AtcaBadParam);
1096        }
1097
1098        match algorithm {
1099            MacAlgorithm::HmacSha256(mac_param) => {
1100                self.verify_mac_hmac_sha256(mac_param, slot_id, data)
1101            }
1102            MacAlgorithm::Cbcmac(mac_param) => self.verify_mac_cbcmac(mac_param, slot_id, data),
1103            MacAlgorithm::Cmac(mac_param) => self.verify_mac_cmac(mac_param, slot_id, data),
1104        }
1105    } // AteccDevice::mac_verify()
1106
1107    /// Request ATECC to return own device type
1108    fn get_device_type(&self) -> AtcaDeviceType {
1109        AtcaDeviceType::from(unsafe {
1110            let _guard = self
1111                .api_mutex
1112                .lock()
1113                .expect("Could not lock atcab API mutex");
1114            cryptoauthlib_sys::atcab_get_device_type()
1115        })
1116    } // AteccDevice::get_device_type()
1117
1118    /// Returns a structure containing configuration data read from ATECC
1119    /// during initialization of the AteccDevice object.
1120    fn get_config(&self, atca_slots: &mut Vec<AtcaSlot>) -> AtcaStatus {
1121        atca_slots.clear();
1122        for idx in 0..self.slots.len() {
1123            atca_slots.push(self.slots[idx])
1124        }
1125        AtcaStatus::AtcaSuccess
1126    } // AteccDevice::get_config()
1127
1128    /// Command accesses some static or dynamic information from the ATECC chip
1129    fn info_cmd(&self, command: InfoCmdType) -> Result<Vec<u8>, AtcaStatus> {
1130        let mut out_data: Vec<u8> = vec![0; 4];
1131        let param2 = 0;
1132        match command {
1133            InfoCmdType::Revision => (),
1134            InfoCmdType::State => (),
1135            _ => return Err(AtcaStatus::AtcaUnimplemented),
1136        }
1137        let result = AtcaStatus::from(unsafe {
1138            let _guard = self
1139                .api_mutex
1140                .lock()
1141                .expect("Could not lock atcab API mutex");
1142            cryptoauthlib_sys::atcab_info_base(command as u8, param2, out_data.as_mut_ptr())
1143        });
1144        match result {
1145            AtcaStatus::AtcaSuccess => Ok(out_data),
1146            _ => Err(result),
1147        }
1148    } // AteccDevice::info_cmd()
1149
1150    /// A function that adds an access key for securely reading or writing data
1151    /// that is located in a specific slot on the ATECCx08 chip.
1152    /// Data is not written to the ATECCx08 chip, but to the AteccDevice structure.
1153    fn add_access_key(&self, slot_id: u8, access_key: &[u8]) -> AtcaStatus {
1154        if let Err(err) = self.access_key_setup_parameters_check(slot_id) {
1155            return err;
1156        };
1157
1158        if access_key.len() != ATCA_KEY_SIZE {
1159            return AtcaStatus::AtcaInvalidSize;
1160        }
1161
1162        let access_keys_mutex = self
1163            .access_keys
1164            .lock()
1165            .expect("Could not lock 'access_keys' mutex");
1166
1167        let access_keys_obj = access_keys_mutex.try_borrow_mut();
1168
1169        match access_keys_obj {
1170            Err(_) => AtcaStatus::AtcaFuncFail,
1171            Ok(mut access_keys) => {
1172                let mut key_arr: [u8; ATCA_KEY_SIZE] = [0; ATCA_KEY_SIZE];
1173                key_arr.copy_from_slice(&access_key[0..]);
1174                access_keys.insert(slot_id, key_arr);
1175                AtcaStatus::AtcaSuccess
1176            }
1177        }
1178    } // AteccDevice::add_access_key()
1179
1180    /// A function that deletes all access keys for secure read or write operations
1181    /// performed by the ATECCx08 chip
1182    fn flush_access_keys(&self) -> AtcaStatus {
1183        let access_keys_mutex = self
1184            .access_keys
1185            .lock()
1186            .expect("Could not lock 'access_keys' mutex");
1187
1188        let access_keys_obj = access_keys_mutex.try_borrow_mut();
1189
1190        match access_keys_obj {
1191            Err(_) => AtcaStatus::AtcaFuncFail,
1192            Ok(mut access_keys) => {
1193                access_keys.clear();
1194                access_keys.shrink_to_fit();
1195                AtcaStatus::AtcaSuccess
1196            }
1197        }
1198    } // AteccDevice::flush_access_keys()
1199
1200    /// wakeup the CryptoAuth device
1201    fn wakeup(&self) -> AtcaStatus {
1202        AtcaStatus::from(unsafe {
1203            let _guard = self
1204                .api_mutex
1205                .lock()
1206                .expect("Could not lock atcab API mutex");
1207            cryptoauthlib_sys::atcab_wakeup()
1208        })
1209    } // AteccDevice::wakeup()
1210
1211    /// invoke sleep on the CryptoAuth device
1212    fn sleep(&self) -> AtcaStatus {
1213        AtcaStatus::from(unsafe {
1214            let _guard = self
1215                .api_mutex
1216                .lock()
1217                .expect("Could not lock atcab API mutex");
1218            cryptoauthlib_sys::atcab_sleep()
1219        })
1220    } // AteccDevice::sleep()
1221
1222    /// ATECC device instance destructor
1223    // Requests:
1224    // 1. Internal rust-cryptoauthlib resource manager to release structure instance
1225    // 2. The structure itself to free the heap allocacted data
1226    // 3. CryptoAuthLib to release the ATECC device
1227    fn release(&self) -> AtcaStatus {
1228        if !ATECC_RESOURCE_MANAGER.lock().unwrap().release() {
1229            return AtcaStatus::AtcaBadParam;
1230        }
1231        AtcaStatus::from(unsafe {
1232            let _guard = self
1233                .api_mutex
1234                .lock()
1235                .expect("Could not lock atcab API mutex");
1236            // Restore iface_cfg from iface_cfg_ptr for the boxed structure to be released
1237            // at the end.
1238            Box::from_raw(self.iface_cfg_ptr.ptr);
1239            cryptoauthlib_sys::atcab_release()
1240        })
1241    } // AteccDevice::release()
1242
1243    //--------------------------------------------------
1244    //
1245    // Functions available only during testing
1246    //
1247    //--------------------------------------------------
1248
1249    /// A generic function that reads data from the chip
1250    fn read_zone(&self, zone: u8, slot: u16, block: u8, offset: u8, data: &mut [u8]) -> AtcaStatus {
1251        AtcaStatus::from(unsafe {
1252            let _guard = self
1253                .api_mutex
1254                .lock()
1255                .expect("Could not lock atcab API mutex");
1256            cryptoauthlib_sys::atcab_read_zone(
1257                zone,
1258                slot,
1259                block,
1260                offset,
1261                data.as_mut_ptr(),
1262                data.len() as u8,
1263            )
1264        })
1265    } // AteccDevice::read_zone()
1266
1267    /// Request ATECC to read and return own configuration zone.
1268    /// Note: this function returns raw data, function get_config(..) implements a more
1269    /// structured return value.
1270    fn read_config_zone(&self, config_data: &mut Vec<u8>) -> AtcaStatus {
1271        config_data.resize(self.get_config_buffer_size(), 0);
1272
1273        AtcaStatus::from(unsafe {
1274            let _guard = self
1275                .api_mutex
1276                .lock()
1277                .expect("Could not lock atcab API mutex");
1278            cryptoauthlib_sys::atcab_read_config_zone(config_data.as_mut_ptr())
1279        })
1280    } // AteccDevice::read_config_zone()
1281
1282    /// Compare internal config zone contents vs. config_data.
1283    /// Diagnostic function.
1284    #[allow(dead_code)]
1285    fn cmp_config_zone(&self, config_data: &mut [u8]) -> Result<bool, AtcaStatus> {
1286        let buffer_size = self.get_config_buffer_size();
1287        if config_data.len() != buffer_size {
1288            return Err(AtcaStatus::AtcaBadParam);
1289        }
1290        let mut same_config: bool = false;
1291        let result = AtcaStatus::from(unsafe {
1292            let _guard = self
1293                .api_mutex
1294                .lock()
1295                .expect("Could not lock atcab API mutex");
1296            cryptoauthlib_sys::atcab_cmp_config_zone(config_data.as_mut_ptr(), &mut same_config)
1297        });
1298        if AtcaStatus::AtcaSuccess == result {
1299            Ok(same_config)
1300        } else {
1301            Err(result)
1302        }
1303    } // AteccDevice::cmp_config_zone()
1304
1305    /// A function that takes an access key for securely reading or writing data
1306    /// that is located in a specific slot on an ATECCx08 chip.
1307    /// Data is not taken directly from the ATECCx08 chip, but from the AteccDevice structure
1308    fn get_access_key(&self, slot_id: u8, key: &mut Vec<u8>) -> AtcaStatus {
1309        if let Err(err) = self.access_key_setup_parameters_check(slot_id) {
1310            return err;
1311        };
1312
1313        key.resize(ATCA_KEY_SIZE, 0);
1314
1315        let access_keys_mutex = self
1316            .access_keys
1317            .lock()
1318            .expect("Could not lock 'access_keys' mutex");
1319
1320        let access_keys_obj = access_keys_mutex.try_borrow_mut();
1321
1322        match access_keys_obj {
1323            Err(_) => AtcaStatus::AtcaFuncFail,
1324            Ok(access_keys) => match access_keys.get(&slot_id) {
1325                None => AtcaStatus::AtcaInvalidId,
1326                Some(access_key) => {
1327                    *key = access_key.to_vec();
1328                    AtcaStatus::AtcaSuccess
1329                }
1330            },
1331        }
1332    } // AteccDevice::get_access_key()
1333
1334    /// A helper function that returns number of blocks and bytes of data
1335    /// available for a given socket
1336    fn get_slot_capacity(&self, slot_id: u8) -> AtcaSlotCapacity {
1337        let mut slot_capacity: AtcaSlotCapacity = Default::default();
1338        match slot_id {
1339            0x00..=0x07 => {
1340                slot_capacity.blocks = 2;
1341                slot_capacity.last_block_bytes = 4;
1342                slot_capacity.bytes = 36;
1343            }
1344            0x08 => {
1345                slot_capacity.blocks = 13;
1346                slot_capacity.last_block_bytes = 32;
1347                slot_capacity.bytes = 416;
1348            }
1349            0x09..=0x0F => {
1350                slot_capacity.blocks = 3;
1351                slot_capacity.last_block_bytes = 8;
1352                slot_capacity.bytes = 72;
1353            }
1354            _ => {}
1355        }
1356        slot_capacity
1357    } // AteccDevice::get_slot_capacity()
1358
1359    // ---------------------------------------------------------------
1360    // Private functions
1361    // ---------------------------------------------------------------
1362
1363    /// function that writes the key type 'P256EccKey' to the ATECCx08 chip
1364    fn write_p256_ecc_key_to_slot(&self, key_data: &[u8], slot: u16) -> AtcaStatus {
1365        match key_data.len() {
1366            ATCA_ATECC_PUB_KEY_SIZE => {
1367                if slot < ATCA_ATECC_MIN_SLOT_IDX_FOR_PUB_KEY as u16 {
1368                    return AtcaStatus::AtcaInvalidId;
1369                }
1370
1371                AtcaStatus::from(unsafe {
1372                    let _guard = self
1373                        .api_mutex
1374                        .lock()
1375                        .expect("Could not lock atcab API mutex");
1376                    cryptoauthlib_sys::atcab_write_pubkey(slot, key_data.as_ptr())
1377                })
1378            }
1379            ATCA_ATECC_PRIV_KEY_SIZE => {
1380                let mut temp_key: Vec<u8> = vec![0; 4];
1381                temp_key.extend_from_slice(key_data);
1382
1383                if let Some(write_key_idx) = self.get_write_key_idx(slot as u8) {
1384                    let mut write_key = vec![0; ATCA_KEY_SIZE];
1385                    let result = self.get_access_key(write_key_idx, &mut write_key);
1386
1387                    if AtcaStatus::AtcaSuccess == result {
1388                        let mut num_in: [u8; ATCA_NONCE_NUMIN_SIZE] = [0; ATCA_NONCE_NUMIN_SIZE];
1389
1390                        AtcaStatus::from(unsafe {
1391                            let _guard = self
1392                                .api_mutex
1393                                .lock()
1394                                .expect("Could not lock atcab API mutex");
1395                            cryptoauthlib_sys::atcab_priv_write(
1396                                slot,
1397                                temp_key.as_ptr(),
1398                                write_key_idx as u16,
1399                                write_key.as_ptr(),
1400                                num_in.as_mut_ptr(),
1401                            )
1402                        })
1403                    } else {
1404                        result
1405                    }
1406                } else {
1407                    AtcaStatus::AtcaBadParam
1408                }
1409            }
1410            _ => AtcaStatus::AtcaInvalidSize,
1411        }
1412    } // AteccDevice::write_p256_ecc_key_to_slot()
1413
1414    /// function that writes the key type 'Aes' to the ATECCx08 chip
1415    fn write_aes_key_to_slot(&self, key_data: &[u8], slot: u16) -> AtcaStatus {
1416        const BLOCK_IDX: u8 = 0;
1417
1418        if key_data.len() != ATCA_AES_KEY_SIZE {
1419            return AtcaStatus::AtcaInvalidSize;
1420        }
1421
1422        let mut temp_key: Vec<u8> = vec![0x00; ATCA_BLOCK_SIZE];
1423        temp_key[..key_data.len()].copy_from_slice(key_data);
1424
1425        if slot != ATCA_ATECC_TEMPKEY_KEYID {
1426            self.write_slot(slot, BLOCK_IDX, &temp_key)
1427        } else {
1428            self.nonce(NonceTarget::TempKey, &temp_key)
1429        }
1430    } // AteccDevice::write_aes_key_to_slot()
1431
1432    /// function that writes the key type 'ShaOrText' to the ATECCx08 chip
1433    fn write_sha_or_text_key_to_slot(&self, key_data: &[u8], slot: u16) -> AtcaStatus {
1434        if (ATCA_ATECC_TEMPKEY_KEYID == slot) && (key_data.len() <= ATCA_BLOCK_SIZE) {
1435            let mut temp_key: Vec<u8> = vec![0x00; ATCA_NONCE_SIZE];
1436            temp_key[..key_data.len()].copy_from_slice(key_data);
1437            return self.nonce(NonceTarget::TempKey, &temp_key);
1438        }
1439        if key_data.len() > self.get_slot_capacity(slot as u8).bytes as usize {
1440            return AtcaStatus::AtcaInvalidSize;
1441        }
1442
1443        let mut start_pos: usize = 0;
1444        let mut block: u8 = 0;
1445        let mut shift: usize = min(key_data.len(), ATCA_BLOCK_SIZE);
1446        let mut result: AtcaStatus = AtcaStatus::AtcaUnknown;
1447
1448        while shift > 0 {
1449            match shift {
1450                ATCA_BLOCK_SIZE => {
1451                    result = self.write_slot(slot, block, &key_data[start_pos..(start_pos + shift)])
1452                }
1453                _ => {
1454                    let mut buffer: [u8; ATCA_BLOCK_SIZE] = [0x00; ATCA_BLOCK_SIZE];
1455                    buffer[..shift].clone_from_slice(&key_data[start_pos..(start_pos + shift)]);
1456                    result = self.write_slot(slot, block, &buffer)
1457                }
1458            }
1459            if result != AtcaStatus::AtcaSuccess {
1460                return result;
1461            }
1462
1463            start_pos += shift;
1464            block += 1;
1465            let remaining_bytes = key_data.len() - start_pos;
1466            if remaining_bytes < ATCA_BLOCK_SIZE {
1467                shift = remaining_bytes
1468            }
1469        }
1470
1471        result
1472    } // AteccDevice::write_sha_or_text_key_to_slot()
1473
1474    /// Function that reads a key of the 'Aes' type from the indicated slot
1475    fn read_aes_key_from_slot(&self, slot_id: u8, key: &mut Vec<u8>) -> AtcaStatus {
1476        const BLOCK_IDX: u8 = 0;
1477
1478        if slot_id >= ATCA_ATECC_SLOTS_COUNT {
1479            return AtcaStatus::AtcaInvalidId;
1480        };
1481
1482        let slot_data = self.slots[slot_id as usize].config;
1483        if KeyType::Aes != slot_data.key_type {
1484            return AtcaStatus::AtcaBadParam;
1485        }
1486
1487        let mut data_block: [u8; ATCA_BLOCK_SIZE] = [0; ATCA_BLOCK_SIZE];
1488
1489        let result = self.read_slot(slot_id as u16, BLOCK_IDX, &mut data_block);
1490
1491        if AtcaStatus::AtcaSuccess == result {
1492            *key = data_block[..ATCA_AES_KEY_SIZE].to_vec()
1493        }
1494
1495        result
1496    } // AteccDevice::read_aes_key_from_slot()
1497
1498    /// Function that reads a key of the 'ShaOrText' type from the indicated slot
1499    fn read_sha_or_text_key_from_slot(&self, slot_id: u8, data: &mut [u8]) -> AtcaStatus {
1500        if slot_id >= ATCA_ATECC_SLOTS_COUNT {
1501            return AtcaStatus::AtcaInvalidId;
1502        };
1503
1504        let slot_data = self.slots[slot_id as usize].config;
1505        if KeyType::ShaOrText != slot_data.key_type {
1506            return AtcaStatus::AtcaBadParam;
1507        }
1508        if data.len() > self.get_slot_capacity(slot_id).bytes as usize {
1509            return AtcaStatus::AtcaInvalidSize;
1510        }
1511
1512        let mut data_block: [u8; ATCA_BLOCK_SIZE] = [0x00; ATCA_BLOCK_SIZE];
1513        let mut result: AtcaStatus = AtcaStatus::AtcaUnknown;
1514
1515        let modulo = data.len() % ATCA_BLOCK_SIZE;
1516        let runs = (data.len() / ATCA_BLOCK_SIZE) + (0 != modulo) as usize - 1;
1517
1518        for idx in 0..=runs {
1519            result = self.read_slot(slot_id as u16, idx as u8, &mut data_block);
1520            if AtcaStatus::AtcaSuccess != result {
1521                break;
1522            }
1523
1524            let start_pos: usize = idx * ATCA_BLOCK_SIZE;
1525            let shift: usize = match (runs == idx) && (0 != modulo) {
1526                false => ATCA_BLOCK_SIZE,
1527                _ => modulo,
1528            };
1529            data[start_pos..(start_pos + shift)].copy_from_slice(&data_block[..shift])
1530        }
1531
1532        result
1533    } // AteccDevice::read_sha_or_text_key_from_slot()
1534
1535    /// A helper function for the gen_key() and import_key() methods,
1536    /// pre-checking combinations of input parameters
1537    fn encryption_key_setup_parameters_check(
1538        &self,
1539        key_type: KeyType,
1540        slot_id: u8,
1541    ) -> Result<(), AtcaStatus> {
1542        if slot_id > ATCA_ATECC_SLOTS_COUNT {
1543            return Err(AtcaStatus::AtcaInvalidId);
1544        }
1545        if ((key_type == KeyType::Aes) && !self.chip_options.aes_enabled)
1546            || ((slot_id < ATCA_ATECC_SLOTS_COUNT)
1547                && (key_type != self.slots[slot_id as usize].config.key_type))
1548        {
1549            return Err(AtcaStatus::AtcaBadParam);
1550        }
1551        Ok(())
1552    } // AteccDevice::encryption_key_setup_parameters_check()
1553
1554    /// A helper function for the add_access_key() and get_access_key()
1555    /// methods, pre-checking combinations of input parameters
1556    fn access_key_setup_parameters_check(&self, slot_id: u8) -> Result<(), AtcaStatus> {
1557        if (slot_id > ATCA_ATECC_SLOTS_COUNT) ||
1558            // special condition for the key encrypting IO transmission between host and cryptochip 
1559            ((slot_id == ATCA_ATECC_SLOTS_COUNT) &&
1560            (self.get_device_type() != AtcaDeviceType::ATECC608A))
1561        {
1562            return Err(AtcaStatus::AtcaInvalidId);
1563        }
1564        Ok(())
1565    } // AteccDevice::access_key_setup_parameters_check()
1566
1567    /// A helper function that returns socket index containing encryption key
1568    /// required for operation of encrypted write to the given socket
1569    /// or value 'None' when such an operation cannot be performed for the given socket
1570    fn get_write_key_idx(&self, slot_id: u8) -> Option<u8> {
1571        if slot_id < ATCA_ATECC_SLOTS_COUNT {
1572            let slot_data = self.slots[slot_id as usize].config;
1573            if slot_data.write_config == WriteConfig::Encrypt {
1574                Some(slot_data.write_key)
1575            } else {
1576                None
1577            }
1578        } else {
1579            None
1580        }
1581    } // AteccDevice::get_write_key_idx()
1582
1583    /// A helper function that returns socket index containing encryption key
1584    /// required for operation of encrypted reading from the given socket
1585    /// or value 'None' when such an operation cannot be performed for the given socket
1586    fn get_read_key_idx(&self, slot_id: u8) -> Option<u8> {
1587        if slot_id < ATCA_ATECC_SLOTS_COUNT {
1588            let slot_data = self.slots[slot_id as usize].config;
1589            if slot_data.read_key.encrypt_read
1590                && slot_data.is_secret
1591                && !slot_data.ecc_key_attr.is_private
1592            {
1593                Some(slot_data.read_key.slot_number)
1594            } else {
1595                None
1596            }
1597        } else {
1598            None
1599        }
1600    } // AteccDevice::get_read_key_idx()
1601
1602    /// A helper function that checks locking of configuration and data zones on the ATECC chip.
1603    #[inline]
1604    fn check_that_configuration_is_not_locked(&self, both: bool) -> bool {
1605        (!self.data_zone_locked && both) || !self.config_zone_locked
1606    } // AteccDevice::check_that_configuration_is_not_locked()
1607
1608    /// A function that reads the configuration data to check if the specified zone is locked
1609    fn is_locked(&self, config_data: &[u8], zone: u8) -> bool {
1610        const LOCK_DATA_BYTE_INDEX: usize = 86;
1611        const LOCK_CONFIG_BYTE_INDEX: usize = LOCK_DATA_BYTE_INDEX + 1;
1612
1613        match zone {
1614            ATCA_LOCK_ZONE_CONFIG => config_data[LOCK_CONFIG_BYTE_INDEX] != 0x55,
1615            ATCA_LOCK_ZONE_DATA => config_data[LOCK_DATA_BYTE_INDEX] != 0x55,
1616            _ => false,
1617        }
1618    } // AteccDevice::is_locked()
1619
1620    fn get_chip_options_data_from_chip_config_data(&mut self, config_data: &[u8]) {
1621        const INDEX_OF_AES_BYTE: usize = 13;
1622        const KDF_IV_LOC_BYTE: usize = 72;
1623        const KDF_IV_STR_FIRST_BYTE: usize = KDF_IV_LOC_BYTE + 1;
1624        const KDF_IV_STR_SECOND_BYTE: usize = KDF_IV_STR_FIRST_BYTE + 1;
1625        const CHIP_OPTIONS_FIRST_BYTE: usize = 90;
1626        const CHIP_OPTIONS_SECOND_BYTE: usize = CHIP_OPTIONS_FIRST_BYTE + 1;
1627        const IO_KEY_EN_POS: u8 = 1;
1628        const KDF_AES_EN_POS: u8 = 2;
1629
1630        self.chip_options = ChipOptions {
1631            io_key_enabled: atcab_get_bit_value(
1632                config_data[CHIP_OPTIONS_FIRST_BYTE],
1633                IO_KEY_EN_POS,
1634            ),
1635            io_key_in_slot: (config_data[CHIP_OPTIONS_SECOND_BYTE] >> 4) & 0b00001111,
1636            kdf_aes_enabled: atcab_get_bit_value(
1637                config_data[CHIP_OPTIONS_FIRST_BYTE],
1638                KDF_AES_EN_POS,
1639            ),
1640            ecdh_output_protection: (config_data[CHIP_OPTIONS_SECOND_BYTE] & 0b00000011).into(),
1641            kdf_output_protection: ((config_data[CHIP_OPTIONS_SECOND_BYTE] >> 2) & 0b00000011)
1642                .into(),
1643            aes_enabled: (config_data[INDEX_OF_AES_BYTE] & 0x01) != 0x00,
1644            kdf_iv_enabled: config_data[KDF_IV_LOC_BYTE] != 0xF0,
1645            kdf_iv_location_at: config_data[KDF_IV_LOC_BYTE] as usize,
1646            kdf_iv_str: [
1647                config_data[KDF_IV_STR_FIRST_BYTE],
1648                config_data[KDF_IV_STR_SECOND_BYTE],
1649            ],
1650        }
1651    } // AteccDevice::get_chip_options_data_from_chip_config_data()
1652
1653    /// Request ATECC to read the configuration zone data and return it as a vector
1654    fn get_config_from_chip(&self, config_data: &mut Vec<u8>) -> AtcaStatus {
1655        let result = self.read_config_zone(config_data);
1656        if AtcaStatus::AtcaSuccess != result {
1657            return result;
1658        }
1659        if config_data.len() != self.get_config_buffer_size() {
1660            return AtcaStatus::AtcaBadParam;
1661        }
1662        AtcaStatus::AtcaSuccess
1663    } // AteccDevice::get_config_from_chip()
1664
1665    /// Function returns size (in bytes) of the chip configuration data
1666    fn get_config_buffer_size(&self) -> usize {
1667        let device_type = self.get_device_type();
1668        match device_type {
1669            AtcaDeviceType::ATECC508A | AtcaDeviceType::ATECC608A | AtcaDeviceType::ATECC108A => {
1670                ATCA_ATECC_CONFIG_BUFFER_SIZE
1671            }
1672            _ => ATCA_ATSHA_CONFIG_BUFFER_SIZE,
1673        }
1674    } // AteccDevice::get_config_buffer_size()
1675
1676    /// get 9 byte serial number of the device from the config data
1677    fn get_serial_number(&self, config_data: &[u8]) -> [u8; ATCA_SERIAL_NUM_SIZE] {
1678        let mut serial_number: [u8; ATCA_SERIAL_NUM_SIZE] = [0; ATCA_SERIAL_NUM_SIZE];
1679
1680        serial_number[..4].clone_from_slice(&config_data[..4]);
1681        serial_number[4..].clone_from_slice(&config_data[8..=12]);
1682
1683        serial_number
1684    } // AteccDevice::get_serial_number()
1685
1686    /// A generic function that reads data from a specific slot on a chip
1687    fn read_slot(&self, slot: u16, block: u8, data: &mut [u8]) -> AtcaStatus {
1688        let slot_data = self.slots[slot as usize].config;
1689        let result: AtcaStatus;
1690
1691        if slot_data.is_secret && slot_data.read_key.encrypt_read {
1692            let num_in: [u8; ATCA_NONCE_NUMIN_SIZE] = [0; ATCA_NONCE_NUMIN_SIZE];
1693            result = self.read_slot_with_encryption(slot as u16, block, data, &num_in)
1694        } else {
1695            const OFFSET: u8 = 0;
1696            result = self.read_zone(ATCA_ZONE_DATA, slot as u16, block, OFFSET, data)
1697        }
1698
1699        result
1700    } // AteccDevice::read_slot()
1701
1702    /// A generic function that reads encrypted data from a specific slot on a chip
1703    fn read_slot_with_encryption(
1704        &self,
1705        slot: u16,
1706        block: u8,
1707        data: &mut [u8],
1708        num_in: &[u8],
1709    ) -> AtcaStatus {
1710        if (data.len() != ATCA_BLOCK_SIZE) || (num_in.len() != ATCA_NONCE_NUMIN_SIZE) {
1711            return AtcaStatus::AtcaInvalidSize;
1712        }
1713        if (slot >= ATCA_ATECC_SLOTS_COUNT as u16)
1714            || (block >= self.get_slot_capacity(slot as u8).blocks)
1715        {
1716            return AtcaStatus::AtcaInvalidId;
1717        }
1718
1719        if let Some(read_key_idx) = self.get_read_key_idx(slot as u8) {
1720            let mut read_key = vec![0; ATCA_KEY_SIZE];
1721            let result = self.get_access_key(read_key_idx, &mut read_key);
1722
1723            if AtcaStatus::AtcaSuccess == result {
1724                AtcaStatus::from(unsafe {
1725                    let _guard = self
1726                        .api_mutex
1727                        .lock()
1728                        .expect("Could not lock atcab API mutex");
1729                    cryptoauthlib_sys::atcab_read_enc(
1730                        slot,
1731                        block,
1732                        data.as_mut_ptr(),
1733                        read_key.as_ptr(),
1734                        read_key_idx as u16,
1735                        num_in.as_ptr(),
1736                    )
1737                })
1738            } else {
1739                result
1740            }
1741        } else {
1742            AtcaStatus::AtcaBadParam
1743        }
1744    } // AteccDevice::read_slot_with_encryption()
1745
1746    /// Generic function that writes data to a specific slot on a chip
1747    fn write_slot(&self, slot: u16, block: u8, data: &[u8]) -> AtcaStatus {
1748        const OFFSET: u8 = 0;
1749
1750        match self.slots[slot as usize].config.write_config {
1751            WriteConfig::Always => self.write_zone(ATCA_ZONE_DATA, slot, block, OFFSET, data),
1752            WriteConfig::Encrypt => {
1753                let num_in: [u8; ATCA_NONCE_NUMIN_SIZE] = [0; ATCA_NONCE_NUMIN_SIZE];
1754                self.write_slot_with_encryption(slot, block, data, &num_in)
1755            }
1756            _ => AtcaStatus::AtcaBadParam,
1757        }
1758    } // AteccDevice::write_slot()
1759
1760    /// Generic function that writes data to the chip
1761    fn write_zone(&self, zone: u8, slot: u16, block: u8, offset: u8, data: &[u8]) -> AtcaStatus {
1762        AtcaStatus::from(unsafe {
1763            let _guard = self
1764                .api_mutex
1765                .lock()
1766                .expect("Could not lock atcab API mutex");
1767            cryptoauthlib_sys::atcab_write_zone(
1768                zone,
1769                slot,
1770                block,
1771                offset,
1772                data.as_ptr(),
1773                data.len() as u8,
1774            )
1775        })
1776    } // AteccDevice::write_zone()
1777
1778    /// Generic function that writes encrypted data to the chip
1779    fn write_slot_with_encryption(
1780        &self,
1781        slot: u16,
1782        block: u8,
1783        data: &[u8],
1784        num_in: &[u8],
1785    ) -> AtcaStatus {
1786        if (data.len() != ATCA_BLOCK_SIZE) || (num_in.len() != ATCA_NONCE_NUMIN_SIZE) {
1787            return AtcaStatus::AtcaInvalidSize;
1788        }
1789        if (slot >= ATCA_ATECC_SLOTS_COUNT as u16)
1790            || (block >= self.get_slot_capacity(slot as u8).blocks)
1791        {
1792            return AtcaStatus::AtcaInvalidId;
1793        }
1794
1795        if let Some(write_key_idx) = self.get_write_key_idx(slot as u8) {
1796            let mut write_key = vec![0; ATCA_KEY_SIZE];
1797            let result = self.get_access_key(write_key_idx, &mut write_key);
1798
1799            if AtcaStatus::AtcaSuccess == result {
1800                AtcaStatus::from(unsafe {
1801                    let _guard = self
1802                        .api_mutex
1803                        .lock()
1804                        .expect("Could not lock atcab API mutex");
1805                    cryptoauthlib_sys::atcab_write_enc(
1806                        slot,
1807                        block,
1808                        data.as_ptr(),
1809                        write_key.as_ptr(),
1810                        write_key_idx as u16,
1811                        num_in.as_ptr(),
1812                    )
1813                })
1814            } else {
1815                result
1816            }
1817        } else {
1818            AtcaStatus::AtcaBadParam
1819        }
1820    } // AteccDevice::write_slot_with_encryption()
1821}
1822
1823// ---------------------------------------------------------------
1824// Free Auxiliary Functions
1825// ---------------------------------------------------------------
1826
1827fn atcab_get_bit_value(byte: u8, bit_pos: u8) -> bool {
1828    if bit_pos < 8 {
1829        ((byte >> bit_pos) & 1) != 0
1830    } else {
1831        false
1832    }
1833}
1834
1835fn atcab_get_write_config(data: u8) -> WriteConfig {
1836    match data & 0b00001111 {
1837        0 => WriteConfig::Always,
1838        1 => WriteConfig::PubInvalid,
1839        2..=3 => WriteConfig::Never,
1840        4..=7 => WriteConfig::Encrypt,
1841        8..=11 => WriteConfig::Never,
1842        _ => WriteConfig::Encrypt,
1843    }
1844}
1845
1846fn atcab_get_key_type(data: u8) -> KeyType {
1847    match data & 0b00000111 {
1848        4 => KeyType::P256EccKey,
1849        6 => KeyType::Aes,
1850        7 => KeyType::ShaOrText,
1851        _ => KeyType::Rfu,
1852    }
1853}
1854
1855pub fn atcab_get_slots_config_from_config_data(config_data: &[u8], atca_slots: &mut Vec<AtcaSlot>) {
1856    const IDX_SLOT_LOCKED: usize = 88;
1857    const IDX_SLOT_CONFIG: usize = 20;
1858    const IDX_KEY_CONFIG: usize = 96;
1859    for idx in 0..ATCA_ATECC_SLOTS_COUNT {
1860        let slot_cfg_pos = IDX_SLOT_CONFIG + (idx * 2) as usize;
1861        let key_cfg_pos = IDX_KEY_CONFIG + (idx * 2) as usize;
1862        let read_key_struct = ReadKey {
1863            encrypt_read: atcab_get_bit_value(config_data[slot_cfg_pos], 6),
1864            slot_number: config_data[slot_cfg_pos] & 0b00001111,
1865        };
1866        let ecc_key_attr_struct = EccKeyAttr {
1867            is_private: atcab_get_bit_value(config_data[key_cfg_pos], 0),
1868            ext_sign: atcab_get_bit_value(config_data[slot_cfg_pos], 0),
1869            int_sign: atcab_get_bit_value(config_data[slot_cfg_pos], 1),
1870            ecdh_operation: atcab_get_bit_value(config_data[slot_cfg_pos], 2),
1871            ecdh_secret_out: atcab_get_bit_value(config_data[slot_cfg_pos], 3),
1872        };
1873        let config_struct = SlotConfig {
1874            write_config: atcab_get_write_config(config_data[slot_cfg_pos + 1] >> 4),
1875            key_type: atcab_get_key_type(config_data[key_cfg_pos] >> 2),
1876            read_key: read_key_struct,
1877            ecc_key_attr: ecc_key_attr_struct,
1878            x509id: (config_data[key_cfg_pos + 1] >> 6) & 0b00000011,
1879            auth_key: config_data[key_cfg_pos + 1] & 0b00001111,
1880            write_key: config_data[slot_cfg_pos + 1] & 0b00001111,
1881            is_secret: atcab_get_bit_value(config_data[slot_cfg_pos], 7),
1882            limited_use: atcab_get_bit_value(config_data[slot_cfg_pos], 5),
1883            no_mac: atcab_get_bit_value(config_data[slot_cfg_pos], 4),
1884            persistent_disable: atcab_get_bit_value(config_data[key_cfg_pos + 1], 4),
1885            req_auth: atcab_get_bit_value(config_data[key_cfg_pos], 7),
1886            req_random: atcab_get_bit_value(config_data[key_cfg_pos], 6),
1887            lockable: atcab_get_bit_value(config_data[key_cfg_pos], 5),
1888            pub_info: atcab_get_bit_value(config_data[key_cfg_pos], 1),
1889        };
1890        let slot = AtcaSlot {
1891            id: idx,
1892            is_locked: {
1893                let index = IDX_SLOT_LOCKED + (idx / 8) as usize;
1894                let bit_position = idx % 8;
1895                let bit_value = (config_data[index] >> bit_position) & 1;
1896                bit_value != 1
1897            },
1898            config: config_struct,
1899        };
1900        atca_slots.push(slot);
1901    }
1902}