rust_cryptoauthlib/hw_impl/
ecdh.rs

1use std::ptr;
2
3use super::{
4    AtcaDeviceType, AtcaStatus, AteccDevice, EcdhParams, EcdhResult, EcdhSource, EcdhTarget,
5    OutputProtectionState, WriteConfig,
6};
7
8use super::{
9    ATCA_ATECC_PUB_KEY_SIZE, ATCA_ATECC_SLOTS_COUNT, ATCA_ECDH_KEY_SIZE, ATCA_SHA2_256_DIGEST_SIZE,
10};
11
12impl AteccDevice {
13    /// Function for generating premaster secret key using ECDH
14    pub(crate) fn ecdh(
15        &self,
16        parameters: EcdhParams,
17        peer_public_key: &[u8],
18    ) -> Result<EcdhResult, AtcaStatus> {
19        if self.check_that_configuration_is_not_locked(true) {
20            return Err(AtcaStatus::AtcaNotLocked);
21        }
22
23        let key_id: u16 = self.parse_ecdh_input_parameters(&parameters, peer_public_key.len())?;
24
25        let mode: u8 = parameters.key_source.clone() as u8
26            | parameters.out_target.clone() as u8
27            | ((parameters.out_encrypt as u8) << 0x01);
28
29        let mut out_data: Vec<u8> = vec![0x00; ATCA_ECDH_KEY_SIZE];
30        let out_data_ptr: *mut u8 = match parameters.out_target {
31            EcdhTarget::Output => out_data.as_mut_ptr(),
32            EcdhTarget::Compatibility => match self.slots[key_id as usize]
33                .config
34                .ecc_key_attr
35                .ecdh_secret_out
36            {
37                false => out_data.as_mut_ptr(),
38                _ => ptr::null_mut(),
39            },
40            _ => ptr::null_mut(),
41        };
42
43        let mut out_nonce: Vec<u8> = vec![0x00; ATCA_ECDH_KEY_SIZE];
44        let out_nonce_ptr: *mut u8 = match parameters.out_target {
45            EcdhTarget::Output => out_nonce.as_mut_ptr(),
46            _ => ptr::null_mut(),
47        };
48
49        let result = AtcaStatus::from(unsafe {
50            let _guard = self
51                .api_mutex
52                .lock()
53                .expect("Could not lock atcab API mutex");
54            cryptoauthlib_sys::atcab_ecdh_base(
55                mode,
56                key_id,
57                peer_public_key.as_ptr(),
58                out_data_ptr,
59                out_nonce_ptr,
60            )
61        });
62
63        match result {
64            AtcaStatus::AtcaSuccess => {
65                let output = EcdhResult {
66                    pms: if out_data_ptr.is_null() {
67                        None
68                    } else {
69                        out_data.resize(ATCA_ECDH_KEY_SIZE, 0x00);
70                        Some(out_data)
71                    },
72                    out_nonce: if out_nonce_ptr.is_null() {
73                        None
74                    } else {
75                        out_nonce.resize(ATCA_SHA2_256_DIGEST_SIZE, 0x00);
76                        Some(out_nonce)
77                    },
78                };
79                Ok(output)
80            }
81            _ => Err(result),
82        }
83    } // AteccDevice::ecdh()
84
85    /// Auxiliary function that searches input parameters
86    /// for the slot number in ATECCx08A/B chip (if provided)
87    fn parse_ecdh_slot(&self, parameters: &EcdhParams) -> Result<u16, AtcaStatus> {
88        let mut slot: u16 = 0x0000;
89
90        if (parameters.key_source == EcdhSource::Slot)
91            || (parameters.out_target == EcdhTarget::Slot)
92        {
93            match parameters.slot_id {
94                Some(val) => {
95                    if val < ATCA_ATECC_SLOTS_COUNT {
96                        if (parameters.out_target == EcdhTarget::Slot)
97                            && (self.slots[val as usize].config.write_config != WriteConfig::Always)
98                        {
99                            return Err(AtcaStatus::AtcaBadParam);
100                        }
101                        if (parameters.key_source == EcdhSource::Slot)
102                            && !self.slots[val as usize].config.ecc_key_attr.ecdh_operation
103                        {
104                            return Err(AtcaStatus::AtcaInvalidId);
105                        }
106                        slot = val as u16;
107                    } else {
108                        return Err(AtcaStatus::AtcaInvalidId);
109                    }
110                }
111                None => return Err(AtcaStatus::AtcaBadParam),
112            }
113        } else if parameters.slot_id.is_some() {
114            return Err(AtcaStatus::AtcaBadParam);
115        }
116
117        Ok(slot)
118    } // AteccDevice::parse_ecdh_slot()
119
120    /// Auxiliary function checking correctness of the combination of input parameters to call ECDH function.
121    fn parse_ecdh_input_parameters(
122        &self,
123        parameters: &EcdhParams,
124        peer_public_key_length: usize,
125    ) -> Result<u16, AtcaStatus> {
126        let mut slot: u16 = 0x0000;
127        let device_type = self.get_device_type();
128
129        if (device_type != AtcaDeviceType::ATECC508A) && (device_type != AtcaDeviceType::ATECC608A)
130        {
131            return Err(AtcaStatus::AtcaBadOpcode);
132        }
133
134        let mut bad_param: bool = (device_type == AtcaDeviceType::ATECC508A)
135            && ((parameters.key_source != EcdhSource::Slot)
136                || (parameters.out_target != EcdhTarget::Compatibility)
137                || parameters.out_encrypt);
138
139        bad_param = bad_param
140            || ((parameters.key_source == EcdhSource::Slot)
141                && (parameters.out_target == EcdhTarget::Slot));
142
143        if !bad_param {
144            slot = self.parse_ecdh_slot(parameters)?;
145
146            if (device_type == AtcaDeviceType::ATECC608A) && self.chip_options.io_key_enabled {
147                let ecdh_out_to_n_plus_1: bool = self.slots[slot as usize]
148                    .config
149                    .ecc_key_attr
150                    .ecdh_secret_out;
151
152                match self.chip_options.ecdh_output_protection {
153                    OutputProtectionState::ClearTextAllowed => {
154                        if parameters.out_encrypt {
155                            match parameters.out_target {
156                                EcdhTarget::Compatibility => {
157                                    if ecdh_out_to_n_plus_1 {
158                                        bad_param = true;
159                                    }
160                                }
161                                EcdhTarget::Slot | EcdhTarget::TempKey => bad_param = true,
162                                _ => (),
163                            }
164                        }
165                    }
166                    OutputProtectionState::EncryptedOutputOnly => match parameters.out_target {
167                        EcdhTarget::Compatibility => {
168                            if !parameters.out_encrypt || ecdh_out_to_n_plus_1 {
169                                bad_param = true;
170                            }
171                        }
172                        EcdhTarget::Slot | EcdhTarget::TempKey => {
173                            if parameters.out_encrypt {
174                                bad_param = true;
175                            }
176                        }
177                        EcdhTarget::Output => {
178                            if !parameters.out_encrypt {
179                                bad_param = true;
180                            }
181                        }
182                    },
183                    OutputProtectionState::ForbiddenOutputOutsideChip => {
184                        match parameters.out_target {
185                            EcdhTarget::Compatibility => {
186                                if parameters.out_encrypt && ecdh_out_to_n_plus_1 {
187                                    bad_param = true;
188                                }
189                            }
190                            EcdhTarget::Slot | EcdhTarget::TempKey => {
191                                if parameters.out_encrypt {
192                                    bad_param = true;
193                                }
194                            }
195                            EcdhTarget::Output => bad_param = true,
196                        }
197                    }
198                    _ => bad_param = true,
199                }
200            }
201        }
202
203        if bad_param {
204            return Err(AtcaStatus::AtcaBadParam);
205        }
206
207        if peer_public_key_length != ATCA_ATECC_PUB_KEY_SIZE {
208            return Err(AtcaStatus::AtcaInvalidSize);
209        }
210
211        Ok(slot)
212    } // AteccDevice::parse_ecdh_input_parameters()
213}