use std::convert::TryFrom;
use std::sync::{Arc, Mutex, MutexGuard};
use super::*;
use crate::CryptoCtx;
use crate::Limits;
#[derive(Clone)]
pub struct SymmetricState {
inner: Arc<Mutex<Box<dyn SymmetricStateLike>>>,
}
impl SymmetricState {
fn new(symmetric_state_like: Box<dyn SymmetricStateLike>) -> Self {
SymmetricState {
inner: Arc::new(Mutex::new(symmetric_state_like)),
}
}
fn inner(&self) -> MutexGuard<'_, Box<dyn SymmetricStateLike>> {
self.inner.lock().unwrap()
}
fn locked<T, U>(&self, mut f: T) -> U
where
T: FnMut(MutexGuard<'_, Box<dyn SymmetricStateLike>>) -> U,
{
f(self.inner())
}
fn open(
alg_str: &str,
key: Option<SymmetricKey>,
options: Option<SymmetricOptions>,
limits: &Limits,
) -> Result<SymmetricState, CryptoError> {
let alg = SymmetricAlgorithm::try_from(alg_str)?;
if let Some(ref key) = key {
ensure!(key.alg() == alg, CryptoError::InvalidKey);
}
let size_limit = limits.0.get(alg_str).copied();
let symmetric_state = match alg {
SymmetricAlgorithm::HmacSha256 | SymmetricAlgorithm::HmacSha512 => SymmetricState::new(
Box::new(HmacSha2SymmetricState::new(alg, key, options, size_limit)?),
),
SymmetricAlgorithm::Sha256
| SymmetricAlgorithm::Sha384
| SymmetricAlgorithm::Sha512
| SymmetricAlgorithm::Sha512_256 => SymmetricState::new(Box::new(
Sha2SymmetricState::new(alg, None, options, size_limit)?,
)),
SymmetricAlgorithm::HkdfSha256Expand
| SymmetricAlgorithm::HkdfSha512Expand
| SymmetricAlgorithm::HkdfSha256Extract
| SymmetricAlgorithm::HkdfSha512Extract => SymmetricState::new(Box::new(
HkdfSymmetricState::new(alg, key, options, size_limit)?,
)),
SymmetricAlgorithm::Aes128Gcm | SymmetricAlgorithm::Aes256Gcm => SymmetricState::new(
Box::new(AesGcmSymmetricState::new(alg, key, options, size_limit)?),
),
SymmetricAlgorithm::ChaCha20Poly1305 => SymmetricState::new(Box::new(
ChaChaPolySymmetricState::new(alg, key, options, size_limit)?,
)),
SymmetricAlgorithm::XChaCha20Poly1305 => SymmetricState::new(Box::new(
ChaChaPolySymmetricState::new(alg, key, options, size_limit)?,
)),
SymmetricAlgorithm::Xoodyak128 | SymmetricAlgorithm::Xoodyak160 => SymmetricState::new(
Box::new(XoodyakSymmetricState::new(alg, key, options, size_limit)?),
),
_ => bail!(CryptoError::UnsupportedAlgorithm),
};
Ok(symmetric_state)
}
}
pub trait SymmetricStateLike: Sync + Send {
fn alg(&self) -> SymmetricAlgorithm;
fn options_get(&self, name: &str) -> Result<Vec<u8>, CryptoError>;
fn options_get_u64(&self, name: &str) -> Result<u64, CryptoError>;
fn size_limit(&self) -> Option<usize>;
fn absorb_unchecked(&mut self, _data: &[u8]) -> Result<(), CryptoError> {
bail!(CryptoError::InvalidOperation)
}
fn absorb(&mut self, data: &[u8]) -> Result<(), CryptoError> {
ensure!(
self.size_limit().map_or(true, |l| data.len() <= l),
CryptoError::Overflow
);
self.absorb_unchecked(data)
}
fn squeeze_unchecked(&mut self, _out: &mut [u8]) -> Result<(), CryptoError> {
bail!(CryptoError::InvalidOperation)
}
fn squeeze(&mut self, out: &mut [u8]) -> Result<(), CryptoError> {
ensure!(
self.size_limit().map_or(true, |l| out.len() <= l),
CryptoError::Overflow
);
self.squeeze_unchecked(out)
}
fn squeeze_key(&mut self, _alg_str: &str) -> Result<SymmetricKey, CryptoError> {
bail!(CryptoError::InvalidOperation)
}
fn squeeze_tag(&mut self) -> Result<SymmetricTag, CryptoError> {
bail!(CryptoError::InvalidOperation)
}
fn max_tag_len(&mut self) -> Result<usize, CryptoError> {
bail!(CryptoError::InvalidOperation)
}
fn encrypt_unchecked(&mut self, _out: &mut [u8], _data: &[u8]) -> Result<usize, CryptoError> {
bail!(CryptoError::InvalidOperation)
}
fn encrypt(&mut self, out: &mut [u8], data: &[u8]) -> Result<usize, CryptoError> {
ensure!(
out.len()
== data
.len()
.checked_add(self.max_tag_len()?)
.ok_or(CryptoError::Overflow)?,
CryptoError::InvalidLength
);
ensure!(
self.size_limit().map_or(true, |l| data.len() <= l),
CryptoError::Overflow
);
self.encrypt_unchecked(out, data)
}
fn encrypt_detached_unchecked(
&mut self,
_out: &mut [u8],
_data: &[u8],
) -> Result<SymmetricTag, CryptoError> {
bail!(CryptoError::InvalidOperation)
}
fn encrypt_detached(
&mut self,
out: &mut [u8],
data: &[u8],
) -> Result<SymmetricTag, CryptoError> {
ensure!(out.len() == data.len(), CryptoError::InvalidLength);
ensure!(
self.size_limit().map_or(true, |l| data.len() <= l),
CryptoError::Overflow
);
self.encrypt_detached_unchecked(out, data)
}
fn decrypt_unchecked(&mut self, _out: &mut [u8], _data: &[u8]) -> Result<usize, CryptoError> {
bail!(CryptoError::InvalidOperation)
}
fn decrypt(&mut self, out: &mut [u8], data: &[u8]) -> Result<usize, CryptoError> {
ensure!(
out.len()
== data
.len()
.checked_sub(self.max_tag_len()?)
.ok_or(CryptoError::Overflow)?,
CryptoError::Overflow
);
ensure!(
self.size_limit().map_or(true, |l| data.len() <= l),
CryptoError::Overflow
);
match self.decrypt_unchecked(out, data) {
Ok(out_len) => Ok(out_len),
Err(e) => {
out.iter_mut().for_each(|x| *x = 0);
Err(e)
}
}
}
fn decrypt_detached_unchecked(
&mut self,
_out: &mut [u8],
_data: &[u8],
_raw_tag: &[u8],
) -> Result<usize, CryptoError> {
bail!(CryptoError::InvalidOperation)
}
fn decrypt_detached(
&mut self,
out: &mut [u8],
data: &[u8],
raw_tag: &[u8],
) -> Result<usize, CryptoError> {
ensure!(out.len() == data.len(), CryptoError::InvalidLength);
ensure!(
self.size_limit().map_or(true, |l| data.len() <= l),
CryptoError::Overflow
);
match self.decrypt_detached_unchecked(out, data, raw_tag) {
Ok(out_len) => Ok(out_len),
Err(e) => {
out.iter_mut().for_each(|x| *x = 0);
Err(e)
}
}
}
fn ratchet(&mut self) -> Result<(), CryptoError> {
bail!(CryptoError::InvalidOperation)
}
}
impl CryptoCtx {
pub fn symmetric_state_open(
&self,
alg_str: &str,
key_handle: Option<Handle>,
options_handle: Option<Handle>,
) -> Result<Handle, CryptoError> {
let key = match key_handle {
None => None,
Some(symmetric_key_handle) => {
Some(self.handles.symmetric_key.get(symmetric_key_handle)?)
}
};
let options = match options_handle {
None => None,
Some(options_handle) => {
Some(self.handles.options.get(options_handle)?.into_symmetric()?)
}
};
let symmetric_state = SymmetricState::open(alg_str, key, options, &self.limits)?;
let handle = self.handles.symmetric_state.register(symmetric_state)?;
Ok(handle)
}
pub fn symmetric_state_options_get(
&self,
symmetric_state_handle: Handle,
name: &str,
value: &mut [u8],
) -> Result<usize, CryptoError> {
let symmetric_state = self.handles.symmetric_state.get(symmetric_state_handle)?;
let v = symmetric_state.inner().options_get(name)?;
let v_len = v.len();
ensure!(v_len <= value.len(), CryptoError::Overflow);
value[..v_len].copy_from_slice(&v);
Ok(v_len)
}
pub fn symmetric_state_options_get_u64(
&self,
symmetric_state_handle: Handle,
name: &str,
) -> Result<u64, CryptoError> {
let symmetric_state = self.handles.symmetric_state.get(symmetric_state_handle)?;
let v = symmetric_state.inner().options_get_u64(name)?;
Ok(v)
}
pub fn symmetric_state_close(&self, symmetric_state_handle: Handle) -> Result<(), CryptoError> {
self.handles.symmetric_state.close(symmetric_state_handle)
}
pub fn symmetric_state_clone(
&self,
symmetric_state_handle: Handle,
) -> Result<Handle, CryptoError> {
let symmetric_state = self.handles.symmetric_state.get(symmetric_state_handle)?;
let symmetric_state = symmetric_state.clone();
let handle = self.handles.symmetric_state.register(symmetric_state)?;
Ok(handle)
}
pub fn symmetric_state_absorb(
&self,
symmetric_state_handle: Handle,
data: &[u8],
) -> Result<(), CryptoError> {
let symmetric_state = self.handles.symmetric_state.get(symmetric_state_handle)?;
symmetric_state.locked(|mut state| state.absorb_unchecked(data))
}
pub fn symmetric_state_squeeze(
&self,
symmetric_state_handle: Handle,
out: &mut [u8],
) -> Result<(), CryptoError> {
let symmetric_state = self.handles.symmetric_state.get(symmetric_state_handle)?;
symmetric_state.locked(|mut state| state.squeeze_unchecked(out))
}
pub fn symmetric_state_squeeze_tag(
&self,
symmetric_state_handle: Handle,
) -> Result<Handle, CryptoError> {
let symmetric_state = self.handles.symmetric_state.get(symmetric_state_handle)?;
let tag = symmetric_state.locked(|mut state| state.squeeze_tag())?;
let handle = self.handles.symmetric_tag.register(tag)?;
Ok(handle)
}
pub fn symmetric_state_squeeze_key(
&self,
symmetric_state_handle: Handle,
alg_str: &str,
) -> Result<Handle, CryptoError> {
let symmetric_state = self.handles.symmetric_state.get(symmetric_state_handle)?;
let symmetric_key = symmetric_state.locked(|mut state| state.squeeze_key(alg_str))?;
let handle = self.handles.symmetric_key.register(symmetric_key)?;
Ok(handle)
}
pub fn symmetric_state_max_tag_len(
&self,
symmetric_state_handle: Handle,
) -> Result<usize, CryptoError> {
let symmetric_state = self.handles.symmetric_state.get(symmetric_state_handle)?;
let max_tag_len = symmetric_state.inner().max_tag_len()?;
Ok(max_tag_len)
}
pub fn symmetric_state_encrypt(
&self,
symmetric_state_handle: Handle,
out: &mut [u8],
data: &[u8],
) -> Result<usize, CryptoError> {
let symmetric_state = self.handles.symmetric_state.get(symmetric_state_handle)?;
symmetric_state.locked(|mut state| state.encrypt(out, data))
}
pub fn symmetric_state_encrypt_detached(
&self,
symmetric_state_handle: Handle,
out: &mut [u8],
data: &[u8],
) -> Result<Handle, CryptoError> {
let symmetric_state = self.handles.symmetric_state.get(symmetric_state_handle)?;
let symmetric_tag = symmetric_state.inner().encrypt_detached(out, data)?;
let handle = self.handles.symmetric_tag.register(symmetric_tag)?;
Ok(handle)
}
pub fn symmetric_state_decrypt(
&self,
symmetric_state_handle: Handle,
out: &mut [u8],
data: &[u8],
) -> Result<usize, CryptoError> {
let symmetric_state = self.handles.symmetric_state.get(symmetric_state_handle)?;
symmetric_state.locked(|mut state| state.decrypt(out, data))
}
pub fn symmetric_state_decrypt_detached(
&self,
symmetric_state_handle: Handle,
out: &mut [u8],
data: &[u8],
raw_tag: &[u8],
) -> Result<usize, CryptoError> {
let symmetric_state = self.handles.symmetric_state.get(symmetric_state_handle)?;
symmetric_state.locked(|mut state| state.decrypt_detached(out, data, raw_tag))
}
pub fn symmetric_state_ratchet(
&self,
symmetric_state_handle: Handle,
) -> Result<(), CryptoError> {
let symmetric_state = self.handles.symmetric_state.get(symmetric_state_handle)?;
symmetric_state.locked(|mut state| state.ratchet())
}
}