1use core::mem::MaybeUninit;
2
3use aead::generic_array::GenericArray;
4use typenum::marker_traits::Unsigned;
5
6use super::{c_void, CryptoErr};
7
8#[derive(Copy, Clone)]
16#[repr(u8)]
17pub enum Algorithm {
18 #[cfg(feature = "chacha20poly1305")]
19 ChaCha20Poly1305,
20 #[cfg(feature = "aes-ccm")]
21 AesCcm16_64_128,
22 #[cfg(feature = "aes-ccm")]
23 AesCcm16_128_128,
24 #[cfg(feature = "aes-gcm")]
25 A128GCM,
26 #[cfg(feature = "aes-gcm")]
27 A256GCM,
28}
29
30#[cfg(feature = "chacha20poly1305")]
31type AlgtypeChaCha20Poly1305 = chacha20poly1305::ChaCha20Poly1305;
32#[cfg(feature = "aes-ccm")]
33type AlgtypeAesCcm16_64_128 = ccm::Ccm<aes::Aes128, ccm::consts::U8, ccm::consts::U13>;
34#[cfg(feature = "aes-ccm")]
35type AlgtypeAesCcm16_128_128 = ccm::Ccm<aes::Aes128, ccm::consts::U8, ccm::consts::U7>;
36#[cfg(feature = "aes-gcm")]
37type AlgtypeA128GCM = aes_gcm::Aes128Gcm;
38#[cfg(feature = "aes-gcm")]
39type AlgtypeA256GCM = aes_gcm::Aes256Gcm;
40
41impl Algorithm {
45 fn from_number(num: i32) -> Option<Self> {
46 match num {
47 #[cfg(feature = "chacha20poly1305")]
48 24 => Some(Algorithm::ChaCha20Poly1305),
49 #[cfg(feature = "aes-ccm")]
50 10 => Some(Algorithm::AesCcm16_64_128),
51 #[cfg(feature = "aes-ccm")]
52 30 => Some(Algorithm::AesCcm16_128_128),
53 #[cfg(feature = "aes-gcm")]
54 1 => Some(Algorithm::A128GCM),
55 #[cfg(feature = "aes-gcm")]
56 3 => Some(Algorithm::A256GCM),
57 _ => None,
58 }
59 }
60
61 fn to_number(&self) -> Option<i32> {
62 Some(match self {
63 #[cfg(feature = "chacha20poly1305")]
64 Algorithm::ChaCha20Poly1305 => 24,
65 #[cfg(feature = "aes-ccm")]
66 Algorithm::AesCcm16_64_128 => 10,
67 #[cfg(feature = "aes-ccm")]
68 Algorithm::AesCcm16_128_128 => 30,
69 #[cfg(feature = "aes-gcm")]
70 Algorithm::A128GCM => 1,
71 #[cfg(feature = "aes-gcm")]
72 Algorithm::A256GCM => 3,
73 })
74 }
75
76 fn tag_length(&self) -> usize {
77 match self {
78 #[cfg(feature = "chacha20poly1305")]
79 Algorithm::ChaCha20Poly1305 => {
80 <AlgtypeChaCha20Poly1305 as aead::AeadCore>::TagSize::to_usize()
81 }
82 #[cfg(feature = "aes-ccm")]
83 Algorithm::AesCcm16_64_128 => {
84 <AlgtypeAesCcm16_64_128 as aead::AeadCore>::TagSize::to_usize()
85 }
86 #[cfg(feature = "aes-ccm")]
87 Algorithm::AesCcm16_128_128 => {
88 <AlgtypeAesCcm16_128_128 as aead::AeadCore>::TagSize::to_usize()
89 }
90 #[cfg(feature = "aes-gcm")]
91 Algorithm::A128GCM => <AlgtypeA128GCM as aead::AeadCore>::TagSize::to_usize(),
92 #[cfg(feature = "aes-gcm")]
93 Algorithm::A256GCM => <AlgtypeA256GCM as aead::AeadCore>::TagSize::to_usize(),
94 }
95 }
96
97 fn iv_length(&self) -> usize {
98 match self {
99 #[cfg(feature = "chacha20poly1305")]
100 Algorithm::ChaCha20Poly1305 => {
101 <AlgtypeChaCha20Poly1305 as aead::AeadCore>::NonceSize::to_usize()
102 }
103 #[cfg(feature = "aes-ccm")]
104 Algorithm::AesCcm16_64_128 => {
105 <AlgtypeAesCcm16_64_128 as aead::AeadCore>::NonceSize::to_usize()
106 }
107 #[cfg(feature = "aes-ccm")]
108 Algorithm::AesCcm16_128_128 => {
109 <AlgtypeAesCcm16_128_128 as aead::AeadCore>::NonceSize::to_usize()
110 }
111 #[cfg(feature = "aes-gcm")]
112 Algorithm::A128GCM => <AlgtypeA128GCM as aead::AeadCore>::NonceSize::to_usize(),
113 #[cfg(feature = "aes-gcm")]
114 Algorithm::A256GCM => <AlgtypeA256GCM as aead::AeadCore>::NonceSize::to_usize(),
115 }
116 }
117
118 fn key_length(&self) -> usize {
119 match self {
120 #[cfg(feature = "chacha20poly1305")]
121 Algorithm::ChaCha20Poly1305 => {
122 <AlgtypeChaCha20Poly1305 as aead::KeySizeUser>::key_size()
123 }
124 #[cfg(feature = "aes-ccm")]
125 Algorithm::AesCcm16_64_128 => <AlgtypeAesCcm16_64_128 as aead::KeySizeUser>::key_size(),
126 #[cfg(feature = "aes-ccm")]
127 Algorithm::AesCcm16_128_128 => {
128 <AlgtypeAesCcm16_128_128 as aead::KeySizeUser>::key_size()
129 }
130 #[cfg(feature = "aes-gcm")]
131 Algorithm::A128GCM => <AlgtypeA128GCM as aead::KeySizeUser>::key_size(),
132 #[cfg(feature = "aes-gcm")]
133 Algorithm::A256GCM => <AlgtypeA256GCM as aead::KeySizeUser>::key_size(),
134 }
135 }
136}
137
138const AAD_BUFFER_SIZE: usize = 32;
139
140pub struct EncryptState {
144 alg: Algorithm,
145 iv: *const u8,
146 key: *const u8,
147 buffered_aad: heapless::Vec<u8, AAD_BUFFER_SIZE>,
148}
149
150#[repr(transparent)]
151pub struct DecryptState {
152 actually_encrypt: EncryptState,
153}
154
155#[no_mangle]
156pub unsafe extern "C" fn oscore_crypto_aead_from_number(
157 alg: &mut MaybeUninit<Algorithm>,
158 num: i32,
159) -> CryptoErr {
160 if let Some(found) = Algorithm::from_number(num) {
161 alg.write(found);
162 CryptoErr::Ok
163 } else {
164 CryptoErr::NoSuchAlgorithm
165 }
166}
167
168#[no_mangle]
169pub unsafe extern "C" fn oscore_crypto_aead_get_number(
170 alg: Algorithm,
171 num: &mut MaybeUninit<i32>,
172) -> CryptoErr {
173 if let Some(found) = alg.to_number() {
174 num.write(found);
175 CryptoErr::Ok
176 } else {
177 CryptoErr::NoIdentifier
178 }
179}
180
181#[no_mangle]
182pub unsafe extern "C" fn oscore_crypto_aead_from_string(
183 _alg: &mut MaybeUninit<Algorithm>,
184 _string: *const u8,
185 _string_len: usize,
186) -> CryptoErr {
187 CryptoErr::NoSuchAlgorithm
188}
189
190#[no_mangle]
191pub unsafe extern "C" fn oscore_crypto_aead_get_taglength(alg: Algorithm) -> usize {
192 alg.tag_length()
193}
194
195#[no_mangle]
196pub unsafe extern "C" fn oscore_crypto_aead_get_ivlength(alg: Algorithm) -> usize {
197 alg.iv_length()
198}
199
200#[no_mangle]
201pub unsafe extern "C" fn oscore_crypto_aead_get_keylength(alg: Algorithm) -> usize {
202 alg.key_length()
203}
204
205#[no_mangle]
206pub unsafe extern "C" fn oscore_crypto_aead_encrypt_start(
207 state: &mut MaybeUninit<EncryptState>,
208 alg: Algorithm,
209 aad_len: usize,
210 _plaintext_len: usize,
211 iv: *const u8,
212 key: *const u8,
213) -> CryptoErr {
214 if aad_len > AAD_BUFFER_SIZE {
215 return CryptoErr::AadPreallocationExceeded;
216 }
217
218 let created = EncryptState {
219 alg,
220 iv,
221 key,
222 buffered_aad: heapless::Vec::new(),
223 };
224 state.write(created);
225
226 CryptoErr::Ok
227}
228
229#[no_mangle]
230pub unsafe extern "C" fn oscore_crypto_aead_encrypt_feed_aad(
231 state: *mut c_void,
232 aad_chunk: *const u8,
233 aad_chunk_len: usize,
234) -> CryptoErr {
235 let state: &mut EncryptState = unsafe { core::mem::transmute(state) };
236
237 let aad_chunk = unsafe { core::slice::from_raw_parts(aad_chunk, aad_chunk_len) };
238 match state.buffered_aad.extend_from_slice(aad_chunk) {
239 Ok(()) => CryptoErr::Ok,
240 Err(_) => CryptoErr::UnexpectedDataLength,
241 }
242}
243
244fn _encrypt_inplace<A>(state: &mut EncryptState, buffer: *mut u8, buffer_len: usize) -> CryptoErr
251where
252 A: aead::AeadMutInPlace + aead::KeyInit,
253{
254 let taglen = A::TagSize::to_usize();
255
256 let buffer = unsafe { core::slice::from_raw_parts_mut(buffer, buffer_len) };
257 let plaintextlength = match buffer.len().checked_sub(taglen) {
258 Some(x) => x,
259 None => return CryptoErr::BufferShorterThanTag,
260 };
261 let (plaincipher, tag) = buffer.split_at_mut(plaintextlength);
262 log_secrets!("Encrypting plaintext {:?}", plaincipher);
263
264 let keylen = A::KeySize::to_usize();
266 let key = unsafe { core::slice::from_raw_parts(state.key, keylen) };
267 let key = GenericArray::clone_from_slice(key);
268 log_secrets!("Encrypting with key {:?}", key);
269
270 let noncelen = A::NonceSize::to_usize();
272 let nonce = unsafe { core::slice::from_raw_parts(state.iv, noncelen) };
273 let nonce = GenericArray::from_slice(nonce);
274 log_secrets!("Encrypting with nonce {:?}", nonce);
275 log_secrets!("Encrypting with AAD {:?}", state.buffered_aad);
276
277 let mut aead = A::new(&key);
278 let tagdata =
279 match aead.encrypt_in_place_detached(nonce, state.buffered_aad.as_ref(), plaincipher) {
280 Ok(tagdata) => tagdata,
281 Err(_) => return CryptoErr::BufferShorterThanTag,
285 };
286 tag.copy_from_slice(&tagdata);
287
288 log_secrets!("Encrypted ciphertext {:?}", buffer);
289
290 CryptoErr::Ok
291}
292
293#[no_mangle]
294pub unsafe extern "C" fn oscore_crypto_aead_encrypt_inplace(
295 state: &mut EncryptState,
296 buffer: *mut u8,
297 buffer_len: usize,
298) -> CryptoErr {
299 match state.alg {
300 #[cfg(feature = "chacha20poly1305")]
301 Algorithm::ChaCha20Poly1305 => {
302 _encrypt_inplace::<AlgtypeChaCha20Poly1305>(state, buffer, buffer_len)
303 }
304 #[cfg(feature = "aes-ccm")]
305 Algorithm::AesCcm16_64_128 => {
306 _encrypt_inplace::<AlgtypeAesCcm16_64_128>(state, buffer, buffer_len)
307 }
308 #[cfg(feature = "aes-ccm")]
309 Algorithm::AesCcm16_128_128 => {
310 _encrypt_inplace::<AlgtypeAesCcm16_128_128>(state, buffer, buffer_len)
311 }
312 #[cfg(feature = "aes-gcm")]
313 Algorithm::A128GCM => _encrypt_inplace::<AlgtypeA128GCM>(state, buffer, buffer_len),
314 #[cfg(feature = "aes-gcm")]
315 Algorithm::A256GCM => _encrypt_inplace::<AlgtypeA256GCM>(state, buffer, buffer_len),
316 }
317}
318
319#[no_mangle]
320pub unsafe extern "C" fn oscore_crypto_aead_decrypt_start(
321 state: &mut MaybeUninit<DecryptState>,
322 alg: Algorithm,
323 aad_len: usize,
324 plaintext_len: usize,
325 iv: *const u8,
326 key: *const u8,
327) -> CryptoErr {
328 let mut tempstate = MaybeUninit::uninit();
331 let ret =
332 oscore_crypto_aead_encrypt_start(&mut tempstate, alg, aad_len, plaintext_len, iv, key);
333 if let CryptoErr::Ok = ret {
334 state.write(DecryptState {
335 actually_encrypt: unsafe { tempstate.assume_init() },
336 });
337 }
338 ret
339}
340
341#[no_mangle]
342pub unsafe extern "C" fn oscore_crypto_aead_decrypt_feed_aad(
343 state: *mut c_void,
344 aad_chunk: *const u8,
345 aad_chunk_len: usize,
346) -> CryptoErr {
347 let state: &mut DecryptState = unsafe { core::mem::transmute(state) };
348
349 oscore_crypto_aead_encrypt_feed_aad(
350 unsafe { core::mem::transmute(&mut state.actually_encrypt) },
351 aad_chunk,
352 aad_chunk_len,
353 )
354}
355
356fn _decrypt_inplace<A>(state: &mut DecryptState, buffer: *mut u8, buffer_len: usize) -> CryptoErr
363where
364 A: aead::AeadMutInPlace + aead::KeyInit,
365{
366 let state = &mut state.actually_encrypt;
367
368 let taglen = state.alg.tag_length();
369
370 let buffer = unsafe { core::slice::from_raw_parts_mut(buffer, buffer_len) };
371 log_secrets!("Decrypting ciphertext {:?}", buffer);
372 let plaintextlength = match buffer.len().checked_sub(taglen) {
373 Some(x) => x,
374 None => return CryptoErr::BufferShorterThanTag,
375 };
376 let (plaincipher, tag) = buffer.split_at_mut(plaintextlength);
377
378 let keylen = state.alg.key_length();
381 let key = unsafe { core::slice::from_raw_parts(state.key, keylen) };
382 let key = GenericArray::clone_from_slice(key);
383 log_secrets!("Decrypting with key {:?}", key);
384
385 let noncelen = state.alg.iv_length();
387 let nonce = unsafe { core::slice::from_raw_parts(state.iv, noncelen) };
388 let nonce = GenericArray::from_slice(nonce);
389 log_secrets!("Decrypting with nonce {:?}", nonce);
390 log_secrets!("Decrypting with AAD {:?}", state.buffered_aad);
391
392 let tag = GenericArray::from_slice(tag);
394
395 let _aad: &[u8] = state.buffered_aad.as_ref();
396 let _nonce: &[u8] = nonce.as_ref();
397 let _key: &[u8] = key.as_ref();
398
399 let mut aead = A::new(&key);
400 match aead.decrypt_in_place_detached(nonce, state.buffered_aad.as_ref(), plaincipher, tag) {
401 Ok(()) => {
402 log_secrets!("Decrypted into plaintext {:?}", plaincipher);
403 CryptoErr::Ok
404 }
405 Err(_) => {
406 log_secrets!("Decryption failed"); CryptoErr::DecryptError
409 }
410 }
411}
412
413#[no_mangle]
414pub unsafe extern "C" fn oscore_crypto_aead_decrypt_inplace(
415 state: &mut DecryptState,
416 buffer: *mut u8,
417 buffer_len: usize,
418) -> CryptoErr {
419 match state.actually_encrypt.alg {
420 #[cfg(feature = "chacha20poly1305")]
421 Algorithm::ChaCha20Poly1305 => {
422 _decrypt_inplace::<AlgtypeChaCha20Poly1305>(state, buffer, buffer_len)
423 }
424 #[cfg(feature = "aes-ccm")]
425 Algorithm::AesCcm16_64_128 => {
426 _decrypt_inplace::<AlgtypeAesCcm16_64_128>(state, buffer, buffer_len)
427 }
428 #[cfg(feature = "aes-ccm")]
429 Algorithm::AesCcm16_128_128 => {
430 _decrypt_inplace::<AlgtypeAesCcm16_128_128>(state, buffer, buffer_len)
431 }
432 #[cfg(feature = "aes-gcm")]
433 Algorithm::A128GCM => _decrypt_inplace::<AlgtypeA128GCM>(state, buffer, buffer_len),
434 #[cfg(feature = "aes-gcm")]
435 Algorithm::A256GCM => _decrypt_inplace::<AlgtypeA256GCM>(state, buffer, buffer_len),
436 }
437}