rust_cryptoauthlib/lib.rs
1#![allow(clippy::upper_case_acronyms)]
2
3#[macro_use]
4extern crate strum_macros; // 0.10.0
5#[macro_use]
6extern crate lazy_static;
7
8include!("types.rs");
9include!("constants.rs");
10
11mod atca_iface_cfg;
12mod hw_impl;
13mod sw_impl;
14#[cfg(test)]
15mod unit_tests;
16
17#[cfg(test)]
18use cryptoauthlib_sys::atca_aes_cbc_ctx_t;
19
20pub trait AteccDeviceTrait {
21 /// Request ATECC to generate a vector of random bytes
22 fn random(&self, rand_out: &mut Vec<u8>) -> AtcaStatus;
23 /// Request ATECC to compute a message hash (SHA256)
24 fn sha(&self, message: Vec<u8>, digest: &mut Vec<u8>) -> AtcaStatus;
25 /// Execute a Nonce command in pass-through mode to load one of the
26 /// device's internal buffers with a fixed value.
27 /// For the ATECC608A, available targets are TempKey (32 or 64 bytes), Message
28 /// Digest Buffer (32 or 64 bytes), or the Alternate Key Buffer (32 bytes). For
29 /// all other devices, only TempKey (32 bytes) is available.
30 fn nonce(&self, target: NonceTarget, data: &[u8]) -> AtcaStatus;
31 /// Execute a Nonce command to generate a random nonce combining a host
32 /// nonce and a device random number.
33 fn nonce_rand(&self, host_nonce: &[u8], rand_out: &mut Vec<u8>) -> AtcaStatus;
34 /// Request ATECC to generate a cryptographic key
35 fn gen_key(&self, key_type: KeyType, slot_id: u8) -> AtcaStatus;
36 /// Request ATECC to import a cryptographic key
37 fn import_key(&self, key_type: KeyType, key_data: &[u8], slot_id: u8) -> AtcaStatus;
38 /// Request ATECC to export a cryptographic key.
39 /// For cryptographic security reasons,
40 /// with KeyType = P256EccKey this function exports only public key
41 fn export_key(&self, key_type: KeyType, key_data: &mut Vec<u8>, slot_id: u8) -> AtcaStatus;
42 /// Depending on the socket configuration, this function calculates
43 /// public key based on an existing private key in the socket
44 /// or exports the public key directly
45 fn get_public_key(&self, slot_id: u8, public_key: &mut Vec<u8>) -> AtcaStatus;
46 /// Request ATECC to generate an ECDSA signature
47 fn sign_hash(&self, mode: SignMode, slot_id: u8, signature: &mut Vec<u8>) -> AtcaStatus;
48 /// Request ATECC to verify ECDSA signature
49 fn verify_hash(
50 &self,
51 mode: VerifyMode,
52 hash: &[u8],
53 signature: &[u8],
54 ) -> Result<bool, AtcaStatus>;
55 /// Data encryption function in AES unauthenticated cipher alhorithms modes
56 fn cipher_encrypt(
57 &self,
58 algorithm: CipherAlgorithm,
59 slot_id: u8,
60 data: &mut Vec<u8>,
61 ) -> AtcaStatus;
62 /// Data decryption function in AES unauthenticated cipher alhorithms modes
63 fn cipher_decrypt(
64 &self,
65 algorithm: CipherAlgorithm,
66 slot_id: u8,
67 data: &mut Vec<u8>,
68 ) -> AtcaStatus;
69 /// Data encryption function in AES AEAD (authenticated encryption with associated data) modes
70 fn aead_encrypt(
71 &self,
72 algorithm: AeadAlgorithm,
73 slot_id: u8,
74 data: &mut Vec<u8>,
75 ) -> Result<Vec<u8>, AtcaStatus>;
76 /// Data decryption function in AES AEAD (authenticated encryption with associated data) modes
77 fn aead_decrypt(
78 &self,
79 algorithm: AeadAlgorithm,
80 slot_id: u8,
81 data: &mut Vec<u8>,
82 ) -> Result<bool, AtcaStatus>;
83 /// A function that calculates the MAC (Message Authentication Code) value for a message
84 fn mac_compute(
85 &self,
86 algorithm: MacAlgorithm,
87 slot_id: u8,
88 data: &[u8],
89 ) -> Result<Vec<u8>, AtcaStatus>;
90 /// A function that verifies the value of MAC (Message Authentication Code) for a message
91 fn mac_verify(
92 &self,
93 algorithm: MacAlgorithm,
94 slot_id: u8,
95 data: &[u8],
96 ) -> Result<bool, AtcaStatus>;
97 /// KDF command function, which derives a new key in PRF, AES, or HKDF modes.
98 /// According to RFC-5869, the HKDF mode consists of two steps, extract and expand.
99 /// The "HMAC-Hash" base operation is implemented in the ATECC608x chip,
100 /// so to perform full HKDF operation, proceed as described in chapter 2 of RFC-5869,
101 /// first calculate PRK = HMAC-Hash(salt, IKM) and then use obtained PRK
102 /// to obtain the resulting OKM, again using the same "HMAC-Hash" function,
103 /// i.e. this "fn kdf", according to the algorithm from section 2.3 of RFC-5869.
104 fn kdf(
105 &self,
106 algorithm: KdfAlgorithm,
107 parameters: KdfParams,
108 message: Option<&[u8]>,
109 message_length: usize,
110 ) -> Result<KdfResult, AtcaStatus>;
111 /// Function for generating premaster secret key using ECDH
112 fn ecdh(
113 &self,
114 parameters: EcdhParams,
115 peer_public_key: &[u8],
116 ) -> Result<EcdhResult, AtcaStatus>;
117 /// Execute this command prevents future modifications of the Configuration zone.
118 /// This command fails if the designated area is already locked.
119 fn lock_config_zone(&self) -> AtcaStatus;
120 /// Execute this command prevents future modifications of the Data and OTP zones.
121 /// This command fails if the designated area is already locked.
122 fn lock_data_zone(&self) -> AtcaStatus;
123 /// Lock an individual slot in the data zone on an ATECC device. Not available for ATSHA devices.
124 /// Slot must be configured to be slot lockable slots[slot_idx].config.lockable = true.
125 /// This command fails if the designated area is already locked.
126 fn lock_slot(&self, slot_id: u8) -> AtcaStatus;
127 /// Function for uploading configuration to the chip.
128 /// First 16 bytes of data are skipped as they are not writable. LockValue and LockConfig
129 /// are also skipped and can only be changed via the Lock command.
130 /// This command may fail if UserExtra and/or Selector bytes have already been set to non-zero values.
131 fn load_config_into_chip(&self, config: &[u8]) -> AtcaStatus;
132 /// Request ATECC to return own device type
133 fn get_device_type(&self) -> AtcaDeviceType;
134 /// Request ATECC to check if its configuration is locked.
135 /// If true, a chip can be used for cryptographic operations
136 fn is_configuration_locked(&self) -> bool;
137 /// Request ATECC to check if its Data Zone is locked.
138 /// If true, a chip can be used for cryptographic operations
139 fn is_data_zone_locked(&self) -> bool;
140 /// Returns a structure containing configuration data read from ATECC
141 /// during initialization of the AteccDevice object.
142 fn get_config(&self, atca_slots: &mut Vec<AtcaSlot>) -> AtcaStatus;
143 /// Command accesses some static or dynamic information from the ATECC chip
144 fn info_cmd(&self, _command: InfoCmdType) -> Result<Vec<u8>, AtcaStatus>;
145 /// A function that adds an encryption key for securely reading or writing data
146 /// that is located in a specific slot on the ATECCx08 chip.
147 /// Data is not written to the ATECCx08 chip, but to the AteccDevice structure
148 fn add_access_key(&self, slot_id: u8, encryption_key: &[u8]) -> AtcaStatus;
149 /// A function that deletes all encryption keys for secure read or write operations
150 /// performed by the ATECCx08 chip
151 fn flush_access_keys(&self) -> AtcaStatus;
152 /// Get serial number of the ATECC device
153 fn get_serial_number(&self) -> [u8; ATCA_SERIAL_NUM_SIZE];
154 /// Checks if the chip supports AES encryption.
155 /// (only relevant for the ATECC608x chip)
156 fn is_aes_enabled(&self) -> bool;
157 /// Checks if the chip supports AES for KDF operations
158 /// (only relevant for the ATECC608x chip)
159 fn is_kdf_aes_enabled(&self) -> bool;
160 /// Checks if the special KDF Initialization Vector function is enabled
161 /// (only relevant for the ATECC608x chip)
162 fn is_kdf_iv_enabled(&self) -> bool;
163 /// Checks whether transmission between chip and host is to be encrypted
164 /// (IO encryption is only possible for ATECC608x chip)
165 fn is_io_protection_key_enabled(&self) -> bool;
166 /// Function that reads the read security settings of the ECDH function from chip
167 /// (only relevant for the ATECC608x chip)
168 fn get_ecdh_output_protection_state(&self) -> OutputProtectionState;
169 /// Function that reads the read security settings of the KDF function from chip
170 /// (only relevant for the ATECC608x chip)
171 fn get_kdf_output_protection_state(&self) -> OutputProtectionState;
172 /// wakeup the CryptoAuth device
173 fn wakeup(&self) -> AtcaStatus;
174 /// invoke sleep on the CryptoAuth device
175 fn sleep(&self) -> AtcaStatus;
176 /// ATECC device instance destructor
177 fn release(&self) -> AtcaStatus;
178
179 //--------------------------------------------------
180 //
181 // Functions available only during testing
182 //
183 //--------------------------------------------------
184
185 /// A generic function that reads data from the chip
186 #[cfg(test)]
187 fn read_zone(&self, zone: u8, slot: u16, block: u8, offset: u8, data: &mut [u8]) -> AtcaStatus;
188 /// Request ATECC to read and return own configuration zone.
189 /// Note: this function returns raw data, function get_config(..) implements a more
190 /// structured return value.
191 #[cfg(test)]
192 fn read_config_zone(&self, config_data: &mut Vec<u8>) -> AtcaStatus;
193 /// Compare internal config zone contents vs. config_data.
194 /// Diagnostic function.
195 #[cfg(test)]
196 fn cmp_config_zone(&self, config_data: &mut [u8]) -> Result<bool, AtcaStatus>;
197 /// A function that takes an encryption key for securely reading or writing data
198 /// that is located in a specific slot on an ATECCx08 chip.
199 /// Data is not taken directly from the ATECCx08 chip, but from the AteccDevice structure
200 #[cfg(test)]
201 fn get_access_key(&self, slot_id: u8, key: &mut Vec<u8>) -> AtcaStatus;
202 /// Perform an AES-128 encrypt operation with a key in the device
203 #[cfg(test)]
204 fn aes_encrypt_block(
205 &self,
206 key_id: u16,
207 key_block: u8,
208 input: &[u8],
209 ) -> Result<[u8; ATCA_AES_DATA_SIZE], AtcaStatus>;
210 /// Perform an AES-128 decrypt operation with a key in the device
211 #[cfg(test)]
212 fn aes_decrypt_block(
213 &self,
214 key_id: u16,
215 key_block: u8,
216 input: &[u8],
217 ) -> Result<[u8; ATCA_AES_DATA_SIZE], AtcaStatus>;
218 /// Initialize context for AES CTR operation with an existing IV, which
219 /// is common when start a decrypt operation
220 #[cfg(test)]
221 fn aes_ctr_init(
222 &self,
223 slot_id: u8,
224 counter_size: u8,
225 iv: &[u8],
226 ) -> Result<atca_aes_ctr_ctx_t, AtcaStatus>;
227 /// Increments AES CTR counter value
228 #[cfg(test)]
229 fn aes_ctr_increment(&self, ctx: atca_aes_ctr_ctx_t) -> Result<atca_aes_ctr_ctx_t, AtcaStatus>;
230 /// Initialize context for AES CBC operation.
231 #[cfg(test)]
232 fn aes_cbc_init(&self, slot_id: u8, iv: &[u8]) -> Result<atca_aes_cbc_ctx_t, AtcaStatus>;
233 /// A helper function that returns number of blocks and bytes of data
234 /// available for a given socket
235 #[cfg(test)]
236 fn get_slot_capacity(&self, slot_id: u8) -> AtcaSlotCapacity;
237}
238
239pub type AteccDevice = Box<dyn AteccDeviceTrait + Send + Sync>;
240
241pub fn setup_atecc_device(r_iface_cfg: AtcaIfaceCfg) -> Result<AteccDevice, String> {
242 match r_iface_cfg.devtype {
243 AtcaDeviceType::AtcaTestDevSuccess
244 | AtcaDeviceType::AtcaTestDevFail
245 | AtcaDeviceType::AtcaTestDevFailUnimplemented => {
246 match sw_impl::AteccDevice::new(r_iface_cfg) {
247 Ok(x) => Ok(Box::new(x)),
248 Err(err) => Err(err),
249 }
250 }
251 AtcaDeviceType::AtcaDevUnknown => {
252 Err(String::from("Attempting to create an unknown device type"))
253 }
254 _ => match hw_impl::AteccDevice::new(r_iface_cfg) {
255 Ok(x) => Ok(Box::new(x)),
256 Err(err) => Err(err),
257 },
258 }
259}
260
261impl AtcaSlot {
262 pub fn is_valid(self) -> bool {
263 // As long as exclusive range is experimental, this should work.
264 // self.id is always greater than 0
265 self.id < ATCA_ATECC_SLOTS_COUNT
266 }
267}