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