wasi-crypto 0.1.15

Experimental implementation of the WASI cryptography APIs
Documentation
use ::xoodyak::*;
use subtle::ConstantTimeEq;
use zeroize::Zeroize;

use super::state::*;
use super::*;
use crate::rand::SecureRandom;

#[derive(Clone, Debug)]
pub struct XoodyakSymmetricState {
    pub alg: SymmetricAlgorithm,
    options: Option<SymmetricOptions>,
    size_limit: Option<usize>,
    xoodyak_state: XoodyakAny,
}

#[derive(Clone, Debug, Eq)]
pub struct XoodyakSymmetricKey {
    alg: SymmetricAlgorithm,
    raw: Vec<u8>,
}

impl PartialEq for XoodyakSymmetricKey {
    fn eq(&self, other: &Self) -> bool {
        self.alg == other.alg && self.raw.ct_eq(&other.raw).unwrap_u8() == 1
    }
}

impl Drop for XoodyakSymmetricKey {
    fn drop(&mut self) {
        self.raw.zeroize();
    }
}

impl SymmetricKeyLike for XoodyakSymmetricKey {
    fn alg(&self) -> SymmetricAlgorithm {
        self.alg
    }

    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_raw(&self) -> Result<&[u8], CryptoError> {
        Ok(&self.raw)
    }
}

impl XoodyakSymmetricKey {
    fn new(alg: SymmetricAlgorithm, raw: &[u8]) -> Result<Self, CryptoError> {
        Ok(XoodyakSymmetricKey {
            alg,
            raw: raw.to_vec(),
        })
    }
}

pub struct XoodyakSymmetricKeyBuilder {
    alg: SymmetricAlgorithm,
}

impl XoodyakSymmetricKeyBuilder {
    pub fn new(alg: SymmetricAlgorithm) -> Box<dyn SymmetricKeyBuilder> {
        Box::new(Self { alg })
    }
}

impl SymmetricKeyBuilder for XoodyakSymmetricKeyBuilder {
    fn generate(&self, _options: Option<SymmetricOptions>) -> Result<SymmetricKey, CryptoError> {
        let mut rng = SecureRandom::new();
        let mut raw = vec![0u8; self.key_len()?];
        rng.fill(&mut raw)?;
        self.import(&raw)
    }

    fn import(&self, raw: &[u8]) -> Result<SymmetricKey, CryptoError> {
        let key = XoodyakSymmetricKey::new(self.alg, raw)?;
        Ok(SymmetricKey::new(Box::new(key)))
    }

    fn key_len(&self) -> Result<usize, CryptoError> {
        match self.alg {
            SymmetricAlgorithm::Xoodyak128 => Ok(16),
            SymmetricAlgorithm::Xoodyak160 => Ok(20),
            _ => bail!(CryptoError::UnsupportedAlgorithm),
        }
    }
}

impl XoodyakSymmetricState {
    pub fn new(
        alg: SymmetricAlgorithm,
        key: Option<SymmetricKey>,
        options: Option<SymmetricOptions>,
        size_limit: Option<usize>,
    ) -> Result<Self, CryptoError> {
        let key = match key {
            None => None,
            Some(key) => {
                let key = key.inner();
                let key = key
                    .as_any()
                    .downcast_ref::<XoodyakSymmetricKey>()
                    .ok_or(CryptoError::InvalidKey)?
                    .clone();
                Some(key)
            }
        };
        let nonce = options
            .as_ref()
            .and_then(|options| options.inner.lock().unwrap().nonce.as_ref().cloned());
        let nonce = nonce.as_deref();
        let xoodyak_state = match key {
            None => XoodyakAny::Hash(XoodyakHash::new()),
            Some(key) => XoodyakAny::Keyed(
                XoodyakKeyed::new(key.as_raw()?, nonce, None, None)
                    .map_err(|_| CryptoError::InvalidKey)?,
            ),
        };
        Ok(XoodyakSymmetricState {
            alg,
            options,
            size_limit,
            xoodyak_state,
        })
    }
}

impl SymmetricStateLike for XoodyakSymmetricState {
    fn alg(&self) -> SymmetricAlgorithm {
        self.alg
    }

    fn options_get(&self, name: &str) -> Result<Vec<u8>, CryptoError> {
        self.options
            .as_ref()
            .ok_or(CryptoError::OptionNotSet)?
            .get(name)
    }

