TCBInfo contract (#98)

Co-authored-by: hu55a1n1 <sufialhussaini@gmail.com>
This commit is contained in:
Ajinkya Kulkarni 2024-08-13 15:31:10 +02:00 committed by GitHub
parent 4fd86c089a
commit f93da16c47
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 1796 additions and 1 deletions

26
Cargo.lock generated
View file

@ -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"

View file

@ -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)?
} }

View 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"

View 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
View 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

File diff suppressed because it is too large Load diff

View 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"

View 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"}}'
```

View 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-----

View 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-----

File diff suppressed because one or more lines are too long

View 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,
}
}

View 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());
}
}

View 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,
}

View 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)
}
}

View 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();
}
}
}

View 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;

View 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,
}

View 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");