TCBInfo contract (#98)
Co-authored-by: hu55a1n1 <sufialhussaini@gmail.com>
This commit is contained in:
parent
4fd86c089a
commit
f93da16c47
19 changed files with 1796 additions and 1 deletions
26
Cargo.lock
generated
26
Cargo.lock
generated
|
@ -1590,6 +1590,7 @@ dependencies = [
|
||||||
"ff",
|
"ff",
|
||||||
"generic-array",
|
"generic-array",
|
||||||
"group",
|
"group",
|
||||||
|
"pem-rfc7468",
|
||||||
"pkcs8",
|
"pkcs8",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
"sec1",
|
"sec1",
|
||||||
|
@ -2193,6 +2194,7 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"allocator-api2",
|
"allocator-api2",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -4781,6 +4783,30 @@ version = "0.12.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tcbinfo"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"cosmwasm-schema",
|
||||||
|
"cosmwasm-std",
|
||||||
|
"cw-multi-test",
|
||||||
|
"cw-storage-plus",
|
||||||
|
"cw2",
|
||||||
|
"der",
|
||||||
|
"getrandom",
|
||||||
|
"hashbrown 0.14.5",
|
||||||
|
"hex",
|
||||||
|
"mc-attestation-verifier",
|
||||||
|
"p256",
|
||||||
|
"quartz-tee-ra",
|
||||||
|
"schemars",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"x509-cert",
|
||||||
|
"x509-parser",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.10.1"
|
version = "3.10.1"
|
||||||
|
|
|
@ -54,7 +54,7 @@ impl CertificateChainVerifier for TlsCertificateChainVerifier {
|
||||||
let mut issuers: Vec<usize> = (1..v.len()).collect();
|
let mut issuers: Vec<usize> = (1..v.len()).collect();
|
||||||
issuers.push(v.len() - 1);
|
issuers.push(v.len() - 1);
|
||||||
let subjects: Vec<usize> = (0..v.len()).collect();
|
let subjects: Vec<usize> = (0..v.len()).collect();
|
||||||
for (i, s) in std::iter::zip(issuers, subjects) {
|
for (i, s) in core::iter::zip(issuers, subjects) {
|
||||||
let r = v[s].1.verify_signature(Some(v[i].1.public_key()));
|
let r = v[s].1.verify_signature(Some(v[i].1.public_key()));
|
||||||
r.map_err(|_| CertificateChainVerifierError::SignatureVerification)?
|
r.map_err(|_| CertificateChainVerifierError::SignatureVerification)?
|
||||||
}
|
}
|
||||||
|
|
5
cosmwasm/packages/tcbinfo/.cargo/config.toml
Normal file
5
cosmwasm/packages/tcbinfo/.cargo/config.toml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[alias]
|
||||||
|
wasm = "build --release --lib --target wasm32-unknown-unknown"
|
||||||
|
unit-test = "test --lib"
|
||||||
|
schema = "run --bin schema"
|
||||||
|
integration-test = "test --lib integration_tests"
|
11
cosmwasm/packages/tcbinfo/.editorconfig
Normal file
11
cosmwasm/packages/tcbinfo/.editorconfig
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.rs]
|
||||||
|
indent_size = 4
|
18
cosmwasm/packages/tcbinfo/.gitignore
vendored
Normal file
18
cosmwasm/packages/tcbinfo/.gitignore
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Build results
|
||||||
|
/target
|
||||||
|
/schema
|
||||||
|
|
||||||
|
# Cargo+Git helper file (https://github.com/rust-lang/cargo/blob/0.44.1/src/cargo/sources/git/utils.rs#L320-L327)
|
||||||
|
.cargo-ok
|
||||||
|
|
||||||
|
# Text file backups
|
||||||
|
**/*.rs.bk
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# IDEs
|
||||||
|
*.iml
|
||||||
|
.idea
|
||||||
|
|
||||||
|
.editorconfig
|
1197
cosmwasm/packages/tcbinfo/Cargo.lock
generated
Normal file
1197
cosmwasm/packages/tcbinfo/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
59
cosmwasm/packages/tcbinfo/Cargo.toml
Normal file
59
cosmwasm/packages/tcbinfo/Cargo.toml
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
[package]
|
||||||
|
name = "tcbinfo"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["quinine"]
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
opt-level = 3
|
||||||
|
debug = false
|
||||||
|
rpath = false
|
||||||
|
lto = true
|
||||||
|
debug-assertions = false
|
||||||
|
codegen-units = 1
|
||||||
|
panic = 'abort'
|
||||||
|
incremental = false
|
||||||
|
overflow-checks = true
|
||||||
|
|
||||||
|
[features]
|
||||||
|
# use library feature to disable all instantiate/execute/query exports
|
||||||
|
library = []
|
||||||
|
|
||||||
|
[package.metadata.scripts]
|
||||||
|
optimize = """docker run --rm -v "$(pwd)":/code \
|
||||||
|
--mount type=volume,source="$(basename "$(pwd)")_cache",target=/target \
|
||||||
|
--mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \
|
||||||
|
cosmwasm/optimizer:0.15.0
|
||||||
|
"""
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
cosmwasm-schema = "2.0.1"
|
||||||
|
cosmwasm-std = { version = "2.0.1", features = [
|
||||||
|
"cosmwasm_1_3",
|
||||||
|
# Enable this if you only deploy to chains that have CosmWasm 1.4 or higher
|
||||||
|
# "cosmwasm_1_4",
|
||||||
|
] }
|
||||||
|
cw-storage-plus = "2.0.0"
|
||||||
|
cw2 = "2.0.0"
|
||||||
|
getrandom = { version = "0.2", features = ["js"] }
|
||||||
|
schemars = "0.8.16"
|
||||||
|
serde = { version = "1.0.197", default-features = false, features = ["derive"] }
|
||||||
|
thiserror = { version = "1.0.58" }
|
||||||
|
x509-cert = { version = "0.2.5", default-features = false, features = ["pem"] }
|
||||||
|
x509-parser = {version = "0.16.0", features = ["verify"] }
|
||||||
|
der = { version = "0.7.9" }
|
||||||
|
quartz-tee-ra = { path = "../quartz-tee-ra" }
|
||||||
|
mc-attestation-verifier = {git = "https://github.com/informalsystems/attestation", default-features = false}
|
||||||
|
p256 = "0.13.2"
|
||||||
|
serde_json = { version = "1.0", default-features = false }
|
||||||
|
hashbrown = {version = "0.14.5", features = ["serde"]}
|
||||||
|
hex = {version = "0.4.3", default-features = false, features = ["serde"]}
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
cw-multi-test = "2.0.0"
|
31
cosmwasm/packages/tcbinfo/README.md
Normal file
31
cosmwasm/packages/tcbinfo/README.md
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# CosmWasm SGX TcbInfo Smart Contract
|
||||||
|
|
||||||
|
This smart contract facilitates the storage and verification of `TcbInfo`s for Intel SGX. The contract ensures that
|
||||||
|
TcbInfos are kept up-to-date so other contracts can query the latest TcbInfo state using the quote's `fmspc` during
|
||||||
|
remote attestation verification to ensure the attesting enclave setup is up-to-date.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The contract provides the following functionalities:
|
||||||
|
|
||||||
|
- Instantiate: Initialize the contract with a root certificate.
|
||||||
|
- Execute: Store and verify TcbInfo along with the provided certificate and optional timestamp.
|
||||||
|
- Query: Retrieve the latest TcbInfo using the FMSPC.
|
||||||
|
|
||||||
|
## Usage (with wasmd)
|
||||||
|
|
||||||
|
- Submit a new `TcbInfo` for a specific `fmspc`
|
||||||
|
|
||||||
|
```shell
|
||||||
|
export EXECUTE='{
|
||||||
|
"tcb_info": "{\"tcbInfo\":{ /* ... */ },\"signature\":\"647bac99371750892415557b838237839e52b02afe027a43322fe661f4a1a693b04a82717120d74bccf2b3787bf7e9ecbe44caa06e6e532b7a68a21b2765663d\"}
|
||||||
|
"certificate": "-----BEGIN CERTIFICATE-----\\n /* ... */ \\n-----END CERTIFICATE-----"
|
||||||
|
}'
|
||||||
|
wasmd tx wasm execute "$CONTRACT" "$EXECUTE" --from alice --chain-id testing -y
|
||||||
|
```
|
||||||
|
|
||||||
|
- Query the latest `TcbInfo` by `fmspc`
|
||||||
|
|
||||||
|
```shell
|
||||||
|
wasmd query wasm contract-state smart "$CONTRACT" '{"get_tcb_info": {"fmspc": "00906ED50000"}}'
|
||||||
|
```
|
16
cosmwasm/packages/tcbinfo/data/root_ca.pem
Normal file
16
cosmwasm/packages/tcbinfo/data/root_ca.pem
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw
|
||||||
|
aDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv
|
||||||
|
cnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ
|
||||||
|
BgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG
|
||||||
|
A1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0
|
||||||
|
aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT
|
||||||
|
AlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7
|
||||||
|
1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB
|
||||||
|
uzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ
|
||||||
|
MEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50
|
||||||
|
ZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV
|
||||||
|
Ur9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI
|
||||||
|
KoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg
|
||||||
|
AiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=
|
||||||
|
-----END CERTIFICATE-----
|
16
cosmwasm/packages/tcbinfo/data/tcb_signer.pem
Normal file
16
cosmwasm/packages/tcbinfo/data/tcb_signer.pem
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICizCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIw
|
||||||
|
aDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv
|
||||||
|
cnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ
|
||||||
|
BgNVBAYTAlVTMB4XDTE4MDUyMTEwNTAxMFoXDTI1MDUyMTEwNTAxMFowbDEeMBwG
|
||||||
|
A1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jw
|
||||||
|
b3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYD
|
||||||
|
VQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGv
|
||||||
|
P+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2lju
|
||||||
|
ypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0f
|
||||||
|
BEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2Vz
|
||||||
|
LmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlK
|
||||||
|
QEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqG
|
||||||
|
SM49BAMCA0cAMEQCIB9C8wOAN/ImxDtGACV246KcqjagZOR0kyctyBrsGGJVAiAj
|
||||||
|
ftbrNGsGU8YH211dRiYNoPPu19Zp/ze8JmhujB0oBw==
|
||||||
|
-----END CERTIFICATE-----
|
1
cosmwasm/packages/tcbinfo/data/tcbinfo.json
Normal file
1
cosmwasm/packages/tcbinfo/data/tcbinfo.json
Normal file
File diff suppressed because one or more lines are too long
10
cosmwasm/packages/tcbinfo/src/bin/schema.rs
Normal file
10
cosmwasm/packages/tcbinfo/src/bin/schema.rs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
use cosmwasm_schema::write_api;
|
||||||
|
use tcbinfo::msg::{ExecuteMsg, InstantiateMsg, QueryMsg};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
write_api! {
|
||||||
|
instantiate: InstantiateMsg,
|
||||||
|
execute: ExecuteMsg,
|
||||||
|
query: QueryMsg,
|
||||||
|
}
|
||||||
|
}
|
201
cosmwasm/packages/tcbinfo/src/contract.rs
Normal file
201
cosmwasm/packages/tcbinfo/src/contract.rs
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
#[cfg(not(feature = "library"))]
|
||||||
|
use cosmwasm_std::entry_point;
|
||||||
|
use cosmwasm_std::{
|
||||||
|
to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult,
|
||||||
|
};
|
||||||
|
use cw2::set_contract_version;
|
||||||
|
use der::{DateTime, DecodePem};
|
||||||
|
use mc_attestation_verifier::{CertificateChainVerifier, SignedTcbInfo, TcbInfo as McTcbInfo};
|
||||||
|
use p256::ecdsa::VerifyingKey;
|
||||||
|
use quartz_tee_ra::intel_sgx::dcap::certificate_chain::TlsCertificateChainVerifier;
|
||||||
|
use serde_json::Value;
|
||||||
|
use x509_cert::Certificate;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
error::ContractError,
|
||||||
|
msg::{ExecuteMsg, GetTcbInfoResponse, InstantiateMsg, QueryMsg},
|
||||||
|
state::{TcbInfo, DATABASE, ROOT_CERTIFICATE},
|
||||||
|
};
|
||||||
|
// version info for migration info
|
||||||
|
const CONTRACT_NAME: &str = "crates.io:tcbinfo";
|
||||||
|
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
|
#[cfg_attr(not(feature = "library"), entry_point)]
|
||||||
|
pub fn instantiate(
|
||||||
|
deps: DepsMut,
|
||||||
|
_env: Env,
|
||||||
|
_info: MessageInfo,
|
||||||
|
msg: InstantiateMsg,
|
||||||
|
) -> Result<Response, ContractError> {
|
||||||
|
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
|
||||||
|
let root = Certificate::from_pem(&msg.root_cert).expect("could not parse PEM");
|
||||||
|
let verifier = TlsCertificateChainVerifier::new(&msg.root_cert);
|
||||||
|
verifier
|
||||||
|
.verify_certificate_chain(vec![&root], vec![], None)
|
||||||
|
.map_err(|_| ContractError::CertificateVerificationError)?;
|
||||||
|
ROOT_CERTIFICATE
|
||||||
|
.save(deps.storage, &msg.root_cert.to_string())
|
||||||
|
.map_err(ContractError::Std)?;
|
||||||
|
assert!(DATABASE.is_empty(deps.storage));
|
||||||
|
Ok(Response::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(not(feature = "library"), entry_point)]
|
||||||
|
pub fn execute(
|
||||||
|
deps: DepsMut,
|
||||||
|
_env: Env,
|
||||||
|
_info: MessageInfo,
|
||||||
|
msg: ExecuteMsg,
|
||||||
|
) -> Result<Response, ContractError> {
|
||||||
|
let signed_tcb_info: SignedTcbInfo =
|
||||||
|
SignedTcbInfo::try_from(msg.tcb_info.as_ref()).expect("failed to parse TCBInfo");
|
||||||
|
let raw_root = ROOT_CERTIFICATE.load(deps.storage).unwrap();
|
||||||
|
let root = Certificate::from_pem(raw_root.clone()).expect("could not parse PEM");
|
||||||
|
let verifier = TlsCertificateChainVerifier::new(&raw_root);
|
||||||
|
let fmspc = execute::get_fmspc(&msg.tcb_info);
|
||||||
|
let certificate = Certificate::from_pem(msg.certificate.clone()).expect("failed to parse PEM");
|
||||||
|
|
||||||
|
let time = msg
|
||||||
|
.time
|
||||||
|
.map(|time| time.parse::<DateTime>().expect("could not parse datetime"));
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
execute::check_certificate_validity(&root, time),
|
||||||
|
"On-chain root certificate validity check failed"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
execute::check_certificate_validity(&certificate, time),
|
||||||
|
"Certificate validity check failed"
|
||||||
|
);
|
||||||
|
|
||||||
|
let key = VerifyingKey::from_sec1_bytes(
|
||||||
|
certificate
|
||||||
|
.tbs_certificate
|
||||||
|
.subject_public_key_info
|
||||||
|
.subject_public_key
|
||||||
|
.as_bytes()
|
||||||
|
.expect("Failed to parse public key"),
|
||||||
|
)
|
||||||
|
.expect("Failed to decode public key");
|
||||||
|
|
||||||
|
verifier
|
||||||
|
.verify_certificate_chain(vec![&certificate, &root], vec![], None)
|
||||||
|
.map_err(|_| ContractError::CertificateVerificationError)?;
|
||||||
|
|
||||||
|
signed_tcb_info
|
||||||
|
.verify(Some(&key), time)
|
||||||
|
.map_err(|_| ContractError::TcbInfoVerificationError)?;
|
||||||
|
|
||||||
|
let _ = DATABASE
|
||||||
|
.save(
|
||||||
|
deps.storage,
|
||||||
|
fmspc,
|
||||||
|
&TcbInfo {
|
||||||
|
info: msg.tcb_info.to_string(),
|
||||||
|
// certificate: msg.certificate.to_string(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.map_err(ContractError::Std);
|
||||||
|
|
||||||
|
Ok(Response::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod execute {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn get_fmspc(tcbinfo: &str) -> [u8; 6] {
|
||||||
|
let tcbinfo_raw: Value = serde_json::from_str(tcbinfo).expect("could not read tcbinfo");
|
||||||
|
let fmspc_raw = hex::decode(
|
||||||
|
tcbinfo_raw
|
||||||
|
.get("tcbInfo")
|
||||||
|
.unwrap()
|
||||||
|
.get("fmspc")
|
||||||
|
.unwrap()
|
||||||
|
.as_str()
|
||||||
|
.expect("could not find fmspc string"),
|
||||||
|
)
|
||||||
|
.expect("failed to decode fmspc hex string");
|
||||||
|
fmspc_raw.try_into().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_certificate_validity(cert: &Certificate, time: Option<DateTime>) -> bool {
|
||||||
|
match time {
|
||||||
|
None => true,
|
||||||
|
Some(time) => {
|
||||||
|
let validity = cert.tbs_certificate.validity;
|
||||||
|
let start = validity.not_before.to_date_time();
|
||||||
|
let end = validity.not_after.to_date_time();
|
||||||
|
time >= start && time <= end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(not(feature = "library"), entry_point)]
|
||||||
|
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult<Binary> {
|
||||||
|
match msg {
|
||||||
|
QueryMsg::GetTcbInfo { fmspc } => to_json_binary(&query::get_info(deps, fmspc)?),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod query {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn get_info(deps: Deps, fmspc: String) -> StdResult<GetTcbInfoResponse> {
|
||||||
|
let key: [u8; 6] = hex::decode(fmspc)
|
||||||
|
.unwrap()
|
||||||
|
.try_into()
|
||||||
|
.expect("invalid fmspc");
|
||||||
|
let tcb_info = DATABASE.load(deps.storage, key)?;
|
||||||
|
let tcb_info_response = serde_json::from_str(&tcb_info.info).map_err(|_| {
|
||||||
|
StdError::parse_err(tcb_info.info, "Could not prarse on-chain TcbInfo as JSON")
|
||||||
|
})?;
|
||||||
|
Ok(GetTcbInfoResponse {
|
||||||
|
tcb_info: tcb_info_response,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use cosmwasm_std::{
|
||||||
|
coins,
|
||||||
|
testing::{mock_dependencies, mock_env, mock_info},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
const TCB_SIGNER: &str = include_str!("../data/tcb_signer.pem");
|
||||||
|
const ROOT_CA: &str = include_str!("../data/root_ca.pem");
|
||||||
|
const TCB_INFO: &str = include_str!("../data/tcbinfo.json");
|
||||||
|
const FMSPC: &str = "00606a000000";
|
||||||
|
// const TIME: &str = "2024-07-15T15:19:13Z";
|
||||||
|
#[test]
|
||||||
|
fn verify_init_and_exec() {
|
||||||
|
let time = "2024-07-11T15:19:13Z";
|
||||||
|
let info = mock_info("creator", &coins(1000, "earth"));
|
||||||
|
let init_msg = InstantiateMsg {
|
||||||
|
root_cert: ROOT_CA.to_string(),
|
||||||
|
};
|
||||||
|
let mut deps = mock_dependencies();
|
||||||
|
let res = instantiate(deps.as_mut(), mock_env(), info, init_msg);
|
||||||
|
assert!(res.is_ok());
|
||||||
|
|
||||||
|
let exec_msg = ExecuteMsg {
|
||||||
|
tcb_info: TCB_INFO.to_string(),
|
||||||
|
certificate: TCB_SIGNER.to_string(),
|
||||||
|
time: Some(time.to_string()),
|
||||||
|
};
|
||||||
|
let info = mock_info("creator", &coins(1000, "earth"));
|
||||||
|
let exec = execute(deps.as_mut(), mock_env(), info, exec_msg);
|
||||||
|
assert!(exec.is_ok());
|
||||||
|
let query = query(
|
||||||
|
deps.as_ref(),
|
||||||
|
mock_env(),
|
||||||
|
QueryMsg::GetTcbInfo {
|
||||||
|
fmspc: FMSPC.to_string(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
assert!(query.is_ok());
|
||||||
|
println!("{:?}", query.unwrap());
|
||||||
|
}
|
||||||
|
}
|
23
cosmwasm/packages/tcbinfo/src/error.rs
Normal file
23
cosmwasm/packages/tcbinfo/src/error.rs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
use cosmwasm_std::StdError;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum ContractError {
|
||||||
|
#[error("{0}")]
|
||||||
|
Std(#[from] StdError),
|
||||||
|
|
||||||
|
#[error("Unauthorized")]
|
||||||
|
Unauthorized {},
|
||||||
|
// Add any other custom errors you like here.
|
||||||
|
// Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details.
|
||||||
|
#[error("Certificate verification failed")]
|
||||||
|
CertificateVerificationError,
|
||||||
|
#[error("failed to verify tcbinfo")]
|
||||||
|
TcbInfoVerificationError,
|
||||||
|
#[error("invalid public key")]
|
||||||
|
PublicKeyReadError,
|
||||||
|
#[error("invalid date and time")]
|
||||||
|
DateTimeReadError,
|
||||||
|
#[error("invalid tcbinfo")]
|
||||||
|
TcbInfoReadError,
|
||||||
|
}
|
50
cosmwasm/packages/tcbinfo/src/helpers.rs
Normal file
50
cosmwasm/packages/tcbinfo/src/helpers.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
use cosmwasm_std::{
|
||||||
|
to_json_binary, Addr, CosmosMsg, CustomQuery, Querier, QuerierWrapper, StdResult, WasmMsg,
|
||||||
|
WasmQuery,
|
||||||
|
};
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::msg::{ExecuteMsg, GetTcbInfoResponse, QueryMsg};
|
||||||
|
|
||||||
|
const FMSPC: &str = "00606a000000";
|
||||||
|
|
||||||
|
/// CwTemplateContract is a wrapper around Addr that provides a lot of helpers
|
||||||
|
/// for working with this.
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema)]
|
||||||
|
pub struct CwTemplateContract(pub Addr);
|
||||||
|
|
||||||
|
impl CwTemplateContract {
|
||||||
|
pub fn addr(&self) -> Addr {
|
||||||
|
self.0.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn call<T: Into<ExecuteMsg>>(&self, msg: T) -> StdResult<CosmosMsg> {
|
||||||
|
let msg = to_json_binary(&msg.into())?;
|
||||||
|
Ok(WasmMsg::Execute {
|
||||||
|
contract_addr: self.addr().into(),
|
||||||
|
msg,
|
||||||
|
funds: vec![],
|
||||||
|
}
|
||||||
|
.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Count
|
||||||
|
pub fn get_tcbinfo<Q, T, CQ>(&self, querier: &Q) -> StdResult<GetTcbInfoResponse>
|
||||||
|
where
|
||||||
|
Q: Querier,
|
||||||
|
T: Into<String>,
|
||||||
|
CQ: CustomQuery,
|
||||||
|
{
|
||||||
|
let msg = QueryMsg::GetTcbInfo {
|
||||||
|
fmspc: FMSPC.to_string(),
|
||||||
|
};
|
||||||
|
let query = WasmQuery::Smart {
|
||||||
|
contract_addr: self.addr().into(),
|
||||||
|
msg: to_json_binary(&msg)?,
|
||||||
|
}
|
||||||
|
.into();
|
||||||
|
let res: GetTcbInfoResponse = QuerierWrapper::<CQ>::new(querier).query(&query)?;
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
}
|
86
cosmwasm/packages/tcbinfo/src/integration_tests.rs
Normal file
86
cosmwasm/packages/tcbinfo/src/integration_tests.rs
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use cosmwasm_std::{testing::MockApi, Addr, Coin, Empty, Uint128};
|
||||||
|
use cw_multi_test::{App, AppBuilder, Contract, ContractWrapper, Executor};
|
||||||
|
|
||||||
|
use crate::{helpers::CwTemplateContract, msg::InstantiateMsg};
|
||||||
|
|
||||||
|
pub fn contract_template() -> Box<dyn Contract<Empty>> {
|
||||||
|
let contract = ContractWrapper::new(
|
||||||
|
crate::contract::execute,
|
||||||
|
crate::contract::instantiate,
|
||||||
|
crate::contract::query,
|
||||||
|
);
|
||||||
|
Box::new(contract)
|
||||||
|
}
|
||||||
|
|
||||||
|
const USER: &str = "USER";
|
||||||
|
const ADMIN: &str = "ADMIN";
|
||||||
|
const NATIVE_DENOM: &str = "denom";
|
||||||
|
const TCB_SIGNER: &str = include_str!("../data/tcb_signer.pem");
|
||||||
|
const ROOT_CA: &str = include_str!("../data/root_ca.pem");
|
||||||
|
const TCB_INFO: &str = include_str!("../data/tcbinfo.json");
|
||||||
|
const TIME: &str = "2024-07-11T15:19:13Z";
|
||||||
|
|
||||||
|
fn mock_app() -> App {
|
||||||
|
AppBuilder::new().build(|router, _, storage| {
|
||||||
|
router
|
||||||
|
.bank
|
||||||
|
.init_balance(
|
||||||
|
storage,
|
||||||
|
&MockApi::default().addr_make(USER),
|
||||||
|
vec![Coin {
|
||||||
|
denom: NATIVE_DENOM.to_string(),
|
||||||
|
amount: Uint128::new(1),
|
||||||
|
}],
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fn proper_instantiate() -> (App, CwTemplateContract) {
|
||||||
|
let mut app = mock_app();
|
||||||
|
let cw_template_id = app.store_code(contract_template());
|
||||||
|
|
||||||
|
let user = app.api().addr_make(USER);
|
||||||
|
assert_eq!(
|
||||||
|
app.wrap().query_balance(user, NATIVE_DENOM).unwrap().amount,
|
||||||
|
Uint128::new(1)
|
||||||
|
);
|
||||||
|
|
||||||
|
let msg = InstantiateMsg {
|
||||||
|
root_cert: ROOT_CA.to_string(),
|
||||||
|
};
|
||||||
|
let cw_template_contract_addr = app
|
||||||
|
.instantiate_contract(
|
||||||
|
cw_template_id,
|
||||||
|
Addr::unchecked(ADMIN),
|
||||||
|
&msg,
|
||||||
|
&[],
|
||||||
|
"test",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let cw_template_contract = CwTemplateContract(cw_template_contract_addr);
|
||||||
|
|
||||||
|
(app, cw_template_contract)
|
||||||
|
}
|
||||||
|
|
||||||
|
mod add_tcbinfo {
|
||||||
|
use super::*;
|
||||||
|
use crate::msg::ExecuteMsg;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_tcbinfo() {
|
||||||
|
let (mut app, cw_template_contract) = proper_instantiate();
|
||||||
|
|
||||||
|
let msg = ExecuteMsg {
|
||||||
|
tcb_info: TCB_INFO.to_string(),
|
||||||
|
certificate: TCB_SIGNER.to_string(),
|
||||||
|
time: Some(TIME.to_string()),
|
||||||
|
};
|
||||||
|
let cosmos_msg = cw_template_contract.call(msg).unwrap();
|
||||||
|
app.execute(Addr::unchecked(USER), cosmos_msg).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
cosmwasm/packages/tcbinfo/src/lib.rs
Normal file
7
cosmwasm/packages/tcbinfo/src/lib.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
pub mod contract;
|
||||||
|
mod error;
|
||||||
|
pub mod helpers;
|
||||||
|
pub mod integration_tests;
|
||||||
|
pub mod msg;
|
||||||
|
pub mod state;
|
||||||
|
pub use crate::error::ContractError;
|
25
cosmwasm/packages/tcbinfo/src/msg.rs
Normal file
25
cosmwasm/packages/tcbinfo/src/msg.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
use cosmwasm_schema::{cw_serde, QueryResponses};
|
||||||
|
|
||||||
|
#[cw_serde]
|
||||||
|
pub struct InstantiateMsg {
|
||||||
|
pub root_cert: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cw_serde]
|
||||||
|
pub struct ExecuteMsg {
|
||||||
|
pub tcb_info: String,
|
||||||
|
pub certificate: String,
|
||||||
|
pub time: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cw_serde]
|
||||||
|
#[derive(QueryResponses)]
|
||||||
|
pub enum QueryMsg {
|
||||||
|
#[returns(GetTcbInfoResponse)]
|
||||||
|
GetTcbInfo { fmspc: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cw_serde]
|
||||||
|
pub struct GetTcbInfoResponse {
|
||||||
|
pub tcb_info: serde_json::Value,
|
||||||
|
}
|
13
cosmwasm/packages/tcbinfo/src/state.rs
Normal file
13
cosmwasm/packages/tcbinfo/src/state.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
use cosmwasm_schema::cw_serde;
|
||||||
|
use cw_storage_plus::{Item, Map};
|
||||||
|
|
||||||
|
pub type Fmspc = [u8; 6];
|
||||||
|
|
||||||
|
#[cw_serde]
|
||||||
|
pub struct TcbInfo {
|
||||||
|
pub info: String,
|
||||||
|
// pub certificate: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const DATABASE: Map<Fmspc, TcbInfo> = Map::new("state");
|
||||||
|
pub const ROOT_CERTIFICATE: Item<String> = Item::new("root_certificate");
|
Loading…
Reference in a new issue