Skip to content

Commit

Permalink
feat: use wasi-crypto as an optionl wasi feature
Browse files Browse the repository at this point in the history
Signed-off-by: Richard Zak <richard@profian.com>
  • Loading branch information
rjzak committed Oct 26, 2022
1 parent 30c5cc1 commit 7f2de29
Show file tree
Hide file tree
Showing 9 changed files with 225 additions and 6 deletions.
10 changes: 9 additions & 1 deletion .cargo/config
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
[env]
RUST_BACKTRACE = "1"
WASMTIME_BACKTRACE_DETAILS = "1"

[build]
target = "wasm32-wasi"

[target.wasm32-wasi]
rustflags = ["--cfg", "tokio_unstable"]
runner = ["enarx", "run", "--wasmcfgfile", "Enarx.toml"]
# runner = ["./enarx", "run", "--wasmcfgfile", "Enarx.toml"]
runner = ["/home/rjzak/bin/wasmtime-wasi-crypto", "--wasi-modules", "experimental-wasi-crypto", "--"]
30 changes: 30 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ confargs = { version = "^0.1.3", default-features = false }
[target.'cfg(not(target_os = "wasi"))'.dependencies]
tokio = { version = "^1.21.2", features = ["rt-multi-thread", "macros"], default-features = false }

[target.'cfg(target_os = "wasi")'.dependencies]
wasi-crypto-guest = { git = "https://github.com/WebAssembly/wasi-crypto", branch = "main", optional = true }

[dev-dependencies]
tower = { version = "^0.4.11", features = ["util"], default-features = false }
axum = { version = "^0.5.17", default-features = false }
Expand All @@ -50,6 +53,10 @@ memoffset = { version = "0.7.1", default-features = false }
rstest = { version = "0.15", default-features = false }
testaso = { version = "0.1", default-features = false }

[features]
default = []
wasi-crypto = ["dep:wasi-crypto-guest"]

