Skip to main content

ctaes_rs/
lib.rs

1// Copyright (c) 2023 Blockstream
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or https://opensource.org/licenses/mit-license.php.
4
5//! Rust bindings and API for CTAES (constant-time AES implementation from Bitcoin Core found at
6//! https://github.com/bitcoin-core/ctaes)
7//!
8//! The CTAES Library provides a constant time implementation of the AES algorithm. For completeness
9//! this crate provides the interface to the AES-ECB methods, but they should not be used. Rather,
10//! use the AES-CBC methods.
11//!
12//! The crate also provides a Padding utility implementation to help the user prepare, pad and unpad
13//! buffers. Zero Padding and PKCS7 padding implementations are provided
14//!
15//! # Examples
16//!
17//! ```
18//! extern crate hex_conservative;
19//! use hex_conservative::FromHex;
20//! use ctaes_rs::{Padding, Pkcs7, AesCbcBlockCipher, Aes128Cbc};
21//!
22//! let key = <[u8; 16]>::from_hex("2b7e151628aed2a6abf7158809cf4f3c").unwrap();
23//! let iv = <[u8; 16]>::from_hex("000102030405060708090a0b0c0d0e0f").unwrap();
24//! let message = <Vec<u8>>::from_hex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710").unwrap();
25//!
26//! let padded_buffer_length = Pkcs7::padded_buffer_length(message.len(), 16);
27//! let mut plaintext = vec![0u8; padded_buffer_length];
28//! plaintext[0..message.len()].copy_from_slice(message.as_slice());
29//! Pkcs7::pad(plaintext.as_mut_slice(), message.len(), 16).unwrap();
30//! let mut ciphertext = vec![0u8; padded_buffer_length];
31//!
32//! let cipher = Aes128Cbc::new(key.as_slice(), iv.as_slice()).unwrap();
33//! cipher.encrypt(plaintext.as_slice(), ciphertext.as_mut_slice()).unwrap();
34//!
35//! let mut deciphered = vec![0u8; padded_buffer_length];
36//! cipher.decrypt(ciphertext.as_slice(), deciphered.as_mut_slice()).unwrap();
37//! let unpadded_result = Pkcs7::unpad(deciphered.as_slice()).unwrap();
38//! assert_eq!(message.as_slice(), unpadded_result);
39//! ```
40//!
41
42extern crate thiserror;
43extern crate zeroize;
44
45#[allow(non_snake_case)]
46mod ctaes_ffi;
47mod error;
48mod padding;
49
50use ctaes_ffi::{
51    AES128_CBC_ctx, AES128_CBC_decrypt, AES128_CBC_encrypt, AES128_CBC_init, AES128_ctx,
52    AES128_decrypt, AES128_encrypt, AES128_init, AES192_CBC_ctx, AES192_CBC_decrypt,
53    AES192_CBC_encrypt, AES192_CBC_init, AES192_ctx, AES192_decrypt, AES192_encrypt, AES192_init,
54    AES256_CBC_ctx, AES256_CBC_decrypt, AES256_CBC_encrypt, AES256_CBC_init, AES256_ctx,
55    AES256_decrypt, AES256_encrypt, AES256_init, FfiAesCbcCipher, FfiAesCipher,
56};
57pub use error::Error;
58pub use padding::{Padding, Pkcs7, ZeroPadding};
59
60pub const AES128_KEY_LENGTH: usize = 16;
61pub const AES192_KEY_LENGTH: usize = 24;
62pub const AES256_KEY_LENGTH: usize = 32;
63pub const AES_BLOCK_SIZE: usize = 16;
64
65/// Trait that implements the common `encrypt` and `decrypt` methods for all AES ciphers
66pub trait AesBlockCipher: FfiAesCipher {
67    /// Encrypt the contents of `plaintext` and place the result in the `ciphertext` out parameter
68    fn encrypt(&self, plaintext: &[u8], ciphertext: &mut [u8]) -> Result<(), Error> {
69        if plaintext.len() % AES_BLOCK_SIZE != 0 {
70            return Err(Error::NonBlockSizeAlignedBuffer);
71        }
72        if plaintext.len() > ciphertext.len() {
73            return Err(Error::InsufficientBufferSize);
74        }
75
76        let num_blocks = plaintext.len() / AES_BLOCK_SIZE;
77        self.ffi_encrypt(num_blocks, ciphertext, plaintext);
78
79        Ok(())
80    }
81
82    /// Decrypt the contents of `ciphertext` and place the result in the `plaintext` out parameter
83    fn decrypt(&self, ciphertext: &[u8], plaintext: &mut [u8]) -> Result<(), Error> {
84        if ciphertext.len() % AES_BLOCK_SIZE != 0 {
85            return Err(Error::NonBlockSizeAlignedBuffer);
86        }
87        if plaintext.len() < ciphertext.len() {
88            return Err(Error::InsufficientBufferSize);
89        }
90
91        let num_blocks = ciphertext.len() / AES_BLOCK_SIZE;
92        self.ffi_decrypt(num_blocks, plaintext, ciphertext);
93
94        Ok(())
95    }
96}
97
98/// 128-bit AES-ECB cipher
99#[derive(Default)]
100pub struct Aes128 {
101    context: AES128_ctx,
102}
103
104impl FfiAesCipher for Aes128 {
105    fn ffi_init(&mut self, key: &[u8]) {
106        unsafe {
107            AES128_init(&mut self.context, key.as_ptr());
108        }
109    }
110
111    fn ffi_decrypt(&self, num_blocks: usize, plaintext: &mut [u8], ciphertext: &[u8]) {
112        unsafe {
113            AES128_decrypt(&self.context, num_blocks, plaintext.as_mut_ptr(), ciphertext.as_ptr());
114        }
115    }
116
117    fn ffi_encrypt(&self, num_blocks: usize, ciphertext: &mut [u8], plaintext: &[u8]) {
118        unsafe {
119            AES128_encrypt(&self.context, num_blocks, ciphertext.as_mut_ptr(), plaintext.as_ptr());
120        }
121    }
122}
123
124impl Aes128 {
125    pub fn new(key: &[u8]) -> Result<Self, Error> {
126        if key.len() != AES128_KEY_LENGTH {
127            return Err(Error::KeyIncorrectLength(AES128_KEY_LENGTH));
128        }
129
130        let context = AES128_ctx::default();
131        let mut cipher = Aes128 { context };
132        cipher.ffi_init(key);
133
134        Ok(cipher)
135    }
136}
137
138impl AesBlockCipher for Aes128 {}
139
140/// 192-bit AES-ECB cipher
141#[derive(Default)]
142pub struct Aes192 {
143    context: AES192_ctx,
144}
145
146impl FfiAesCipher for Aes192 {
147    fn ffi_init(&mut self, key: &[u8]) {
148        unsafe {
149            AES192_init(&mut self.context, key.as_ptr());
150        }
151    }
152
153    fn ffi_decrypt(&self, num_blocks: usize, plaintext: &mut [u8], ciphertext: &[u8]) {
154        unsafe {
155            AES192_decrypt(&self.context, num_blocks, plaintext.as_mut_ptr(), ciphertext.as_ptr());
156        }
157    }
158
159    fn ffi_encrypt(&self, num_blocks: usize, ciphertext: &mut [u8], plaintext: &[u8]) {
160        unsafe {
161            AES192_encrypt(&self.context, num_blocks, ciphertext.as_mut_ptr(), plaintext.as_ptr());
162        }
163    }
164}
165
166impl Aes192 {
167    pub fn new(key: &[u8]) -> Result<Self, Error> {
168        if key.len() != AES192_KEY_LENGTH {
169            return Err(Error::KeyIncorrectLength(AES192_KEY_LENGTH));
170        }
171
172        let context = AES192_ctx::default();
173        let mut cipher = Aes192 { context };
174        cipher.ffi_init(key);
175
176        Ok(cipher)
177    }
178}
179
180impl AesBlockCipher for Aes192 {}
181
182/// 256-bit AES-ECB cipher
183#[derive(Default)]
184pub struct Aes256 {
185    context: AES256_ctx,
186}
187
188impl FfiAesCipher for Aes256 {
189    fn ffi_init(&mut self, key: &[u8]) {
190        unsafe {
191            AES256_init(&mut self.context, key.as_ptr());
192        }
193    }
194
195    fn ffi_decrypt(&self, num_blocks: usize, plaintext: &mut [u8], ciphertext: &[u8]) {
196        unsafe {
197            AES256_decrypt(&self.context, num_blocks, plaintext.as_mut_ptr(), ciphertext.as_ptr());
198        }
199    }
200
201    fn ffi_encrypt(&self, num_blocks: usize, ciphertext: &mut [u8], plaintext: &[u8]) {
202        unsafe {
203            AES256_encrypt(&self.context, num_blocks, ciphertext.as_mut_ptr(), plaintext.as_ptr());
204        }
205    }
206}
207
208impl Aes256 {
209    pub fn new(key: &[u8]) -> Result<Self, Error> {
210        if key.len() != AES256_KEY_LENGTH {
211            return Err(Error::KeyIncorrectLength(AES256_KEY_LENGTH));
212        }
213
214        let context = AES256_ctx::default();
215        let mut cipher = Aes256 { context };
216        cipher.ffi_init(key);
217
218        Ok(cipher)
219    }
220}
221
222impl AesBlockCipher for Aes256 {}
223
224/// Trait that implements the common `encrypt` and `decrypt` methods for all AES-CBC ciphers
225pub trait AesCbcBlockCipher: FfiAesCbcCipher {
226    fn encrypt(&self, plaintext: &[u8], ciphertext: &mut [u8]) -> Result<(), Error> {
227        if plaintext.len() % AES_BLOCK_SIZE != 0 {
228            return Err(Error::NonBlockSizeAlignedBuffer);
229        }
230        if plaintext.len() > ciphertext.len() {
231            return Err(Error::InsufficientBufferSize);
232        }
233
234        let num_blocks = plaintext.len() / AES_BLOCK_SIZE;
235        self.ffi_encrypt(num_blocks, ciphertext, plaintext);
236
237        Ok(())
238    }
239
240    fn decrypt(&self, ciphertext: &[u8], plaintext: &mut [u8]) -> Result<(), Error> {
241        if ciphertext.len() % AES_BLOCK_SIZE != 0 {
242            return Err(Error::NonBlockSizeAlignedBuffer);
243        }
244        if plaintext.len() < ciphertext.len() {
245            return Err(Error::InsufficientBufferSize);
246        }
247
248        let num_blocks = ciphertext.len() / AES_BLOCK_SIZE;
249        self.ffi_decrypt(num_blocks, plaintext, ciphertext);
250
251        Ok(())
252    }
253}
254
255/// 128-bit AES-CBC cipher
256pub struct Aes128Cbc<'a> {
257    key: &'a [u8],
258    iv: &'a [u8],
259}
260
261impl<'a> Aes128Cbc<'a> {
262    pub fn new(key: &'a [u8], iv: &'a [u8]) -> Result<Self, Error> {
263        if key.len() != AES128_KEY_LENGTH {
264            return Err(Error::KeyIncorrectLength(AES128_KEY_LENGTH));
265        }
266        if iv.len() != AES_BLOCK_SIZE {
267            return Err(Error::IvIncorrectLength);
268        }
269
270        Ok(Self { key, iv })
271    }
272}
273
274impl FfiAesCbcCipher for Aes128Cbc<'_> {
275    fn ffi_decrypt(&self, num_blocks: usize, plaintext: &mut [u8], ciphertext: &[u8]) {
276        let mut context = AES128_CBC_ctx::default();
277        unsafe {
278            AES128_CBC_init(&mut context, self.key.as_ptr(), self.iv.as_ptr());
279            AES128_CBC_decrypt(
280                &mut context,
281                num_blocks,
282                plaintext.as_mut_ptr(),
283                ciphertext.as_ptr(),
284            );
285        }
286    }
287
288    fn ffi_encrypt(&self, num_blocks: usize, ciphertext: &mut [u8], plaintext: &[u8]) {
289        let mut context = AES128_CBC_ctx::default();
290        unsafe {
291            AES128_CBC_init(&mut context, self.key.as_ptr(), self.iv.as_ptr());
292            AES128_CBC_encrypt(
293                &mut context,
294                num_blocks,
295                ciphertext.as_mut_ptr(),
296                plaintext.as_ptr(),
297            );
298        }
299    }
300}
301
302impl AesCbcBlockCipher for Aes128Cbc<'_> {}
303
304/// 192-bit AES-CBC cipher
305pub struct Aes192Cbc<'a> {
306    key: &'a [u8],
307    iv: &'a [u8],
308}
309
310impl<'a> Aes192Cbc<'a> {
311    pub fn new(key: &'a [u8], iv: &'a [u8]) -> Result<Self, Error> {
312        if key.len() != AES192_KEY_LENGTH {
313            return Err(Error::KeyIncorrectLength(AES192_KEY_LENGTH));
314        }
315        if iv.len() != AES_BLOCK_SIZE {
316            return Err(Error::IvIncorrectLength);
317        }
318
319        Ok(Self { key, iv })
320    }
321}
322
323impl FfiAesCbcCipher for Aes192Cbc<'_> {
324    fn ffi_decrypt(&self, num_blocks: usize, plaintext: &mut [u8], ciphertext: &[u8]) {
325        let mut context = AES192_CBC_ctx::default();
326        unsafe {
327            AES192_CBC_init(&mut context, self.key.as_ptr(), self.iv.as_ptr());
328            AES192_CBC_decrypt(
329                &mut context,
330                num_blocks,
331                plaintext.as_mut_ptr(),
332                ciphertext.as_ptr(),
333            );
334        }
335    }
336
337    fn ffi_encrypt(&self, num_blocks: usize, ciphertext: &mut [u8], plaintext: &[u8]) {
338        let mut context = AES192_CBC_ctx::default();
339        unsafe {
340            AES192_CBC_init(&mut context, self.key.as_ptr(), self.iv.as_ptr());
341            AES192_CBC_encrypt(
342                &mut context,
343                num_blocks,
344                ciphertext.as_mut_ptr(),
345                plaintext.as_ptr(),
346            );
347        }
348    }
349}
350
351impl AesCbcBlockCipher for Aes192Cbc<'_> {}
352
353/// 256-bit AES-CBC cipher
354pub struct Aes256Cbc<'a> {
355    key: &'a [u8],
356    iv: &'a [u8],
357}
358
359impl<'a> Aes256Cbc<'a> {
360    pub fn new(key: &'a [u8], iv: &'a [u8]) -> Result<Self, Error> {
361        if key.len() != AES256_KEY_LENGTH {
362            return Err(Error::KeyIncorrectLength(AES256_KEY_LENGTH));
363        }
364        if iv.len() != AES_BLOCK_SIZE {
365            return Err(Error::IvIncorrectLength);
366        }
367
368        Ok(Self { key, iv })
369    }
370}
371
372impl FfiAesCbcCipher for Aes256Cbc<'_> {
373    fn ffi_decrypt(&self, num_blocks: usize, plaintext: &mut [u8], ciphertext: &[u8]) {
374        let mut context = AES256_CBC_ctx::default();
375        unsafe {
376            AES256_CBC_init(&mut context, self.key.as_ptr(), self.iv.as_ptr());
377            AES256_CBC_decrypt(
378                &mut context,
379                num_blocks,
380                plaintext.as_mut_ptr(),
381                ciphertext.as_ptr(),
382            );
383        }
384    }
385
386    fn ffi_encrypt(&self, num_blocks: usize, ciphertext: &mut [u8], plaintext: &[u8]) {
387        let mut context = AES256_CBC_ctx::default();
388        unsafe {
389            AES256_CBC_init(&mut context, self.key.as_ptr(), self.iv.as_ptr());
390            AES256_CBC_encrypt(
391                &mut context,
392                num_blocks,
393                ciphertext.as_mut_ptr(),
394                plaintext.as_ptr(),
395            );
396        }
397    }
398}
399
400impl AesCbcBlockCipher for Aes256Cbc<'_> {}
401
402#[cfg(test)]
403mod test {
404    use crate::AES128_KEY_LENGTH;
405    use crate::AES192_KEY_LENGTH;
406    use crate::AES256_KEY_LENGTH;
407    use crate::{
408        Aes128, Aes128Cbc, Aes192, Aes192Cbc, Aes256, Aes256Cbc, AesBlockCipher, AesCbcBlockCipher,
409        Error,
410    };
411
412    #[test]
413    fn test_buffer_validation() {
414        assert!(matches!(
415            Aes128::new([0u8; 17].as_slice()),
416            Err(Error::KeyIncorrectLength(AES128_KEY_LENGTH))
417        ));
418        assert!(matches!(
419            Aes192::new([0u8; 25].as_slice()),
420            Err(Error::KeyIncorrectLength(AES192_KEY_LENGTH))
421        ));
422        assert!(matches!(
423            Aes256::new([0u8; 33].as_slice()),
424            Err(Error::KeyIncorrectLength(AES256_KEY_LENGTH))
425        ));
426        assert!(matches!(
427            Aes128Cbc::new([0u8; 17].as_slice(), [0u8; 16].as_slice()),
428            Err(Error::KeyIncorrectLength(AES128_KEY_LENGTH))
429        ));
430        assert!(matches!(
431            Aes128Cbc::new([0u8; 16].as_slice(), [0u8; 17].as_slice()),
432            Err(Error::IvIncorrectLength)
433        ));
434        assert!(matches!(
435            Aes192Cbc::new([0u8; 25].as_slice(), [0u8; 16].as_slice()),
436            Err(Error::KeyIncorrectLength(AES192_KEY_LENGTH))
437        ));
438        assert!(matches!(
439            Aes192Cbc::new([0u8; 24].as_slice(), [0u8; 17].as_slice()),
440            Err(Error::IvIncorrectLength)
441        ));
442        assert!(matches!(
443            Aes256Cbc::new([0u8; 33].as_slice(), [0u8; 16].as_slice()),
444            Err(Error::KeyIncorrectLength(AES256_KEY_LENGTH))
445        ));
446        assert!(matches!(
447            Aes256Cbc::new([0u8; 32].as_slice(), [0u8; 17].as_slice()),
448            Err(Error::IvIncorrectLength)
449        ));
450
451        let cipher = Aes128::new([0u8; 16].as_slice()).unwrap();
452        assert!(matches!(
453            cipher.encrypt([0u8; 33].as_slice(), [0u8; 33].as_mut_slice()),
454            Err(Error::NonBlockSizeAlignedBuffer)
455        ));
456        assert!(matches!(
457            cipher.encrypt([0u8; 64].as_slice(), [0u8; 32].as_mut_slice()),
458            Err(Error::InsufficientBufferSize)
459        ));
460        assert!(matches!(
461            cipher.decrypt([0u8; 64].as_slice(), [0u8; 32].as_mut_slice()),
462            Err(Error::InsufficientBufferSize)
463        ));
464        assert!(matches!(cipher.decrypt([0u8; 0].as_slice(), [0u8; 32768].as_mut_slice()), Ok(())));
465        assert!(matches!(
466            cipher.decrypt([0u8; crate::AES_BLOCK_SIZE].as_slice(), [0u8; 32768].as_mut_slice()),
467            Ok(())
468        ));
469
470        let cipher = Aes128Cbc::new([0u8; 16].as_slice(), [0u8; 16].as_slice()).unwrap();
471        assert!(matches!(
472            cipher.encrypt([0u8; 33].as_slice(), [0u8; 33].as_mut_slice()),
473            Err(Error::NonBlockSizeAlignedBuffer)
474        ));
475        assert!(matches!(
476            cipher.encrypt([0u8; 64].as_slice(), [0u8; 32].as_mut_slice()),
477            Err(Error::InsufficientBufferSize)
478        ));
479        assert!(matches!(
480            cipher.decrypt([0u8; 64].as_slice(), [0u8; 32].as_mut_slice()),
481            Err(Error::InsufficientBufferSize)
482        ));
483        assert!(matches!(cipher.decrypt([0u8; 0].as_slice(), [0u8; 32768].as_mut_slice()), Ok(())));
484        assert!(matches!(
485            cipher.decrypt([0u8; crate::AES_BLOCK_SIZE].as_slice(), [0u8; 32768].as_mut_slice()),
486            Ok(())
487        ));
488    }
489}