2017-12-25 01:05:27 -05:00
|
|
|
// ring has a garbage API so its use is avoided, but rust-crypto doesn't have RFC-variant poly1305
|
|
|
|
// Instead, we steal rust-crypto's implementation and tweak it to match the RFC.
|
2020-08-10 15:00:09 -04:00
|
|
|
//
|
|
|
|
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
|
|
|
|
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
|
|
|
|
// You may not use this file except in accordance with one or both of these
|
|
|
|
// licenses.
|
|
|
|
//
|
2017-12-25 01:05:27 -05:00
|
|
|
// This is a port of Andrew Moons poly1305-donna
|
|
|
|
// https://github.com/floodyberry/poly1305-donna
|
|
|
|
|
2022-02-17 19:29:59 +00:00
|
|
|
#[cfg(not(fuzzing))]
|
2018-03-19 17:34:51 -04:00
|
|
|
mod real_chachapoly {
|
2018-12-13 17:18:31 -05:00
|
|
|
use util::chacha20::ChaCha20;
|
2018-12-14 13:29:55 -05:00
|
|
|
use util::poly1305::Poly1305;
|
2020-04-27 16:41:54 +02:00
|
|
|
use bitcoin::hashes::cmp::fixed_time_eq;
|
2018-08-02 20:05:14 -04:00
|
|
|
|
2018-03-19 17:34:51 -04:00
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
pub struct ChaCha20Poly1305RFC {
|
2018-12-13 17:18:31 -05:00
|
|
|
cipher: ChaCha20,
|
2018-03-19 17:34:51 -04:00
|
|
|
mac: Poly1305,
|
|
|
|
finished: bool,
|
|
|
|
data_len: usize,
|
|
|
|
aad_len: u64,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ChaCha20Poly1305RFC {
|
|
|
|
#[inline]
|
|
|
|
fn pad_mac_16(mac: &mut Poly1305, len: usize) {
|
|
|
|
if len % 16 != 0 {
|
|
|
|
mac.input(&[0; 16][0..16 - (len % 16)]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pub fn new(key: &[u8], nonce: &[u8], aad: &[u8]) -> ChaCha20Poly1305RFC {
|
|
|
|
assert!(key.len() == 16 || key.len() == 32);
|
|
|
|
assert!(nonce.len() == 12);
|
|
|
|
|
|
|
|
// Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
|
|
|
|
assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
|
|
|
|
|
|
|
|
let mut cipher = ChaCha20::new(key, &nonce[4..]);
|
|
|
|
let mut mac_key = [0u8; 64];
|
|
|
|
let zero_key = [0u8; 64];
|
|
|
|
cipher.process(&zero_key, &mut mac_key);
|
|
|
|
|
|
|
|
let mut mac = Poly1305::new(&mac_key[..32]);
|
|
|
|
mac.input(aad);
|
|
|
|
ChaCha20Poly1305RFC::pad_mac_16(&mut mac, aad.len());
|
|
|
|
|
|
|
|
ChaCha20Poly1305RFC {
|
2020-10-06 16:47:23 -07:00
|
|
|
cipher,
|
|
|
|
mac,
|
2018-03-19 17:34:51 -04:00
|
|
|
finished: false,
|
|
|
|
data_len: 0,
|
|
|
|
aad_len: aad.len() as u64,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-14 15:48:05 -05:00
|
|
|
pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
|
2018-03-19 17:34:51 -04:00
|
|
|
assert!(input.len() == output.len());
|
|
|
|
assert!(self.finished == false);
|
|
|
|
self.cipher.process(input, output);
|
|
|
|
self.data_len += input.len();
|
|
|
|
self.mac.input(output);
|
|
|
|
ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
|
|
|
|
self.finished = true;
|
2021-10-08 22:54:32 +00:00
|
|
|
self.mac.input(&self.aad_len.to_le_bytes());
|
|
|
|
self.mac.input(&(self.data_len as u64).to_le_bytes());
|
2018-03-19 17:34:51 -04:00
|
|
|
self.mac.raw_result(out_tag);
|
|
|
|
}
|
|
|
|
|
2018-12-14 15:48:05 -05:00
|
|
|
pub fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
|
2018-03-19 17:34:51 -04:00
|
|
|
assert!(input.len() == output.len());
|
|
|
|
assert!(self.finished == false);
|
|
|
|
|
|
|
|
self.finished = true;
|
|
|
|
|
|
|
|
self.mac.input(input);
|
|
|
|
|
|
|
|
self.data_len += input.len();
|
|
|
|
ChaCha20Poly1305RFC::pad_mac_16(&mut self.mac, self.data_len);
|
2021-10-08 22:54:32 +00:00
|
|
|
self.mac.input(&self.aad_len.to_le_bytes());
|
|
|
|
self.mac.input(&(self.data_len as u64).to_le_bytes());
|
2018-03-19 17:34:51 -04:00
|
|
|
|
|
|
|
let mut calc_tag = [0u8; 16];
|
|
|
|
self.mac.raw_result(&mut calc_tag);
|
|
|
|
if fixed_time_eq(&calc_tag, tag) {
|
|
|
|
self.cipher.process(input, output);
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-12-25 01:05:27 -05:00
|
|
|
}
|
2022-02-17 19:29:59 +00:00
|
|
|
#[cfg(not(fuzzing))]
|
2018-12-13 17:18:31 -05:00
|
|
|
pub use self::real_chachapoly::ChaCha20Poly1305RFC;
|
2018-03-19 17:34:51 -04:00
|
|
|
|
2022-02-17 19:29:59 +00:00
|
|
|
#[cfg(fuzzing)]
|
2018-03-19 17:34:51 -04:00
|
|
|
mod fuzzy_chachapoly {
|
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
pub struct ChaCha20Poly1305RFC {
|
|
|
|
tag: [u8; 16],
|
|
|
|
finished: bool,
|
|
|
|
}
|
|
|
|
impl ChaCha20Poly1305RFC {
|
|
|
|
pub fn new(key: &[u8], nonce: &[u8], _aad: &[u8]) -> ChaCha20Poly1305RFC {
|
|
|
|
assert!(key.len() == 16 || key.len() == 32);
|
|
|
|
assert!(nonce.len() == 12);
|
|
|
|
|
|
|
|
// Ehh, I'm too lazy to *also* tweak ChaCha20 to make it RFC-compliant
|
|
|
|
assert!(nonce[0] == 0 && nonce[1] == 0 && nonce[2] == 0 && nonce[3] == 0);
|
|
|
|
|
|
|
|
let mut tag = [0; 16];
|
|
|
|
tag.copy_from_slice(&key[0..16]);
|
|
|
|
|
|
|
|
ChaCha20Poly1305RFC {
|
|
|
|
tag,
|
|
|
|
finished: false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-14 15:48:05 -05:00
|
|
|
pub fn encrypt(&mut self, input: &[u8], output: &mut [u8], out_tag: &mut [u8]) {
|
2018-03-19 17:34:51 -04:00
|
|
|
assert!(input.len() == output.len());
|
|
|
|
assert!(self.finished == false);
|
|
|
|
|
|
|
|
output.copy_from_slice(&input);
|
|
|
|
out_tag.copy_from_slice(&self.tag);
|
|
|
|
self.finished = true;
|
|
|
|
}
|
|
|
|
|
2018-12-14 15:48:05 -05:00
|
|
|
pub fn decrypt(&mut self, input: &[u8], output: &mut [u8], tag: &[u8]) -> bool {
|
2018-03-19 17:34:51 -04:00
|
|
|
assert!(input.len() == output.len());
|
|
|
|
assert!(self.finished == false);
|
|
|
|
|
|
|
|
if tag[..] != self.tag[..] { return false; }
|
|
|
|
output.copy_from_slice(input);
|
|
|
|
self.finished = true;
|
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
2017-12-25 01:05:27 -05:00
|
|
|
}
|
2022-02-17 19:29:59 +00:00
|
|
|
#[cfg(fuzzing)]
|
2018-12-13 17:18:31 -05:00
|
|
|
pub use self::fuzzy_chachapoly::ChaCha20Poly1305RFC;
|