use super::*;
fn xor(a: Block, b: Block) -> Block {
[a[0] ^ b[0], a[1] ^ b[1]]
}
fn counter(nonce: Block, i: usize) -> Block {
[nonce[0], nonce[1].wrapping_add(i as u64)]
}
fn random(blocks: &mut [Block]) {
if let Err(_) = getrandom::fill(dataview::bytes_mut(blocks)) {
random_error()
}
}
#[inline(never)]
#[cold]
fn random_error() -> ! {
panic!("random unavailable")
}
#[inline(never)]
pub fn encrypt_section(blocks: &mut [Block], section: &mut Section, &key: &Key) {
random(slice::from_mut(&mut section.nonce));
let rk = cipher::expand(key);
let rke = cipher::expand(cipher::encrypt(counter(section.nonce, 0), &rk));
let rkm = cipher::expand(cipher::encrypt(counter(section.nonce, 1), &rk));
let ne = cipher::encrypt(counter(section.nonce, 2), &rk);
let nm = cipher::encrypt(counter(section.nonce, 3), &rk);
let mut mac = nm;
for i in 0..blocks.len() {
let pt = blocks[i];
let ct = xor(cipher::encrypt(counter(ne, i), &rke), pt);
mac = cipher::encrypt(xor(mac, ct), &rkm);
blocks[i] = ct;
}
section.mac = mac;
}
#[inline(never)]
pub fn decrypt_section(blocks: &mut [Block], section: &Section, &key: &Key) -> bool {
let rk = cipher::expand(key);
let rke = cipher::expand(cipher::encrypt(counter(section.nonce, 0), &rk));
let rkm = cipher::expand(cipher::encrypt(counter(section.nonce, 1), &rk));
let ne = cipher::encrypt(counter(section.nonce, 2), &rk);
let nm = cipher::encrypt(counter(section.nonce, 3), &rk);
let mut mac = nm;
for i in 0..blocks.len() {
let ct = blocks[i];
let pt = xor(cipher::encrypt(counter(ne, i), &rke), ct);
mac = cipher::encrypt(xor(mac, ct), &rkm);
blocks[i] = pt;
}
section.mac[0] ^ mac[0] | section.mac[1] ^ mac[1] == 0
}
#[inline(never)]
pub fn verify_section(blocks: &[Block], section: &Section, &key: &Key) -> bool {
let rk = cipher::expand(key);
let rkm = cipher::expand(cipher::encrypt(counter(section.nonce, 1), &rk));
let nm = cipher::encrypt(counter(section.nonce, 3), &rk);
let mut mac = nm;
for &ct in blocks {
mac = cipher::encrypt(xor(mac, ct), &rkm);
}
section.mac[0] ^ mac[0] | section.mac[1] ^ mac[1] == 0
}
#[inline]
pub fn decrypt_desc_unchecked(desc: &Descriptor, section: &Section, block_offset: usize, &key: &Key) -> Descriptor {
let rk = cipher::expand(key);
let rke = cipher::expand(cipher::encrypt(counter(section.nonce, 0), &rk));
let ne = cipher::encrypt(counter(section.nonce, 2), &rk);
let mut blocks = *desc.as_ref();
for i in 0..Descriptor::BLOCKS_LEN {
blocks[i] = xor(cipher::encrypt(counter(ne, block_offset + i), &rke), blocks[i]);
}
Descriptor::from(blocks)
}
#[test]
fn test_roundtrip() {
let data = [[1, 2], [3, 4], [5, !0]];
let ref key = [13, 42];
let mut blocks = data;
let mut section = Section {
offset: 0,
size: 3,
nonce: Block::default(),
mac: Block::default(),
};
encrypt_section(&mut blocks, &mut section, key);
eprintln!("{:#?}", section);
assert!(decrypt_section(&mut blocks, §ion, key));
assert_eq!(data, blocks);
}
#[inline]
pub fn encrypt_header(header: &mut Header, key: &Key) {
header.info.version = InfoHeader::VERSION;
header.info._unused = 0;
let mut section = Section::default();
crypt::encrypt_section(header.info.as_mut(), &mut section, key);
header.nonce = section.nonce;
header.mac = section.mac;
}
#[inline]
pub fn decrypt_header(header: &mut Header, key: &Key) -> bool {
let section = Section {
nonce: header.nonce,
mac: header.mac,
..Header::SECTION
};
crypt::decrypt_section(header.info.as_mut(), §ion, key)
&& header.info.version == InfoHeader::VERSION
}