use std::convert::TryInto;
use sodiumoxide::{
base64,
padding::{pad, unpad},
};
use super::error::{Error, Result};
pub type StrBase64 = str;
pub type StringBase64 = String;
pub const SALT_SIZE: usize = 16; pub const PRIVATE_KEY_SIZE: usize = 32; pub const SYMMETRIC_KEY_SIZE: usize = 32; pub const SYMMETRIC_TAG_SIZE: usize = 16; pub const SYMMETRIC_NONCE_SIZE: usize = 24;
pub fn randombytes(size: usize) -> Vec<u8> {
sodiumoxide::randombytes::randombytes(size)
}
pub fn randombytes_array<const N: usize>() -> [u8; N] {
sodiumoxide::randombytes::randombytes(N)
.try_into()
.expect("randombytes() returned a Vec with wrong size")
}
pub fn randombytes_deterministic(size: usize, seed: &[u8; 32]) -> Vec<u8> {
let nonce =
sodiumoxide::crypto::stream::xchacha20::Nonce(*b"LibsodiumDRG\0\0\0\0\0\0\0\0\0\0\0\0");
let key = sodiumoxide::crypto::stream::xchacha20::Key(*seed);
sodiumoxide::crypto::stream::xchacha20::stream(size, &nonce, &key)
}
pub fn memcmp(x: &[u8], y: &[u8]) -> bool {
sodiumoxide::utils::memcmp(x, y)
}
pub fn from_base64(string: &StrBase64) -> Result<Vec<u8>> {
match base64::decode(string, base64::Variant::UrlSafeNoPadding) {
Ok(bytes) => Ok(bytes),
Err(_) => Err(Error::Base64("Failed decoding base64 string")),
}
}
pub fn to_base64(bytes: &[u8]) -> Result<StringBase64> {
Ok(base64::encode(bytes, base64::Variant::UrlSafeNoPadding))
}
pub(crate) fn shuffle<T>(a: &mut [T]) -> Vec<usize> {
let len = a.len();
let mut shuffled_indices: Vec<usize> = (0..len).collect();
for i in 0..len {
let j = i + sodiumoxide::randombytes::randombytes_uniform((len - i) as u32) as usize;
a.swap(i, j);
shuffled_indices.swap(i, j);
}
let mut ret = vec![0; len];
for i in 0..len {
ret[shuffled_indices[i]] = i;
}
ret
}
pub fn get_padding(length: u32) -> u32 {
if length < (1 << 14) {
let size = (1 << 10) - 1;
return (length | size) + 1;
}
let e = (length as f64).log2().floor();
let s = (e.log2().floor() as u32) + 1;
let last_bits = (e as u32) - s;
let bit_mask = (1 << last_bits) - 1;
(length + bit_mask) & !bit_mask
}
pub(crate) fn buffer_pad_small(buf: &[u8]) -> Result<Vec<u8>> {
let len = buf.len();
let padding = len + 1;
buffer_pad_fixed(buf, padding)
}
pub(crate) fn buffer_pad(buf: &[u8]) -> Result<Vec<u8>> {
let len = buf.len();
let padding = get_padding(len as u32) as usize;
buffer_pad_fixed(buf, padding)
}
pub(crate) fn buffer_unpad(buf: &[u8]) -> Result<Vec<u8>> {
let len = buf.len();
buffer_unpad_fixed(buf, len)
}
pub(crate) fn buffer_pad_fixed(buf: &[u8], blocksize: usize) -> Result<Vec<u8>> {
let len = buf.len();
let missing = blocksize - (len % blocksize);
let padding = len + missing;
let mut ret = vec![0; padding];
ret[..len].copy_from_slice(buf);
pad(&mut ret[..], len, blocksize).map_err(|_| Error::Padding("Failed padding"))?;
Ok(ret)
}
pub(crate) fn buffer_unpad_fixed(buf: &[u8], blocksize: usize) -> Result<Vec<u8>> {
let len = buf.len();
if len == 0 {
return Ok(vec![0; 0]);
}
let mut buf = buf.to_vec();
let new_len =
unpad(&buf[..], len, blocksize).map_err(|_| Error::Padding("Failed unpadding"))?;
buf.truncate(new_len);
Ok(buf)
}
pub trait MsgPackSerilization {
type Output;
fn to_msgpack(&self) -> Result<Vec<u8>>;
fn from_msgpack(data: &[u8]) -> Result<Self::Output>;
}
#[cfg(test)]
mod tests {
#[test]
fn padding() {
crate::init().unwrap();
for i in 1..(1 << 14) {
if super::get_padding(i) <= i {
println!("Yo");
assert_eq!(format!("Failed for {}", i), "");
}
}
assert_eq!(super::get_padding(2343242), 2359296);
}
#[test]
fn pad_unpad() {
crate::init().unwrap();
let buf = [0; 1076];
let padded = super::buffer_pad(&buf).unwrap();
let unpadded = super::buffer_unpad(&padded[..]).unwrap();
assert_eq!(unpadded, &buf[..]);
}
#[test]
fn pad_unpad_fixed() {
crate::init().unwrap();
let blocksize = 32;
for i in 0..(blocksize * 2) {
let buf = vec![60; i];
let padded = super::buffer_pad_fixed(&buf, blocksize).unwrap();
let unpadded = super::buffer_unpad_fixed(&padded[..], blocksize).unwrap();
assert_eq!(unpadded, &buf[..]);
}
}
}