wasi-crypto 0.1.9

Experimental implementation of the WASI cryptography APIs
Documentation
use std::convert::TryFrom;
use std::sync::Arc;

use ::sha2::{Digest, Sha256, Sha384};
use k256::ecdsa::{
    self as ecdsa_k256, signature::DigestVerifier as _, signature::RandomizedDigestSigner as _,
};
use k256::elliptic_curve::sec1::ToEncodedPoint as _;
use k256::pkcs8::{
    DecodePrivateKey as _, DecodePublicKey as _, EncodePrivateKey as _, EncodePublicKey as _,
};
use p256::ecdsa::{
    self as ecdsa_p256, signature::DigestVerifier as _, signature::RandomizedDigestSigner as _,
};
use p256::elliptic_curve::sec1::ToEncodedPoint as _;
use p256::pkcs8::{
    DecodePrivateKey as _, DecodePublicKey as _, EncodePrivateKey as _, EncodePublicKey as _,
};
use p384::ecdsa::{
    self as ecdsa_p384, signature::DigestVerifier as _, signature::RandomizedDigestSigner as _,
};
use p384::elliptic_curve::sec1::ToEncodedPoint as _;
use p384::pkcs8::{
    DecodePrivateKey as _, DecodePublicKey as _, EncodePrivateKey as _, EncodePublicKey as _,
};

use super::signature::*;
use super::*;
use crate::asymmetric_common::*;
use crate::error::*;
use crate::rand::SecureRandom;

#[derive(Debug, Clone)]
pub struct EcdsaSignatureSecretKey {
    pub alg: SignatureAlgorithm,
}

enum EcdsaSigningKeyVariant {
    P256(ecdsa_p256::SigningKey),
    K256(ecdsa_k256::SigningKey),
    P384(ecdsa_p384::SigningKey),
}

#[derive(Clone, Derivative)]
#[derivative(Debug)]
pub struct EcdsaSignatureKeyPair {
    pub alg: SignatureAlgorithm,
    #[derivative(Debug = "ignore")]
    ctx: Arc<EcdsaSigningKeyVariant>,
}

impl EcdsaSignatureKeyPair {
    fn from_raw(alg: SignatureAlgorithm, raw: &[u8]) -> Result<Self, CryptoError> {
        let ctx = match alg {
            SignatureAlgorithm::ECDSA_P256_SHA256 => {
                let ecdsa_sk =
                    ecdsa_p256::SigningKey::from_bytes(raw).map_err(|_| CryptoError::InvalidKey)?;
                EcdsaSigningKeyVariant::P256(ecdsa_sk)
            }
            SignatureAlgorithm::ECDSA_K256_SHA256 => {
                let ecdsa_sk =
                    ecdsa_k256::SigningKey::from_bytes(raw).map_err(|_| CryptoError::InvalidKey)?;
                EcdsaSigningKeyVariant::K256(ecdsa_sk)
            }
            SignatureAlgorithm::ECDSA_P384_SHA384 => {
                let ecdsa_sk =
                    ecdsa_p384::SigningKey::from_bytes(raw).map_err(|_| CryptoError::InvalidKey)?;
                EcdsaSigningKeyVariant::P384(ecdsa_sk)
            }
            _ => bail!(CryptoError::UnsupportedAlgorithm),
        };
        Ok(EcdsaSignatureKeyPair {
            alg,
            ctx: Arc::new(ctx),
        })
    }

    fn from_pkcs8(alg: SignatureAlgorithm, pkcs8: &[u8]) -> Result<Self, CryptoError> {
        let ctx = match alg {
            SignatureAlgorithm::ECDSA_K256_SHA256 => {
                let ecdsa_sk = ecdsa_k256::SigningKey::from_pkcs8_der(pkcs8)
                    .map_err(|_| CryptoError::InvalidKey)?;
                EcdsaSigningKeyVariant::K256(ecdsa_sk)
            }
            SignatureAlgorithm::ECDSA_P384_SHA384 => {
                let ecdsa_sk = ecdsa_p384::SigningKey::from_pkcs8_der(pkcs8)
                    .map_err(|_| CryptoError::InvalidKey)?;
                EcdsaSigningKeyVariant::P384(ecdsa_sk)
            }
            _ => bail!(CryptoError::UnsupportedAlgorithm),
        };
        Ok(EcdsaSignatureKeyPair {
            alg,
            ctx: Arc::new(ctx),
        })
    }

