Skip to content
File

Blob: firmware/crates/esp32-radio/src/platform/crypto/native.rs

rust213 lines
1//! Synchronous mbedTLS calls. Native contexts live only inside C calls; SHA
2//! snapshots contain values only, with their layout hidden from Rust.
3use super::super::ffi;
4 
5pub(super) type Result<T> = std::result::Result<T, ()>;
6fn check(code: i32) -> Result<()> {
7 if code == 0 { Ok(()) } else { Err(()) }
8}
9 
10pub(super) fn ctr(key: &[u8], iv: &[u8; 16], input: &[u8], output: &mut [u8]) -> Result<()> {
11 // SAFETY: live, nonoverlapping slice storage; C validates capacities and key
12 // size, finishes all hardware operations, and retains no pointer.
13 check(unsafe {
14 ffi::radio_crypto_aes_ctr(
15 key.as_ptr(),
16 key.len(),
17 iv.as_ptr(),
18 input.as_ptr(),
19 input.len(),
20 output.as_mut_ptr(),
21 output.len(),
22 )
23 })
24}
25pub(super) fn ecb(key: &[u8], input: &[u8; 16], output: &mut [u8; 16]) -> Result<()> {
26 // SAFETY: exactly one block in/out, key is borrowed only during the call.
27 check(unsafe {
28 ffi::radio_crypto_aes_ecb(key.as_ptr(), key.len(), input.as_ptr(), output.as_mut_ptr())
29 })
30}
31pub(super) fn gcm(
32 decrypt: bool,
33 key: &[u8],
34 iv: &[u8; 12],
35 aad: &[u8],
36 input: &[u8],
37 output: &mut [u8],
38) -> Result<()> {
39 // SAFETY: distinct live input/output slices; C checks all lengths and fully
40 // authenticates before returning success. No pointers survive the call.
41 check(unsafe {
42 ffi::radio_crypto_aes_gcm(
43 i32::from(decrypt),
44 key.as_ptr(),
45 key.len(),
46 iv.as_ptr(),
47 aad.as_ptr(),
48 aad.len(),
49 input.as_ptr(),
50 input.len(),
51 output.as_mut_ptr(),
52 output.len(),
53 )
54 })
55}
56pub(super) fn gcm_in_place(
57 decrypt: bool,
58 key: &[u8],
59 iv: &[u8; 12],
60 aad: &[u8],
61 buffer: &mut [u8],
62 input_length: usize,
63) -> Result<()> {
64 if input_length > buffer.len() {
65 return Err(());
66 }
67 let capacity = buffer.len();
68 let pointer = buffer.as_mut_ptr();
69 // SAFETY: the one exclusive buffer owns both input and output. mbedTLS GCM
70 // supports identical input/output addresses. Capacity includes encryption's
71 // tag space; C validates lengths and clears plaintext on authentication failure.
72 check(unsafe {
73 ffi::radio_crypto_aes_gcm(
74 i32::from(decrypt),
75 key.as_ptr(),
76 key.len(),
77 iv.as_ptr(),
78 aad.as_ptr(),
79 aad.len(),
80 pointer.cast_const(),
81 input_length,
82 pointer,
83 capacity,
84 )
85 })
86}
87pub(super) fn sha256(input: &[u8]) -> Result<[u8; 32]> {
88 let mut output = [0; 32];
89 // SAFETY: caller-owned 32-byte output and a live, call-bounded input slice.
90 check(unsafe { ffi::radio_crypto_sha256(input.as_ptr(), input.len(), output.as_mut_ptr()) })?;
91 Ok(output)
92}
93pub(super) fn hmac(bits: i32, key: &[u8], parts: &[&[u8]], output: &mut [u8]) -> Result<()> {
94 if parts.len() > 8 {
95 return Err(());
96 }
97 let mut views = [ffi::CryptoPart {
98 bytes: std::ptr::null(),
99 length: 0,
100 }; 8];
101 for (view, bytes) in views.iter_mut().zip(parts) {
102 *view = ffi::CryptoPart {
103 bytes: bytes.as_ptr(),
104 length: bytes.len(),
105 };
106 }
107 // SAFETY: repr(C) views refer to input slices alive throughout this call;
108 // only initialized views are counted. C owns/frees its context synchronously.
109 check(unsafe {
110 ffi::radio_crypto_hmac(
111 bits,
112 key.as_ptr(),
113 key.len(),
114 views.as_ptr(),
115 parts.len(),
116 output.as_mut_ptr(),
117 output.len(),
118 )
119 })
120}
121 
122pub(super) struct HashState(zeroize::Zeroizing<[u8; ffi::CRYPTO_HASH_STATE_BYTES]>);
123impl HashState {
124 pub(super) fn new(bits: i32) -> Result<Self> {
125 let mut state = Self(zeroize::Zeroizing::new([0; ffi::CRYPTO_HASH_STATE_BYTES]));
126 // SAFETY: C initializes all bytes of the fixed-capacity private snapshot.
127 check(unsafe { ffi::radio_crypto_hash_init(state.0.as_mut_ptr(), bits) })?;
128 Ok(state)
129 }
130 pub(super) fn update(&mut self, data: &[u8]) -> Result<()> {
131 // SAFETY: snapshot comes only from successful C initialization/updates;
132 // its storage is exclusive and C retains no pointer or hardware lock.
133 check(unsafe {
134 ffi::radio_crypto_hash_update(self.0.as_mut_ptr(), data.as_ptr(), data.len())
135 })
136 }
137 pub(super) fn finish(&self, output: &mut [u8]) -> Result<()> {
138 // SAFETY: C clones the pointer-free state before finalizing, leaving this
139 // snapshot unchanged. The output slice accurately describes capacity.
140 check(unsafe {
141 ffi::radio_crypto_hash_finish(self.0.as_ptr(), output.as_mut_ptr(), output.len())
142 })
143 }
144}
145 
146pub(super) fn verify_ec(cert: &[u8], data: &[u8], signature: &[u8], hash_bits: i32) -> Result<()> {
147 // SAFETY: C validates all encodings/sizes, allocates and frees its temporary
148 // certificate/key context, and retains no Rust input pointer.
149 check(unsafe {
150 ffi::radio_crypto_verify_ec(
151 cert.as_ptr(),
152 cert.len(),
153 data.as_ptr(),
154 data.len(),
155 signature.as_ptr(),
156 signature.len(),
157 hash_bits,
158 )
159 })
160}
161 
162pub(super) fn key_info(der: &[u8]) -> Result<i32> {
163 let mut bits = 0;
164 // SAFETY: C parses the borrowed DER into temporary native storage and writes
165 // one initialized scalar. It retains no key bytes or pointers.
166 check(unsafe { ffi::radio_crypto_key_info(der.as_ptr(), der.len(), &mut bits) })?;
167 if bits != 256 && bits != 384 {
168 return Err(());
169 }
170 Ok(bits)
171}
172pub(super) fn sign(der: &[u8], data: &[u8], bits: i32, out: &mut [u8; 128]) -> Result<usize> {
173 let mut used = 0;
174 // SAFETY: C borrows key/data, writes at most 128 signature bytes, and frees
175 // its native key context before return. No callback enters Rust.
176 check(unsafe {
177 ffi::radio_crypto_sign(
178 der.as_ptr(),
179 der.len(),
180 data.as_ptr(),
181 data.len(),
182 bits,
183 out.as_mut_ptr(),
184 out.len(),
185 &mut used,
186 )
187 })?;
188 if used == 0 || used > out.len() {
189 return Err(());
190 }
191 Ok(used)
192}
193pub(super) fn p256_keygen() -> Result<(zeroize::Zeroizing<[u8; 32]>, [u8; 65])> {
194 let mut secret = zeroize::Zeroizing::new([0; 32]);
195 let mut public = [0; 65];
196 // SAFETY: output arrays match the fixed ABI; C owns and frees its contexts.
197 // Wi-Fi is started before constructing this provider, enabling hardware entropy.
198 check(unsafe { ffi::radio_crypto_p256_keygen(secret.as_mut_ptr(), public.as_mut_ptr()) })?;
199 Ok((secret, public))
200}
201pub(super) fn p256_shared(
202 secret: &[u8; 32],
203 public: &[u8; 65],
204) -> Result<zeroize::Zeroizing<[u8; 32]>> {
205 let mut value = zeroize::Zeroizing::new([0; 32]);
206 // SAFETY: fixed-size inputs/outputs; C validates the scalar and remote point,
207 // supplies blinding randomness and destroys all temporary MPI/EC allocations.
208 check(unsafe {
209 ffi::radio_crypto_p256_shared(secret.as_ptr(), public.as_ptr(), value.as_mut_ptr())
210 })?;
211 Ok(value)
212}