Skip to content

Commit f27c0bd

Browse files
committed
feat: use wasi-crypto as an optionl wasi feature
Signed-off-by: Richard Zak <[email protected]>
1 parent 30c5cc1 commit f27c0bd

File tree

9 files changed

+212
-6
lines changed

9 files changed

+212
-6
lines changed

.cargo/config

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
[env]
2+
RUST_BACKTRACE = "1"
3+
WASMTIME_BACKTRACE_DETAILS = "1"
4+
5+
[build]
6+
target = "wasm32-wasi"
7+
18
[target.wasm32-wasi]
29
rustflags = ["--cfg", "tokio_unstable"]
3-
runner = ["enarx", "run", "--wasmcfgfile", "Enarx.toml"]
10+
# runner = ["./enarx", "run", "--wasmcfgfile", "Enarx.toml"]
11+
runner = ["/home/rjzak/bin/wasmtime-wasi-crypto", "--wasi-modules", "experimental-wasi-crypto", "--"]

Cargo.lock

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ confargs = { version = "^0.1.3", default-features = false }
4242
[target.'cfg(not(target_os = "wasi"))'.dependencies]
4343
tokio = { version = "^1.21.2", features = ["rt-multi-thread", "macros"], default-features = false }
4444

45+
[target.'cfg(target_os = "wasi")'.dependencies]
46+
wasi-crypto-guest = { git = "https://github.com/WebAssembly/wasi-crypto", branch = "main", optional = true }
47+
4548
[dev-dependencies]
4649
tower = { version = "^0.4.11", features = ["util"], default-features = false }
4750
axum = { version = "^0.5.17", default-features = false }
@@ -50,6 +53,10 @@ memoffset = { version = "0.7.1", default-features = false }
5053
rstest = { version = "0.15", default-features = false }
5154
testaso = { version = "0.1", default-features = false }
5255

56+
[features]
57+
default = []
58+
wasi-crypto = ["dep:wasi-crypto-guest"]
59+
5360
[profile.release]
5461
incremental = false
5562
codegen-units = 1

