1#![forbid(unsafe_code)]
2
3use aes::cipher::{BlockCipherEncrypt, KeyInit};
22use aes::{Aes128, Aes256};
23use chacha20::cipher::{KeyIvInit, StreamCipher, StreamCipherSeek};
24use chacha20::ChaCha20;
25use oxicrypto_core::CryptoError;
26
27pub const AES_BLOCK_LEN: usize = 16;
29
30pub const AES128_KEY_LEN: usize = 16;
32
33pub const AES256_KEY_LEN: usize = 32;
35
36pub fn aes128_encrypt_block(key: &[u8], block: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
49 if key.len() != AES128_KEY_LEN {
50 return Err(CryptoError::InvalidKey);
51 }
52 if block.len() != AES_BLOCK_LEN {
53 return Err(CryptoError::BadInput);
54 }
55 if out.len() < AES_BLOCK_LEN {
56 return Err(CryptoError::BufferTooSmall);
57 }
58 let cipher = Aes128::new_from_slice(key).map_err(|_| CryptoError::InvalidKey)?;
59 let mut buf: aes::Block = aes::Block::try_from(block).map_err(|_| CryptoError::BadInput)?;
60 cipher.encrypt_block(&mut buf);
61 out[..AES_BLOCK_LEN].copy_from_slice(&buf);
62 Ok(())
63}
64
65pub fn aes256_encrypt_block(key: &[u8], block: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
74 if key.len() != AES256_KEY_LEN {
75 return Err(CryptoError::InvalidKey);
76 }
77 if block.len() != AES_BLOCK_LEN {
78 return Err(CryptoError::BadInput);
79 }
80 if out.len() < AES_BLOCK_LEN {
81 return Err(CryptoError::BufferTooSmall);
82 }
83 let cipher = Aes256::new_from_slice(key).map_err(|_| CryptoError::InvalidKey)?;
84 let mut buf: aes::Block = aes::Block::try_from(block).map_err(|_| CryptoError::BadInput)?;
85 cipher.encrypt_block(&mut buf);
86 out[..AES_BLOCK_LEN].copy_from_slice(&buf);
87 Ok(())
88}
89
90pub const CHACHA20_KEY_LEN: usize = 32;
92
93pub const CHACHA20_NONCE_LEN: usize = 12;
95
96pub fn chacha20_keystream_block(
113 key: &[u8],
114 counter: u32,
115 nonce: &[u8],
116 out: &mut [u8],
117) -> Result<(), CryptoError> {
118 if key.len() != CHACHA20_KEY_LEN {
119 return Err(CryptoError::InvalidKey);
120 }
121 if nonce.len() != CHACHA20_NONCE_LEN {
122 return Err(CryptoError::InvalidNonce);
123 }
124 if out.is_empty() {
125 return Err(CryptoError::BadInput);
126 }
127
128 let key_arr: chacha20::Key =
129 chacha20::Key::try_from(key).map_err(|_| CryptoError::InvalidKey)?;
130 let nonce_arr: chacha20::cipher::Iv<ChaCha20> =
131 chacha20::cipher::Iv::<ChaCha20>::try_from(nonce).map_err(|_| CryptoError::InvalidNonce)?;
132 let mut cipher = ChaCha20::new(&key_arr, &nonce_arr);
133 let byte_offset = u64::from(counter)
135 .checked_mul(64)
136 .ok_or(CryptoError::BadInput)?;
137 cipher.seek(byte_offset);
138 for b in out.iter_mut() {
140 *b = 0;
141 }
142 cipher.apply_keystream(out);
143 Ok(())
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149
150 fn hex_decode(s: &str) -> Vec<u8> {
151 (0..s.len())
152 .step_by(2)
153 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).expect("valid hex"))
154 .collect()
155 }
156
157 #[test]
160 fn aes128_fips197_appendix_b() {
161 let key = hex_decode("000102030405060708090a0b0c0d0e0f");
162 let pt = hex_decode("00112233445566778899aabbccddeeff");
163 let mut out = [0u8; 16];
164 aes128_encrypt_block(&key, &pt, &mut out).expect("aes128");
165 assert_eq!(out.to_vec(), hex_decode("69c4e0d86a7b0430d8cdb78070b4c55a"));
166 }
167
168 #[test]
171 fn aes256_fips197_appendix_c() {
172 let key = hex_decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
173 let pt = hex_decode("00112233445566778899aabbccddeeff");
174 let mut out = [0u8; 16];
175 aes256_encrypt_block(&key, &pt, &mut out).expect("aes256");
176 assert_eq!(out.to_vec(), hex_decode("8ea2b7ca516745bfeafc49904b496089"));
177 }
178
179 #[test]
187 fn rfc9001_a5_chacha20_header_mask() {
188 let hp = hex_decode("25a282b9e82f06f21f488917a4fc8f1b73573685608597d0efcb076b0ab7a7a4");
189 let sample = hex_decode("5e5cd55c41f69080575d7999c25a5bfb");
190 let counter = u32::from_le_bytes([sample[0], sample[1], sample[2], sample[3]]);
192 let nonce = &sample[4..16];
193 let mut mask = [0u8; 5];
194 chacha20_keystream_block(&hp, counter, nonce, &mut mask).expect("ks");
195 assert_eq!(
196 mask.to_vec(),
197 hex_decode("aefefe7d03"),
198 "RFC 9001 A.5 mask mismatch"
199 );
200 }
201
202 #[test]
203 fn aes_invalid_lengths() {
204 let mut out = [0u8; 16];
205 assert_eq!(
206 aes128_encrypt_block(&[0u8; 15], &[0u8; 16], &mut out),
207 Err(CryptoError::InvalidKey)
208 );
209 assert_eq!(
210 aes128_encrypt_block(&[0u8; 16], &[0u8; 15], &mut out),
211 Err(CryptoError::BadInput)
212 );
213 assert_eq!(
214 aes256_encrypt_block(&[0u8; 32], &[0u8; 16], &mut [0u8; 8]),
215 Err(CryptoError::BufferTooSmall)
216 );
217 }
218
219 #[test]
220 fn chacha20_invalid_lengths() {
221 let mut out = [0u8; 5];
222 assert_eq!(
223 chacha20_keystream_block(&[0u8; 31], 0, &[0u8; 12], &mut out),
224 Err(CryptoError::InvalidKey)
225 );
226 assert_eq!(
227 chacha20_keystream_block(&[0u8; 32], 0, &[0u8; 11], &mut out),
228 Err(CryptoError::InvalidNonce)
229 );
230 assert_eq!(
231 chacha20_keystream_block(&[0u8; 32], 0, &[0u8; 12], &mut []),
232 Err(CryptoError::BadInput)
233 );
234 }
235
236 #[test]
239 fn chacha20_keystream_deterministic() {
240 let key = [0x01u8; 32];
241 let nonce = [0x02u8; 12];
242 let mut a = [0u8; 16];
243 let mut b = [0u8; 16];
244 chacha20_keystream_block(&key, 1, &nonce, &mut a).expect("a");
245 chacha20_keystream_block(&key, 1, &nonce, &mut b).expect("b");
246 assert_eq!(a, b);
247 assert_ne!(a, [0u8; 16]);
248 }
249}