    fn from_pem(alg: SignatureAlgorithm, pem: &[u8]) -> Result<Self, CryptoError> {
        let ctx = match alg {
            SignatureAlgorithm::ECDSA_K256_SHA256 => {
                let ecdsa_sk = ecdsa_k256::SigningKey::from_pkcs8_pem(
                    std::str::from_utf8(pem).map_err(|_| CryptoError::InvalidKey)?,
                )
                .map_err(|_| CryptoError::InvalidKey)?;
                EcdsaSigningKeyVariant::K256(ecdsa_sk)
            }
            SignatureAlgorithm::ECDSA_P384_SHA384 => {
                let ecdsa_sk = ecdsa_p384::SigningKey::from_pkcs8_pem(
                    std::str::from_utf8(pem).map_err(|_| CryptoError::InvalidKey)?,
                )
                .map_err(|_| CryptoError::InvalidKey)?;
                EcdsaSigningKeyVariant::P384(ecdsa_sk)
            }
            _ => bail!(CryptoError::UnsupportedAlgorithm),
        };
        Ok(EcdsaSignatureKeyPair {
            alg,
            ctx: Arc::new(ctx),
        })
    }

    fn as_raw(&self) -> Result<Vec<u8>, CryptoError> {
        let raw = match self.ctx.as_ref() {
            EcdsaSigningKeyVariant::P256(x) => x.to_bytes().to_vec(),
            EcdsaSigningKeyVariant::K256(x) => x.to_bytes().to_vec(),
            EcdsaSigningKeyVariant::P384(x) => x.to_bytes().to_vec(),
        };
        Ok(raw)
    }

    pub fn generate(
        alg: SignatureAlgorithm,
        _options: Option<SignatureOptions>,
    ) -> Result<Self, CryptoError> {
        let mut rng = SecureRandom::new();
        match alg {
            SignatureAlgorithm::ECDSA_P256_SHA256 => {
                let ecdsa_sk = ecdsa_p256::SigningKey::random(&mut rng);
                Self::from_raw(alg, ecdsa_sk.to_bytes().as_slice())
            }
            SignatureAlgorithm::ECDSA_K256_SHA256 => {
                let ecdsa_sk = ecdsa_k256::SigningKey::random(&mut rng);
                Self::from_raw(alg, ecdsa_sk.to_bytes().as_slice())
            }
            SignatureAlgorithm::ECDSA_P384_SHA384 => {
                let ecdsa_sk = ecdsa_p384::SigningKey::random(&mut rng);
                Self::from_raw(alg, ecdsa_sk.to_bytes().as_slice())
            }
            _ => bail!(CryptoError::UnsupportedAlgorithm),
        }
    }

    pub fn import(
        alg: SignatureAlgorithm,
        encoded: &[u8],
        encoding: KeyPairEncoding,
    ) -> Result<Self, CryptoError> {
        ensure!(
            alg == SignatureAlgorithm::ECDSA_P256_SHA256
                || alg == SignatureAlgorithm::ECDSA_K256_SHA256,
            CryptoError::UnsupportedAlgorithm
        );
        let kp = match encoding {
            KeyPairEncoding::Raw => EcdsaSignatureKeyPair::from_raw(alg, encoded)?,
            KeyPairEncoding::Pkcs8 => EcdsaSignatureKeyPair::from_pkcs8(alg, encoded)?,
            KeyPairEncoding::Pem => EcdsaSignatureKeyPair::from_pem(alg, encoded)?,
            _ => bail!(CryptoError::UnsupportedEncoding),
        };
        Ok(kp)
    }

    pub fn export(&self, encoding: KeyPairEncoding) -> Result<Vec<u8>, CryptoError> {
        match encoding {
            KeyPairEncoding::Raw => self.as_raw(),
            _ => bail!(CryptoError::UnsupportedEncoding),
        }
    }

