Skip to content

Commit

Permalink
Merge pull request #1 from Phala-Network/wasm-support
Browse files Browse the repository at this point in the history
Add wasm support for both web and node environment
  • Loading branch information
0xshawn authored Oct 17, 2024
2 parents 9e28b73 + b6f7c5e commit 006d887
Show file tree
Hide file tree
Showing 12 changed files with 200 additions and 9 deletions.
10 changes: 9 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ log = { version = "0.4.20", default-features = false }

anyhow = { version = "1", optional = true }

ring = { version = "0.17.8", default-features = false, features = [
ring = { version = "0.17", default-features = false, features = [
"alloc",
] }
reqwest = { version = "0.11.27", optional = true, default-features = false, features = [
Expand All @@ -45,6 +45,10 @@ serde_json = { version = "1.0.108", optional = true, features = [
] }
tracing = { version = "0.1", optional = true }
futures = { version = "0.3", optional = true }
getrandom = { version = "0.2", optional = true, features = ["js"] }
serde-wasm-bindgen = { version = "0.4", optional = true}
wasm-bindgen = { version = "0.2.95", optional = true }
serde_bytes = { version = "0.11" }

[dependencies.webpki]
version = "0.102.8"
Expand All @@ -56,6 +60,9 @@ features = ["alloc", "ring"]
insta = "1"
tokio = { version = "1", features = ["full"] }

[lib]
crate-type = ["cdylib", "rlib"]

[features]
default = ["std", "report"]
std = [
Expand All @@ -74,3 +81,4 @@ std = [
"urlencoding",
]
report = ["std", "tracing", "futures"]
js = ["ring/wasm32_unknown_unknown_js", "getrandom", "serde-wasm-bindgen", "wasm-bindgen"]
29 changes: 29 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
WASM_PACK = wasm-pack
INSTALL_TOOL = cargo install wasm-pack
BUILD_WEB = $(WASM_PACK) build --release --target web --out-dir pkg/web --out-name dcap-qvl-web -- --features=js
BUILD_NODE = $(WASM_PACK) build --release --target nodejs --out-dir pkg/node --out-name dcap-qvl-node -- --features=js

all: install_wasm_tool build_web_pkg build_node_pkg

install_wasm_tool:
@echo "Installing wasm-pack if not already installed..."
@if ! command -v $(WASM_PACK) &> /dev/null; then \
echo "wasm-pack not found, installing..."; \
$(INSTALL_TOOL); \
else \
echo "wasm-pack is already installed."; \
fi

build_web_pkg: install_wasm_tool
@echo "Building for web browsers..."
$(BUILD_WEB)

build_node_pkg: install_wasm_tool
@echo "Building for Node.js..."
$(BUILD_NODE)

clean:
@echo "Cleaning up..."
rm -rf pkg

.PHONY: all install_wasm_tool build_web_pkg build_node_pkg clean
1 change: 1 addition & 0 deletions src/collateral.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ fn get_header(resposne: &reqwest::Response, name: &str) -> Result<String> {
///
/// * `Ok(QuoteCollateralV3)` - The quote collateral
/// * `Err(Error)` - The error
#[cfg(not(feature = "js"))]
pub async fn get_collateral(
pccs_url: &str,
mut quote: &[u8],
Expand Down
1 change: 0 additions & 1 deletion src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ pub const ATTESTATION_KEY_LEN: usize = 64;
pub const AUTHENTICATION_DATA_LEN: usize = 32;
pub const QE_HASH_DATA_BYTE_LEN: usize = ATTESTATION_KEY_LEN + AUTHENTICATION_DATA_LEN;


pub const PCK_ID_PLAIN: u16 = 1;
pub const PCK_ID_RSA_2048_OAEP: u16 = 2;
pub const PCK_ID_RSA_3072_OAEP: u16 = 3;
Expand Down
5 changes: 3 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ extern crate alloc;

use scale::{Decode, Encode};
use scale_info::TypeInfo;
use serde::{Deserialize, Serialize};

#[derive(Encode, Decode, TypeInfo, Debug, Clone, PartialEq, Eq)]
#[derive(Encode, Decode, TypeInfo, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Error {
InvalidCertificate,
InvalidSignature,
Expand Down Expand Up @@ -75,7 +76,7 @@ pub enum Error {
OidIsMissing,
}

#[derive(Encode, Decode, Clone, PartialEq, Eq, Debug)]
#[derive(Encode, Decode, Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
pub struct QuoteCollateralV3 {
pub pck_crl_issuer_chain: String,
pub root_ca_crl: String,
Expand Down
35 changes: 31 additions & 4 deletions src/quote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use alloc::vec::Vec;

use anyhow::Result;
use scale::{Decode, Input};
use serde::{Deserialize, Serialize};

use crate::{constants::*, utils, Error};

Expand Down Expand Up @@ -41,45 +42,71 @@ pub struct Body {
pub size: u32,
}

#[derive(Decode, Debug, Clone)]
#[derive(Serialize, Deserialize, Decode, Debug, Clone)]
pub struct EnclaveReport {
#[serde(with = "serde_bytes")]
pub cpu_svn: [u8; 16],
pub misc_select: u32,
#[serde(with = "serde_bytes")]
pub reserved1: [u8; 28],
#[serde(with = "serde_bytes")]
pub attributes: [u8; 16],
#[serde(with = "serde_bytes")]
pub mr_enclave: [u8; 32],
#[serde(with = "serde_bytes")]
pub reserved2: [u8; 32],
#[serde(with = "serde_bytes")]
pub mr_signer: [u8; 32],
#[serde(with = "serde_bytes")]
pub reserved3: [u8; 96],
pub isv_prod_id: u16,
pub isv_svn: u16,
#[serde(with = "serde_bytes")]
pub reserved4: [u8; 60],
#[serde(with = "serde_bytes")]
pub report_data: [u8; 64],
}

#[derive(Decode, Debug, Clone)]
#[derive(Decode, Debug, Clone, Serialize, Deserialize)]
pub struct TDReport10 {
#[serde(with = "serde_bytes")]
pub tee_tcb_svn: [u8; 16],
#[serde(with = "serde_bytes")]
pub mr_seam: [u8; 48],
#[serde(with = "serde_bytes")]
pub mr_signer_seam: [u8; 48],
#[serde(with = "serde_bytes")]
pub seam_attributes: [u8; 8],
#[serde(with = "serde_bytes")]
pub td_attributes: [u8; 8],
#[serde(with = "serde_bytes")]
pub xfam: [u8; 8],
#[serde(with = "serde_bytes")]
pub mr_td: [u8; 48],
#[serde(with = "serde_bytes")]
pub mr_config_id: [u8; 48],
#[serde(with = "serde_bytes")]
pub mr_owner: [u8; 48],
#[serde(with = "serde_bytes")]
pub mr_owner_config: [u8; 48],
#[serde(with = "serde_bytes")]
pub rt_mr0: [u8; 48],
#[serde(with = "serde_bytes")]
pub rt_mr1: [u8; 48],
#[serde(with = "serde_bytes")]
pub rt_mr2: [u8; 48],
#[serde(with = "serde_bytes")]
pub rt_mr3: [u8; 48],
#[serde(with = "serde_bytes")]
pub report_data: [u8; 64],
}

#[derive(Decode, Debug, Clone)]
#[derive(Decode, Debug, Clone, Serialize, Deserialize)]
pub struct TDReport15 {
pub base: TDReport10,
#[serde(with = "serde_bytes")]
pub tee_tcb_svn2: [u8; 16],
#[serde(with = "serde_bytes")]
pub mr_service_td: [u8; 48],
}

Expand Down Expand Up @@ -183,7 +210,7 @@ fn decode_auth_data(ver: u16, input: &mut &[u8]) -> Result<AuthData, scale::Erro
}
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Report {
SgxEnclave(EnclaveReport),
TD10(TDReport10),
Expand Down
29 changes: 28 additions & 1 deletion src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,41 @@ use crate::{
utils::{self, encode_as_der, extract_certs, verify_certificate_chain},
};
use crate::{Error, QuoteCollateralV3};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone)]
#[cfg(feature = "js")]
use wasm_bindgen::prelude::*;

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct VerifiedReport {
pub status: String,
pub advisory_ids: Vec<String>,
pub report: Report,
}

#[cfg(feature = "js")]
#[wasm_bindgen]
pub fn js_verify(
raw_quote: JsValue,
quote_collateral: JsValue,
now: u64,
) -> Result<JsValue, JsValue> {
let raw_quote: Vec<u8> = serde_wasm_bindgen::from_value(raw_quote)
.map_err(|_| JsValue::from_str("Failed to decode raw_quote"))?;
let quote_collateral_bytes: Vec<u8> = serde_wasm_bindgen::from_value(quote_collateral)
.map_err(|_| JsValue::from_str("Failed to decode quote_collateral"))?;
let quote_collateral = QuoteCollateralV3::decode(&mut quote_collateral_bytes.as_slice())
.map_err(|_| JsValue::from_str("Failed to decode quote_collateral_bytes"))?;

let verified_report = verify(&raw_quote, &quote_collateral, now).map_err(|e| {
serde_wasm_bindgen::to_value(&e)
.unwrap_or_else(|_| JsValue::from_str("Failed to encode Error"))
})?;

serde_wasm_bindgen::to_value(&verified_report)
.map_err(|_| JsValue::from_str("Failed to encode verified_report"))
}

/// Verify a quote
///
/// # Arguments
Expand Down
2 changes: 2 additions & 0 deletions tests/js/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pkg
sample
19 changes: 19 additions & 0 deletions tests/js/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Test the JS bindings

## Verify Quote with Node

```
cd tests/js
node verify_quote_node.js
```

## Verify Quote with Web

```
cd tests/js
ln -sf ../../pkg pkg
ln -sf ../../sample sample
python3 -m http.server 8000
```

Open http://localhost:8000/index.html in browser, and check the console for the result.
12 changes: 12 additions & 0 deletions tests/js/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Verify Quote</title>
</head>
<body>
<h1>Verify Quote</h1>
<script type="module" src="./verify_quote_web.js"></script>
</body>
</html>
30 changes: 30 additions & 0 deletions tests/js/verify_quote_node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const fs = require('fs');
const path = require('path');
const { js_verify } = require('../../pkg/node/dcap-qvl-node');

// Function to read a file as a Uint8Array
function readFileAsUint8Array(filePath) {
const data = fs.readFileSync(filePath);
return new Uint8Array(data);
}

// Paths to your sample files
const rawQuotePath = path.join(__dirname, '../../sample', 'tdx_quote');
const quoteCollateralPath = path.join(__dirname, '../../sample', 'tdx_quote_collateral');

// Read the files
const rawQuote = readFileAsUint8Array(rawQuotePath);
const quoteCollateral = readFileAsUint8Array(quoteCollateralPath);

// Current timestamp
// TCBInfoExpired when using current timestamp, pick the time from verify_quote.rs
// const now = BigInt(Math.floor(Date.now() / 1000));
const now = BigInt(1725258675);

try {
// Call the js_verify function
const result = js_verify(rawQuote, quoteCollateral, now);
console.log('Verification Result:', result);
} catch (error) {
console.error('Verification failed:', error);
}
36 changes: 36 additions & 0 deletions tests/js/verify_quote_web.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import init, { js_verify } from '/pkg/web/dcap-qvl-web.js';

// Function to fetch a file as a Uint8Array
async function fetchFileAsUint8Array(url) {
const response = await fetch(url);
const data = await response.arrayBuffer();
return new Uint8Array(data);
}

// URLs to your sample files
const rawQuoteUrl = '/sample/tdx_quote';
const quoteCollateralUrl = '/sample/tdx_quote_collateral';

// Load the files
async function loadFilesAndVerify() {
try {
// Initialize the WASM module
await init('/pkg/web/dcap-qvl-web_bg.wasm');

const rawQuote = await fetchFileAsUint8Array(rawQuoteUrl);
const quoteCollateral = await fetchFileAsUint8Array(quoteCollateralUrl);

// Current timestamp
const now = BigInt(1725258675);

// Call the js_verify function
const result = js_verify(rawQuote, quoteCollateral, now);
console.log('Verification Result:', result);
} catch (error) {
console.error('Verification failed:', error);
}
}

// Execute the verification
loadFilesAndVerify();

0 comments on commit 006d887

Please sign in to comment.