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#[allow(unused)]
14pub const XCHACHAPOLY_MAX: usize = CHACHAPOLY_MAX;
15
16pub const XCHACHAPOLY_KEY: usize = CHACHAPOLY_KEY;
18pub const XCHACHAPOLY_NONCE: usize = 24;
20pub const XCHACHAPOLY_TAG: usize = CHACHAPOLY_TAG;
22
23
24fn xchachapoly_seal(data: &mut[u8], tag: &mut[u8], ad: &[u8], key: &[u8], nonce: &[u8]) {
26 XChaCha20::xor(key, nonce, 1, data);
28
29 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 let mut pkey = vec![0; 32];
36 XChaCha20::xor(key, nonce, 0, &mut pkey);
37 Poly1305::chachapoly_auth(tag, ad, data, &foot, &pkey);
38}
39fn xchachapoly_open(data: &mut[u8], tag: &[u8], ad: &[u8], key: &[u8], nonce: &[u8])
41 -> Result<(), Box<dyn Error + 'static>>
42{
43 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 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 Ok(match eq_ct!(&tag, &vfy_tag) {
55 true => XChaCha20::xor(key, nonce, 1, data),
56 false => Err(ChachaPolyError::InvalidData)?
57 })
58}
59
60
61pub struct XChachaPoly;
63impl XChachaPoly {
64 pub fn cipher() -> Box<dyn Cipher> {
66 Box::new(Self)
67 }
68 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 vfy_keygen!(XCHACHAPOLY_KEY => buf);
77
78 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 vfy_seal!(
125 key => [XCHACHAPOLY_KEY], nonce => [XCHACHAPOLY_NONCE],
126 plaintext_len => [buf, XCHACHAPOLY_MAX]
127 );
128
129 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 vfy_seal!(
139 key => [XCHACHAPOLY_KEY], nonce => [XCHACHAPOLY_NONCE],
140 plaintext => [buf, XCHACHAPOLY_MAX]
141 );
142
143 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 vfy_open!(
155 key => [XCHACHAPOLY_KEY], nonce => [XCHACHAPOLY_NONCE],
156 ciphertext_len => [buf, XCHACHAPOLY_TAG, XCHACHAPOLY_MAX]
157 );
158
159 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 vfy_open!(
169 key => [XCHACHAPOLY_KEY], nonce => [XCHACHAPOLY_NONCE],
170 ciphertext => [buf, XCHACHAPOLY_TAG, XCHACHAPOLY_MAX]
171 );
172
173 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}