crypto_api_chachapoly/
chachapoly_ietf.rs

1use crate::{ ChachaPolyError, ChaCha20Ietf, Poly1305 };
2use crypto_api::{
3    cipher::{ CipherInfo, Cipher, AeadCipher },
4    rng::{ SecureRng, SecKeyGen }
5};
6use std::error::Error;
7
8
9/// The maximum amount of bytes that can be processed with one key/nonce combination
10#[cfg(target_pointer_width = "64")]
11pub const CHACHAPOLY_MAX: usize = (4_294_967_296 - 1) * 64; // (2^32 - 1) * BLOCK_SIZE
12/// The maximum amount of bytes that can be processed with one key/nonce combination
13#[cfg(target_pointer_width = "32")]
14pub const CHACHAPOLY_MAX: usize = usize::max_value() - 16; // 2^32 - 1 - 16
15
16/// The size of a ChaChaPoly key (256 bits/32 bytes)
17pub const CHACHAPOLY_KEY: usize = 32;
18/// The size of a ChaChaPoly nonce (96 bits/12 bytes)
19pub const CHACHAPOLY_NONCE: usize = 12;
20/// The size of a ChaChaPoly authentication tag
21pub const CHACHAPOLY_TAG: usize = 16;
22
23
24/// Encrypts `data` in place and authenticates it with `ad` into `tag` using `key` and `nonce`
25pub fn chachapoly_seal(data: &mut[u8], tag: &mut[u8], ad: &[u8], key: &[u8], nonce: &[u8]) {
26    // Encrypt the data
27    ChaCha20Ietf::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    ChaCha20Ietf::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`
40pub fn chachapoly_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    ChaCha20Ietf::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 => ChaCha20Ietf::xor(key, nonce, 1, data),
56        false => Err(ChachaPolyError::InvalidData)?
57    })
58}
59
60
61/// An implementation of the
62/// [ChachaPoly-IETF AEAD-construction](https://tools.ietf.org/html/rfc8439)
63pub struct ChachaPolyIetf;
64impl ChachaPolyIetf {
65    /// Creates a `Cipher` instance with `ChachaPolyIetf` as underlying cipher
66    pub fn cipher() -> Box<dyn Cipher> {
67        Box::new(Self)
68    }
69    /// Creates a `AeadCipher` instance with `ChachaPolyIetf` as underlying AEAD cipher
70    pub fn aead_cipher() -> Box<dyn AeadCipher> {
71        Box::new(Self)
72    }
73}
74impl SecKeyGen for ChachaPolyIetf {
75    fn new_sec_key(&self, buf: &mut[u8], rng: &mut dyn SecureRng) -> Result<usize, Box<dyn Error + 'static>> {
76        // Validate input
77        vfy_keygen!(CHACHAPOLY_KEY => buf);
78        
79        // Generate key
80        rng.random(&mut buf[..CHACHAPOLY_KEY])?;
81        Ok(CHACHAPOLY_KEY)
82    }
83}
84impl Cipher for ChachaPolyIetf {
85    fn info(&self) -> CipherInfo {
86        CipherInfo {
87            name: "ChachaPolyIetf", is_otc: true,
88            key_len_r: CHACHAPOLY_KEY..(CHACHAPOLY_KEY + 1),
89            nonce_len_r: CHACHAPOLY_NONCE..(CHACHAPOLY_NONCE + 1),
90            aead_tag_len_r: CHACHAPOLY_TAG..(CHACHAPOLY_TAG + 1)
91        }
92    }
93    
94    fn encrypted_len_max(&self, plaintext_len: usize) -> usize {
95        plaintext_len + 16
96    }
97    
98    fn encrypt(&self, buf: &mut[u8], plaintext_len: usize, key: &[u8], nonce: &[u8])
99        -> Result<usize, Box<dyn Error + 'static>>
100    {
101        self.seal(buf, plaintext_len, &[], key, nonce)
102    }
103    fn encrypt_to(&self, buf: &mut[u8], plaintext: &[u8], key: &[u8], nonce: &[u8])
104        -> Result<usize, Box<dyn Error + 'static>>
105    {
106        self.seal_to(buf, plaintext, &[], key, nonce)
107    }
108    
109    fn decrypt(&self, buf: &mut[u8], ciphertext_len: usize, key: &[u8], nonce: &[u8])
110        -> Result<usize, Box<dyn Error + 'static>>
111    {
112        self.open(buf, ciphertext_len, &[], key, nonce)
113    }
114    fn decrypt_to(&self, buf: &mut[u8], ciphertext: &[u8], key: &[u8], nonce: &[u8])
115        -> Result<usize, Box<dyn Error + 'static>>
116    {
117        self.open_to(buf, ciphertext, &[], key, nonce)
118    }
119}
120impl AeadCipher for ChachaPolyIetf {
121    fn seal(&self, buf: &mut[u8], plaintext_len: usize, ad: &[u8], key: &[u8], nonce: &[u8])
122        -> Result<usize, Box<dyn Error + 'static>>
123    {
124        // Verify input
125        vfy_seal!(
126            key => [CHACHAPOLY_KEY], nonce => [CHACHAPOLY_NONCE],
127            plaintext_len => [buf, CHACHAPOLY_MAX]
128        );
129        
130        // Seal the data
131        let (data, tag) = buf.split_at_mut(plaintext_len);
132        chachapoly_seal(data, &mut tag[..CHACHAPOLY_TAG], ad, key, nonce);
133        Ok(plaintext_len + CHACHAPOLY_TAG)
134    }
135    fn seal_to(&self, buf: &mut[u8], plaintext: &[u8], ad: &[u8], key: &[u8], nonce: &[u8])
136        -> Result<usize, Box<dyn Error + 'static>>
137    {
138        // Verify input
139        vfy_seal!(
140            key => [CHACHAPOLY_KEY], nonce => [CHACHAPOLY_NONCE],
141            plaintext => [buf, CHACHAPOLY_MAX]
142        );
143        
144        // Copy the plaintext into buf and seal in place
145        let (data, tag) = buf.split_at_mut(plaintext.len());
146        data.copy_from_slice(plaintext);
147        chachapoly_seal(data, &mut tag[..CHACHAPOLY_TAG], ad, key, nonce);
148        Ok(plaintext.len() + CHACHAPOLY_TAG)
149    }
150    
151    fn open(&self, buf: &mut[u8], ciphertext_len: usize, ad: &[u8], key: &[u8], nonce: &[u8])
152        -> Result<usize, Box<dyn Error + 'static>>
153    {
154        // Verify input
155        vfy_open!(
156            key => [CHACHAPOLY_KEY], nonce => [CHACHAPOLY_NONCE],
157            ciphertext_len => [buf, CHACHAPOLY_TAG, CHACHAPOLY_MAX]
158        );
159        
160        // Open the data
161        let (data, tag) = buf.split_at_mut(ciphertext_len - CHACHAPOLY_TAG);
162        chachapoly_open(data, &tag[..CHACHAPOLY_TAG], ad, key, nonce)?;
163        Ok(ciphertext_len - CHACHAPOLY_TAG)
164    }
165    fn open_to(&self, buf: &mut[u8], ciphertext: &[u8], ad: &[u8], key: &[u8], nonce: &[u8])
166        -> Result<usize, Box<dyn Error + 'static>>
167    {
168        // Verify input
169        vfy_open!(
170            key => [CHACHAPOLY_KEY], nonce => [CHACHAPOLY_NONCE],
171            ciphertext => [buf, CHACHAPOLY_TAG, CHACHAPOLY_MAX]
172        );
173        
174        // Copy the ciphertext into buf and decrypt in place
175        let (data, tag) = ciphertext.split_at(ciphertext.len() - CHACHAPOLY_TAG);
176        buf[..data.len()].copy_from_slice(data);
177        chachapoly_open(&mut buf[..data.len()], &tag[..CHACHAPOLY_TAG], ad, key, nonce)?;
178        Ok(ciphertext.len() - CHACHAPOLY_TAG)
179    }
180}