rust-toolchain.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[toolchain]
2-
channel = "stable"
2+
channel = "nightly"
33
components = [ "rustfmt", "clippy" ]
44
profile = "minimal"
55
targets = [

src/crypto/hashing.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// SPDX-FileCopyrightText: 2022 Profian Inc. <[email protected]>
2+
// SPDX-License-Identifier: AGPL-3.0-only
3+
4+
use anyhow::Result;
5+
6+
#[cfg(all(target_os = "wasi", feature = "wasi-crypto"))]
7+
use anyhow::anyhow;
8+
#[cfg(all(target_os = "wasi", feature = "wasi-crypto"))]
9+
use wasi_crypto_guest::prelude::Hash;
10+
11+
#[cfg(any(not(target_os = "wasi"), not(feature = "wasi-crypto")))]
12+
use sha2::{Digest, Sha256, Sha384};
13+
14+
#[inline]
15+
pub fn sha256(data: impl AsRef<[u8]>) -> Result<Vec<u8>> {
16+
#[cfg(all(target_os = "wasi", feature = "wasi-crypto"))]
17+
return Ok(Hash::hash("SHA-256", data, 32, None).or_else(|_| Err(anyhow!("hash error")))?);
18+
19+
#[cfg(any(not(target_os = "wasi"), not(feature = "wasi-crypto")))]
20+
Ok(Sha256::digest(data).as_slice().to_vec())
21+
}
22+
23+
#[inline]
24+
pub fn sha384(data: impl AsRef<[u8]>) -> Result<Vec<u8>> {
25+
#[cfg(all(target_os = "wasi", feature = "wasi-crypto"))]
26+
return Ok(Hash::hash("SHA-384", data, 48, None).or_else(|_| Err(anyhow!("hash error")))?);
27+
28+
#[cfg(any(not(target_os = "wasi"), not(feature = "wasi-crypto")))]
29+
Ok(Sha384::digest(data).as_slice().to_vec())
30+
}
31+
32+
#[cfg(all(target_os = "wasi", feature = "wasi-crypto"))]
33+
#[cfg(test)]
34+
mod wasi_crypto {
35+
use crate::{sha256, sha384};
36+
use sha2::Digest;
37+
38+
const DATA: &[u8] = b"SOME_TEST_DATA";
39+
40+
#[test]
41+
fn test_sha256() {
42+
let hash = sha256(DATA).unwrap();
43+
assert_eq!(hash, sha2::Sha256::digest(DATA).as_slice());
44+
}
45+
46+
#[test]
47+
fn test_sha384() {
48+
let hash = sha384(DATA).unwrap();
49+
assert_eq!(hash, sha2::Sha384::digest(DATA).as_slice());
50+
}
51+
}

src/crypto/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33

44
mod cert;
55
mod certreq;
6+
mod hashing;
67
mod pki;
78
mod spki;
89

910
pub use self::cert::TbsCertificateExt;
1011
pub use self::certreq::{CertReqExt, CertReqInfoExt};
12+
pub use self::hashing::{sha256, sha384};
1113
pub use self::pki::PrivateKeyInfoExt;
1214
pub use self::spki::SubjectPublicKeyInfoExt;

src/crypto/spki.rs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,22 @@
44
use anyhow::{anyhow, Result};
55
use const_oid::ObjectIdentifier;
66
use der::{asn1::AnyRef, Sequence};
7+
78
use rsa::pkcs1::DecodeRsaPublicKey;
9+
#[cfg(all(target_os = "wasi", feature = "wasi-crypto"))]
10+
use spki::EncodePublicKey;
811
use spki::{AlgorithmIdentifier, SubjectPublicKeyInfo};
912

1013
use const_oid::db::rfc5912::{
1114
ECDSA_WITH_SHA_256, ECDSA_WITH_SHA_384, ID_EC_PUBLIC_KEY as ECPK, ID_MGF_1, ID_RSASSA_PSS,
1215
ID_SHA_256 as SHA256, ID_SHA_384 as SHA384, ID_SHA_512 as SHA512, RSA_ENCRYPTION as RSA,
1316
SECP_256_R_1 as P256, SECP_384_R_1 as P384,
1417
};
18+
#[cfg(all(target_os = "wasi", feature = "wasi-crypto"))]
19+
use der::pem::LineEnding;
20+
21+
#[cfg(all(target_os = "wasi", feature = "wasi-crypto"))]
22+
use wasi_crypto_guest::signatures::{Signature, SignaturePublicKey};
1523

1624
const ES256: (ObjectIdentifier, Option<AnyRef<'static>>) = (ECDSA_WITH_SHA_256, None);
1725
const ES384: (ObjectIdentifier, Option<AnyRef<'static>>) = (ECDSA_WITH_SHA_384, None);
@@ -43,6 +51,108 @@ pub trait SubjectPublicKeyInfoExt {
4351
}
4452

4553
impl<'a> SubjectPublicKeyInfoExt for SubjectPublicKeyInfo<'a> {
54+
#[cfg(all(target_os = "wasi", feature = "wasi-crypto"))]
55+
fn verify(&self, body: &[u8], algo: AlgorithmIdentifier<'_>, sign: &[u8]) -> Result<()> {
56+
let algo_name = match (self.algorithm.oids()?, (algo.oid, algo.parameters)) {
57+
((ECPK, Some(P256)), ES256) => "ECDSA_P256_SHA256",
58+
((ECPK, Some(P384)), ES384) => "ECDSA_P384_SHA384",
59+
((RSA, None), (ID_RSASSA_PSS, Some(p))) => {
60+
// Decompose the RSA PSS parameters.
61+
let RsaSsaPssParams {
62+
hash_algorithm: hash,
63+
mask_algorithm: mask,
64+
salt_length: salt,
65+
trailer_field: tfld,
66+
} = p.decode_into().unwrap();
67+
68+
// Validate the sanity of the mask algorithm.
69+
let algo = match (mask.oid, mask.parameters) {
70+
(ID_MGF_1, Some(p)) => {
71+
let p = p.decode_into::<AlgorithmIdentifier<'_>>()?;
72+
match (p.oids()?, salt, tfld) {
73+
((SHA256, None), 32, 1) => Ok(SHA256),
74+
((SHA384, None), 48, 1) => Ok(SHA384),
75+
((SHA512, None), 64, 1) => Ok(SHA512),
76+
((x, y), s, t) => {
77+
eprint!(
78+
"Unknown RSA hash and components: {:?}, {:?}, salt {}, tfld {}",
79+
x, y, s, t
80+
);
81+
Err(anyhow!("unsupported"))
82+
}
83+
}
84+
}
85+
(x, _) => {
86+
eprintln!("Unknown RSA OID {:?}", x);
87+
Err(anyhow!("unsupported"))
88+
}
89+
}
90+
.map_err(|e| {
91+
eprintln!("Some algo error {:?}", e);
92+
anyhow!("{:?}", e)
93+
})
94+
.unwrap();
95+
96+
match (hash.oids()?, algo) {
97+
((SHA256, None), SHA256) => "RSA_PSS_2048_SHA256",
98+
((SHA384, None), SHA384) => "RSA_PSS_3072_SHA384",
99+
((SHA512, None), SHA512) => "RSA_PSS_4096_SHA512",
100+
_ => {
101+
eprintln!("Error unknown hash.oids");
102+
bail!("unsupported")
103+
}
104+
}
105+
}
106+
_ => {
107+
eprintln!("Unknown algorithm, should not be here!");
108+
bail!("unsupported")
109+
}
110+
};
111+
112+
let public_key = match algo_name {
113+
"RSA_PSS_2048_SHA256" | "RSA_PSS_3072_SHA384" | "RSA_PSS_4096_SHA512" => {
114+
let pkey = rsa::RsaPublicKey::from_pkcs1_der(self.subject_public_key)?;
115+
SignaturePublicKey::from_pem(
116+
algo_name,
117+
pkey.to_public_key_pem(LineEnding::LF).unwrap().as_bytes(),
118+
)
119+
.map_err(|e| anyhow!("{:?}", e))
120+
.unwrap()
121+
}
122+
_ => SignaturePublicKey::from_raw(algo_name, self.subject_public_key)
123+
.map_err(|e| anyhow!("{:?}", e))
124+
.unwrap(),
125+
};
126+
127+
let signature = match algo_name {
128+
"ECDSA_P256_SHA256" => {
129+
let temp = p256::ecdsa::Signature::from_der(sign)?;
130+
temp.to_vec()
131+
}
132+
"ECDSA_P384_SHA384" => {
133+
let temp = p384::ecdsa::Signature::from_der(sign)?;
134+
temp.to_vec()
135+
}
136+
"RSA_PSS_2048_SHA256" | "RSA_PSS_3072_SHA384" | "RSA_PSS_4096_SHA512" => {
137+
use signature::Signature;
138+
let s = rsa::pss::Signature::from_bytes(sign)?;
139+
eprintln!("Made RSA signature");
140+
s.to_vec()
141+
}
142+
_ => sign.to_vec(),
143+
};
144+
145+
let signature = Signature::from_raw(algo_name, &signature)
146+
.map_err(|e| anyhow!("{:?}", e))
147+
.unwrap();
148+
149+
Ok(public_key
150+
.signature_verify(body, &signature)
151+
.map_err(|e| anyhow!("{:?}", e))
152+
.unwrap())
153+
}
154+
155+
#[cfg(any(not(target_os = "wasi"), not(feature = "wasi-crypto")))]
46156
fn verify(&self, body: &[u8], algo: AlgorithmIdentifier<'_>, sign: &[u8]) -> Result<()> {
47157
match (self.algorithm.oids()?, (algo.oid, algo.parameters)) {
48158
((ECPK, Some(P256)), ES256) => {

src/ext/sgx/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use anyhow::{anyhow, Result};
1313
use const_oid::ObjectIdentifier;
1414
use der::{Decode, Encode};
1515
use sgx::parameters::{Attributes, MiscSelect};
16-
use sha2::{Digest, Sha256};
1716
use x509::{ext::Extension, request::CertReqInfo, Certificate, TbsCertificate};
1817

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

8584
if !dbg {
8685
// TODO: Validate that the certification request came from an SGX enclave.
87-
let hash = Sha256::digest(&cri.public_key.to_vec()?);
86+
let hash = sha256(&cri.public_key.to_vec()?)?;
8887
if hash.as_slice() != &rpt.reportdata[..hash.as_slice().len()] {
8988
return Err(anyhow!("sgx report data is invalid"));
9089
}

src/ext/snp/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use der::asn1::UIntRef;
1313
use der::{Decode, Encode, Sequence};
1414
use flagset::{flags, FlagSet};
1515
use sec1::pkcs8::AlgorithmIdentifier;
16-
use sha2::Digest;
1716
use x509::ext::Extension;
1817
use x509::{request::CertReqInfo, Certificate};
1918
use x509::{PkiPath, TbsCertificate};
@@ -374,7 +373,7 @@ impl ExtVerifier for Snp {
374373

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

0 commit comments

Comments
 (0)