use std::cmp::{min, Ordering};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops::{Deref, DerefMut};
const TRACE: bool = false;
pub struct Protected(*mut [u8]);
unsafe impl Send for Protected {}
unsafe impl Sync for Protected {}
impl Clone for Protected {
fn clone(&self) -> Self {
let mut p = Vec::with_capacity(self.len());
p.extend_from_slice(self);
p.into_boxed_slice().into()
}
}
impl PartialEq for Protected {
fn eq(&self, other: &Self) -> bool {
secure_cmp(self, other) == Ordering::Equal
}
}
impl Eq for Protected {}
impl Hash for Protected {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_ref().hash(state);
}
}
impl Protected {
pub fn new(size: usize) -> Protected {
vec![0; size].into_boxed_slice().into()
}
pub(crate) fn expose_into_unprotected_vec(self) -> Vec<u8> {
let mut p = Vec::with_capacity(self.len());
p.extend_from_slice(&self);
p
}
}
impl Deref for Protected {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl AsRef<[u8]> for Protected {
fn as_ref(&self) -> &[u8] {
unsafe { &*self.0 }
}
}
impl AsMut<[u8]> for Protected {
fn as_mut(&mut self) -> &mut [u8] {
unsafe { &mut *self.0 }
}
}
impl DerefMut for Protected {
fn deref_mut(&mut self) -> &mut [u8] {
self.as_mut()
}
}
impl From<Vec<u8>> for Protected {
fn from(mut v: Vec<u8>) -> Self {
let p = Protected::from(&v[..]);
let capacity = v.capacity();
unsafe {
v.set_len(capacity);
memsec::memzero(v.as_mut_ptr(), capacity);
}
p
}
}
#[allow(dead_code)]
#[inline(never)]
pub(crate) fn zero_stack_after<const N: usize, T>(fun: impl FnOnce() -> T) -> T
{
zero_stack::<N, T>(fun())
}
#[allow(dead_code)]
#[inline(never)]
pub(crate) fn zero_stack<const N: usize, T>(v: T) -> T {
tracer!(TRACE, "zero_stack");
let mut a = [0xffu8; N];
t!("zeroing {:?}..{:?}", a.as_ptr(), unsafe { a.as_ptr().offset(N as _) });
unsafe {
memsec::memzero(a.as_mut_ptr(), a.len());
}
std::hint::black_box(a);
v
}
pub(crate) fn careful_memcpy(from: &[u8], to: &mut [u8]) {
from.iter().zip(to.iter_mut()).for_each(|(f, t)| *t = *f);
}
impl From<Box<[u8]>> for Protected {
fn from(v: Box<[u8]>) -> Self {
Protected(Box::leak(v))
}
}
impl From<&[u8]> for Protected {
fn from(v: &[u8]) -> Self {
let mut p = Protected::new(v.len());
careful_memcpy(v, &mut p);
p
}
}
impl<const N: usize> From<[u8; N]> for Protected {
fn from(mut v: [u8; N]) -> Self {
let mut p = Protected::new(v.len());
careful_memcpy(&v, &mut p);
unsafe {
memsec::memzero(v.as_mut_ptr(), v.len());
}
p
}
}
impl Drop for Protected {
fn drop(&mut self) {
unsafe {
let len = self.len();
memsec::memzero(self.as_mut().as_mut_ptr(), len);
drop(Box::from_raw(self.0));
}
}
}
impl fmt::Debug for Protected {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if cfg!(debug_assertions) {
write!(f, "{:?}", self.0)
} else {
f.write_str("[<Redacted>]")
}
}
}
#[derive(Clone, Debug)]
pub struct Encrypted {
ciphertext: Protected,
salt: [u8; 32],
plaintext_len: usize,
}
assert_send_and_sync!(Encrypted);
impl PartialEq for Encrypted {
fn eq(&self, other: &Self) -> bool {
self.map(|a| other.map(|b| a == b))
}
}
impl Eq for Encrypted {}
impl Hash for Encrypted {
fn hash<H: Hasher>(&self, state: &mut H) {
self.map(|k| Hash::hash(k, state));
}
}
const DANGER_DISABLE_ENCRYPTED_MEMORY: bool = false;
const ENCRYPTED_MEMORY_PREKEY_PAGES: usize = 4;
const ENCRYPTED_MEMORY_PAGE_SIZE: usize = 4096;
mod has_access_to_prekey {
use std::io::{self, Read, Write};
use buffered_reader::Memory;
use crate::types::{AEADAlgorithm, HashAlgorithm, SymmetricAlgorithm};
use crate::crypto::{aead, SessionKey};
use crate::crypto::hash::Digest;
use super::*;
lazy_static::lazy_static! {
static ref PREKEY: Box<[Box<[u8]>]> = {
let mut pages = Vec::new();
for _ in 0..ENCRYPTED_MEMORY_PREKEY_PAGES {
let mut page = vec![0; ENCRYPTED_MEMORY_PAGE_SIZE];
crate::crypto::random(&mut page);
pages.push(page.into());
}
pages.into()
};
}
const HASH_ALGO: HashAlgorithm = HashAlgorithm::SHA256;
const SYMMETRIC_ALGO: SymmetricAlgorithm = SymmetricAlgorithm::AES256;
const AEAD_ALGO: AEADAlgorithm = AEADAlgorithm::const_default();
impl Encrypted {
fn sealing_key(salt: &[u8; 32]) -> SessionKey {
let mut ctx = HASH_ALGO.context()
.expect("Mandatory algorithm unsupported");
ctx.update(salt);
PREKEY.iter().for_each(|page| ctx.update(page));
let mut sk: SessionKey = Protected::new(256/8).into();
let _ = ctx.digest(&mut sk);
sk
}
pub fn new(p: Protected) -> Self {
if DANGER_DISABLE_ENCRYPTED_MEMORY {
return Encrypted {
plaintext_len: p.len(),
ciphertext: p,
salt: Default::default(),
};
}
let mut salt = [0; 32];
crate::crypto::random(&mut salt);
let mut ciphertext = Protected::new(
p.len() + 2 * AEAD_ALGO.digest_size().expect("supported"));
{
let mut encryptor =
aead::Encryptor::new(SYMMETRIC_ALGO,
AEAD_ALGO,
p.len(),
CounterSchedule::default(),
Self::sealing_key(&salt),
io::Cursor::new(&mut ciphertext[..]))
.expect("Mandatory algorithm unsupported");
encryptor.write_all(&p).unwrap();
encryptor.finish().unwrap();
}
Encrypted {
plaintext_len: p.len(),
ciphertext,
salt,
}
}
pub fn map<F, T>(&self, mut fun: F) -> T
where F: FnMut(&Protected) -> T
{
if DANGER_DISABLE_ENCRYPTED_MEMORY {
return fun(&self.ciphertext);
}
let ciphertext =
Memory::with_cookie(&self.ciphertext, Default::default());
let mut plaintext = Protected::new(self.plaintext_len);
let mut decryptor =
aead::Decryptor::from_cookie_reader(
SYMMETRIC_ALGO,
AEAD_ALGO,
self.plaintext_len,
CounterSchedule::default(),
Self::sealing_key(&self.salt),
Box::new(ciphertext))
.expect("Mandatory algorithm unsupported");
let r = decryptor.read_exact(&mut plaintext);
if r.is_err() {
drop(plaintext); panic!("Encrypted memory modified or corrupted");
}
fun(&plaintext)
}
}
#[derive(Default)]
struct CounterSchedule {}
impl aead::Schedule for CounterSchedule {
fn next_chunk<F, R>(&self, index: u64, mut fun: F) -> R
where
F: FnMut(&[u8], &[u8]) -> R,
{
let mut nonce_store = [0u8; aead::MAX_NONCE_LEN];
let nonce_len = AEAD_ALGO.nonce_size()
.expect("Mandatory algorithm unsupported");
assert!(nonce_len >= 8);
let nonce = &mut nonce_store[..nonce_len];
let index_be: [u8; 8] = index.to_be_bytes();
nonce[nonce_len - 8..].copy_from_slice(&index_be);
fun(nonce, &[])
}
fn final_chunk<F, R>(&self, index: u64, length: u64, mut fun: F) -> R
where
F: FnMut(&[u8], &[u8]) -> R
{
let mut nonce_store = [0u8; aead::MAX_NONCE_LEN];
let nonce_len = AEAD_ALGO.nonce_size()
.expect("Mandatory algorithm unsupported");
assert!(nonce_len >= 8);
let nonce = &mut nonce_store[..nonce_len];
let index_be: [u8; 8] = index.to_be_bytes();
nonce[nonce_len - 8..].copy_from_slice(&index_be);
let aad: [u8; 8] = length.to_be_bytes();
fun(nonce, &aad)
}
}
}
pub fn secure_cmp(a: &[u8], b: &[u8]) -> Ordering {
let ord1 = a.len().cmp(&b.len());
let ord2 = unsafe {
memsec::memcmp(a.as_ptr(), b.as_ptr(), min(a.len(), b.len()))
};
let ord2 = match ord2 {
1..=std::i32::MAX => Ordering::Greater,
0 => Ordering::Equal,
std::i32::MIN..=-1 => Ordering::Less,
};
if ord1 == Ordering::Equal { ord2 } else { ord1 }
}