crypto_api_chachapoly/
xchacha20.rs

1use crate::{
2    chacha20_ietf::CHACHA20_KEY,
3    core::chacha20::{ hchacha20_hash, chacha20_block }
4};
5use crypto_api::{
6    cipher::{ CipherInfo, Cipher },
7    rng::{ SecureRng, SecKeyGen }
8};
9use std::{ cmp::min, error::Error };
10
11
12/// The maximum amount of bytes that can be processed by this implementation with one key/nonce
13/// combination
14pub const XCHACHA20_MAX: usize = usize::max_value();
15
16/// The size of a XChaCha20 key (256 bits/32 bytes)
17pub const XCHACHA20_KEY: usize = CHACHA20_KEY;
18/// The size of a XChaCha20 nonce (192 bits/24 bytes)
19pub const XCHACHA20_NONCE: usize = 24;
20
21
22/// An implementation of XChaCha20
23pub struct XChaCha20;
24impl XChaCha20 {
25    /// Creates a `Cipher` instance with `XChaCha20` as underlying cipher
26    pub fn cipher() -> Box<dyn Cipher> {
27        Box::new(Self)
28    }
29    
30    /// XORs the bytes in `data` with the XChaCha20 keystream for `key` and `nonce` starting at the
31    /// `n`th block
32    ///
33    /// ## Warning:
34    /// This function panics if
35    ///  - `key` is smaller or larger than 32 bytes/256 bits
36    ///  - `nonce` is smaller or larger than 24 bytes/192 bits
37    ///  - `n` exceeds `2^64 - 1` (which means that `data` must be smaller than `(2^64 - n) * 64`)
38    ///
39    /// __Consider using the `crypto_api`-interface instead of calling this function directly__
40    pub fn xor(key: &[u8], nonce: &[u8], mut n: u64, mut data: &mut[u8]) {
41        // Verify input
42        assert_eq!(XCHACHA20_KEY, key.len());
43        assert_eq!(XCHACHA20_NONCE, nonce.len());
44        
45        // Derive key
46        let (x_nonce, nonce) = nonce.split_at(16);
47        let mut x_key = vec![0; 32];
48        hchacha20_hash(key, x_nonce, &mut x_key);
49        
50        // XOR `data`
51        let mut buf = vec![0; 64];
52        while !data.is_empty() {
53            // Compute next block
54            chacha20_block(&x_key, nonce, n, &mut buf);
55            n = n.checked_add(1).expect("The ChaCha20 block counter must not exceed 2^64 - 1");
56            
57            // Xor block
58            let to_xor = min(data.len(), buf.len());
59            (0..to_xor).for_each(|i| data[i] = xor!(data[i], buf[i]));
60            data = &mut data[to_xor..];
61        }
62    }
63}
64impl SecKeyGen for XChaCha20 {
65    fn new_sec_key(&self, buf: &mut[u8], rng: &mut dyn SecureRng) -> Result<usize, Box<dyn Error + 'static>> {
66        // Verify input
67        vfy_keygen!(XCHACHA20_KEY => buf);
68        
69        // Generate key
70        rng.random(&mut buf[..XCHACHA20_KEY])?;
71        Ok(XCHACHA20_KEY)
72    }
73}
74impl Cipher for XChaCha20 {
75    fn info(&self) -> CipherInfo {
76        CipherInfo {
77            name: "XChaCha20", is_otc: true,
78            key_len_r: XCHACHA20_KEY..(XCHACHA20_KEY + 1),
79            nonce_len_r: XCHACHA20_NONCE..(XCHACHA20_NONCE + 1),
80            aead_tag_len_r: 0..(0 + 1)
81        }
82    }
83    
84    fn encrypted_len_max(&self, plaintext_len: usize) -> usize {
85        plaintext_len
86    }
87    
88    fn encrypt(&self, buf: &mut[u8], plaintext_len: usize, key: &[u8], nonce: &[u8])
89        -> Result<usize, Box<dyn Error + 'static>>
90    {
91        // Verify input
92        vfy_enc!(
93            key => [XCHACHA20_KEY], nonce => [XCHACHA20_NONCE],
94            plaintext_len => [buf, XCHACHA20_MAX]
95        );
96        
97        // Encrypt the data
98        Self::xor(key, nonce, 0, &mut buf[..plaintext_len]);
99        Ok(plaintext_len)
100    }
101    fn encrypt_to(&self, buf: &mut[u8], plaintext: &[u8], key: &[u8], nonce: &[u8])
102        -> Result<usize, Box<dyn Error + 'static>>
103    {
104        // Verify input
105        vfy_enc!(
106            key => [XCHACHA20_KEY], nonce => [XCHACHA20_NONCE],
107            plaintext => [buf, XCHACHA20_MAX]
108        );
109        
110        // Fill `buf` and encrypt the data in place
111        buf[..plaintext.len()].copy_from_slice(plaintext);
112        Self::xor(key, nonce, 0, &mut buf[..plaintext.len()]);
113        Ok(plaintext.len())
114    }
115    
116    fn decrypt(&self, buf: &mut[u8], ciphertext_len: usize, key: &[u8], nonce: &[u8])
117        -> Result<usize, Box<dyn Error + 'static>>
118    {
119        // Verify input
120        vfy_dec!(
121            key => [XCHACHA20_KEY], nonce => [XCHACHA20_NONCE],
122            ciphertext_len => [buf, XCHACHA20_MAX]
123        );
124        
125        // Encrypt the data
126        Self::xor(key, nonce, 0, &mut buf[..ciphertext_len]);
127        Ok(ciphertext_len)
128    }
129    fn decrypt_to(&self, buf: &mut[u8], ciphertext: &[u8], key: &[u8], nonce: &[u8])
130        -> Result<usize, Box<dyn Error + 'static>>
131    {
132        // Verify input
133        vfy_dec!(
134            key => [XCHACHA20_KEY], nonce => [XCHACHA20_NONCE],
135            ciphertext => [buf, XCHACHA20_MAX]
136        );
137        
138        // Fill `buf` and encrypt the data in place
139        buf[..ciphertext.len()].copy_from_slice(ciphertext);
140        Self::xor(key, nonce, 0, &mut buf[..ciphertext.len()]);
141        Ok(ciphertext.len())
142    }
143}