    fn options_get_u64(&self, name: &str) -> Result<u64, CryptoError> {
        self.options
            .as_ref()
            .ok_or(CryptoError::OptionNotSet)?
            .get_u64(name)
    }

    fn size_limit(&self) -> Option<usize> {
        self.size_limit
    }

    fn absorb_unchecked(&mut self, data: &[u8]) -> Result<(), CryptoError> {
        self.xoodyak_state.absorb(data);
        Ok(())
    }

    fn squeeze_unchecked(&mut self, out: &mut [u8]) -> Result<(), CryptoError> {
        self.xoodyak_state.squeeze(out);
        Ok(())
    }

    fn squeeze_key(&mut self, alg_str: &str) -> Result<SymmetricKey, CryptoError> {
        let builder = SymmetricKey::builder(alg_str)?;
        let mut raw = vec![0u8; builder.key_len()?];
        self.xoodyak_state.squeeze_key(&mut raw);
        builder.import(&raw)
    }

    fn squeeze_tag(&mut self) -> Result<SymmetricTag, CryptoError> {
        let mut raw_tag = vec![0u8; XOODYAK_AUTH_TAG_BYTES];
        self.xoodyak_state.squeeze(&mut raw_tag);
        let symmetric_tag = SymmetricTag::new(self.alg(), raw_tag);
        Ok(symmetric_tag)
    }

    fn max_tag_len(&mut self) -> Result<usize, CryptoError> {
        Ok(XOODYAK_AUTH_TAG_BYTES)
    }

    fn encrypt_unchecked(&mut self, out: &mut [u8], data: &[u8]) -> Result<usize, CryptoError> {
        let ct_len = data
            .len()
            .checked_add(XOODYAK_AUTH_TAG_BYTES)
            .ok_or(CryptoError::Overflow)?;
        match self.xoodyak_state.aead_encrypt(out, Some(data)) {
            Err(XoodyakError::KeyRequired) => Err(CryptoError::InvalidOperation),
            Err(_) => Err(CryptoError::Overflow),
            Ok(()) => Ok(ct_len),
        }
    }

    fn encrypt_detached_unchecked(
        &mut self,
        out: &mut [u8],
        data: &[u8],
    ) -> Result<SymmetricTag, CryptoError> {
        match self.xoodyak_state.aead_encrypt_detached(out, Some(data)) {
            Err(XoodyakError::KeyRequired) => Err(CryptoError::InvalidOperation),
            Err(_) => Err(CryptoError::Overflow),
            Ok(xoodyak_tag) => {
                let symmetric_tag = SymmetricTag::new(self.alg(), xoodyak_tag.as_ref().to_vec());
                Ok(symmetric_tag)
            }
        }
    }

    fn decrypt_unchecked(&mut self, out: &mut [u8], data: &[u8]) -> Result<usize, CryptoError> {
        let msg_len = data
            .len()
            .checked_sub(XOODYAK_AUTH_TAG_BYTES)
            .ok_or(CryptoError::Overflow)?;
        match self.xoodyak_state.aead_decrypt(out, data) {
            Err(XoodyakError::KeyRequired) => Err(CryptoError::InvalidOperation),
            Err(_) => Err(CryptoError::Overflow),
            Ok(()) => Ok(msg_len),
        }
    }

    fn decrypt_detached_unchecked(
        &mut self,
        out: &mut [u8],
        data: &[u8],
        raw_tag: &[u8],
    ) -> Result<usize, CryptoError> {
        let msg_len = data.len();
        let mut raw_tag_ = [0u8; XOODYAK_AUTH_TAG_BYTES];
        ensure!(raw_tag.len() == raw_tag_.len(), CryptoError::InvalidTag);
        raw_tag_.copy_from_slice(raw_tag);
        match self
            .xoodyak_state
            .aead_decrypt_detached(out, &raw_tag_.into(), Some(data))
        {
            Err(XoodyakError::KeyRequired) => Err(CryptoError::InvalidOperation),
            Err(_) => Err(CryptoError::InvalidTag),
            Ok(()) => Ok(msg_len),
        }
    }

    fn ratchet(&mut self) -> Result<(), CryptoError> {
        self.xoodyak_state
            .ratchet()
            .map_err(|_| CryptoError::InvalidOperation)
    }
}