crypto_api_chachapoly/
xchachapoly.rs

1use crate::{
2    ChachaPolyError, XChaCha20, Poly1305,
3    chachapoly_ietf::{ CHACHAPOLY_MAX, CHACHAPOLY_KEY, CHACHAPOLY_TAG }
4};
5use crypto_api::{
6    cipher::{ CipherInfo, Cipher, AeadCipher },
7    rng::{ SecureRng, SecKeyGen }
8};
9use std::error::Error;
10
11
12/// The maximum amount of bytes that can be processed with one key/nonce combination
13#[allow(unused)]
14pub const XCHACHAPOLY_MAX: usize = CHACHAPOLY_MAX;
15
16/// The size of a XChaChaPoly key (256 bits/32 bytes)
17pub const XCHACHAPOLY_KEY: usize = CHACHAPOLY_KEY;
18/// The size of a XChaChaPoly nonce (192 bits/24 bytes)
19pub const XCHACHAPOLY_NONCE: usize = 24;
20/// The size of a XChaChaPoly authentication tag
21pub const XCHACHAPOLY_TAG: usize = CHACHAPOLY_TAG;
22
23
24/// Encrypts `data` in place and authenticates it with `ad` into `tag` using `key` and `nonce`
25fn xchachapoly_seal(data: &mut[u8], tag: &mut[u8], ad: &[u8], key: &[u8], nonce: &[u8]) {
26    // Encrypt the data
27    XChaCha20::xor(key, nonce, 1, data);
28    
29    // Create the footer
30    let mut foot = Vec::with_capacity(16);
31    foot.extend_from_slice(&(ad.len() as u64).to_le_bytes());
32    foot.extend_from_slice(&(data.len() as u64).to_le_bytes());
33    
34    // Compute the Poly1305 key and the authentication tag
35    let mut pkey = vec![0; 32];
36    XChaCha20::xor(key, nonce, 0, &mut pkey);
37    Poly1305::chachapoly_auth(tag, ad, data, &foot, &pkey);
38}
39/// Validates `data` with `ad` and decrypts it in place using `key` and `nonce`
40fn xchachapoly_open(data: &mut[u8], tag: &[u8], ad: &[u8], key: &[u8], nonce: &[u8])
41    -> Result<(), Box<dyn Error + 'static>>
42{
43    // Create the footer
44    let mut foot = Vec::with_capacity(16);
45    foot.extend_from_slice(&(ad.len() as u64).to_le_bytes());
46    foot.extend_from_slice(&(data.len() as u64).to_le_bytes());
47    
48    // Compute the Poly1305 key and the authentication tag
49    let (mut pkey, mut vfy_tag) = (vec![0; 32], vec![0; 16]);
50    XChaCha20::xor(key, nonce, 0, &mut pkey);
51    Poly1305::chachapoly_auth(&mut vfy_tag, ad, data, &foot, &pkey);
52    
53    // Validate the recomputed and the original tag
54    Ok(match eq_ct!(&tag, &vfy_tag) {
55        true => XChaCha20::xor(key, nonce, 1, data),
56        false => Err(ChachaPolyError::InvalidData)?
57    })
58}
59
60
61/// An implementation of XChaChaPoly
62pub struct XChachaPoly;
63impl XChachaPoly {
64    /// Creates a `Cipher` instance with `XChachaPolyIetf` as underlying cipher
65    pub fn cipher() -> Box<dyn Cipher> {
66        Box::new(Self)
67    }
68    /// Creates a `AeadCipher` instance with `XChachaPolyIetf` as underlying AEAD cipher
69    pub fn aead_cipher() -> Box<dyn AeadCipher> {
70        Box::new(Self)
71    }
72}
73impl SecKeyGen for XChachaPoly {
74    fn new_sec_key(&self, buf: &mut[u8], rng: &mut dyn SecureRng) -> Result<usize, Box<dyn Error + 'static>> {
75        // Validate input
76        vfy_keygen!(XCHACHAPOLY_KEY => buf);
77        
78        // Generate key
79        rng.random(&mut buf[..XCHACHAPOLY_KEY])?;
80        Ok(XCHACHAPOLY_KEY)
81    }
82}
83impl Cipher for XChachaPoly {
84    fn info(&self) -> CipherInfo {
85        CipherInfo {
86            name: "XChachaPoly", is_otc: true,
87            key_len_r: XCHACHAPOLY_KEY..(XCHACHAPOLY_KEY + 1),
88            nonce_len_r: XCHACHAPOLY_NONCE..(XCHACHAPOLY_NONCE + 1),
89            aead_tag_len_r: XCHACHAPOLY_TAG..(XCHACHAPOLY_TAG + 1)
90        }
91    }
92    
93    fn encrypted_len_max(&self, plaintext_len: usize) -> usize {
94        plaintext_len + 16
95    }
96    
97    fn encrypt(&self, buf: &mut[u8], plaintext_len: usize, key: &[u8], nonce: &[u8])
98        -> Result<usize, Box<dyn Error + 'static>>
99    {
100        self.seal(buf, plaintext_len, &[], key, nonce)
101    }
102    fn encrypt_to(&self, buf: &mut[u8], plaintext: &[u8], key: &[u8], nonce: &[u8])
103        -> Result<usize, Box<dyn Error + 'static>>
104    {
105        self.seal_to(buf, plaintext, &[], key, nonce)
106    }
107    
108    fn decrypt(&self, buf: &mut[u8], ciphertext_len: usize, key: &[u8], nonce: &[u8])
109        -> Result<usize, Box<dyn Error + 'static>>
110    {
111        self.open(buf, ciphertext_len, &[], key, nonce)
112    }
113    fn decrypt_to(&self, buf: &mut[u8], ciphertext: &[u8], key: &[u8], nonce: &[u8])
114        -> Result<usize, Box<dyn Error + 'static>>
115    {
116        self.open_to(buf, ciphertext, &[], key, nonce)
117    }
118}
119impl AeadCipher for XChachaPoly {
120    fn seal(&self, buf: &mut[u8], plaintext_len: usize, ad: &[u8], key: &[u8], nonce: &[u8])
121        -> Result<usize, Box<dyn Error + 'static>>
122    {
123        // Verify input
124        vfy_seal!(
125            key => [XCHACHAPOLY_KEY], nonce => [XCHACHAPOLY_NONCE],
126            plaintext_len => [buf, XCHACHAPOLY_MAX]
127        );
128        
129        // Seal the data
130        let (data, tag) = buf.split_at_mut(plaintext_len);
131        xchachapoly_seal(data, &mut tag[..XCHACHAPOLY_TAG], ad, key, nonce);
132        Ok(plaintext_len + XCHACHAPOLY_TAG)
133    }
134    fn seal_to(&self, buf: &mut[u8], plaintext: &[u8], ad: &[u8], key: &[u8], nonce: &[u8])
135        -> Result<usize, Box<dyn Error + 'static>>
136    {
137        // Verify input
138        vfy_seal!(
139            key => [XCHACHAPOLY_KEY], nonce => [XCHACHAPOLY_NONCE],
140            plaintext => [buf, XCHACHAPOLY_MAX]
141        );
142        
143        // Copy the plaintext into buf and seal in place
144        let (data, tag) = buf.split_at_mut(plaintext.len());
145        data.copy_from_slice(plaintext);
146        xchachapoly_seal(data, &mut tag[..XCHACHAPOLY_TAG], ad, key, nonce);
147        Ok(plaintext.len() + XCHACHAPOLY_TAG)
148    }
149    
150    fn open(&self, buf: &mut[u8], ciphertext_len: usize, ad: &[u8], key: &[u8], nonce: &[u8])
151        -> Result<usize, Box<dyn Error + 'static>>
152    {
153        // Verify input
154        vfy_open!(
155            key => [XCHACHAPOLY_KEY], nonce => [XCHACHAPOLY_NONCE],
156            ciphertext_len => [buf, XCHACHAPOLY_TAG, XCHACHAPOLY_MAX]
157        );
158        
159        // Open the data
160        let (data, tag) = buf.split_at_mut(ciphertext_len - XCHACHAPOLY_TAG);
161        xchachapoly_open(data, &tag[..XCHACHAPOLY_TAG], ad, key, nonce)?;
162        Ok(ciphertext_len - XCHACHAPOLY_TAG)
163    }
164    fn open_to(&self, buf: &mut[u8], ciphertext: &[u8], ad: &[u8], key: &[u8], nonce: &[u8])
165        -> Result<usize, Box<dyn Error + 'static>>
166    {
167        // Verify input
168        vfy_open!(
169            key => [XCHACHAPOLY_KEY], nonce => [XCHACHAPOLY_NONCE],
170            ciphertext => [buf, XCHACHAPOLY_TAG, XCHACHAPOLY_MAX]
171        );
172        
173        // Copy the ciphertext into buf and decrypt in place
174        let (data, tag) = ciphertext.split_at(ciphertext.len() - XCHACHAPOLY_TAG);
175        buf[..data.len()].copy_from_slice(data);
176        xchachapoly_open(&mut buf[..data.len()], &tag[..XCHACHAPOLY_TAG], ad, key, nonce)?;
177        Ok(ciphertext.len() - XCHACHAPOLY_TAG)
178    }
179}