    pub fn public_key(&self) -> Result<EcdsaSignaturePublicKey, CryptoError> {
        let ctx = match self.ctx.as_ref() {
            EcdsaSigningKeyVariant::P256(x) => EcdsaVerifyingKeyVariant::P256(x.verifying_key()),
            EcdsaSigningKeyVariant::K256(x) => EcdsaVerifyingKeyVariant::K256(x.verifying_key()),
            EcdsaSigningKeyVariant::P384(x) => EcdsaVerifyingKeyVariant::P384(x.verifying_key()),
        };
        Ok(EcdsaSignaturePublicKey {
            alg: self.alg,
            ctx: Arc::new(ctx),
        })
    }
}

#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
enum HashVariant {
    Sha256(Sha256),
    Sha384(Sha384),
}

#[derive(Debug)]
pub struct EcdsaSignatureState {
    pub kp: EcdsaSignatureKeyPair,
    h: HashVariant,
}

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

impl EcdsaSignature {
    pub fn new(raw: Vec<u8>) -> Self {
        EcdsaSignature { raw }
    }

    pub fn from_raw(alg: SignatureAlgorithm, raw: &[u8]) -> Result<Self, CryptoError> {
        let expected_len = match alg {
            SignatureAlgorithm::ECDSA_P256_SHA256 => 64,
            SignatureAlgorithm::ECDSA_K256_SHA256 | SignatureAlgorithm::ECDSA_P384_SHA384 => 96,
            _ => bail!(CryptoError::InvalidSignature),
        };
        ensure!(raw.len() == expected_len, CryptoError::InvalidSignature);
        Ok(Self::new(raw.to_vec()))
    }
}

impl SignatureLike for EcdsaSignature {
    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_ref(&self) -> &[u8] {
        &self.raw
    }
}

impl EcdsaSignatureState {
    pub fn new(kp: EcdsaSignatureKeyPair) -> Self {
        let h = match kp.alg {
            SignatureAlgorithm::ECDSA_P384_SHA384 => HashVariant::Sha384(Sha384::new()),
            _ => HashVariant::Sha256(Sha256::new()),
        };
        EcdsaSignatureState { kp, h }
    }
}

impl SignatureStateLike for EcdsaSignatureState {
    fn update(&mut self, input: &[u8]) -> Result<(), CryptoError> {
        match &mut self.h {
            HashVariant::Sha256(x) => x.update(input),
            HashVariant::Sha384(x) => x.update(input),
        };
        Ok(())
    }

    fn sign(&mut self) -> Result<Signature, CryptoError> {
        let mut rng = SecureRandom::new();
        let encoded_signature = match self.kp.ctx.as_ref() {
            EcdsaSigningKeyVariant::P256(x) => {
                let digest = match &self.h {
                    HashVariant::Sha256(x) => x.clone(),
                    _ => {
                        bail!(CryptoError::UnsupportedAlgorithm)
                    }
                };
                let encoded_signature: ecdsa_p256::Signature =
                    x.sign_digest_with_rng(&mut rng, digest);
                encoded_signature.as_ref().to_vec()
            }
            EcdsaSigningKeyVariant::K256(x) => {
                let digest = match &self.h {
                    HashVariant::Sha256(x) => x.clone(),
                    _ => {
                        bail!(CryptoError::UnsupportedAlgorithm)
                    }
                };
                let encoded_signature: ecdsa_k256::Signature =
                    x.sign_digest_with_rng(&mut rng, digest);
                encoded_signature.as_ref().to_vec()
            }
            EcdsaSigningKeyVariant::P384(x) => {
                let digest = match &self.h {
                    HashVariant::Sha384(x) => x.clone(),
                    _ => {
                        bail!(CryptoError::UnsupportedAlgorithm)
                    }
                };
                let encoded_signature: ecdsa_p384::Signature =
                    x.sign_digest_with_rng(&mut rng, digest);
                encoded_signature.as_ref().to_vec()
            }
        };
        let signature = EcdsaSignature::new(encoded_signature);
        Ok(Signature::new(Box::new(signature)))
    }
}

#[derive(Debug)]
pub struct EcdsaSignatureVerificationState {
    pub pk: EcdsaSignaturePublicKey,
    h: HashVariant,
}