[profile.release]
incremental = false
codegen-units = 1
Expand Down
2 changes: 1 addition & 1 deletion rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[toolchain]
channel = "stable"
channel = "nightly"
components = [ "rustfmt", "clippy" ]
profile = "minimal"
targets = [
Expand Down
51 changes: 51 additions & 0 deletions src/crypto/hashing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-FileCopyrightText: 2022 Profian Inc. <opensource@profian.com>
// SPDX-License-Identifier: AGPL-3.0-only

use anyhow::Result;

#[cfg(all(target_os = "wasi", feature = "wasi-crypto"))]
use anyhow::anyhow;
#[cfg(all(target_os = "wasi", feature = "wasi-crypto"))]
use wasi_crypto_guest::prelude::Hash;

#[cfg(any(not(target_os = "wasi"), not(feature = "wasi-crypto")))]
use sha2::{Digest, Sha256, Sha384};

#[inline]
pub fn sha256(data: impl AsRef<[u8]>) -> Result<Vec<u8>> {
#[cfg(all(target_os = "wasi", feature = "wasi-crypto"))]
return Ok(Hash::hash("SHA-256", data, 32, None).or_else(|_| Err(anyhow!("hash error")))?);

#[cfg(any(not(target_os = "wasi"), not(feature = "wasi-crypto")))]
Ok(Sha256::digest(data).as_slice().to_vec())
}

#[inline]
pub fn sha384(data: impl AsRef<[u8]>) -> Result<Vec<u8>> {
#[cfg(all(target_os = "wasi", feature = "wasi-crypto"))]
return Ok(Hash::hash("SHA-384", data, 48, None).or_else(|_| Err(anyhow!("hash error")))?);

#[cfg(any(not(target_os = "wasi"), not(feature = "wasi-crypto")))]
Ok(Sha384::digest(data).as_slice().to_vec())
}

#[cfg(all(target_os = "wasi", feature = "wasi-crypto"))]
#[cfg(test)]
mod wasi_crypto {
use crate::{sha256, sha384};
use sha2::Digest;

const DATA: &[u8] = b"SOME_TEST_DATA";

#[test]
fn test_sha256() {
let hash = sha256(DATA).unwrap();
assert_eq!(hash, sha2::Sha256::digest(DATA).as_slice());
}

#[test]
fn test_sha384() {
let hash = sha384(DATA).unwrap();
assert_eq!(hash, sha2::Sha384::digest(DATA).as_slice());
}
}
2 changes: 2 additions & 0 deletions src/crypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@

mod cert;
mod certreq;
mod hashing;
mod pki;
mod spki;

pub use self::cert::TbsCertificateExt;
pub use self::certreq::{CertReqExt, CertReqInfoExt};
pub use self::hashing::{sha256, sha384};
pub use self::pki::PrivateKeyInfoExt;
pub use self::spki::SubjectPublicKeyInfoExt;
123 changes: 123 additions & 0 deletions src/crypto/spki.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,22 @@
use anyhow::{anyhow, Result};
use const_oid::ObjectIdentifier;
use der::{asn1::AnyRef, Sequence};

use rsa::pkcs1::DecodeRsaPublicKey;
#[cfg(all(target_os = "wasi", feature = "wasi-crypto"))]
use spki::EncodePublicKey;
use spki::{AlgorithmIdentifier, SubjectPublicKeyInfo};

use const_oid::db::rfc5912::{
ECDSA_WITH_SHA_256, ECDSA_WITH_SHA_384, ID_EC_PUBLIC_KEY as ECPK, ID_MGF_1, ID_RSASSA_PSS,
ID_SHA_256 as SHA256, ID_SHA_384 as SHA384, ID_SHA_512 as SHA512, RSA_ENCRYPTION as RSA,
SECP_256_R_1 as P256, SECP_384_R_1 as P384,
};
#[cfg(all(target_os = "wasi", feature = "wasi-crypto"))]
use der::pem::LineEnding;

#[cfg(all(target_os = "wasi", feature = "wasi-crypto"))]
use wasi_crypto_guest::signatures::{Signature, SignaturePublicKey};

const ES256: (ObjectIdentifier, Option<AnyRef<'static>>) = (ECDSA_WITH_SHA_256, None);
const ES384: (ObjectIdentifier, Option<AnyRef<'static>>) = (ECDSA_WITH_SHA_384, None);
Expand Down Expand Up @@ -43,6 +51,121 @@ pub trait SubjectPublicKeyInfoExt {
}

impl<'a> SubjectPublicKeyInfoExt for SubjectPublicKeyInfo<'a> {
#[cfg(all(target_os = "wasi", feature = "wasi-crypto"))]
fn verify(&self, body: &[u8], algo: AlgorithmIdentifier<'_>, sign: &[u8]) -> Result<()> {
let algo_name = match (self.algorithm.oids()?, (algo.oid, algo.parameters)) {
((ECPK, Some(P256)), ES256) => "ECDSA_P256_SHA256",
((ECPK, Some(P384)), ES384) => "ECDSA_P384_SHA384",
((RSA, None), (ID_RSASSA_PSS, Some(p))) => {
// Decompose the RSA PSS parameters.
let RsaSsaPssParams {
hash_algorithm: hash,
mask_algorithm: mask,
salt_length: salt,
trailer_field: tfld,
} = p.decode_into().unwrap();

// Validate the sanity of the mask algorithm.
let algo = match (mask.oid, mask.parameters) {
(ID_MGF_1, Some(p)) => {
let p = p.decode_into::<AlgorithmIdentifier<'_>>()?;
match (p.oids()?, salt, tfld) {
((SHA256, None), 32, 1) => Ok(SHA256),
((SHA384, None), 48, 1) => Ok(SHA384),
((SHA512, None), 64, 1) => Ok(SHA512),
((x, y), s, t) => {
eprint!(
"Unknown RSA hash and components: {:?}, {:?}, salt {}, tfld {}",
x, y, s, t
);
Err(anyhow!("unsupported"))
}
}
}
(x, _) => {
eprintln!("Unknown RSA OID {:?}", x);
Err(anyhow!("unsupported"))
}
}
.map_err(|e| {
eprintln!("Some algo error {:?}", e);
anyhow!("{:?}", e)
})
.unwrap();

match (hash.oids()?, algo) {
((SHA256, None), SHA256) => "RSA_PSS_2048_SHA256",
((SHA384, None), SHA384) => "RSA_PSS_3072_SHA384",
((SHA512, None), SHA512) => "RSA_PSS_4096_SHA512",
_ => {
eprintln!("Error unknown hash.oids");
bail!("unsupported")
}
}
}
_ => {
eprintln!("Unknown algorithm, should not be here!");
bail!("unsupported")
}
};

/*eprintln!("spki.rs verify() algo: {}", algo_name);
if algo_name == "RSA_PSS_3072_SHA384" {
eprintln!("RSA Pub Key: ");
for x in self.subject_public_key {
eprint!("{:02x}", x);
}
eprintln!();
}*/

let public_key = match algo_name {
"RSA_PSS_2048_SHA256" | "RSA_PSS_3072_SHA384" | "RSA_PSS_4096_SHA512" => {
let pkey = rsa::RsaPublicKey::from_pkcs1_der(self.subject_public_key)?;
eprintln!("RSA from_pkcs8()");
SignaturePublicKey::from_pem(
algo_name,
pkey.to_public_key_pem(LineEnding::LF).unwrap().as_bytes(),
)
.map_err(|e| anyhow!("{:?}", e))
.unwrap()
}
_ => SignaturePublicKey::from_raw(algo_name, self.subject_public_key)
.map_err(|e| anyhow!("{:?}", e))
.unwrap(),
};

//let public_key = SignaturePublicKey::from_raw(algo_name, self.subject_public_key)
// .map_err(|e| anyhow!("{:?}", e))
// .unwrap();

let signature = match algo_name {
"ECDSA_P256_SHA256" => {
let temp = p256::ecdsa::Signature::from_der(sign)?;
temp.to_vec()
}
"ECDSA_P384_SHA384" => {
let temp = p384::ecdsa::Signature::from_der(sign)?;
temp.to_vec()
}
"RSA_PSS_2048_SHA256" | "RSA_PSS_3072_SHA384" | "RSA_PSS_4096_SHA512" => {
use signature::Signature;
let s = rsa::pss::Signature::from_bytes(sign)?;
s.to_vec()
}
_ => sign.to_vec(),
};

let signature = Signature::from_raw(algo_name, &signature)
.map_err(|e| anyhow!("{:?}", e))
.unwrap();

Ok(public_key
.signature_verify(body, &signature)
.map_err(|e| anyhow!("{:?}", e))
.unwrap())
}

#[cfg(any(not(target_os = "wasi"), not(feature = "wasi-crypto")))]
fn verify(&self, body: &[u8], algo: AlgorithmIdentifier<'_>, sign: &[u8]) -> Result<()> {
match (self.algorithm.oids()?, (algo.oid, algo.parameters)) {
((ECPK, Some(P256)), ES256) => {
Expand Down
3 changes: 1 addition & 2 deletions src/ext/sgx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use anyhow::{anyhow, Result};
use const_oid::ObjectIdentifier;
use der::{Decode, Encode};
use sgx::parameters::{Attributes, MiscSelect};
use sha2::{Digest, Sha256};
use x509::{ext::Extension, request::CertReqInfo, Certificate, TbsCertificate};

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -84,7 +83,7 @@ impl ExtVerifier for Sgx {

if !dbg {
// TODO: Validate that the certification request came from an SGX enclave.
let hash = Sha256::digest(&cri.public_key.to_vec()?);
let hash = sha256(&cri.public_key.to_vec()?)?;
if hash.as_slice() != &rpt.reportdata[..hash.as_slice().len()] {
return Err(anyhow!("sgx report data is invalid"));
}
Expand Down
3 changes: 1 addition & 2 deletions src/ext/snp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use der::asn1::UIntRef;
use der::{Decode, Encode, Sequence};
use flagset::{flags, FlagSet};
use sec1::pkcs8::AlgorithmIdentifier;
use sha2::Digest;
use x509::ext::Extension;
use x509::{request::CertReqInfo, Certificate};
use x509::{PkiPath, TbsCertificate};
Expand Down Expand Up @@ -374,7 +373,7 @@ impl ExtVerifier for Snp {

if !dbg {
// Validate that the certification request came from an SNP VM.
let hash = sha2::Sha384::digest(&cri.public_key.to_vec()?);
let hash = sha384(&cri.public_key.to_vec()?)?;
if hash.as_slice() != &report.body.report_data[..hash.as_slice().len()] {
return Err(anyhow!("snp report.report_data is invalid"));
}
Expand Down

0 comments on commit 7f2de29

Please sign in to comment.