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
#![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;

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 AEAD (authenticated encryption with associated data) modes
    fn aead_encrypt(
        &self,
        algorithm: AeadAlgorithm,
        slot_id: u8,
        data: &mut [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 [u8],
    ) -> Result<bool, 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 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;
    /// 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 Vec<u8>,
        len: 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;
}

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
    }
}