impl EcdsaSignatureVerificationState {
    pub fn new(pk: EcdsaSignaturePublicKey) -> Self {
        let h = match pk.alg {
            SignatureAlgorithm::ECDSA_P384_SHA384 => HashVariant::Sha384(Sha384::new()),
            SignatureAlgorithm::ECDSA_P256_SHA256 | SignatureAlgorithm::ECDSA_K256_SHA256 => {
                HashVariant::Sha256(Sha256::new())
            }
            _ => unreachable!(),
        };
        EcdsaSignatureVerificationState { pk, h }
    }
}

impl SignatureVerificationStateLike for EcdsaSignatureVerificationState {
    fn update(&mut self, input: &[u8]) -> Result<(), CryptoError> {
        match &mut self.h {
            HashVariant::Sha256(x) => x.update(input),
            HashVariant::Sha384(x) => x.update(input),
        };
        Ok(())
    }

    fn verify(&self, signature: &Signature) -> Result<(), CryptoError> {
        let signature = signature.inner();
        let signature = signature
            .as_any()
            .downcast_ref::<EcdsaSignature>()
            .ok_or(CryptoError::InvalidSignature)?;

        match self.pk.ctx.as_ref() {
            EcdsaVerifyingKeyVariant::P256(x) => {
                let ecdsa_signature = ecdsa_p256::Signature::try_from(signature.as_ref())
                    .map_err(|_| CryptoError::VerificationFailed)?;
                let digest = match &self.h {
                    HashVariant::Sha256(x) => x.clone(),
                    _ => {
                        bail!(CryptoError::UnsupportedAlgorithm)
                    }
                };
                x.verify_digest(digest, &ecdsa_signature)
            }
            EcdsaVerifyingKeyVariant::K256(x) => {
                let ecdsa_signature = ecdsa_k256::Signature::try_from(signature.as_ref())
                    .map_err(|_| CryptoError::VerificationFailed)?;
                let digest = match &self.h {
                    HashVariant::Sha256(x) => x.clone(),
                    _ => {
                        bail!(CryptoError::UnsupportedAlgorithm)
                    }
                };
                x.verify_digest(digest, &ecdsa_signature)
            }
            EcdsaVerifyingKeyVariant::P384(x) => {
                let ecdsa_signature = ecdsa_p384::Signature::try_from(signature.as_ref())
                    .map_err(|_| CryptoError::VerificationFailed)?;
                let digest = match &self.h {
                    HashVariant::Sha384(x) => x.clone(),
                    _ => {
                        bail!(CryptoError::UnsupportedAlgorithm)
                    }
                };
                x.verify_digest(digest, &ecdsa_signature)
            }
        }
        .map_err(|_| CryptoError::VerificationFailed)?;
        Ok(())
    }
}

enum EcdsaVerifyingKeyVariant {
    P256(ecdsa_p256::VerifyingKey),
    K256(ecdsa_k256::VerifyingKey),
    P384(ecdsa_p384::VerifyingKey),
}

#[derive(Clone, Derivative)]
#[derivative(Debug)]
pub struct EcdsaSignaturePublicKey {
    pub alg: SignatureAlgorithm,
    #[derivative(Debug = "ignore")]
    ctx: Arc<EcdsaVerifyingKeyVariant>,
}

impl EcdsaSignaturePublicKey {
    fn from_sec(alg: SignatureAlgorithm, sec: &[u8]) -> Result<Self, CryptoError> {
        let ctx = match alg {
            SignatureAlgorithm::ECDSA_P256_SHA256 => {
                let ecdsa_sk = ecdsa_p256::VerifyingKey::from_sec1_bytes(sec)
                    .map_err(|_| CryptoError::InvalidKey)?;
                EcdsaVerifyingKeyVariant::P256(ecdsa_sk)
            }
            SignatureAlgorithm::ECDSA_K256_SHA256 => {
                let ecdsa_sk = ecdsa_k256::VerifyingKey::from_sec1_bytes(sec)
                    .map_err(|_| CryptoError::InvalidKey)?;
                EcdsaVerifyingKeyVariant::K256(ecdsa_sk)
            }
            SignatureAlgorithm::ECDSA_P384_SHA384 => {
                let ecdsa_sk = ecdsa_p384::VerifyingKey::from_sec1_bytes(sec)
                    .map_err(|_| CryptoError::InvalidKey)?;
                EcdsaVerifyingKeyVariant::P384(ecdsa_sk)
            }
            _ => bail!(CryptoError::UnsupportedAlgorithm),
        };
        let pk = EcdsaSignaturePublicKey {
            alg,
            ctx: Arc::new(ctx),
        };
        Ok(pk)
    }

