use crate::memguard::SecretBytes;
use crate::secrets_store::padding::Padding;
use crate::secrets_store::{SecretStoreError, SecretStoreResult};
use rand::{thread_rng, RngCore};
use std::io::Write;
pub struct RandomFrontBack;
impl Padding for RandomFrontBack {
fn pad_secret_data(data: &[u8], align: usize) -> SecretStoreResult<SecretBytes> {
let encoded_length = Self::encode_length(data.len());
let effective_length = data.len() + 1 + encoded_length.len();
let pad_length = align - (effective_length % align);
let mut pad_bytes = vec![0u8; pad_length];
let (head_pad, tail_pad) = if pad_bytes.is_empty() {
(&pad_bytes[..], &pad_bytes[..])
} else {
let mut rng = thread_rng();
rng.fill_bytes(&mut pad_bytes);
match pad_bytes.iter().position(|b| *b == 0) {
Some(random_zero) => (&pad_bytes[0..random_zero], &pad_bytes[random_zero..]),
None => {
let pos = rng.next_u64() as usize % pad_bytes.len();
(&pad_bytes[0..pos], &pad_bytes[pos..])
}
}
};
let mut padded_data = SecretBytes::with_capacity(effective_length + head_pad.len() + tail_pad.len());
{
let mut padded_writer = padded_data.borrow_mut();
padded_writer.write_all(head_pad)?;
padded_writer.write_all(&[0u8])?;
padded_writer.write_all(&encoded_length)?;
padded_writer.write_all(data)?;
padded_writer.write_all(tail_pad)?;
}
Ok(padded_data)
}
fn unpad_data(padded: &[u8]) -> SecretStoreResult<&[u8]> {
match padded.iter().position(|b| *b == 0) {
Some(pos) if pos < padded.len() - 1 => {
let (offset, length) = Self::decode_length(&padded[pos + 1..])?;
if pos + 1 + offset + length > padded.len() {
Err(SecretStoreError::Padding)
} else {
Ok(&padded[pos + 1 + offset..pos + 1 + offset + length])
}
}
_ => Err(SecretStoreError::Padding),
}
}
}
impl RandomFrontBack {
fn encode_length(mut length: usize) -> Vec<u8> {
let mut encoded = Vec::with_capacity(8);
loop {
let b = (length & 0x7f) as u8;
length >>= 7;
encoded.push(b);
if length == 0 {
break;
}
}
encoded.reverse();
let prefix = encoded.len() - 1;
for b in &mut encoded[0..prefix] {
*b |= 0x80;
}
encoded
}
fn decode_length(bytes: &[u8]) -> SecretStoreResult<(usize, usize)> {
let mut offset = 0;
let mut length = 0usize;
loop {
if offset >= bytes.len() {
return Err(SecretStoreError::Padding);
}
let b = bytes[offset];
length <<= 7;
length |= (b & 0x7f) as usize;
offset += 1;
if b & 0x80 == 0 {
break;
}
}
Ok((offset, length))
}
}
#[cfg(test)]
mod tests {
use super::*;
use quickcheck::quickcheck;
use spectral::prelude::*;
#[test]
fn test_encode_length() {
assert_that(&RandomFrontBack::encode_length(0)).is_equal_to(vec![0u8]);
assert_that(&RandomFrontBack::encode_length(0x12)).is_equal_to(vec![0x12u8]);
assert_that(&RandomFrontBack::encode_length(0x7f)).is_equal_to(vec![0x7fu8]);
assert_that(&RandomFrontBack::encode_length(0x80)).is_equal_to(vec![0x81u8, 0x0u8]);
assert_that(&RandomFrontBack::encode_length(0x1234)).is_equal_to(vec![0xa4u8, 0x34u8]);
assert_that(&RandomFrontBack::decode_length(&[0u8])).is_ok_containing((1, 0));
assert_that(&RandomFrontBack::decode_length(&[0x12u8])).is_ok_containing((1, 0x12));
assert_that(&RandomFrontBack::decode_length(&[0x7fu8])).is_ok_containing((1, 0x7f));
assert_that(&RandomFrontBack::decode_length(&[0x81u8, 0x0u8])).is_ok_containing((2, 0x80));
assert_that(&RandomFrontBack::decode_length(&[0xa4u8, 0x34u8])).is_ok_containing((2, 0x1234));
}
#[test]
fn test_encode_length_quick() {
fn check_encode_decode(length: usize) -> bool {
let encoded = RandomFrontBack::encode_length(length);
if encoded.is_empty() {
return false;
}
let (offset, actual) = RandomFrontBack::decode_length(&encoded).unwrap();
offset == encoded.len() && actual == length
}
quickcheck(check_encode_decode as fn(usize) -> bool);
}
}