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",
|
||||
"generic-array",
|
||||
"group",
|
||||
"pem-rfc7468",
|
||||
"pkcs8",
|
||||
"rand_core",
|
||||
"sec1",
|
||||
|
@ -2193,6 +2194,7 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
|||
dependencies = [
|
||||
"ahash",
|
||||
"allocator-api2",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4781,6 +4783,30 @@ version = "0.12.16"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "tempfile"
|
||||
version = "3.10.1"
|
||||
|
|
|
@ -54,7 +54,7 @@ impl CertificateChainVerifier for TlsCertificateChainVerifier {
|
|||
let mut issuers: Vec<usize> = (1..v.len()).collect();
|
||||
issuers.push(v.len() - 1);
|
||||
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()));
|
||||
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