wasi-crypto 0.1.9

Experimental implementation of the WASI cryptography APIs
Documentation
use ::sha2::{Sha256, Sha512};
use hmac::{Hmac, Mac};
use subtle::ConstantTimeEq;
use zeroize::Zeroize;

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

#[allow(clippy::large_enum_variant)]
#[derive(Debug, Clone)]
enum HmacVariant {
    Sha256(Hmac<Sha256>),
    Sha512(Hmac<Sha512>),
}

#[derive(Derivative, Clone)]
#[derivative(Debug)]
pub struct HmacSha2SymmetricState {
    pub alg: SymmetricAlgorithm,
    options: Option<SymmetricOptions>,
    size_limit: Option<usize>,
    ctx: HmacVariant,
}

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

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

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

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

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

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

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

pub struct HmacSha2SymmetricKeyBuilder {
    alg: SymmetricAlgorithm,
}

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

impl SymmetricKeyBuilder for HmacSha2SymmetricKeyBuilder {
    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 = HmacSha2SymmetricKey::new(self.alg, raw)?;
        Ok(SymmetricKey::new(Box::new(key)))
    }

    fn key_len(&self) -> Result<usize, CryptoError> {
        match self.alg {
            SymmetricAlgorithm::HmacSha256 => Ok(32),
            SymmetricAlgorithm::HmacSha512 => Ok(64),
            _ => bail!(CryptoError::UnsupportedAlgorithm),
        }
    }
}

impl HmacSha2SymmetricState {
    pub fn new(
        alg: SymmetricAlgorithm,
        key: Option<SymmetricKey>,
        options: Option<SymmetricOptions>,
        size_limit: Option<usize>,
    ) -> Result<Self, CryptoError> {
        let key = key.ok_or(CryptoError::KeyRequired)?;
        let key = key.inner();
        let key = key
            .as_any()
            .downcast_ref::<HmacSha2SymmetricKey>()
            .ok_or(CryptoError::InvalidKey)?;
        let ctx = match alg {
            SymmetricAlgorithm::HmacSha256 => HmacVariant::Sha256(
                Hmac::<Sha256>::new_from_slice(key.as_raw()?)
                    .map_err(|_| CryptoError::InvalidKey)?,
            ),
            SymmetricAlgorithm::HmacSha512 => HmacVariant::Sha512(
                Hmac::<Sha512>::new_from_slice(key.as_raw()?)
                    .map_err(|_| CryptoError::InvalidKey)?,
            ),
            _ => bail!(CryptoError::UnsupportedAlgorithm),
        };
        Ok(HmacSha2SymmetricState {
            alg,
            options,
            size_limit,
            ctx,
        })
    }
}

impl SymmetricStateLike for HmacSha2SymmetricState {
    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> {
        match &mut self.ctx {
            HmacVariant::Sha256(x) => x.update(data),
            HmacVariant::Sha512(x) => x.update(data),
        };
        Ok(())
    }

    fn squeeze_tag(&mut self) -> Result<SymmetricTag, CryptoError> {
        let raw = match &self.ctx {
            HmacVariant::Sha256(x) => x.clone().finalize().into_bytes().to_vec(),
            HmacVariant::Sha512(x) => x.clone().finalize().into_bytes().to_vec(),
        };
        Ok(SymmetricTag::new(self.alg, raw))
    }
}