    fn from_pkcs8(alg: SignatureAlgorithm, pkcs8: &[u8]) -> Result<Self, CryptoError> {
        let ctx = match alg {
            SignatureAlgorithm::ECDSA_K256_SHA256 => {
                let ecdsa_sk = ecdsa_k256::VerifyingKey::from_public_key_der(pkcs8)
                    .map_err(|_| CryptoError::InvalidKey)?;
                EcdsaVerifyingKeyVariant::K256(ecdsa_sk)
            }
            SignatureAlgorithm::ECDSA_P384_SHA384 => {
                let ecdsa_sk = ecdsa_p384::VerifyingKey::from_public_key_der(pkcs8)
                    .map_err(|_| CryptoError::InvalidKey)?;
                EcdsaVerifyingKeyVariant::P384(ecdsa_sk)
            }
            _ => bail!(CryptoError::UnsupportedAlgorithm),
        };
        let pk = EcdsaSignaturePublicKey {
            alg,
            ctx: Arc::new(ctx),
        };
        Ok(pk)
    }

    fn from_pem(alg: SignatureAlgorithm, pem: &[u8]) -> Result<Self, CryptoError> {
        let ctx = match alg {
            SignatureAlgorithm::ECDSA_K256_SHA256 => {
                let ecdsa_sk = ecdsa_k256::VerifyingKey::from_public_key_pem(
                    std::str::from_utf8(pem).map_err(|_| CryptoError::InvalidKey)?,
                )
                .map_err(|_| CryptoError::InvalidKey)?;
                EcdsaVerifyingKeyVariant::K256(ecdsa_sk)
            }
            SignatureAlgorithm::ECDSA_P384_SHA384 => {
                let ecdsa_sk = ecdsa_p384::VerifyingKey::from_public_key_pem(
                    std::str::from_utf8(pem).map_err(|_| CryptoError::InvalidKey)?,
                )
                .map_err(|_| CryptoError::InvalidKey)?;
                EcdsaVerifyingKeyVariant::P384(ecdsa_sk)
            }
            _ => bail!(CryptoError::UnsupportedAlgorithm),
        };
        let pk = EcdsaSignaturePublicKey {
            alg,
            ctx: Arc::new(ctx),
        };
        Ok(pk)
    }

    fn from_raw(alg: SignatureAlgorithm, raw: &[u8]) -> Result<Self, CryptoError> {
        Self::from_sec(alg, raw)
    }

    fn as_sec(&self, compress: bool) -> Result<Vec<u8>, CryptoError> {
        let raw = match self.ctx.as_ref() {
            EcdsaVerifyingKeyVariant::P256(x) => x.to_encoded_point(compress).to_bytes().to_vec(),
            EcdsaVerifyingKeyVariant::K256(x) => x.to_encoded_point(compress).to_bytes().to_vec(),
            EcdsaVerifyingKeyVariant::P384(x) => x.to_encoded_point(compress).to_bytes().to_vec(),
        };
        Ok(raw)
    }

    fn as_raw(&self) -> Result<Vec<u8>, CryptoError> {
        self.as_sec(true)
    }

    pub fn import(
        alg: SignatureAlgorithm,
        encoded: &[u8],
        encoding: PublicKeyEncoding,
    ) -> Result<Self, CryptoError> {
        match encoding {
            PublicKeyEncoding::Raw => Self::from_raw(alg, encoded),
            PublicKeyEncoding::Sec => Self::from_sec(alg, encoded),
            PublicKeyEncoding::Pkcs8 => Self::from_pkcs8(alg, encoded),
            PublicKeyEncoding::Pem => Self::from_pem(alg, encoded),
            _ => bail!(CryptoError::UnsupportedEncoding),
        }
    }

    pub fn export(&self, encoding: PublicKeyEncoding) -> Result<Vec<u8>, CryptoError> {
        match encoding {
            PublicKeyEncoding::Raw => self.as_raw(),
            PublicKeyEncoding::Sec => self.as_sec(false),
            _ => bail!(CryptoError::UnsupportedEncoding),
        }
    }
}