4627 lines
173 KiB
Diff
4627 lines
173 KiB
Diff
From 85f0bca3d385699ffca8d15c70ff9ac563d34512 Mon Sep 17 00:00:00 2001
|
||
From: xuraoqing <xuraoqing@huawei.com>
|
||
Date: Thu, 22 Aug 2024 22:46:30 +0800
|
||
Subject: [PATCH] add attestation service
|
||
|
||
---
|
||
service/attestation/.gitignore | 3 +
|
||
.../attestation/attestation-agent/Cargo.toml | 29 ++
|
||
.../attestation/attestation-agent/README.md | 5 +
|
||
.../attestation-agent/agent/Cargo.toml | 47 ++
|
||
.../agent/attestation-agent.conf | 7 +
|
||
.../agent/src/bin/aa-test/main.rs | 195 ++++++++
|
||
.../agent/src/bin/generate-headers/main.rs | 14 +
|
||
.../attestation-agent/agent/src/lib.rs | 387 ++++++++++++++++
|
||
.../attestation-agent/agent/src/main.rs | 67 +++
|
||
.../agent/src/restapi/mod.rs | 140 ++++++
|
||
.../attestation-agent/agent/src/result/mod.rs | 50 ++
|
||
.../attestation-agent/attester/Cargo.toml | 18 +
|
||
.../attester/src/itrustee/itrustee.rs | 51 +++
|
||
.../attester/src/itrustee/mod.rs | 130 ++++++
|
||
.../attestation-agent/attester/src/lib.rs | 79 ++++
|
||
.../attester/src/virtcca/mod.rs | 93 ++++
|
||
.../attester/src/virtcca/virtcca.rs | 109 +++++
|
||
.../attestation-agent/token/Cargo.toml | 13 +
|
||
.../attestation-agent/token/src/lib.rs | 114 +++++
|
||
.../attestation-service/Cargo.toml | 42 ++
|
||
.../attestation/attestation-service/README.md | 6 +
|
||
.../attestation-service/policy/Cargo.toml | 12 +
|
||
.../attestation-service/policy/src/lib.rs | 181 ++++++++
|
||
.../policy/src/opa/default_itrustee.rego | 10 +
|
||
.../policy/src/opa/default_vcca.rego | 10 +
|
||
.../attestation-service/policy/src/opa/mod.rs | 167 +++++++
|
||
.../policy/src/policy_engine.rs | 73 +++
|
||
.../attestation-service/reference/Cargo.toml | 16 +
|
||
.../reference/src/extractor/mod.rs | 30 ++
|
||
.../attestation-service/reference/src/lib.rs | 141 ++++++
|
||
.../reference/src/local_fs/mod.rs | 87 ++++
|
||
.../reference/src/reference/mod.rs | 147 ++++++
|
||
.../reference/src/store/mod.rs | 19 +
|
||
.../attestation-service/service/Cargo.toml | 35 ++
|
||
.../service/attestation-service.conf | 9 +
|
||
.../attestation-service/service/src/lib.rs | 204 +++++++++
|
||
.../attestation-service/service/src/main.rs | 76 ++++
|
||
.../service/src/restapi/mod.rs | 139 ++++++
|
||
.../service/src/result/mod.rs | 55 +++
|
||
.../service/src/session.rs | 58 +++
|
||
.../attestation-service/tests/Cargo.toml | 9 +
|
||
.../attestation-service/tests/src/lib.rs | 166 +++++++
|
||
.../attestation-service/token/Cargo.toml | 13 +
|
||
.../attestation-service/token/src/lib.rs | 115 +++++
|
||
.../attestation-service/verifier/Cargo.toml | 27 ++
|
||
.../verifier/src/itrustee/itrustee.rs | 53 +++
|
||
.../verifier/src/itrustee/mod.rs | 76 ++++
|
||
.../attestation-service/verifier/src/lib.rs | 80 ++++
|
||
.../verifier/src/virtcca/ima.rs | 91 ++++
|
||
.../verifier/src/virtcca/mod.rs | 427 ++++++++++++++++++
|
||
.../attestation/attestation-types/Cargo.toml | 8 +
|
||
.../attestation/attestation-types/src/lib.rs | 52 +++
|
||
52 files changed, 4185 insertions(+)
|
||
create mode 100644 service/attestation/.gitignore
|
||
create mode 100644 service/attestation/attestation-agent/Cargo.toml
|
||
create mode 100644 service/attestation/attestation-agent/README.md
|
||
create mode 100644 service/attestation/attestation-agent/agent/Cargo.toml
|
||
create mode 100644 service/attestation/attestation-agent/agent/attestation-agent.conf
|
||
create mode 100644 service/attestation/attestation-agent/agent/src/bin/aa-test/main.rs
|
||
create mode 100644 service/attestation/attestation-agent/agent/src/bin/generate-headers/main.rs
|
||
create mode 100644 service/attestation/attestation-agent/agent/src/lib.rs
|
||
create mode 100644 service/attestation/attestation-agent/agent/src/main.rs
|
||
create mode 100644 service/attestation/attestation-agent/agent/src/restapi/mod.rs
|
||
create mode 100644 service/attestation/attestation-agent/agent/src/result/mod.rs
|
||
create mode 100644 service/attestation/attestation-agent/attester/Cargo.toml
|
||
create mode 100644 service/attestation/attestation-agent/attester/src/itrustee/itrustee.rs
|
||
create mode 100644 service/attestation/attestation-agent/attester/src/itrustee/mod.rs
|
||
create mode 100644 service/attestation/attestation-agent/attester/src/lib.rs
|
||
create mode 100644 service/attestation/attestation-agent/attester/src/virtcca/mod.rs
|
||
create mode 100644 service/attestation/attestation-agent/attester/src/virtcca/virtcca.rs
|
||
create mode 100644 service/attestation/attestation-agent/token/Cargo.toml
|
||
create mode 100644 service/attestation/attestation-agent/token/src/lib.rs
|
||
create mode 100644 service/attestation/attestation-service/Cargo.toml
|
||
create mode 100644 service/attestation/attestation-service/README.md
|
||
create mode 100644 service/attestation/attestation-service/policy/Cargo.toml
|
||
create mode 100644 service/attestation/attestation-service/policy/src/lib.rs
|
||
create mode 100644 service/attestation/attestation-service/policy/src/opa/default_itrustee.rego
|
||
create mode 100644 service/attestation/attestation-service/policy/src/opa/default_vcca.rego
|
||
create mode 100644 service/attestation/attestation-service/policy/src/opa/mod.rs
|
||
create mode 100644 service/attestation/attestation-service/policy/src/policy_engine.rs
|
||
create mode 100644 service/attestation/attestation-service/reference/Cargo.toml
|
||
create mode 100644 service/attestation/attestation-service/reference/src/extractor/mod.rs
|
||
create mode 100644 service/attestation/attestation-service/reference/src/lib.rs
|
||
create mode 100644 service/attestation/attestation-service/reference/src/local_fs/mod.rs
|
||
create mode 100644 service/attestation/attestation-service/reference/src/reference/mod.rs
|
||
create mode 100644 service/attestation/attestation-service/reference/src/store/mod.rs
|
||
create mode 100644 service/attestation/attestation-service/service/Cargo.toml
|
||
create mode 100644 service/attestation/attestation-service/service/attestation-service.conf
|
||
create mode 100644 service/attestation/attestation-service/service/src/lib.rs
|
||
create mode 100644 service/attestation/attestation-service/service/src/main.rs
|
||
create mode 100644 service/attestation/attestation-service/service/src/restapi/mod.rs
|
||
create mode 100644 service/attestation/attestation-service/service/src/result/mod.rs
|
||
create mode 100644 service/attestation/attestation-service/service/src/session.rs
|
||
create mode 100644 service/attestation/attestation-service/tests/Cargo.toml
|
||
create mode 100644 service/attestation/attestation-service/tests/src/lib.rs
|
||
create mode 100644 service/attestation/attestation-service/token/Cargo.toml
|
||
create mode 100644 service/attestation/attestation-service/token/src/lib.rs
|
||
create mode 100644 service/attestation/attestation-service/verifier/Cargo.toml
|
||
create mode 100644 service/attestation/attestation-service/verifier/src/itrustee/itrustee.rs
|
||
create mode 100644 service/attestation/attestation-service/verifier/src/itrustee/mod.rs
|
||
create mode 100644 service/attestation/attestation-service/verifier/src/lib.rs
|
||
create mode 100644 service/attestation/attestation-service/verifier/src/virtcca/ima.rs
|
||
create mode 100644 service/attestation/attestation-service/verifier/src/virtcca/mod.rs
|
||
create mode 100644 service/attestation/attestation-types/Cargo.toml
|
||
create mode 100644 service/attestation/attestation-types/src/lib.rs
|
||
|
||
diff --git a/service/attestation/.gitignore b/service/attestation/.gitignore
|
||
new file mode 100644
|
||
index 0000000..8094f6e
|
||
--- /dev/null
|
||
+++ b/service/attestation/.gitignore
|
||
@@ -0,0 +1,3 @@
|
||
+.vscode
|
||
+target
|
||
+Cargo.lock
|
||
diff --git a/service/attestation/attestation-agent/Cargo.toml b/service/attestation/attestation-agent/Cargo.toml
|
||
new file mode 100644
|
||
index 0000000..bdc7b12
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-agent/Cargo.toml
|
||
@@ -0,0 +1,29 @@
|
||
+[workspace]
|
||
+resolver = "2"
|
||
+members = [
|
||
+ "agent",
|
||
+ "attester",
|
||
+ "token"
|
||
+]
|
||
+
|
||
+[workspace.dependencies]
|
||
+anyhow = "1.0"
|
||
+config = "0.14.0"
|
||
+serde = { version = "1.0", features = ["derive"] }
|
||
+serde_json = "1.0"
|
||
+rand = "0.8.5"
|
||
+base64-url = "3.0.0"
|
||
+async-trait = "0.1.78"
|
||
+tokio = {version = "1.0", features = ["rt"]}
|
||
+log = "0.4.14"
|
||
+env_logger = "0.9"
|
||
+safer-ffi = {version = "0.1.8", features = ["alloc"]}
|
||
+futures = "0.3.30"
|
||
+reqwest = { version = "0.12", features = ["cookies", "json"] }
|
||
+jsonwebtoken = "9.3.0"
|
||
+thiserror = "1.0"
|
||
+actix-web = "4.5"
|
||
+clap = { version = "4.5.7", features = ["derive"] }
|
||
+
|
||
+verifier = {path = "../attestation-service/verifier", default-features = false}
|
||
+attestation-types = {path = "../attestation-types"}
|
||
diff --git a/service/attestation/attestation-agent/README.md b/service/attestation/attestation-agent/README.md
|
||
new file mode 100644
|
||
index 0000000..0157e59
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-agent/README.md
|
||
@@ -0,0 +1,5 @@
|
||
+# Attestation Agent
|
||
+The Attestation Agent is deployed on the TEE node, provide get_evidence, get_token, verify_evidece interface, etc.
|
||
+
|
||
+# Overview
|
||
+TODO
|
||
diff --git a/service/attestation/attestation-agent/agent/Cargo.toml b/service/attestation/attestation-agent/agent/Cargo.toml
|
||
new file mode 100644
|
||
index 0000000..e29f89b
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-agent/agent/Cargo.toml
|
||
@@ -0,0 +1,47 @@
|
||
+[package]
|
||
+name = "attestation-agent"
|
||
+version = "0.1.0"
|
||
+edition = "2021"
|
||
+
|
||
+[[bin]]
|
||
+name = "aa-test"
|
||
+
|
||
+[[bin]]
|
||
+name = "generate-headers"
|
||
+required-features = ["headers"]
|
||
+
|
||
+[lib]
|
||
+name = "attestation_agent"
|
||
+crate-type = ["lib", "cdylib"]
|
||
+
|
||
+[features]
|
||
+no_as = []
|
||
+itrustee-attester = ["attester/itrustee-attester"]
|
||
+virtcca-attester = ["attester/virtcca-attester"]
|
||
+all-attester = ["attester/itrustee-attester", "attester/virtcca-attester"]
|
||
+itrustee-verifier = ["verifier/itrustee-verifier"]
|
||
+virtcca-verifier = ["verifier/virtcca-verifier"]
|
||
+all-verifier = ["verifier/itrustee-verifier", "verifier/virtcca-verifier"]
|
||
+headers = ["safer-ffi/headers"]
|
||
+
|
||
+[dependencies]
|
||
+anyhow.workspace = true
|
||
+config.workspace = true
|
||
+serde.workspace = true
|
||
+serde_json.workspace = true
|
||
+rand.workspace = true
|
||
+async-trait.workspace = true
|
||
+tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
|
||
+log.workspace = true
|
||
+env_logger.workspace = true
|
||
+safer-ffi.workspace = true
|
||
+futures.workspace = true
|
||
+reqwest = { workspace = true, features = ["json"] }
|
||
+base64-url.workspace = true
|
||
+thiserror.workspace = true
|
||
+actix-web.workspace = true
|
||
+clap.workspace = true
|
||
+
|
||
+attester = { path = "../attester" }
|
||
+token_verifier = { path = "../token" }
|
||
+verifier = { workspace = true, features = ["no_as"], optional = true }
|
||
diff --git a/service/attestation/attestation-agent/agent/attestation-agent.conf b/service/attestation/attestation-agent/agent/attestation-agent.conf
|
||
new file mode 100644
|
||
index 0000000..0d68972
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-agent/agent/attestation-agent.conf
|
||
@@ -0,0 +1,7 @@
|
||
+{
|
||
+ "svr_url": "http://192.168.66.88:8888",
|
||
+ "token_cfg": {
|
||
+ "cert": "/home/cert/as_cert.pem",
|
||
+ "iss": "oeas"
|
||
+ }
|
||
+}
|
||
\ No newline at end of file
|
||
diff --git a/service/attestation/attestation-agent/agent/src/bin/aa-test/main.rs b/service/attestation/attestation-agent/agent/src/bin/aa-test/main.rs
|
||
new file mode 100644
|
||
index 0000000..58fc389
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-agent/agent/src/bin/aa-test/main.rs
|
||
@@ -0,0 +1,195 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+
|
||
+//! This is a test bin, test get evidence and verify
|
||
+//! on kunpeng platform, libqca has white ta lists, need copy target/debug/attestation-agent to /vendor/bin/
|
||
+use tokio;
|
||
+use env_logger;
|
||
+use serde_json::json;
|
||
+use reqwest;
|
||
+
|
||
+const TEST_THREAD_NUM: i64 = 1; // multi thread num
|
||
+
|
||
+#[tokio::main]
|
||
+async fn main() {
|
||
+ env_logger::init();
|
||
+ let mut handles = Vec::with_capacity(TEST_THREAD_NUM as usize);
|
||
+ for i in 0..TEST_THREAD_NUM {
|
||
+ let t = tokio::spawn(async move {aa_proc(i).await;});
|
||
+ handles.push(t);
|
||
+ }
|
||
+
|
||
+ for handle in handles {
|
||
+ let _ = tokio::join!(handle);
|
||
+ }
|
||
+ println!("main stop");
|
||
+}
|
||
+
|
||
+async fn aa_proc(i: i64) {
|
||
+ println!("attestation_proc {} start", i);
|
||
+
|
||
+ // get challenge
|
||
+ let client = reqwest::Client::new();
|
||
+ let challenge_endpoint = "http://127.0.0.1:8081/challenge";
|
||
+ let res = client
|
||
+ .get(challenge_endpoint)
|
||
+ .header("Content-Type", "application/json")
|
||
+ .header("content-length", 0)
|
||
+ //.json(&request_body)
|
||
+ .send()
|
||
+ .await
|
||
+ .unwrap();
|
||
+
|
||
+ let challenge = match res.status() {
|
||
+ reqwest::StatusCode::OK => {
|
||
+ let respone = res.text().await.unwrap();
|
||
+ println!("get challenge success, AA Response: {:?}", respone);
|
||
+ respone
|
||
+ }
|
||
+ status => {
|
||
+ println!("get challenge Failed, AA Response: {:?}", status);
|
||
+ return;
|
||
+ }
|
||
+ };
|
||
+
|
||
+ // get evidence
|
||
+ let request_body = json!({
|
||
+ "challenge": challenge,
|
||
+ "uuid": String::from("f68fd704-6eb1-4d14-b218-722850eb3ef0"),
|
||
+ });
|
||
+
|
||
+ let attest_endpoint = "http://127.0.0.1:8081/evidence";
|
||
+ let res = client
|
||
+ .get(attest_endpoint)
|
||
+ .header("Content-Type", "application/json")
|
||
+ .json(&request_body)
|
||
+ .send()
|
||
+ .await
|
||
+ .unwrap();
|
||
+
|
||
+ let evidence = match res.status() {
|
||
+ reqwest::StatusCode::OK => {
|
||
+ let respone = res.text().await.unwrap();
|
||
+ println!("get evidence success, AA Response: {:?}", respone);
|
||
+ respone
|
||
+ }
|
||
+ status => {
|
||
+ println!("get evidence Failed, AA Response: {:?}", status);
|
||
+ return;
|
||
+ }
|
||
+ };
|
||
+ // verify evidence with no challenge
|
||
+ #[cfg(not(feature = "no_as"))]
|
||
+ {
|
||
+ let request_body = json!({
|
||
+ "challenge": "",
|
||
+ "evidence": evidence,
|
||
+ });
|
||
+
|
||
+ let res = client
|
||
+ .post(attest_endpoint)
|
||
+ .header("Content-Type", "application/json")
|
||
+ .json(&request_body)
|
||
+ .send()
|
||
+ .await
|
||
+ .unwrap();
|
||
+
|
||
+ match res.status() {
|
||
+ reqwest::StatusCode::OK => {
|
||
+ let respone = res.text().await.unwrap();
|
||
+ println!("verify evidence with no challenge success, AA Response: {:?}", respone);
|
||
+ }
|
||
+ status => {
|
||
+ println!("verify evidence with no challenge Failed, AA Response: {:?}", status);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ // verify evidence with challenge
|
||
+ let request_body = json!({
|
||
+ "challenge": challenge,
|
||
+ "evidence": evidence,
|
||
+ });
|
||
+
|
||
+ let res = client
|
||
+ .post(attest_endpoint)
|
||
+ .header("Content-Type", "application/json")
|
||
+ .json(&request_body)
|
||
+ .send()
|
||
+ .await
|
||
+ .unwrap();
|
||
+
|
||
+ match res.status() {
|
||
+ reqwest::StatusCode::OK => {
|
||
+ let respone = res.text().await.unwrap();
|
||
+ println!("verify evidence success, AA Response: {:?}", respone);
|
||
+ }
|
||
+ status => {
|
||
+ println!("verify evidence Failed, AA Response: {:?}", status);
|
||
+ }
|
||
+ }
|
||
+
|
||
+ #[cfg(not(feature = "no_as"))]
|
||
+ {
|
||
+ // get token
|
||
+ let token_endpoint = "http://127.0.0.1:8081/token";
|
||
+ let request_body = json!({
|
||
+ "challenge": challenge,
|
||
+ "uuid": String::from("f68fd704-6eb1-4d14-b218-722850eb3ef0"),
|
||
+ });
|
||
+
|
||
+ let res = client
|
||
+ .get(token_endpoint)
|
||
+ .header("Content-Type", "application/json")
|
||
+ .json(&request_body)
|
||
+ .send()
|
||
+ .await
|
||
+ .unwrap();
|
||
+
|
||
+ let token = match res.status() {
|
||
+ reqwest::StatusCode::OK => {
|
||
+ let respone = res.text().await.unwrap();
|
||
+ println!("get token success, AA Response: {:?}", respone);
|
||
+ respone
|
||
+ }
|
||
+ status => {
|
||
+ println!("get token Failed, AA Response: {:?}", status);
|
||
+ return;
|
||
+ }
|
||
+ };
|
||
+
|
||
+ // verify token
|
||
+ let request_body = json!({
|
||
+ "token": token,
|
||
+ });
|
||
+
|
||
+ let res = client
|
||
+ .post(token_endpoint)
|
||
+ .header("Content-Type", "application/json")
|
||
+ .json(&request_body)
|
||
+ .send()
|
||
+ .await
|
||
+ .unwrap();
|
||
+
|
||
+ match res.status() {
|
||
+ reqwest::StatusCode::OK => {
|
||
+ let respone = res.text().await.unwrap();
|
||
+ println!("verify token success, AA Response: {:?}", respone);
|
||
+ }
|
||
+ status => {
|
||
+ println!("verify token Failed, AA Response: {:?}", status);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+
|
||
+ println!("attestation_proc {} end", i);
|
||
+}
|
||
\ No newline at end of file
|
||
diff --git a/service/attestation/attestation-agent/agent/src/bin/generate-headers/main.rs b/service/attestation/attestation-agent/agent/src/bin/generate-headers/main.rs
|
||
new file mode 100644
|
||
index 0000000..f3f62c9
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-agent/agent/src/bin/generate-headers/main.rs
|
||
@@ -0,0 +1,14 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+fn main() -> ::std::io::Result<()> {
|
||
+ attestation_agent::generate_headers()
|
||
+}
|
||
diff --git a/service/attestation/attestation-agent/agent/src/lib.rs b/service/attestation/attestation-agent/agent/src/lib.rs
|
||
new file mode 100644
|
||
index 0000000..4ff9b58
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-agent/agent/src/lib.rs
|
||
@@ -0,0 +1,387 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+
|
||
+//! Attestation Agent
|
||
+//!
|
||
+//! This crate provides some APIs to get and verify the TEE evidence.
|
||
+//! Current supports kunpeng itrustee and virtcca TEE types.
|
||
+
|
||
+use anyhow::{Result, bail, anyhow};
|
||
+use log;
|
||
+use serde::{Serialize, Deserialize};
|
||
+use async_trait::async_trait;
|
||
+use std::fs::File;
|
||
+use std::path::Path;
|
||
+use rand::RngCore;
|
||
+
|
||
+use attester::{Attester, AttesterAPIs};
|
||
+use token_verifier::{TokenVerifyConfig, TokenVerifier, TokenRawData};
|
||
+
|
||
+pub mod result;
|
||
+use result::Error;
|
||
+pub type TeeClaim = serde_json::Value;
|
||
+
|
||
+#[cfg(feature = "no_as")]
|
||
+use verifier::{Verifier, VerifierAPIs};
|
||
+
|
||
+#[cfg(not(feature = "no_as"))]
|
||
+use {serde_json::json, reqwest, base64_url};
|
||
+
|
||
+pub use attester::EvidenceRequest;
|
||
+
|
||
+pub type AsTokenClaim = TokenRawData;
|
||
+
|
||
+pub const DEFAULT_AACONFIG_FILE: &str = "/etc/attestation/attestation-agent/attestation-agent.conf";
|
||
+pub struct TokenRequest {
|
||
+ pub ev_req: EvidenceRequest,
|
||
+ pub policy_id: Option<Vec<String>>,
|
||
+}
|
||
+
|
||
+#[async_trait]
|
||
+pub trait AttestationAgentAPIs {
|
||
+ async fn get_challenge(&self) -> Result<String>;
|
||
+
|
||
+ /// `get_evidence`: get hardware TEE signed evidence due to given user_data,
|
||
+ /// such as input random challenge to prevent replay attacks
|
||
+ async fn get_evidence(&self, user_data: EvidenceRequest) -> Result<Vec<u8>>;
|
||
+
|
||
+ /// `verify_evidence`: verify the integrity of TEE evidence and evaluate the
|
||
+ /// claims against the supplied reference values
|
||
+ async fn verify_evidence(&self,
|
||
+ challenge: &[u8],
|
||
+ evidence: &[u8],
|
||
+ policy_id: Option<Vec<String>>
|
||
+ ) -> Result<TeeClaim>;
|
||
+
|
||
+ //#[cfg(not(feature = "no_as"))]
|
||
+ async fn get_token(&self, user_data: TokenRequest) -> Result<String>;
|
||
+
|
||
+ async fn verify_token(&self, token: String) -> Result<AsTokenClaim>;
|
||
+}
|
||
+
|
||
+#[async_trait]
|
||
+impl AttestationAgentAPIs for AttestationAgent {
|
||
+ // no_as generate by agent; has as generate by as
|
||
+ async fn get_challenge(&self) -> Result<String> {
|
||
+ #[cfg(feature = "no_as")]
|
||
+ return self.generate_challenge_local().await;
|
||
+
|
||
+ #[cfg(not(feature = "no_as"))]
|
||
+ return self.get_challenge_from_as().await;
|
||
+ }
|
||
+ async fn get_evidence(&self, user_data: EvidenceRequest) -> Result<Vec<u8>> {
|
||
+ Attester::default().tee_get_evidence(user_data).await
|
||
+ }
|
||
+ async fn verify_evidence(&self,
|
||
+ challenge: &[u8],
|
||
+ evidence: &[u8],
|
||
+ _policy_id: Option<Vec<String>>
|
||
+ ) -> Result<TeeClaim> {
|
||
+ #[cfg(feature = "no_as")]
|
||
+ {
|
||
+ let ret = Verifier::default().verify_evidence(challenge, evidence).await;
|
||
+ match ret {
|
||
+ Ok(tee_claim) => Ok(tee_claim),
|
||
+ Err(e) => {
|
||
+ log::error!("attestation agent verify evidence with no as failed:{:?}", e);
|
||
+ Err(e)
|
||
+ },
|
||
+ }
|
||
+ }
|
||
+
|
||
+ #[cfg(not(feature = "no_as"))]
|
||
+ {
|
||
+ let ret = self.verify_evidence_by_as(challenge, evidence, _policy_id).await;
|
||
+ match ret {
|
||
+ Ok(token) => { self.token_to_teeclaim(token).await },
|
||
+ Err(e) => {
|
||
+ log::error!("verify evidence with as failed:{:?}", e);
|
||
+ Err(e)
|
||
+ },
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ async fn get_token(&self, user_data: TokenRequest) -> Result<String> {
|
||
+ #[cfg(feature = "no_as")]
|
||
+ {
|
||
+ return Ok("no as in not supprot get token".to_string());
|
||
+ }
|
||
+ // todo token 有效期内,不再重新获取报告
|
||
+ #[cfg(not(feature = "no_as"))]
|
||
+ {
|
||
+ let evidence = self.get_evidence(user_data.ev_req.clone()).await?;
|
||
+ let challenge = &user_data.ev_req.challenge;
|
||
+ let policy_id = user_data.policy_id;
|
||
+ // request as
|
||
+ return self.verify_evidence_by_as(challenge, &evidence, policy_id).await;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ async fn verify_token(&self, token: String) -> Result<AsTokenClaim> {
|
||
+ let verifier = TokenVerifier::new(self.config.token_cfg.clone())?;
|
||
+ let result = verifier.verify(&token);
|
||
+ match result {
|
||
+ Ok(raw_token) => Ok(raw_token as AsTokenClaim),
|
||
+ Err(e) => bail!("verify token failed {:?}", e),
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+#[derive(Clone, Debug, Serialize, Deserialize)]
|
||
+struct AAConfig {
|
||
+ svr_url: String, // Attestation Service url
|
||
+ token_cfg: TokenVerifyConfig,
|
||
+}
|
||
+
|
||
+impl Default for AAConfig {
|
||
+ fn default() -> Self {
|
||
+ Self {
|
||
+ svr_url: String::from("http://127.0.0.1:8080"),
|
||
+ token_cfg: TokenVerifyConfig::default(),
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+impl TryFrom<&Path> for AAConfig {
|
||
+ /// Load `AAConfig` from a configuration file like:
|
||
+ /// {
|
||
+ /// "svr_url": "http://127.0.0.1:8080",
|
||
+ /// "token_cfg": {
|
||
+ /// "cert": "/etc/attestation/attestation-agent/as_cert.pem",
|
||
+ /// "iss": "oeas"
|
||
+ /// }
|
||
+ /// }
|
||
+ type Error = anyhow::Error;
|
||
+ fn try_from(config_path: &Path) -> Result<Self, Self::Error> {
|
||
+ let file = File::open(config_path).unwrap();
|
||
+ serde_json::from_reader::<File, AAConfig>(file).map_err(|e| anyhow!("invalid aaconfig {e}"))
|
||
+ }
|
||
+}
|
||
+
|
||
+#[derive(Debug)]
|
||
+pub struct AttestationAgent {
|
||
+ config: AAConfig,
|
||
+ client: reqwest::Client,
|
||
+}
|
||
+
|
||
+#[allow(dead_code)]
|
||
+impl AttestationAgent {
|
||
+ pub fn new(conf_path: Option<String>) -> Result<Self, Error> {
|
||
+ let config = match conf_path {
|
||
+ Some(conf_path) => {
|
||
+ log::info!("Attestation Agent config file:{conf_path}");
|
||
+ AAConfig::try_from(Path::new(&conf_path))?
|
||
+ }
|
||
+ None => {
|
||
+ log::warn!("No Attestation Agent config file specified. Using a default config");
|
||
+ AAConfig::default()
|
||
+ }
|
||
+ };
|
||
+ let client = reqwest::ClientBuilder::new()
|
||
+ .cookie_store(true)
|
||
+ .user_agent("attestation-agent-client")
|
||
+ .build()
|
||
+ .map_err(|e| result::Error::AttestationAgentError(format!("build http client {e}")))?;
|
||
+ Ok(AttestationAgent {
|
||
+ config,
|
||
+ client,
|
||
+ })
|
||
+ }
|
||
+
|
||
+ #[cfg(not(feature = "no_as"))]
|
||
+ async fn verify_evidence_by_as(&self,
|
||
+ challenge: &[u8],
|
||
+ evidence: &[u8],
|
||
+ policy_id: Option<Vec<String>>
|
||
+ ) -> Result<String> {
|
||
+ let request_body = json!({
|
||
+ "challenge": base64_url::encode(challenge),
|
||
+ "evidence": base64_url::encode(evidence),
|
||
+ "policy_id": policy_id,
|
||
+ });
|
||
+
|
||
+ let attest_endpoint = format!("{}/attestation", self.config.svr_url);
|
||
+ let res = self.client
|
||
+ .post(attest_endpoint)
|
||
+ .header("Content-Type", "application/json")
|
||
+ .json(&request_body)
|
||
+ .send()
|
||
+ .await?;
|
||
+
|
||
+ match res.status() {
|
||
+ reqwest::StatusCode::OK => {
|
||
+ let token = res.text().await?;
|
||
+ log::debug!("Remote Attestation success, AS Response: {:?}", token);
|
||
+ Ok(token)
|
||
+ }
|
||
+ _ => {
|
||
+ bail!("Remote Attestation Failed, AS Response: {:?}", res.text().await?);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ #[cfg(not(feature = "no_as"))]
|
||
+ async fn token_to_teeclaim(&self, token: String) -> Result<TeeClaim> {
|
||
+ let ret = self.verify_token(token).await;
|
||
+ match ret {
|
||
+ Ok(token) => {
|
||
+ let token_claim: serde_json::Value = serde_json::from_slice(token.claim.as_bytes())?;
|
||
+ let tee_claim = json!({
|
||
+ "tee": token_claim["tee"].clone(),
|
||
+ "payload" : token_claim["tcb_status"].clone(),
|
||
+ });
|
||
+ Ok(tee_claim as TeeClaim)
|
||
+ },
|
||
+ Err(e) => {
|
||
+ log::error!("token to teeclaim failed:{:?}", e);
|
||
+ Err(e)
|
||
+ },
|
||
+ }
|
||
+ }
|
||
+
|
||
+ async fn generate_challenge_local(&self) -> Result<String> {
|
||
+ let mut nonce: [u8; 32] = [0; 32];
|
||
+ rand::thread_rng().fill_bytes(&mut nonce);
|
||
+ Ok(base64_url::encode(&nonce))
|
||
+ }
|
||
+ async fn get_challenge_from_as(&self) -> Result<String> {
|
||
+ let challenge_endpoint = format!("{}/challenge", self.config.svr_url);
|
||
+ let res = self.client
|
||
+ .get(challenge_endpoint)
|
||
+ .header("Content-Type", "application/json")
|
||
+ .header("content-length", 0)
|
||
+ //.json(&request_body)
|
||
+ .send()
|
||
+ .await?;
|
||
+ let challenge = match res.status() {
|
||
+ reqwest::StatusCode::OK => {
|
||
+ let respone = res.json().await.unwrap();
|
||
+ log::info!("get challenge success, AS Response: {:?}", respone);
|
||
+ respone
|
||
+ }
|
||
+ status => {
|
||
+ log::info!("get challenge Failed, AS Response: {:?}", status);
|
||
+ bail!("get challenge Failed")
|
||
+ }
|
||
+ };
|
||
+ Ok(challenge)
|
||
+ }
|
||
+}
|
||
+
|
||
+
|
||
+// attestation agent c interface
|
||
+use safer_ffi::prelude::*;
|
||
+use futures::executor::block_on;
|
||
+use tokio::runtime::Runtime;
|
||
+
|
||
+#[ffi_export]
|
||
+pub fn get_report(c_challenge: Option<&repr_c::Vec<u8>>, c_ima: &repr_c::TaggedOption<bool>) -> repr_c::Vec<u8> {
|
||
+ env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
|
||
+ log::debug!("input challenge: {:?}, ima: {:?}", c_challenge, c_ima);
|
||
+ let ima = match c_ima {
|
||
+ repr_c::TaggedOption::None => false,
|
||
+ repr_c::TaggedOption::Some(ima) => *ima,
|
||
+ };
|
||
+ let challenge = match c_challenge {
|
||
+ None => {log::error!("challenge is null"); return Vec::new().into();},
|
||
+ Some(cha) => cha.clone().to_vec(),
|
||
+ };
|
||
+
|
||
+ let input: EvidenceRequest = EvidenceRequest {
|
||
+ uuid: "f68fd704-6eb1-4d14-b218-722850eb3ef0".to_string(),
|
||
+ challenge: challenge,
|
||
+ ima: Some(ima),
|
||
+ };
|
||
+
|
||
+ let fut = async {
|
||
+ AttestationAgent::new(Some(DEFAULT_AACONFIG_FILE.to_string())).unwrap().get_evidence(input).await
|
||
+ };
|
||
+ let report: Vec<u8> = match block_on(fut) {
|
||
+ Ok(report) => report,
|
||
+ Err(e) => {
|
||
+ log::error!("get report failed {:?}", e);
|
||
+ Vec::new()
|
||
+ },
|
||
+ };
|
||
+
|
||
+ report.into()
|
||
+}
|
||
+
|
||
+#[ffi_export]
|
||
+pub fn verify_report(c_challenge: Option<&repr_c::Vec<u8>>, report: Option<&repr_c::Vec<u8>>) -> repr_c::String {
|
||
+ let challenge = match c_challenge {
|
||
+ None => {
|
||
+ log::error!("challenge is null");
|
||
+ return "".to_string().into();
|
||
+ },
|
||
+ Some(cha) => cha.clone().to_vec(),
|
||
+ };
|
||
+ let report = match report {
|
||
+ None => {
|
||
+ log::error!("report is null");
|
||
+ return "".to_string().into();
|
||
+ },
|
||
+ Some(report) => report.clone().to_vec(),
|
||
+ };
|
||
+ let rt = Runtime::new().unwrap();
|
||
+ let fut = async {AttestationAgent::new(Some(DEFAULT_AACONFIG_FILE.to_string())).unwrap().verify_evidence(
|
||
+ &challenge, &report, None).await};
|
||
+ let ret = rt.block_on(fut);
|
||
+
|
||
+ let ret = match ret {
|
||
+ Ok(claim) => {
|
||
+ log::debug!("claim: {:?}", claim);
|
||
+ claim.to_string()
|
||
+ },
|
||
+ Err(e) =>{
|
||
+ log::error!("{e}");
|
||
+ "".to_string()
|
||
+ },
|
||
+ };
|
||
+
|
||
+ return ret.into();
|
||
+}
|
||
+
|
||
+#[ffi_export]
|
||
+pub fn free_rust_vec(vec: repr_c::Vec<u8>) {
|
||
+ drop(vec);
|
||
+}
|
||
+
|
||
+// The following function is only necessary for the header generation.
|
||
+#[cfg(feature = "headers")]
|
||
+pub fn generate_headers() -> ::std::io::Result<()> {
|
||
+ ::safer_ffi::headers::builder()
|
||
+ .to_file("./c_header/rust_attestation_agent.h")?
|
||
+ .generate()
|
||
+}
|
||
+
|
||
+
|
||
+#[cfg(test)]
|
||
+mod tests {
|
||
+ use crate::*;
|
||
+
|
||
+ #[test]
|
||
+ fn aa_new_no_conf_path() {
|
||
+ let aa = AttestationAgent::new(None).unwrap();
|
||
+ assert_eq!(aa.config.svr_url, "http://127.0.0.1:8080");
|
||
+ assert_eq!(aa.config.token_cfg.cert, "/etc/attestation/attestation-agent/as_cert.pem");
|
||
+ assert_eq!(aa.config.token_cfg.iss, "openEulerAS");
|
||
+ }
|
||
+
|
||
+ #[test]
|
||
+ fn aa_new_with_example_conf() {
|
||
+ let aa = AttestationAgent::new(Some("attestation-agent.conf".to_string())).unwrap();
|
||
+ assert_eq!(aa.config.token_cfg.cert, "/home/cert/as_cert.pem");
|
||
+ assert_eq!(aa.config.token_cfg.iss, "oeas");
|
||
+ }
|
||
+}
|
||
diff --git a/service/attestation/attestation-agent/agent/src/main.rs b/service/attestation/attestation-agent/agent/src/main.rs
|
||
new file mode 100644
|
||
index 0000000..76e63dc
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-agent/agent/src/main.rs
|
||
@@ -0,0 +1,67 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+use attestation_agent::{AttestationAgent, DEFAULT_AACONFIG_FILE};
|
||
+mod restapi;
|
||
+use restapi::{get_challenge, get_evidence, verify_evidence, get_token, verify_token};
|
||
+
|
||
+use anyhow::Result;
|
||
+use env_logger;
|
||
+use actix_web::{web, App, HttpServer, HttpResponse};
|
||
+use std::{net::{SocketAddr, IpAddr, Ipv4Addr}, sync::Arc};
|
||
+use tokio::sync::RwLock;
|
||
+use clap::{Parser, command, arg};
|
||
+
|
||
+const DEFAULT_SOCKETADDR: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8081);
|
||
+
|
||
+#[derive(Parser, Debug)]
|
||
+#[command(version, about, long_about = None)]
|
||
+struct Cli {
|
||
+ /// Socket address to listen on
|
||
+ #[arg(short, long, default_value_t = DEFAULT_SOCKETADDR)]
|
||
+ socketaddr: SocketAddr,
|
||
+
|
||
+ /// Load `AAConfig` from a configuration file like:
|
||
+ /// {
|
||
+ /// "svr_url": "http://127.0.0.1:8080",
|
||
+ /// "token_cfg": {
|
||
+ /// "cert": "/etc/attestation/attestation-agent/as_cert.pem",
|
||
+ /// "iss": "oeas"
|
||
+ /// }
|
||
+ /// }
|
||
+ #[arg(short, long, default_value_t = DEFAULT_AACONFIG_FILE.to_string())]
|
||
+ config: String,
|
||
+}
|
||
+
|
||
+#[actix_web::main]
|
||
+async fn main() -> Result<()> {
|
||
+ env_logger::init_from_env(env_logger::Env::new().default_filter_or("debug"));
|
||
+
|
||
+ let cli = Cli::parse();
|
||
+ let server = AttestationAgent::new(Some(cli.config)).unwrap();
|
||
+
|
||
+ let service = web::Data::new(Arc::new(RwLock::new(server)));
|
||
+ HttpServer::new(move || {
|
||
+ App::new()
|
||
+ .app_data(web::Data::clone(&service))
|
||
+ .service(get_challenge)
|
||
+ .service(get_evidence)
|
||
+ .service(verify_evidence)
|
||
+ .service(get_token)
|
||
+ .service(verify_token)
|
||
+ .default_service(web::to(|| HttpResponse::NotFound()))
|
||
+ })
|
||
+ .bind((cli.socketaddr.ip().to_string(), cli.socketaddr.port()))?
|
||
+ .run()
|
||
+ .await?;
|
||
+
|
||
+ Ok(())
|
||
+}
|
||
\ No newline at end of file
|
||
diff --git a/service/attestation/attestation-agent/agent/src/restapi/mod.rs b/service/attestation/attestation-agent/agent/src/restapi/mod.rs
|
||
new file mode 100644
|
||
index 0000000..490242a
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-agent/agent/src/restapi/mod.rs
|
||
@@ -0,0 +1,140 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+use attestation_agent::{AttestationAgent, AttestationAgentAPIs, TokenRequest};
|
||
+use attestation_agent::result::Result;
|
||
+
|
||
+use actix_web::{ post, get, web, HttpResponse};
|
||
+use attester::EvidenceRequest;
|
||
+use serde::{Deserialize, Serialize};
|
||
+use std::sync::Arc;
|
||
+use tokio::sync::RwLock;
|
||
+use log;
|
||
+use base64_url;
|
||
+
|
||
+#[derive(Deserialize, Serialize, Debug)]
|
||
+struct GetChallengeRequest {}
|
||
+
|
||
+#[get("/challenge")]
|
||
+pub async fn get_challenge(
|
||
+ //_request: web::Json<GetChallengeRequest>,
|
||
+ agent: web::Data<Arc<RwLock<AttestationAgent>>>,
|
||
+) -> Result<HttpResponse> {
|
||
+ //let request = request.0;
|
||
+ log::debug!("get challenge request");
|
||
+ let challenge = agent.read().await.get_challenge().await?;
|
||
+
|
||
+ Ok(HttpResponse::Ok().body(challenge))
|
||
+}
|
||
+
|
||
+#[derive(Deserialize, Serialize, Debug)]
|
||
+struct GetEvidenceRequest {
|
||
+ challenge: String,
|
||
+ uuid: String,
|
||
+ ima: Option<bool>,
|
||
+}
|
||
+
|
||
+#[get("/evidence")]
|
||
+pub async fn get_evidence(
|
||
+ request: web::Json<GetEvidenceRequest>,
|
||
+ agent: web::Data<Arc<RwLock<AttestationAgent>>>,
|
||
+) -> Result<HttpResponse> {
|
||
+ let request = request.0;
|
||
+ log::debug!("get evidence request: {:?}", request);
|
||
+ let challenge = base64_url::decode(&request.challenge).expect("base64 decode challenge");
|
||
+ let uuid = request.uuid;
|
||
+ let ima = request.ima;
|
||
+ let input = EvidenceRequest {
|
||
+ uuid: uuid,
|
||
+ challenge: challenge,
|
||
+ ima: ima,
|
||
+ };
|
||
+ let evidence = agent.read().await.get_evidence(input).await?;
|
||
+
|
||
+
|
||
+ Ok(HttpResponse::Ok().body(evidence))
|
||
+}
|
||
+
|
||
+#[derive(Deserialize, Serialize, Debug)]
|
||
+struct VerifyEvidenceRequest {
|
||
+ challenge: String,
|
||
+ evidence: String,
|
||
+ policy_id: Option<Vec<String>>,
|
||
+}
|
||
+#[post("/evidence")]
|
||
+pub async fn verify_evidence(
|
||
+ request: web::Json<VerifyEvidenceRequest>,
|
||
+ agent: web::Data<Arc<RwLock<AttestationAgent>>>,
|
||
+) -> Result<HttpResponse> {
|
||
+ let request = request.0;
|
||
+ log::debug!("verify evidence request: {:?}", request);
|
||
+ let challenge = base64_url::decode(&request.challenge).expect("base64 decode challenge");
|
||
+ let evidence = request.evidence;
|
||
+ let policy_id = request.policy_id;
|
||
+
|
||
+ let claim = agent.read().await.verify_evidence(&challenge, evidence.as_bytes(), policy_id).await?;
|
||
+ let string_claim = serde_json::to_string(&claim)?;
|
||
+
|
||
+ Ok(HttpResponse::Ok().body(string_claim))
|
||
+}
|
||
+
|
||
+#[derive(Deserialize, Serialize, Debug)]
|
||
+struct GetTokenRequest {
|
||
+ challenge: String,
|
||
+ uuid: String,
|
||
+ ima: Option<bool>,
|
||
+ policy_id: Option<Vec<String>>,
|
||
+}
|
||
+
|
||
+#[get("/token")]
|
||
+pub async fn get_token(
|
||
+ request: web::Json<GetTokenRequest>,
|
||
+ agent: web::Data<Arc<RwLock<AttestationAgent>>>,
|
||
+) -> Result<HttpResponse> {
|
||
+ let request = request.0;
|
||
+ log::debug!("get token request: {:?}", request);
|
||
+ let challenge = base64_url::decode(&request.challenge).expect("base64 decode challenge");
|
||
+ let uuid = request.uuid;
|
||
+ let ima = request.ima;
|
||
+ let policy_id = request.policy_id;
|
||
+ let ev = EvidenceRequest {
|
||
+ uuid: uuid,
|
||
+ challenge: challenge,
|
||
+ ima: ima,
|
||
+ };
|
||
+ let input = TokenRequest {
|
||
+ ev_req: ev,
|
||
+ policy_id: policy_id,
|
||
+ };
|
||
+
|
||
+ let token = agent.read().await.get_token(input).await?;
|
||
+
|
||
+
|
||
+ Ok(HttpResponse::Ok().body(token))
|
||
+}
|
||
+
|
||
+#[derive(Deserialize, Serialize, Debug)]
|
||
+struct VerifyTokenRequest {
|
||
+ token: String,
|
||
+}
|
||
+#[post("/token")]
|
||
+pub async fn verify_token(
|
||
+ request: web::Json<VerifyTokenRequest>,
|
||
+ agent: web::Data<Arc<RwLock<AttestationAgent>>>,
|
||
+) -> Result<HttpResponse> {
|
||
+ let request = request.0;
|
||
+ log::debug!("verify token request: {:?}", request);
|
||
+
|
||
+ let claim = agent.read().await.verify_token(request.token).await?;
|
||
+ let string_claim = serde_json::to_string(&claim)?;
|
||
+
|
||
+ Ok(HttpResponse::Ok().body(string_claim))
|
||
+}
|
||
\ No newline at end of file
|
||
diff --git a/service/attestation/attestation-agent/agent/src/result/mod.rs b/service/attestation/attestation-agent/agent/src/result/mod.rs
|
||
new file mode 100644
|
||
index 0000000..f06f064
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-agent/agent/src/result/mod.rs
|
||
@@ -0,0 +1,50 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+use actix_web::{body::BoxBody, HttpResponse, ResponseError};
|
||
+
|
||
+pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||
+
|
||
+/// libdevice error
|
||
+#[derive(Debug, thiserror::Error)]
|
||
+#[non_exhaustive]
|
||
+#[allow(missing_docs)]
|
||
+pub enum Error {
|
||
+ #[error("IO error: {source:?}")]
|
||
+ Io {
|
||
+ #[from]
|
||
+ source: std::io::Error,
|
||
+ },
|
||
+
|
||
+ #[error("Web error: {source:?}")]
|
||
+ Web {
|
||
+ #[from]
|
||
+ source: actix_web::error::Error,
|
||
+ },
|
||
+
|
||
+ #[error("Deserialize error: {source:?}")]
|
||
+ Deserialize {
|
||
+ #[from]
|
||
+ source: serde_json::Error,
|
||
+ },
|
||
+
|
||
+ #[error("Attestation Agent error:{0}")]
|
||
+ AttestationAgentError(String),
|
||
+
|
||
+ #[error(transparent)]
|
||
+ Other(#[from] anyhow::Error),
|
||
+}
|
||
+
|
||
+impl ResponseError for Error {
|
||
+ fn error_response(&self) -> actix_web::HttpResponse<actix_web::body::BoxBody> {
|
||
+ HttpResponse::InternalServerError().body(BoxBody::new(format!("{self:#?}")))
|
||
+ }
|
||
+}
|
||
diff --git a/service/attestation/attestation-agent/attester/Cargo.toml b/service/attestation/attestation-agent/attester/Cargo.toml
|
||
new file mode 100644
|
||
index 0000000..a7dae2a
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-agent/attester/Cargo.toml
|
||
@@ -0,0 +1,18 @@
|
||
+[package]
|
||
+name = "attester"
|
||
+version = "0.1.0"
|
||
+edition = "2021"
|
||
+
|
||
+[features]
|
||
+itrustee-attester = [ "base64-url", "rand" ]
|
||
+virtcca-attester = []
|
||
+
|
||
+[dependencies]
|
||
+anyhow.workspace = true
|
||
+serde.workspace = true
|
||
+serde_json.workspace = true
|
||
+rand = { workspace = true, optional = true }
|
||
+base64-url = { workspace = true, optional = true }
|
||
+async-trait.workspace = true
|
||
+log.workspace = true
|
||
+attestation-types.workspace = true
|
||
diff --git a/service/attestation/attestation-agent/attester/src/itrustee/itrustee.rs b/service/attestation/attestation-agent/attester/src/itrustee/itrustee.rs
|
||
new file mode 100644
|
||
index 0000000..9a711c2
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-agent/attester/src/itrustee/itrustee.rs
|
||
@@ -0,0 +1,51 @@
|
||
+/* automatically generated by rust-bindgen 0.69.4 */
|
||
+
|
||
+#[repr(C)]
|
||
+#[derive(Debug, Copy, Clone)]
|
||
+pub struct ra_buffer_data {
|
||
+ pub size: ::std::os::raw::c_uint,
|
||
+ pub buf: *mut ::std::os::raw::c_uchar,
|
||
+}
|
||
+#[test]
|
||
+fn bindgen_test_layout_ra_buffer_data() {
|
||
+ const UNINIT: ::std::mem::MaybeUninit<ra_buffer_data> = ::std::mem::MaybeUninit::uninit();
|
||
+ let ptr = UNINIT.as_ptr();
|
||
+ assert_eq!(
|
||
+ ::std::mem::size_of::<ra_buffer_data>(),
|
||
+ 16usize,
|
||
+ concat!("Size of: ", stringify!(ra_buffer_data))
|
||
+ );
|
||
+ assert_eq!(
|
||
+ ::std::mem::align_of::<ra_buffer_data>(),
|
||
+ 8usize,
|
||
+ concat!("Alignment of ", stringify!(ra_buffer_data))
|
||
+ );
|
||
+ assert_eq!(
|
||
+ unsafe { ::std::ptr::addr_of!((*ptr).size) as usize - ptr as usize },
|
||
+ 0usize,
|
||
+ concat!(
|
||
+ "Offset of field: ",
|
||
+ stringify!(ra_buffer_data),
|
||
+ "::",
|
||
+ stringify!(size)
|
||
+ )
|
||
+ );
|
||
+ assert_eq!(
|
||
+ unsafe { ::std::ptr::addr_of!((*ptr).buf) as usize - ptr as usize },
|
||
+ 8usize,
|
||
+ concat!(
|
||
+ "Offset of field: ",
|
||
+ stringify!(ra_buffer_data),
|
||
+ "::",
|
||
+ stringify!(buf)
|
||
+ )
|
||
+ );
|
||
+}
|
||
+
|
||
+#[link(name = "qca")]
|
||
+extern "C" {
|
||
+ pub fn RemoteAttest(
|
||
+ in_: *mut ra_buffer_data,
|
||
+ out: *mut ra_buffer_data,
|
||
+ ) -> ::std::os::raw::c_uint;
|
||
+}
|
||
diff --git a/service/attestation/attestation-agent/attester/src/itrustee/mod.rs b/service/attestation/attestation-agent/attester/src/itrustee/mod.rs
|
||
new file mode 100644
|
||
index 0000000..3fde5f7
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-agent/attester/src/itrustee/mod.rs
|
||
@@ -0,0 +1,130 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+
|
||
+//! itrustee tee plugin
|
||
+//!
|
||
+//! Call the hardware sdk or driver to get the specific evidence
|
||
+
|
||
+use anyhow::*;
|
||
+use serde_json;
|
||
+use std::path::Path;
|
||
+use serde::{Serialize, Deserialize};
|
||
+use base64_url;
|
||
+use log;
|
||
+
|
||
+use crate::EvidenceRequest;
|
||
+
|
||
+mod itrustee;
|
||
+
|
||
+#[derive(Debug, Default)]
|
||
+pub struct ItrusteeAttester {}
|
||
+
|
||
+impl ItrusteeAttester {
|
||
+ pub async fn tee_get_evidence(&self, user_data: EvidenceRequest) -> Result<String> {
|
||
+ let ret = itrustee_provision();
|
||
+ if ret.is_err() {
|
||
+ log::error!("itrustee attester provision failed");
|
||
+ bail!("itrustee attester provision failed");
|
||
+ }
|
||
+
|
||
+ itrustee_get_evidence(user_data)
|
||
+ }
|
||
+}
|
||
+
|
||
+pub fn detect_platform() -> bool {
|
||
+ Path::new("/usr/bin/tee").exists()
|
||
+}
|
||
+
|
||
+#[derive(Serialize, Deserialize)]
|
||
+struct ReportInputPayload {
|
||
+ version: String,
|
||
+ nonce: String,
|
||
+ uuid: String,
|
||
+ hash_alg: String,
|
||
+ with_tcb: bool,
|
||
+ request_key: bool,
|
||
+}
|
||
+
|
||
+#[derive(Serialize, Deserialize)]
|
||
+struct ItrusteeInput {
|
||
+ handler: String,
|
||
+ payload: ReportInputPayload,
|
||
+}
|
||
+
|
||
+fn itrustee_get_evidence(user_data: EvidenceRequest) -> Result<String> {
|
||
+ let payload = ReportInputPayload {
|
||
+ nonce: base64_url::encode(&user_data.challenge),
|
||
+ uuid: user_data.uuid,
|
||
+ with_tcb: false,
|
||
+ request_key: true,
|
||
+ version: String::from("TEE.RA.1.0"),
|
||
+ hash_alg: String::from("HS256"),
|
||
+ };
|
||
+
|
||
+ let itrustee_input: ItrusteeInput = ItrusteeInput {
|
||
+ handler: String::from("report-input"),
|
||
+ payload: payload,
|
||
+ };
|
||
+ let mut buf = serde_json::to_string(&itrustee_input)?;
|
||
+ let mut input = itrustee::ra_buffer_data {
|
||
+ size: buf.len() as ::std::os::raw::c_uint,
|
||
+ buf: buf.as_mut_ptr() as *mut ::std::os::raw::c_uchar,
|
||
+ };
|
||
+
|
||
+ let mut report = Vec::new();
|
||
+ report.resize(0x3000, b'\0');
|
||
+ let mut output = itrustee::ra_buffer_data {
|
||
+ size: report.len() as ::std::os::raw::c_uint,
|
||
+ buf: report.as_mut_ptr() as *mut ::std::os::raw::c_uchar,
|
||
+ };
|
||
+
|
||
+ unsafe {
|
||
+ let ret = itrustee::RemoteAttest(&mut input, &mut output);
|
||
+ if ret != 0 {
|
||
+ log::error!("itrustee get report failed, ret:{}", ret);
|
||
+ bail!("itrustee get report failed, ret:{}", ret);
|
||
+ }
|
||
+ let out_len: usize = output.size.try_into()?;
|
||
+ report.set_len(out_len);
|
||
+ }
|
||
+ let str_report = String::from_utf8(report)?;
|
||
+
|
||
+ Ok(str_report)
|
||
+}
|
||
+
|
||
+fn itrustee_provision() -> Result<()> {
|
||
+ let json = r#"{"handler":"provisioning-input","payload":{"version":"TEE.RA.1.0","scenario":"sce_no_as","hash_alg":"HS256"}}"#;
|
||
+
|
||
+ let provision_input: serde_json::Value = serde_json::from_str(json)?;
|
||
+ let mut provision_input = provision_input.to_string();
|
||
+
|
||
+ let mut input = itrustee::ra_buffer_data {
|
||
+ size: provision_input.len() as ::std::os::raw::c_uint,
|
||
+ buf: provision_input.as_mut_ptr() as *mut ::std::os::raw::c_uchar,
|
||
+ };
|
||
+
|
||
+ let mut report = Vec::new();
|
||
+ report.resize(0x3000, b'\0');
|
||
+
|
||
+ let mut output = itrustee::ra_buffer_data {
|
||
+ size: report.len() as ::std::os::raw::c_uint,
|
||
+ buf: report.as_mut_ptr() as *mut ::std::os::raw::c_uchar,
|
||
+ };
|
||
+ unsafe {
|
||
+ let ret = itrustee::RemoteAttest(&mut input, &mut output);
|
||
+ if ret != 0 {
|
||
+ log::error!("itrustee provision failed, ret:{}", ret);
|
||
+ bail!("itrustee provision failed, ret:{}", ret);
|
||
+ }
|
||
+ }
|
||
+ Ok(())
|
||
+}
|
||
diff --git a/service/attestation/attestation-agent/attester/src/lib.rs b/service/attestation/attestation-agent/attester/src/lib.rs
|
||
new file mode 100644
|
||
index 0000000..3c02946
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-agent/attester/src/lib.rs
|
||
@@ -0,0 +1,79 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+
|
||
+//! attester
|
||
+//!
|
||
+//! This crate provides unified APIs to get TEE evidence.
|
||
+
|
||
+use anyhow::*;
|
||
+use async_trait::async_trait;
|
||
+use log;
|
||
+use attestation_types::{TeeType, Evidence};
|
||
+
|
||
+#[cfg(feature = "itrustee-attester")]
|
||
+mod itrustee;
|
||
+
|
||
+#[cfg(feature = "virtcca-attester")]
|
||
+pub mod virtcca;
|
||
+
|
||
+#[derive(Debug, Clone)]
|
||
+pub struct EvidenceRequest {
|
||
+ pub uuid: String,
|
||
+ pub challenge: Vec<u8>,
|
||
+ pub ima: Option<bool>,
|
||
+}
|
||
+
|
||
+#[async_trait]
|
||
+pub trait AttesterAPIs {
|
||
+ /// Call tee plugin to get the hardware evidence.
|
||
+ /// Automatically detect the TEE type of the current running environment.
|
||
+ async fn tee_get_evidence(&self, user_data: EvidenceRequest) -> Result<Vec<u8>>;
|
||
+}
|
||
+
|
||
+#[derive(Default)]
|
||
+pub struct Attester {}
|
||
+
|
||
+
|
||
+const MAX_CHALLENGE_LEN: usize = 64;
|
||
+
|
||
+#[async_trait]
|
||
+impl AttesterAPIs for Attester {
|
||
+ async fn tee_get_evidence(&self, _user_data: EvidenceRequest) -> Result<Vec<u8>> {
|
||
+ let len = _user_data.challenge.len();
|
||
+ if len <= 0 || len > MAX_CHALLENGE_LEN {
|
||
+ log::error!("challenge len is error, expecting 0 < len <= {}, got {}", MAX_CHALLENGE_LEN, len);
|
||
+ bail!("challenge len is error, expecting 0 < len <= {}, got {}", MAX_CHALLENGE_LEN, len);
|
||
+ }
|
||
+ #[cfg(feature = "itrustee-attester")]
|
||
+ if itrustee::detect_platform() {
|
||
+ let evidence = itrustee::ItrusteeAttester::default().tee_get_evidence(_user_data).await?;
|
||
+ let aa_evidence = Evidence {
|
||
+ tee: TeeType::Itrustee,
|
||
+ evidence: evidence,
|
||
+ };
|
||
+ let evidence = serde_json::to_vec(&aa_evidence)?;
|
||
+
|
||
+ return Ok(evidence);
|
||
+ }
|
||
+ #[cfg(feature = "virtcca-attester")]
|
||
+ if virtcca::detect_platform() {
|
||
+ let evidence = virtcca::VirtccaAttester::default().tee_get_evidence(_user_data).await?;
|
||
+ let aa_evidence = Evidence {
|
||
+ tee: TeeType::Virtcca,
|
||
+ evidence: evidence,
|
||
+ };
|
||
+ let evidence = serde_json::to_vec(&aa_evidence)?;
|
||
+ return Ok(evidence);
|
||
+ }
|
||
+ bail!("unkown tee platform");
|
||
+ }
|
||
+}
|
||
\ No newline at end of file
|
||
diff --git a/service/attestation/attestation-agent/attester/src/virtcca/mod.rs b/service/attestation/attestation-agent/attester/src/virtcca/mod.rs
|
||
new file mode 100644
|
||
index 0000000..c981d91
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-agent/attester/src/virtcca/mod.rs
|
||
@@ -0,0 +1,93 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+
|
||
+//! virtcca tee plugin
|
||
+//!
|
||
+//! Call the hardware sdk or driver to get the specific evidence
|
||
+
|
||
+use anyhow::{Result, bail};
|
||
+use std::path::Path;
|
||
+use log;
|
||
+use attestation_types::VirtccaEvidence;
|
||
+
|
||
+use crate::EvidenceRequest;
|
||
+use crate::virtcca::virtcca::tsi_free_ctx;
|
||
+use self::virtcca::{tsi_new_ctx, get_attestation_token, get_dev_cert};
|
||
+
|
||
+mod virtcca;
|
||
+
|
||
+#[derive(Debug, Default)]
|
||
+pub struct VirtccaAttester {}
|
||
+
|
||
+
|
||
+impl VirtccaAttester {
|
||
+ pub async fn tee_get_evidence(&self, user_data: EvidenceRequest) -> Result<String> {
|
||
+ let evidence = virtcca_get_token(user_data)?;
|
||
+ let evidence = serde_json::to_string(&evidence)?;
|
||
+ Ok(evidence)
|
||
+ }
|
||
+}
|
||
+
|
||
+pub fn detect_platform() -> bool {
|
||
+ Path::new("/dev/tsi").exists()
|
||
+}
|
||
+
|
||
+
|
||
+fn virtcca_get_token(user_data: EvidenceRequest) -> Result<VirtccaEvidence> {
|
||
+ unsafe {
|
||
+ let ctx = tsi_new_ctx();
|
||
+
|
||
+ let mut challenge = user_data.challenge.to_vec();
|
||
+ let p_challenge = challenge.as_mut_ptr() as *mut ::std::os::raw::c_uchar;
|
||
+ let challenge_len = challenge.len() as usize;
|
||
+ let mut token = Vec::new();
|
||
+ token.resize(4096, b'\0');
|
||
+ let p_token = token.as_mut_ptr() as *mut ::std::os::raw::c_uchar;
|
||
+ let mut token_len = token.len();
|
||
+ let p_token_len = &mut token_len as *mut usize;
|
||
+ let ret = get_attestation_token(ctx, p_challenge, challenge_len, p_token, p_token_len);
|
||
+ if ret != 0 {
|
||
+ log::error!("virtcca get attestation token failed {}", ret);
|
||
+ bail!("virtcca get attestation token failed {}", ret);
|
||
+ }
|
||
+ token.set_len(token_len);
|
||
+
|
||
+ let mut dev_cert = Vec::new();
|
||
+ dev_cert.resize(4096, b'\0');
|
||
+ let p_dev_cert = dev_cert.as_mut_ptr() as *mut ::std::os::raw::c_uchar;
|
||
+ let mut dev_cert_len = dev_cert.len();
|
||
+ let p_dev_cert_len = &mut dev_cert_len as *mut usize;
|
||
+ let ret = get_dev_cert(ctx, p_dev_cert, p_dev_cert_len);
|
||
+ if ret != 0 {
|
||
+ log::error!("get dev cert failed {}", ret);
|
||
+ bail!("get dev cert failed {}", ret);
|
||
+ }
|
||
+ dev_cert.set_len(dev_cert_len);
|
||
+
|
||
+ let with_ima = match user_data.ima {
|
||
+ Some(ima) => ima,
|
||
+ None => false,
|
||
+ };
|
||
+ let ima_log = match with_ima {
|
||
+ true => Some(std::fs::read("/sys/kernel/security/ima/binary_runtime_measurements").unwrap()),
|
||
+ false => None,
|
||
+ };
|
||
+
|
||
+ let evidence = VirtccaEvidence {
|
||
+ evidence: token,
|
||
+ dev_cert: dev_cert,
|
||
+ ima_log: ima_log,
|
||
+ };
|
||
+ let _ = tsi_free_ctx(ctx);
|
||
+ Ok(evidence)
|
||
+ }
|
||
+}
|
||
\ No newline at end of file
|
||
diff --git a/service/attestation/attestation-agent/attester/src/virtcca/virtcca.rs b/service/attestation/attestation-agent/attester/src/virtcca/virtcca.rs
|
||
new file mode 100644
|
||
index 0000000..33318c7
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-agent/attester/src/virtcca/virtcca.rs
|
||
@@ -0,0 +1,109 @@
|
||
+/* automatically generated by rust-bindgen 0.69.4 */
|
||
+#[allow(non_camel_case_types)]
|
||
+pub type wchar_t = ::std::os::raw::c_int;
|
||
+#[allow(dead_code)]
|
||
+#[repr(C)]
|
||
+#[repr(align(16))]
|
||
+#[derive(Debug, Copy, Clone)]
|
||
+pub struct max_align_t {
|
||
+ pub __clang_max_align_nonce1: ::std::os::raw::c_longlong,
|
||
+ pub __bindgen_padding_0: u64,
|
||
+ pub __clang_max_align_nonce2: u128,
|
||
+}
|
||
+#[test]
|
||
+fn bindgen_test_layout_max_align_t() {
|
||
+ const UNINIT: ::std::mem::MaybeUninit<max_align_t> = ::std::mem::MaybeUninit::uninit();
|
||
+ let ptr = UNINIT.as_ptr();
|
||
+ assert_eq!(
|
||
+ ::std::mem::size_of::<max_align_t>(),
|
||
+ 32usize,
|
||
+ concat!("Size of: ", stringify!(max_align_t))
|
||
+ );
|
||
+ assert_eq!(
|
||
+ ::std::mem::align_of::<max_align_t>(),
|
||
+ 16usize,
|
||
+ concat!("Alignment of ", stringify!(max_align_t))
|
||
+ );
|
||
+ assert_eq!(
|
||
+ unsafe { ::std::ptr::addr_of!((*ptr).__clang_max_align_nonce1) as usize - ptr as usize },
|
||
+ 0usize,
|
||
+ concat!(
|
||
+ "Offset of field: ",
|
||
+ stringify!(max_align_t),
|
||
+ "::",
|
||
+ stringify!(__clang_max_align_nonce1)
|
||
+ )
|
||
+ );
|
||
+ assert_eq!(
|
||
+ unsafe { ::std::ptr::addr_of!((*ptr).__clang_max_align_nonce2) as usize - ptr as usize },
|
||
+ 16usize,
|
||
+ concat!(
|
||
+ "Offset of field: ",
|
||
+ stringify!(max_align_t),
|
||
+ "::",
|
||
+ stringify!(__clang_max_align_nonce2)
|
||
+ )
|
||
+ );
|
||
+}
|
||
+#[repr(C)]
|
||
+#[derive(Debug, Copy, Clone)]
|
||
+pub struct tsi_ctx {
|
||
+ pub fd: wchar_t,
|
||
+}
|
||
+#[test]
|
||
+fn bindgen_test_layout_tsi_ctx() {
|
||
+ const UNINIT: ::std::mem::MaybeUninit<tsi_ctx> = ::std::mem::MaybeUninit::uninit();
|
||
+ let ptr = UNINIT.as_ptr();
|
||
+ assert_eq!(
|
||
+ ::std::mem::size_of::<tsi_ctx>(),
|
||
+ 4usize,
|
||
+ concat!("Size of: ", stringify!(tsi_ctx))
|
||
+ );
|
||
+ assert_eq!(
|
||
+ ::std::mem::align_of::<tsi_ctx>(),
|
||
+ 4usize,
|
||
+ concat!("Alignment of ", stringify!(tsi_ctx))
|
||
+ );
|
||
+ assert_eq!(
|
||
+ unsafe { ::std::ptr::addr_of!((*ptr).fd) as usize - ptr as usize },
|
||
+ 0usize,
|
||
+ concat!(
|
||
+ "Offset of field: ",
|
||
+ stringify!(tsi_ctx),
|
||
+ "::",
|
||
+ stringify!(fd)
|
||
+ )
|
||
+ );
|
||
+}
|
||
+
|
||
+#[link(name = "vccaattestation")]
|
||
+extern "C" {
|
||
+ pub fn tsi_new_ctx() -> *mut tsi_ctx;
|
||
+}
|
||
+extern "C" {
|
||
+ pub fn tsi_free_ctx(ctx: *mut tsi_ctx);
|
||
+}
|
||
+extern "C" {
|
||
+ #[allow(dead_code)]
|
||
+ pub fn get_version(
|
||
+ ctx: *mut tsi_ctx,
|
||
+ major: *mut wchar_t,
|
||
+ minor: *mut wchar_t,
|
||
+ ) -> wchar_t;
|
||
+}
|
||
+extern "C" {
|
||
+ pub fn get_attestation_token(
|
||
+ ctx: *mut tsi_ctx,
|
||
+ challenge: *mut ::std::os::raw::c_uchar,
|
||
+ challenge_len: usize,
|
||
+ token: *mut ::std::os::raw::c_uchar,
|
||
+ token_len: *mut usize,
|
||
+ ) -> wchar_t;
|
||
+}
|
||
+extern "C" {
|
||
+ pub fn get_dev_cert(
|
||
+ ctx: *mut tsi_ctx,
|
||
+ dev_cert: *mut ::std::os::raw::c_uchar,
|
||
+ dev_cert_len: *mut usize,
|
||
+ ) -> wchar_t;
|
||
+}
|
||
diff --git a/service/attestation/attestation-agent/token/Cargo.toml b/service/attestation/attestation-agent/token/Cargo.toml
|
||
new file mode 100644
|
||
index 0000000..aa5cafc
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-agent/token/Cargo.toml
|
||
@@ -0,0 +1,13 @@
|
||
+[package]
|
||
+name = "token_verifier"
|
||
+version = "0.1.0"
|
||
+edition = "2021"
|
||
+
|
||
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||
+
|
||
+[dependencies]
|
||
+jsonwebtoken.workspace = true
|
||
+serde.workspace = true
|
||
+serde_json.workspace = true
|
||
+anyhow.workspace = true
|
||
+attestation-types.workspace = true
|
||
\ No newline at end of file
|
||
diff --git a/service/attestation/attestation-agent/token/src/lib.rs b/service/attestation/attestation-agent/token/src/lib.rs
|
||
new file mode 100644
|
||
index 0000000..50a7a7a
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-agent/token/src/lib.rs
|
||
@@ -0,0 +1,114 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+use anyhow::{Result, bail};
|
||
+use std::path::Path;
|
||
+use serde::{Deserialize, Serialize};
|
||
+use jsonwebtoken::{decode, decode_header, Algorithm, DecodingKey, Validation };
|
||
+use attestation_types::Claims;
|
||
+
|
||
+#[derive(Clone, Debug, Serialize, Deserialize)]
|
||
+pub struct TokenVerifyConfig {
|
||
+ pub cert: String, // Attestation Service cert to verify jwt token signature
|
||
+ pub iss: String, // Attestation Service name
|
||
+ //pub root_cert: String,
|
||
+}
|
||
+
|
||
+impl Default for TokenVerifyConfig {
|
||
+ fn default() -> Self {
|
||
+ TokenVerifyConfig {
|
||
+ cert: "/etc/attestation/attestation-agent/as_cert.pem".to_string(),
|
||
+ iss: "oeas".to_string(),
|
||
+ }
|
||
+ }
|
||
+}
|
||
+pub struct TokenVerifier
|
||
+{
|
||
+ pub config: TokenVerifyConfig,
|
||
+}
|
||
+
|
||
+impl Default for TokenVerifier
|
||
+{
|
||
+ fn default() -> Self {
|
||
+ TokenVerifier {
|
||
+ config: TokenVerifyConfig::default(),
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+// 返回token的原始数据
|
||
+#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||
+pub struct TokenRawData {
|
||
+ pub header: String,
|
||
+ pub claim: String,
|
||
+}
|
||
+
|
||
+impl TokenVerifier {
|
||
+ pub fn new(config: TokenVerifyConfig) -> Result<Self> {
|
||
+ Ok(TokenVerifier { config })
|
||
+ }
|
||
+ fn support_rs(alg: &Algorithm) -> bool
|
||
+ {
|
||
+ if *alg == Algorithm::RS256 || *alg == Algorithm::RS384 || *alg == Algorithm::RS512{
|
||
+ return true;
|
||
+ }
|
||
+ return false;
|
||
+ }
|
||
+ fn support_ps(alg: &Algorithm) -> bool
|
||
+ {
|
||
+ if *alg == Algorithm::PS256 || *alg == Algorithm::PS384 || *alg == Algorithm::PS512 {
|
||
+ return true;
|
||
+ }
|
||
+ return false;
|
||
+ }
|
||
+ pub fn verify(
|
||
+ &self,
|
||
+ token: &String
|
||
+ ) -> Result<TokenRawData> {
|
||
+ let header = match decode_header(&token) {
|
||
+ Ok(h) => h,
|
||
+ Err(e) => bail!("decode jwt header error {:?}", e),
|
||
+ };
|
||
+ let alg: Algorithm = header.alg;
|
||
+
|
||
+ if !Self::support_rs(&alg) && !Self::support_ps(&alg) {
|
||
+ bail!("unknown algrithm {:?}", alg);
|
||
+ }
|
||
+ if !Path::new(&self.config.cert).exists() {
|
||
+ bail!("token verfify failed, {:?} cert not exist", self.config.cert);
|
||
+ }
|
||
+ let cert = std::fs::read(&self.config.cert).unwrap();
|
||
+
|
||
+ /* 使用配置的公钥 */
|
||
+ let key_value: DecodingKey = match DecodingKey::from_rsa_pem(&cert)
|
||
+ {
|
||
+ Ok(key) => key,
|
||
+ Err(e) => bail!("get key from pem error {:?}", e),
|
||
+ };
|
||
+
|
||
+ let mut validation = Validation::new(alg);
|
||
+ validation.set_issuer(&[self.config.iss.clone()]);
|
||
+ validation.validate_exp = true;
|
||
+
|
||
+ let data = decode::<Claims>(&token, &key_value, &validation);
|
||
+ match data {
|
||
+ Ok(d) => {
|
||
+ let header = d.header.clone();
|
||
+ let claims = d.claims.clone();
|
||
+ Ok(TokenRawData {
|
||
+ header: serde_json::to_string(&header).unwrap(),
|
||
+ claim: serde_json::to_string(&claims).unwrap(),
|
||
+ })
|
||
+ }
|
||
+ Err(e) => bail!("verfiy jwt failed {:?}", e),
|
||
+ }
|
||
+ }
|
||
+}
|
||
diff --git a/service/attestation/attestation-service/Cargo.toml b/service/attestation/attestation-service/Cargo.toml
|
||
new file mode 100644
|
||
index 0000000..cf0dd87
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/Cargo.toml
|
||
@@ -0,0 +1,42 @@
|
||
+[workspace]
|
||
+resolver = "2"
|
||
+members = [
|
||
+ "service",
|
||
+ "verifier",
|
||
+ "token",
|
||
+ "reference",
|
||
+ "policy",
|
||
+ "tests"
|
||
+]
|
||
+
|
||
+[workspace.dependencies]
|
||
+anyhow = "1.0.80"
|
||
+serde = "1.0"
|
||
+serde_json = "1.0"
|
||
+async-trait = "0.1.78"
|
||
+cose-rust = "0.1.7"
|
||
+ciborium = "0.2.2"
|
||
+hex = "0.4"
|
||
+openssl = "0.10.64"
|
||
+log = "0.4.14"
|
||
+futures = "0.3.30"
|
||
+rand = "0.8.5"
|
||
+ima-measurements = "0.2.0"
|
||
+fallible-iterator = "0.2.0"
|
||
+
|
||
+actix-web = "4.5"
|
||
+env_logger = "0.9"
|
||
+tokio = { version = "1", features = ["full"] }
|
||
+strum = { version = "0.25", features = ["derive"] }
|
||
+thiserror = "1.0"
|
||
+base64-url = "3.0.0"
|
||
+base64 = "0.22.0"
|
||
+jsonwebtoken = "9.3.0"
|
||
+clap = { version = "4.5.7", features = ["derive"] }
|
||
+regorus = "0.2.2"
|
||
+sled = "0.34.7"
|
||
+lazy_static = "1.5.0"
|
||
+uuid = { version = "1.2.2", features = ["serde", "v4"] }
|
||
+scc = "2.1"
|
||
+
|
||
+attestation-types = {path = "../attestation-types"}
|
||
diff --git a/service/attestation/attestation-service/README.md b/service/attestation/attestation-service/README.md
|
||
new file mode 100644
|
||
index 0000000..c64e6f1
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/README.md
|
||
@@ -0,0 +1,6 @@
|
||
+# Attestation Service
|
||
+The Attestation Service verifies hardware TEE evidence.
|
||
+The first phase aims to support Kunpeng Trustzone, virtCCA and QingTian Enclave. In the future, it will support ARM CCA, Intel TDX, Hygon CSV etc.
|
||
+
|
||
+# Overview
|
||
+TODO
|
||
diff --git a/service/attestation/attestation-service/policy/Cargo.toml b/service/attestation/attestation-service/policy/Cargo.toml
|
||
new file mode 100644
|
||
index 0000000..87917a4
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/policy/Cargo.toml
|
||
@@ -0,0 +1,12 @@
|
||
+[package]
|
||
+name = "policy"
|
||
+version = "0.1.0"
|
||
+edition = "2021"
|
||
+
|
||
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||
+
|
||
+[dependencies]
|
||
+regorus.workspace = true
|
||
+base64.workspace = true
|
||
+tokio.workspace = true
|
||
+futures.workspace = true
|
||
diff --git a/service/attestation/attestation-service/policy/src/lib.rs b/service/attestation/attestation-service/policy/src/lib.rs
|
||
new file mode 100644
|
||
index 0000000..0677f45
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/policy/src/lib.rs
|
||
@@ -0,0 +1,181 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+pub mod opa;
|
||
+pub mod policy_engine;
|
||
+
|
||
+#[cfg(test)]
|
||
+mod tests {
|
||
+ use base64::Engine;
|
||
+ use std::fs;
|
||
+
|
||
+ use crate::{
|
||
+ opa::OPA,
|
||
+ policy_engine::{PolicyEngine, PolicyEngineError},
|
||
+ };
|
||
+
|
||
+ #[tokio::test]
|
||
+ async fn test_new_policy_engine() {
|
||
+ let policy_dir = String::from("/etc/attestation/attestation-service/policy");
|
||
+ let ret = OPA::new(&policy_dir).await;
|
||
+ assert!(ret.is_ok());
|
||
+ }
|
||
+
|
||
+ #[tokio::test]
|
||
+ async fn test_new_policy_engine_dir_exist() {
|
||
+ let policy_dir = String::from("/etc/attestation/attestation-service/policy");
|
||
+ let _ = fs::create_dir_all(&policy_dir);
|
||
+ let ret = OPA::new(&policy_dir).await;
|
||
+ assert!(ret.is_ok());
|
||
+ }
|
||
+
|
||
+ #[tokio::test]
|
||
+ async fn test_new_policy_engine_dir_failed() {
|
||
+ let policy_dir = String::from("/sys/invalid_dir");
|
||
+ let ret = OPA::new(&policy_dir).await;
|
||
+ assert!(ret.is_err());
|
||
+ if let PolicyEngineError::CreatePolicyDirError(msg) = ret.err().unwrap() {
|
||
+ assert_eq!(msg, "policy dir create failed");
|
||
+ } else {
|
||
+ panic!("Unexpected error type");
|
||
+ }
|
||
+ }
|
||
+
|
||
+ #[tokio::test]
|
||
+ async fn test_set_policy() {
|
||
+ let policy_dir = String::from("/etc/attestation/attestation-service/policy");
|
||
+ let engine = OPA::new(&policy_dir).await;
|
||
+
|
||
+ let policy_id = "test.rego".to_string();
|
||
+ let policy = r#"package attestation
|
||
+import rego.v1
|
||
+expect_keys := ["RIM", "RPV"]
|
||
+input_keys := object.keys(input)
|
||
+output[exist] := input[exist] if {
|
||
+ some exist in expect_keys
|
||
+ exist in input_keys
|
||
+}
|
||
+output[exist] := null if {
|
||
+ some exist in expect_keys
|
||
+ not exist in input_keys
|
||
+}
|
||
+output["Other"] := "other" if {
|
||
+ "test" in input_keys
|
||
+}"#;
|
||
+ let _ =
|
||
+ tokio::fs::remove_file("/etc/attestation/attestation-service/policy/test.rego").await;
|
||
+
|
||
+ let ret = engine
|
||
+ .unwrap()
|
||
+ .set_policy(
|
||
+ &policy_id,
|
||
+ &base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(policy),
|
||
+ )
|
||
+ .await;
|
||
+ assert!(ret.is_ok());
|
||
+ }
|
||
+
|
||
+ #[tokio::test]
|
||
+ async fn test_get_all_policy() {
|
||
+ let policy_dir = String::from("/etc/attestation/attestation-service/policy");
|
||
+ let engine = OPA::new(&policy_dir).await;
|
||
+ let ret = engine.unwrap().get_all_policy().await;
|
||
+ println!("{:?}", ret);
|
||
+ assert!(ret.is_ok());
|
||
+ }
|
||
+
|
||
+ #[tokio::test]
|
||
+ async fn test_evaluate_by_default() {
|
||
+ let policy_dir = String::from("/etc/attestation/attestation-service/policy");
|
||
+ let engine = OPA::new(&policy_dir).await.unwrap();
|
||
+ let refs_from_report = String::from(
|
||
+ r#"{
|
||
+ "RIM": "7d2e49c8d29f18b748e658e7243ecf26bc292e5fee93f72af11ad9da9810142a",
|
||
+ "RPV": "igliurbwjlkfxvr3wk2kqrttyz4gds42h9sdf72dgpcw8lspts1nnmxuvqzeqyq0",
|
||
+ "test": "u4eyoqgqsiju43aooetb02j0rymx6ijhhxs5oryj8344x7kehzjrwsi3vi7wqo2y"
|
||
+ }"#,
|
||
+ );
|
||
+ let data = String::new();
|
||
+ let policy_id: Vec<String> = vec![];
|
||
+ let result = engine.evaluate(&String::from("vcca"), &refs_from_report, &data, &policy_id).await;
|
||
+ println!("{:?}", result);
|
||
+ assert!(result.is_ok());
|
||
+ match result {
|
||
+ Ok(ret) => {
|
||
+ for i in ret.keys() {
|
||
+ println!("{} : {}", i, ret[i]);
|
||
+ }
|
||
+ }
|
||
+ Err(err) => {
|
||
+ println!("{err}");
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ #[tokio::test]
|
||
+ async fn test_evaluate_use_specified_policy() {
|
||
+ // 先设置指定的策略
|
||
+ let policy_dir = String::from("/etc/attestation/attestation-service/policy");
|
||
+ let engine = OPA::new(&policy_dir).await.unwrap();
|
||
+
|
||
+ let policy_id = "test.rego".to_string();
|
||
+ // 该策略提取期望的基线值,如果不存在则设置为null;同时包含“test”基线,则将Other设置为"other"
|
||
+ let policy = r#"package attestation
|
||
+import rego.v1
|
||
+expect_keys := ["RIM", "RPV"]
|
||
+input_keys := object.keys(input)
|
||
+output[exist] := input[exist] if {
|
||
+ some exist in expect_keys
|
||
+ exist in input_keys
|
||
+}
|
||
+output[exist] := null if {
|
||
+ some exist in expect_keys
|
||
+ not exist in input_keys
|
||
+}
|
||
+output["Other"] := "other" if {
|
||
+ "test" in input_keys
|
||
+}"#;
|
||
+ // 删除已重复存在的policy
|
||
+ let _ =
|
||
+ tokio::fs::remove_file("/etc/attestation/attestation-service/policy/test.rego").await;
|
||
+
|
||
+ let ret = engine
|
||
+ .set_policy(
|
||
+ &policy_id,
|
||
+ &base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(policy),
|
||
+ )
|
||
+ .await;
|
||
+ assert!(ret.is_ok());
|
||
+
|
||
+ // 使用自定义的策略进行报告评估
|
||
+ let refs_from_report = String::from(
|
||
+ r#"{
|
||
+ "RIM": "7d2e49c8d29f18b748e658e7243ecf26bc292e5fee93f72af11ad9da9810142a",
|
||
+ "RPV": "v598upciquf97yngfi4g2k5r9z6pyl1gcudj1vsgpn7v49ad2oafs11m0esdgv7r",
|
||
+ "test": "c4ca91mhcxwqi4ka6ysjgl8nn5hhhln9k2n7ppn3zs1jes4aohlflh5krsogqlpz"
|
||
+ }"#,
|
||
+ );
|
||
+ let data = String::new();
|
||
+ let policy_id: Vec<String> = vec!["test.rego".to_string()];
|
||
+ let result = engine.evaluate(&String::from("vcca"), &refs_from_report, &data, &policy_id).await;
|
||
+ assert!(result.is_ok());
|
||
+ match result {
|
||
+ Ok(ret) => {
|
||
+ for i in ret.keys() {
|
||
+ println!("{} : {}", i, ret[i]);
|
||
+ }
|
||
+ }
|
||
+ Err(err) => {
|
||
+ println!("{err}");
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+}
|
||
diff --git a/service/attestation/attestation-service/policy/src/opa/default_itrustee.rego b/service/attestation/attestation-service/policy/src/opa/default_itrustee.rego
|
||
new file mode 100644
|
||
index 0000000..f55449c
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/policy/src/opa/default_itrustee.rego
|
||
@@ -0,0 +1,10 @@
|
||
+# if create a new rego file, "output" should exist,
|
||
+# package name should be "attestation"
|
||
+package attestation
|
||
+import rego.v1
|
||
+expect_keys := ["itrustee.ta_img", "itrustee.ta_mem"]
|
||
+input_keys := object.keys(input)
|
||
+output[exist] := input[exist] if {
|
||
+ some exist in expect_keys
|
||
+ exist in input_keys
|
||
+}
|
||
diff --git a/service/attestation/attestation-service/policy/src/opa/default_vcca.rego b/service/attestation/attestation-service/policy/src/opa/default_vcca.rego
|
||
new file mode 100644
|
||
index 0000000..32229e4
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/policy/src/opa/default_vcca.rego
|
||
@@ -0,0 +1,10 @@
|
||
+# if create a new rego file, "output" should exist,
|
||
+# package name should be "attestation"
|
||
+package attestation
|
||
+import rego.v1
|
||
+expect_keys := ["vcca.cvm.rim"]
|
||
+input_keys := object.keys(input)
|
||
+output[exist] := input[exist] if {
|
||
+ some exist in expect_keys
|
||
+ exist in input_keys
|
||
+}
|
||
diff --git a/service/attestation/attestation-service/policy/src/opa/mod.rs b/service/attestation/attestation-service/policy/src/opa/mod.rs
|
||
new file mode 100644
|
||
index 0000000..c2e1cdb
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/policy/src/opa/mod.rs
|
||
@@ -0,0 +1,167 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+use base64::Engine;
|
||
+use policy_engine::{PolicyEngine, PolicyEngineError};
|
||
+use regorus::Value;
|
||
+use std::{collections::HashMap, path::PathBuf};
|
||
+
|
||
+use crate::policy_engine;
|
||
+
|
||
+#[derive(Debug, Clone, PartialEq)]
|
||
+pub struct OPA {
|
||
+ policy_dir: PathBuf,
|
||
+ default_policy_dir: PathBuf,
|
||
+ default_policy_vcca: String,
|
||
+ default_policy_itrustee: String,
|
||
+}
|
||
+
|
||
+const DEFAULT_POLICY_DIR: &str = "/etc/attestation/attestation-service/policy/";
|
||
+const DEFAULT_VCCA_REGO: &str = "default_vcca.rego";
|
||
+const DEFAULT_ITRUSTEE_REGO: &str = "default_itrustee.rego";
|
||
+
|
||
+impl PolicyEngine for OPA {
|
||
+ /// refs comes from report, by using query reference API
|
||
+ async fn evaluate(
|
||
+ &self,
|
||
+ tee: &String,
|
||
+ refs: &String,
|
||
+ data_for_policy: &String,
|
||
+ policy_id: &Vec<String>,
|
||
+ ) -> Result<HashMap<String, String>, PolicyEngineError> {
|
||
+ let mut policy_id_used = policy_id.clone();
|
||
+ let policy_path: PathBuf;
|
||
+ if policy_id_used.is_empty() {
|
||
+ if tee == "vcca" {
|
||
+ policy_id_used.push(String::from(DEFAULT_VCCA_REGO));
|
||
+ } else if tee == "itrustee" {
|
||
+ policy_id_used.push(String::from(DEFAULT_ITRUSTEE_REGO));
|
||
+ } else {
|
||
+ return Err(PolicyEngineError::TeeTypeUnknown(format!("tee type unknown: {tee}")));
|
||
+ }
|
||
+ policy_path = self.default_policy_dir.clone();
|
||
+ } else {
|
||
+ policy_path = self.policy_dir.clone();
|
||
+ }
|
||
+
|
||
+ let mut result: HashMap<String, String> = HashMap::new();
|
||
+ for id in policy_id_used {
|
||
+ let mut path = policy_path.clone();
|
||
+ path.push(id.clone());
|
||
+ let engine_policy = tokio::fs::read_to_string(path.clone()).await.map_err(|err| {
|
||
+ PolicyEngineError::ReadPolicyError(format!("read policy failed: {}", err))
|
||
+ })?;
|
||
+ let mut engine = regorus::Engine::new();
|
||
+ engine.add_policy(id.clone(), engine_policy).map_err(|err| {
|
||
+ PolicyEngineError::EngineLoadPolicyError(format!("policy load failed: {}", err))
|
||
+ })?;
|
||
+
|
||
+ let input = Value::from_json_str(refs).map_err(|err| {
|
||
+ PolicyEngineError::InvalidReport(format!("report to Value failed: {}", err))
|
||
+ })?;
|
||
+ engine.set_input(input);
|
||
+
|
||
+ if !data_for_policy.is_empty() {
|
||
+ let data = Value::from_json_str(data_for_policy).map_err(|err| {
|
||
+ PolicyEngineError::EngineLoadDataError(format!("data to Value failed: {}", err))
|
||
+ })?;
|
||
+ engine.add_data(data).map_err(|err| {
|
||
+ PolicyEngineError::EngineLoadDataError(format!("engine add data failed: {}", err))
|
||
+ })?;
|
||
+ }
|
||
+
|
||
+ let eval = engine
|
||
+ .eval_rule(String::from("data.attestation.output"))
|
||
+ .map_err(|err| {
|
||
+ PolicyEngineError::EngineEvalError(format!("engine eval error:{}", err))
|
||
+ })?;
|
||
+ result.insert(id, eval.to_string());
|
||
+ }
|
||
+ Ok(result)
|
||
+ }
|
||
+ async fn set_policy(
|
||
+ &self,
|
||
+ policy_id: &String,
|
||
+ policy: &String,
|
||
+ ) -> Result<(), PolicyEngineError> {
|
||
+ let raw = base64::engine::general_purpose::URL_SAFE_NO_PAD
|
||
+ .decode(policy)
|
||
+ .map_err(|err| PolicyEngineError::InvalidPolicy(format!("policy decode failed: {}", err)))?;
|
||
+
|
||
+ let mut policy_file: PathBuf = self.policy_dir.clone();
|
||
+ policy_file.push(format!("{}", policy_id));
|
||
+ tokio::fs::write(policy_file.as_path(), &raw)
|
||
+ .await
|
||
+ .map_err(|err| PolicyEngineError::WritePolicyError(format!("write policy failed: {}", err)))?;
|
||
+ Ok(())
|
||
+ }
|
||
+
|
||
+ async fn get_all_policy(&self) -> Result<HashMap<String, String>, PolicyEngineError> {
|
||
+ let mut items = tokio::fs::read_dir(&self.policy_dir.as_path())
|
||
+ .await
|
||
+ .map_err(|err| PolicyEngineError::ReadPolicyError(format!("read policy failed: {}", err)))?;
|
||
+ let mut policies = HashMap::new();
|
||
+ while let Some(item) = items
|
||
+ .next_entry()
|
||
+ .await
|
||
+ .map_err(|err| PolicyEngineError::ReadPolicyError(format!("read policy failed: {}", err)))?
|
||
+ {
|
||
+ let path = item.path();
|
||
+ if path.extension().and_then(std::ffi::OsStr::to_str) == Some("rego") {
|
||
+ let content: String =
|
||
+ tokio::fs::read_to_string(path.clone()).await.map_err(|err| {
|
||
+ PolicyEngineError::ReadPolicyError(format!("read policy failed: {}", err))
|
||
+ })?;
|
||
+ let name = path
|
||
+ .file_stem()
|
||
+ .ok_or(PolicyEngineError::ReadPolicyError(
|
||
+ "get policy name failed".to_string(),
|
||
+ ))?
|
||
+ .to_str()
|
||
+ .ok_or(PolicyEngineError::ReadPolicyError(
|
||
+ "get policy name failed".to_string(),
|
||
+ ))?;
|
||
+ let content =
|
||
+ base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(content.as_bytes());
|
||
+ policies.insert(name.to_string() + ".rego", content);
|
||
+ }
|
||
+ }
|
||
+ return Ok(policies);
|
||
+ }
|
||
+
|
||
+ async fn get_policy(&self, policy_id: &String) -> Result<String, PolicyEngineError> {
|
||
+ let mut policy_file: PathBuf = self.policy_dir.clone();
|
||
+ policy_file.push(format!("{}", policy_id));
|
||
+ let policy = tokio::fs::read(policy_file.as_path())
|
||
+ .await
|
||
+ .map_err(|err| PolicyEngineError::ReadPolicyError(format!("read policy failed: {}", err)))?;
|
||
+ let policy_base64 = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(policy);
|
||
+ Ok(policy_base64)
|
||
+ }
|
||
+}
|
||
+
|
||
+impl OPA {
|
||
+ pub async fn new(policy_dir: &String) -> Result<Self, PolicyEngineError> {
|
||
+ let policy_path = PathBuf::from(policy_dir);
|
||
+ if !policy_path.as_path().exists() {
|
||
+ std::fs::create_dir_all(&policy_dir).map_err(|err| {
|
||
+ PolicyEngineError::CreatePolicyDirError(format!("policy dir create failed: {}", err))
|
||
+ })?;
|
||
+ }
|
||
+
|
||
+ Ok(OPA {
|
||
+ policy_dir: policy_path,
|
||
+ default_policy_dir: PathBuf::from(DEFAULT_POLICY_DIR),
|
||
+ default_policy_vcca: String::from(DEFAULT_VCCA_REGO),
|
||
+ default_policy_itrustee: String::from(DEFAULT_ITRUSTEE_REGO),
|
||
+ })
|
||
+ }
|
||
+}
|
||
diff --git a/service/attestation/attestation-service/policy/src/policy_engine.rs b/service/attestation/attestation-service/policy/src/policy_engine.rs
|
||
new file mode 100644
|
||
index 0000000..a03a8cc
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/policy/src/policy_engine.rs
|
||
@@ -0,0 +1,73 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+use std::{collections::HashMap, fmt::Display};
|
||
+#[derive(Debug)]
|
||
+pub enum PolicyEngineError {
|
||
+ InvalidPolicy(String),
|
||
+ InvalidPolicyId(String),
|
||
+ InvalidPolicyDir(String),
|
||
+ InvalidReport(String),
|
||
+ CreatePolicyDirError(String),
|
||
+ CreatePolicyError(String),
|
||
+ ReadPolicyError(String),
|
||
+ WritePolicyError(String),
|
||
+ EngineLoadPolicyError(String),
|
||
+ EngineLoadDataError(String),
|
||
+ EngineEvalError(String),
|
||
+ TeeTypeUnknown(String),
|
||
+}
|
||
+impl Display for PolicyEngineError {
|
||
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
+ match self {
|
||
+ PolicyEngineError::InvalidPolicy(msg) => write!(f, "invalid policy: {}", msg),
|
||
+ PolicyEngineError::InvalidPolicyId(msg) => write!(f, "invalid policy id: {}", msg),
|
||
+ PolicyEngineError::InvalidReport(msg) => write!(f, "invalid report: {}", msg),
|
||
+ PolicyEngineError::CreatePolicyDirError(msg) => {
|
||
+ write!(f, "create policy dir error: {}", msg)
|
||
+ }
|
||
+ PolicyEngineError::CreatePolicyError(msg) => write!(f, "create policy error: {}", msg),
|
||
+ PolicyEngineError::ReadPolicyError(msg) => write!(f, "read policy error: {}", msg),
|
||
+ PolicyEngineError::InvalidPolicyDir(msg) => write!(f, "invalid policy error: {}", msg),
|
||
+ PolicyEngineError::WritePolicyError(msg) => write!(f, "write policy error: {}", msg),
|
||
+ PolicyEngineError::EngineLoadPolicyError(msg) => {
|
||
+ write!(f, "engine load policy error: {}", msg)
|
||
+ }
|
||
+ PolicyEngineError::EngineLoadDataError(msg) => {
|
||
+ write!(f, "engine read data error: {}", msg)
|
||
+ }
|
||
+ PolicyEngineError::EngineEvalError(msg) => write!(f, "engine evaluate error: {}", msg),
|
||
+ PolicyEngineError::TeeTypeUnknown(msg) => write!(f, "tee type error: {}", msg),
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+pub trait PolicyEngine {
|
||
+ fn evaluate(
|
||
+ &self,
|
||
+ tee: &String,
|
||
+ refs: &String,
|
||
+ data_for_policy: &String,
|
||
+ policy_id: &Vec<String>,
|
||
+ ) -> impl std::future::Future<Output = Result<HashMap<String, String>, PolicyEngineError>> + Send;
|
||
+ fn set_policy(
|
||
+ &self,
|
||
+ policy_id: &String,
|
||
+ policy: &String,
|
||
+ ) -> impl std::future::Future<Output = Result<(), PolicyEngineError>> + Send;
|
||
+ fn get_all_policy(
|
||
+ &self,
|
||
+ ) -> impl std::future::Future<Output = Result<HashMap<String, String>, PolicyEngineError>> + Send;
|
||
+ fn get_policy(
|
||
+ &self,
|
||
+ policy_id: &String,
|
||
+ ) -> impl std::future::Future<Output = Result<String, PolicyEngineError>> + Send;
|
||
+}
|
||
diff --git a/service/attestation/attestation-service/reference/Cargo.toml b/service/attestation/attestation-service/reference/Cargo.toml
|
||
new file mode 100644
|
||
index 0000000..b36991e
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/reference/Cargo.toml
|
||
@@ -0,0 +1,16 @@
|
||
+[package]
|
||
+name = "reference"
|
||
+version = "0.1.0"
|
||
+edition = "2021"
|
||
+
|
||
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||
+
|
||
+[dependencies]
|
||
+serde.workspace = true
|
||
+serde_json.workspace = true
|
||
+rand.workspace = true
|
||
+base64.workspace = true
|
||
+sled.workspace = true
|
||
+openssl.workspace = true
|
||
+hex.workspace = true
|
||
+lazy_static.workspace = true
|
||
\ No newline at end of file
|
||
diff --git a/service/attestation/attestation-service/reference/src/extractor/mod.rs b/service/attestation/attestation-service/reference/src/extractor/mod.rs
|
||
new file mode 100644
|
||
index 0000000..41f61aa
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/reference/src/extractor/mod.rs
|
||
@@ -0,0 +1,30 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+use crate::reference::Ref;
|
||
+use serde_json::Value;
|
||
+pub struct Extractor {}
|
||
+impl Extractor {
|
||
+ pub fn split(ref_set: &String) -> Option<Vec<Ref>> {
|
||
+ // expect ref_set as a json string, like follow:
|
||
+ // {"refname1":xx,"refname2":yy}
|
||
+ let mut ret: Vec<Ref> = vec![];
|
||
+ let refs: Value = serde_json::from_str(ref_set.as_str()).ok()?;
|
||
+ for (key, val) in refs.as_object().unwrap() {
|
||
+ let ref_obj = Ref {
|
||
+ name: key.clone(),
|
||
+ value: val.clone(),
|
||
+ };
|
||
+ ret.push(ref_obj);
|
||
+ }
|
||
+ Some(ret)
|
||
+ }
|
||
+}
|
||
diff --git a/service/attestation/attestation-service/reference/src/lib.rs b/service/attestation/attestation-service/reference/src/lib.rs
|
||
new file mode 100644
|
||
index 0000000..4347fc1
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/reference/src/lib.rs
|
||
@@ -0,0 +1,141 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+pub mod local_fs;
|
||
+pub mod reference;
|
||
+pub mod store;
|
||
+mod extractor;
|
||
+
|
||
+#[cfg(test)]
|
||
+mod tests {
|
||
+ use std::thread;
|
||
+
|
||
+ use super::*;
|
||
+ use rand::{distributions::Alphanumeric, Rng};
|
||
+ use serde_json::Value;
|
||
+
|
||
+ #[test]
|
||
+ fn localfs_default_test() {
|
||
+ let mut ops_default = reference::ReferenceOps::default();
|
||
+ let refs = r#"{"test1":"hash1","test2":"hash2"}"#.to_string();
|
||
+ assert_eq!(ops_default.register(&refs), Ok(()));
|
||
+ let ref_query = ops_default.query(&refs).unwrap();
|
||
+ println!("ref:{refs}, query:{ref_query}");
|
||
+ assert_eq!(ref_query, refs);
|
||
+ }
|
||
+
|
||
+ #[test]
|
||
+ fn localfs_empty_reference_test() {
|
||
+ let mut ops_default = reference::ReferenceOps::default();
|
||
+ assert_ne!(ops_default.register(&r#""#.to_string()), Ok(()));
|
||
+ assert_eq!(ops_default.query(&r#""#.to_string()), None);
|
||
+
|
||
+ let refs = r#"{}"#.to_string();
|
||
+ assert_eq!(ops_default.register(&refs), Ok(()));
|
||
+ let ref_query = ops_default.query(&refs).unwrap();
|
||
+ println!("ref:{refs}, query:{ref_query}");
|
||
+ assert_eq!(ref_query, refs);
|
||
+ }
|
||
+
|
||
+ #[test]
|
||
+ fn localfs_query_fail_test() {
|
||
+ let mut ops_default = reference::ReferenceOps::default();
|
||
+ let refs = r#"{"test1":"hash1"}"#.to_string();
|
||
+ assert_eq!(ops_default.register(&refs), Ok(()));
|
||
+ let ref_query = ops_default
|
||
+ .query(&r#"{"test":"hash1"}"#.to_string())
|
||
+ .unwrap();
|
||
+ println!("ref:{refs}, query:{ref_query}");
|
||
+ assert_ne!(ref_query, refs);
|
||
+ }
|
||
+
|
||
+ #[test]
|
||
+ fn localfs_default_complex_reference_test() {
|
||
+ let mut ops_default = reference::ReferenceOps::default();
|
||
+ let refs = r#"{"test1": { "name1":123, "name2": "val2"},"test2":123}"#.to_string();
|
||
+ assert_eq!(ops_default.register(&refs), Ok(()));
|
||
+ let ref_query = ops_default.query(&refs).unwrap();
|
||
+ let json_obj: Value = serde_json::from_str(refs.as_str()).unwrap();
|
||
+ println!("ref:{}, query:{ref_query}", json_obj.to_string());
|
||
+ assert_eq!(ref_query, json_obj.to_string());
|
||
+ }
|
||
+
|
||
+ #[test]
|
||
+ fn localfs_new_test() {
|
||
+ let store = local_fs::LocalFs::new(&String::from("/var/attestation/data_new"));
|
||
+ let mut ops = reference::ReferenceOps::new(store);
|
||
+ let refs = r#"{"test1":"hash1","test2":"hash2"}"#.to_string();
|
||
+ assert_eq!(ops.register(&refs), Ok(()));
|
||
+ let ref_query = ops.query(&refs).unwrap();
|
||
+ println!("ref:{refs}, query:{ref_query}");
|
||
+ assert_eq!(ref_query, refs);
|
||
+ }
|
||
+
|
||
+ #[test]
|
||
+ fn localfs_register_reference_repeat_test() {
|
||
+ let mut ops_default = reference::ReferenceOps::default();
|
||
+ let refs = r#"{"test1":"hash1","test2":"hash2"}"#.to_string();
|
||
+ assert_eq!(ops_default.register(&refs), Ok(()));
|
||
+ assert_eq!(ops_default.register(&refs), Ok(()));
|
||
+ let ref_query = ops_default.query(&refs).unwrap();
|
||
+ println!("ref:{refs}, query:{ref_query}");
|
||
+ assert_eq!(ref_query, refs);
|
||
+ }
|
||
+
|
||
+ #[test]
|
||
+ fn localfs_unregister_reference_test() {
|
||
+ let mut ops_default = reference::ReferenceOps::default();
|
||
+ let refs = r#"{"name1":"hash1","name2":"hash2"}"#.to_string();
|
||
+ assert_eq!(ops_default.register(&refs), Ok(()));
|
||
+ let ref_query = ops_default.query(&refs).unwrap();
|
||
+ println!("ref:{refs}, query:{ref_query}");
|
||
+ assert_eq!(ref_query, refs);
|
||
+
|
||
+ assert_eq!(ops_default.unregister(&refs), Ok(()));
|
||
+ let ref_query = ops_default.query(&refs).unwrap();
|
||
+ println!("ref:{refs}, query:{ref_query}");
|
||
+ assert_ne!(refs, ref_query);
|
||
+ }
|
||
+
|
||
+ #[test]
|
||
+ fn localfs_register_query_concurrently() {
|
||
+ let mut thread_all = vec![];
|
||
+ let thread_cnt = 1000;
|
||
+ for i in 0..thread_cnt {
|
||
+ let seq_start = i * thread_cnt;
|
||
+ let seq_end = seq_start + thread_cnt;
|
||
+ thread_all.push(thread::spawn(move || {
|
||
+ let rng = rand::thread_rng();
|
||
+ let mut ops_default = reference::ReferenceOps::default();
|
||
+
|
||
+ for i in seq_start..seq_end {
|
||
+ //key
|
||
+ let key = format!("ref{}", i);
|
||
+ //value
|
||
+ let value:String = rng.clone().sample_iter(&Alphanumeric).take(128).map(char::from).collect();
|
||
+ let mut reference = serde_json::json!({});
|
||
+ reference.as_object_mut().unwrap().insert(key, Value::String(value));
|
||
+ let _ = ops_default.register(&reference.to_string());
|
||
+ let ref_query = ops_default.query(&reference.to_string()).unwrap();
|
||
+ println!("ref {} query {}", reference.to_string(), ref_query);
|
||
+ assert_eq!(ref_query, reference.to_string());
|
||
+ }
|
||
+ }));
|
||
+ }
|
||
+ for hd in thread_all {
|
||
+ match hd.join() {
|
||
+ Ok(_) => {}
|
||
+ Err(_) => {assert!(false)}
|
||
+ }
|
||
+ }
|
||
+
|
||
+ }
|
||
+}
|
||
diff --git a/service/attestation/attestation-service/reference/src/local_fs/mod.rs b/service/attestation/attestation-service/reference/src/local_fs/mod.rs
|
||
new file mode 100644
|
||
index 0000000..1e03579
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/reference/src/local_fs/mod.rs
|
||
@@ -0,0 +1,87 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+use lazy_static::lazy_static;
|
||
+use std::sync::Arc;
|
||
+use sled::Db;
|
||
+use std::ops::Deref;
|
||
+
|
||
+use crate::store::{KvError, KvStore};
|
||
+
|
||
+
|
||
+pub struct LocalFs {
|
||
+ db: Arc<Db>,
|
||
+}
|
||
+
|
||
+impl Default for LocalFs {
|
||
+ fn default() -> Self {
|
||
+ lazy_static! {
|
||
+ static ref db_handle: Arc<Db> = Arc::new(sled::open("/etc/attestation/attestation-service/reference").unwrap());
|
||
+ }
|
||
+ LocalFs {
|
||
+ db: db_handle.clone(),
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+impl KvStore for LocalFs {
|
||
+ fn write(&mut self, key: &str, value: &[u8]) -> Result<(), KvError> {
|
||
+ match self.db.insert(key.as_bytes(), value) {
|
||
+ Err(_err) => {
|
||
+ return Err(KvError::Err("insert error".to_string()));
|
||
+ }
|
||
+ Ok(_) => {}
|
||
+ }
|
||
+ match self.db.flush() {
|
||
+ Err(_err) => {
|
||
+ return Err(KvError::Err("write flush error".to_string()));
|
||
+ }
|
||
+ Ok(_) => {
|
||
+ return Ok(());
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ fn read(&mut self, key: &str) -> Option<Vec<u8>> {
|
||
+ match self.db.get(key) {
|
||
+ Ok(val) => match val {
|
||
+ Some(iv) => Some(Vec::from(iv.deref())),
|
||
+ None => None,
|
||
+ },
|
||
+ Err(_err) => None,
|
||
+ }
|
||
+ }
|
||
+
|
||
+ fn delete(&mut self, key: &str) -> Result<(), KvError> {
|
||
+ match self.db.remove(key.as_bytes()) {
|
||
+ Err(_err) => {
|
||
+ return Err(KvError::Err("delete fail".to_string()));
|
||
+ }
|
||
+ Ok(_) => (),
|
||
+ }
|
||
+ match self.db.flush() {
|
||
+ Err(_err) => {
|
||
+ return Err(KvError::Err("delete flush fail".to_string()));
|
||
+ }
|
||
+ Ok(_) => {
|
||
+ return Ok(());
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+impl LocalFs {
|
||
+ pub fn new(path: &String) -> LocalFs {
|
||
+ let lfs = LocalFs {
|
||
+ db: Arc::new(sled::open(path).unwrap()),
|
||
+ };
|
||
+ lfs
|
||
+ }
|
||
+}
|
||
diff --git a/service/attestation/attestation-service/reference/src/reference/mod.rs b/service/attestation/attestation-service/reference/src/reference/mod.rs
|
||
new file mode 100644
|
||
index 0000000..bf56c85
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/reference/src/reference/mod.rs
|
||
@@ -0,0 +1,147 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+use crate::extractor::Extractor;
|
||
+use crate::local_fs::LocalFs;
|
||
+use crate::store::{KvError, KvStore};
|
||
+use openssl::sha::sha256;
|
||
+use serde::{Deserialize, Serialize};
|
||
+use serde_json::{json, Value};
|
||
+
|
||
+pub struct ReferenceOps {
|
||
+ store: Box<dyn KvStore>,
|
||
+}
|
||
+
|
||
+impl Default for ReferenceOps {
|
||
+ fn default() -> Self {
|
||
+ ReferenceOps {
|
||
+ store: Box::new(LocalFs::default()),
|
||
+ }
|
||
+ }
|
||
+}
|
||
+#[derive(Debug, Serialize, Deserialize)]
|
||
+pub enum HashAlg {
|
||
+ SHA256(String),
|
||
+ SHA384(String),
|
||
+ SHA512(String),
|
||
+}
|
||
+
|
||
+#[derive(Debug, Serialize, Deserialize)]
|
||
+pub struct Ref {
|
||
+ pub name: String,
|
||
+ pub value: Value,
|
||
+}
|
||
+
|
||
+#[derive(Debug, PartialEq)]
|
||
+pub enum RefOpError {
|
||
+ Err(String),
|
||
+}
|
||
+impl From<KvError> for RefOpError {
|
||
+ fn from(value: KvError) -> Self {
|
||
+ match value {
|
||
+ KvError::Err(v) => RefOpError::Err(v),
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+impl ReferenceOps {
|
||
+ pub fn new(st: impl KvStore + 'static) -> ReferenceOps {
|
||
+ let ops = ReferenceOps {
|
||
+ store: Box::new(st),
|
||
+ };
|
||
+ ops
|
||
+ }
|
||
+
|
||
+ fn generate_reference_key(reference: &Ref) -> String {
|
||
+ let key = reference.name.clone() + reference.value.to_string().as_str();
|
||
+ hex::encode(sha256(key.as_bytes()))
|
||
+ }
|
||
+
|
||
+ fn register_reference(&mut self, reference: &Ref) -> Result<(), RefOpError> {
|
||
+ // generate reference key
|
||
+ let key = Self::generate_reference_key(reference);
|
||
+ match self.store.write(
|
||
+ &key,
|
||
+ serde_json::to_string(&reference)
|
||
+ .unwrap()
|
||
+ .as_bytes()
|
||
+ .as_ref(),
|
||
+ ) {
|
||
+ Ok(_) => {
|
||
+ return Ok(());
|
||
+ }
|
||
+ Err(err) => match err {
|
||
+ KvError::Err(err) => {
|
||
+ return Err(RefOpError::Err(err));
|
||
+ }
|
||
+ },
|
||
+ }
|
||
+ }
|
||
+
|
||
+ fn unregister_reference(&mut self, reference: &Ref) -> Result<(), RefOpError> {
|
||
+ let key = Self::generate_reference_key(reference);
|
||
+ match self.store.delete(&key) {
|
||
+ Ok(_) => {
|
||
+ return Ok(());
|
||
+ }
|
||
+ Err(err) => match err {
|
||
+ KvError::Err(err) => {
|
||
+ return Err(RefOpError::Err(err));
|
||
+ }
|
||
+ },
|
||
+ }
|
||
+ }
|
||
+
|
||
+ fn query_reference(&mut self, reference: &Ref) -> Option<Vec<u8>> {
|
||
+ let key = Self::generate_reference_key(reference);
|
||
+ self.store.read(&key)
|
||
+ }
|
||
+ /// ref_set is a json string like:{"refname1":xx,"refname2":yy}
|
||
+ pub fn register(&mut self, ref_set: &String) -> Result<(), RefOpError> {
|
||
+ let refs =
|
||
+ Extractor::split(ref_set).ok_or(RefOpError::Err("parse reference fail".to_string()))?;
|
||
+ for item in refs {
|
||
+ self.register_reference(&item)?
|
||
+ }
|
||
+ Ok(())
|
||
+ }
|
||
+
|
||
+ pub fn unregister(&mut self, ref_set: &String) -> Result<(), RefOpError> {
|
||
+ let refs =
|
||
+ Extractor::split(ref_set).ok_or(RefOpError::Err("parse reference fail".to_string()))?;
|
||
+ for item in refs {
|
||
+ self.unregister_reference(&item)?
|
||
+ }
|
||
+ Ok(())
|
||
+ }
|
||
+
|
||
+ pub fn query(&mut self, ref_set: &String) -> Option<String> {
|
||
+ let refs = Extractor::split(ref_set)?;
|
||
+ let mut ret: Value = json!({});
|
||
+ for item in refs {
|
||
+ // query each reference, reference is set to NULL if not found
|
||
+ match self.query_reference(&item) {
|
||
+ Some(ref_store) => {
|
||
+ let ref_raw: Ref =
|
||
+ serde_json::from_str(String::from_utf8(ref_store).unwrap().as_str())
|
||
+ .ok()?;
|
||
+ ret.as_object_mut()
|
||
+ .unwrap()
|
||
+ .insert(ref_raw.name, ref_raw.value);
|
||
+ }
|
||
+ None => {
|
||
+ ret.as_object_mut().unwrap().insert(item.name, Value::Null);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ Some(ret.to_string())
|
||
+ }
|
||
+}
|
||
diff --git a/service/attestation/attestation-service/reference/src/store/mod.rs b/service/attestation/attestation-service/reference/src/store/mod.rs
|
||
new file mode 100644
|
||
index 0000000..c8c8260
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/reference/src/store/mod.rs
|
||
@@ -0,0 +1,19 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+pub enum KvError {
|
||
+ Err(String),
|
||
+}
|
||
+pub trait KvStore {
|
||
+ fn write(&mut self, key: &str, value: &[u8]) -> Result<(), KvError>;
|
||
+ fn read(&mut self, key: &str) -> Option<Vec<u8>>;
|
||
+ fn delete(&mut self, key: &str) -> Result<(), KvError>;
|
||
+}
|
||
diff --git a/service/attestation/attestation-service/service/Cargo.toml b/service/attestation/attestation-service/service/Cargo.toml
|
||
new file mode 100644
|
||
index 0000000..e8b88b8
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/service/Cargo.toml
|
||
@@ -0,0 +1,35 @@
|
||
+[package]
|
||
+name = "attestation-service"
|
||
+version = "0.1.0"
|
||
+edition = "2021"
|
||
+
|
||
+[dependencies]
|
||
+anyhow.workspace = true
|
||
+serde.workspace = true
|
||
+hex.workspace = true
|
||
+serde_json.workspace = true
|
||
+
|
||
+actix-web.workspace = true
|
||
+env_logger.workspace = true
|
||
+tokio.workspace = true
|
||
+log.workspace = true
|
||
+base64-url.workspace = true
|
||
+base64.workspace = true
|
||
+
|
||
+verifier = { path = "../verifier" }
|
||
+token_signer = { path = "../token" }
|
||
+reference = { path = "../reference" }
|
||
+policy = { path = "../policy" }
|
||
+strum.workspace = true
|
||
+thiserror.workspace = true
|
||
+clap.workspace = true
|
||
+uuid.workspace = true
|
||
+rand.workspace = true
|
||
+scc.workspace = true
|
||
+attestation-types.workspace = true
|
||
+
|
||
+[dev-dependencies]
|
||
+futures.workspace = true
|
||
+
|
||
+[features]
|
||
+
|
||
diff --git a/service/attestation/attestation-service/service/attestation-service.conf b/service/attestation/attestation-service/service/attestation-service.conf
|
||
new file mode 100644
|
||
index 0000000..64c5ff0
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/service/attestation-service.conf
|
||
@@ -0,0 +1,9 @@
|
||
+{
|
||
+ "token_cfg": {
|
||
+ "key": "/etc/attestation/attestation-service/token/private.pem",
|
||
+ "iss": "oeas",
|
||
+ "nbf": 0,
|
||
+ "valid_duration": 300,
|
||
+ "alg": "PS256"
|
||
+ }
|
||
+}
|
||
diff --git a/service/attestation/attestation-service/service/src/lib.rs b/service/attestation/attestation-service/service/src/lib.rs
|
||
new file mode 100644
|
||
index 0000000..cc3f432
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/service/src/lib.rs
|
||
@@ -0,0 +1,204 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+use anyhow::{Result, anyhow};
|
||
+use std::fs::File;
|
||
+use std::path::Path;
|
||
+use std::str::FromStr;
|
||
+use serde::{Serialize, Deserialize};
|
||
+use serde_json::Value;
|
||
+use rand::RngCore;
|
||
+use base64_url;
|
||
+
|
||
+use verifier::{Verifier, VerifierAPIs};
|
||
+use token_signer::{EvlReport, TokenSigner, TokenSignConfig};
|
||
+use reference::reference::{ReferenceOps, RefOpError};
|
||
+use policy::opa::OPA;
|
||
+use policy::policy_engine::{PolicyEngine, PolicyEngineError};
|
||
+use attestation_types::EvlResult;
|
||
+
|
||
+pub mod result;
|
||
+#[derive(Clone, Debug, Serialize, Deserialize)]
|
||
+pub struct ASConfig {
|
||
+ pub token_cfg: TokenSignConfig,
|
||
+}
|
||
+
|
||
+impl Default for ASConfig {
|
||
+ fn default() -> Self {
|
||
+ Self {
|
||
+ token_cfg: TokenSignConfig::default(),
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+impl TryFrom<&Path> for ASConfig {
|
||
+ /// Load `ASConfig` from a configuration file like:
|
||
+ /// {
|
||
+ /// "token_cfg": {
|
||
+ /// "key": "/etc/attestation/attestation-service/token/private.pem",
|
||
+ /// "iss": "oeas",
|
||
+ /// "nbf": 0,
|
||
+ /// "valid_duration": 300,
|
||
+ /// "alg": "PS256"
|
||
+ /// }
|
||
+ /// }
|
||
+
|
||
+ type Error = anyhow::Error;
|
||
+ fn try_from(config_path: &Path) -> Result<Self, Self::Error> {
|
||
+ let file = File::open(config_path)?;
|
||
+ serde_json::from_reader::<File, ASConfig>(file).map_err(|e| anyhow!("invalid asconfig {e}"))
|
||
+ }
|
||
+}
|
||
+
|
||
+pub struct AttestationService {
|
||
+ pub config: ASConfig,
|
||
+ // verify policy sub service
|
||
+ //policy: ,
|
||
+ // reference value provider sub service
|
||
+ //rvps: ,
|
||
+ // tee verifier sub service
|
||
+ //verifier: ,
|
||
+}
|
||
+
|
||
+impl Default for AttestationService {
|
||
+ fn default() -> Self {
|
||
+ Self {
|
||
+ config: ASConfig::default(),
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+impl AttestationService {
|
||
+ pub fn new(conf_path: Option<String>) -> Result<Self> {
|
||
+ let config = match conf_path {
|
||
+ Some(conf_path) => {
|
||
+ log::info!("Attestation Service config file:{conf_path}");
|
||
+ ASConfig::try_from(Path::new(&conf_path))?
|
||
+ }
|
||
+ None => {
|
||
+ log::warn!("No Attestation Agent config file specified. Using a default config");
|
||
+ ASConfig::default()
|
||
+ }
|
||
+ };
|
||
+ Ok(AttestationService {config})
|
||
+ }
|
||
+ /// evaluate tee evidence with reference and policy, and issue attestation result token
|
||
+ pub async fn evaluate(
|
||
+ &self,
|
||
+ user_data: &[u8],
|
||
+ evidence: &[u8],
|
||
+ policy_ids: &Option<Vec<String>>
|
||
+ ) -> Result<String> {
|
||
+ let verifier = Verifier::default();
|
||
+ let claims_evidence = verifier.verify_evidence(user_data, evidence).await?;
|
||
+
|
||
+ let mut passed = false;
|
||
+ let ima_result = verifier.verify_ima(evidence, &claims_evidence).await;
|
||
+ if ima_result.is_ok() {
|
||
+ passed = true;
|
||
+ }
|
||
+ // get reference by keys in claims_evidence
|
||
+ let mut ops_refs = ReferenceOps::default();
|
||
+ let refs_of_claims = ops_refs.query(&claims_evidence["payload"].to_string());
|
||
+ // apply policy to verify claims_evidence with reference value
|
||
+ let policy_ids = match policy_ids {
|
||
+ Some(polciy_id) => polciy_id.clone(),
|
||
+ None => vec![],
|
||
+ };
|
||
+ let policy_dir = String::from("/etc/attestation/attestation-service/policy");
|
||
+ let engine = OPA::new(&policy_dir).await.unwrap();
|
||
+ let data = String::new();
|
||
+ let result = engine.evaluate(&String::from(claims_evidence["tee"]
|
||
+ .as_str().ok_or(anyhow!("tee type unknown"))?),
|
||
+ &refs_of_claims.unwrap(), &data, &policy_ids).await;
|
||
+ let mut report = serde_json::json!({});
|
||
+ let mut ref_exist_null: bool = false;
|
||
+ match result {
|
||
+ Ok(eval) => {
|
||
+ for id in eval.keys() {
|
||
+ let val = Value::from_str(&eval[id].clone())?;
|
||
+ let refs = match val.as_object().ok_or(Err(anyhow!(""))) {
|
||
+ Err(err) => { return Err(err.unwrap()); }
|
||
+ Ok(ret) => { ret }
|
||
+ };
|
||
+ for key in refs.keys() {
|
||
+ // reference value is null means not found
|
||
+ if refs[key].is_null() {
|
||
+ ref_exist_null = true;
|
||
+ }
|
||
+ }
|
||
+ report.as_object_mut().unwrap().insert(id.clone(), serde_json::Value::String(eval[id].clone()));
|
||
+ }
|
||
+ }
|
||
+ Err(err) => {
|
||
+ return Err(anyhow!("evaluate error: {err}"));
|
||
+ }
|
||
+ }
|
||
+
|
||
+ // issue attestation result token
|
||
+ let evl_report = EvlReport {
|
||
+ tee: String::from(claims_evidence["tee"].as_str().ok_or(anyhow!("tee type unknown"))?),
|
||
+ result: EvlResult {
|
||
+ eval_result: passed & !ref_exist_null,
|
||
+ policy: policy_ids,
|
||
+ report: report,
|
||
+ },
|
||
+ tcb_status: claims_evidence["payload"].clone(),
|
||
+ };
|
||
+ // demo get signer, todo default signer
|
||
+ let signer = TokenSigner::new(self.config.token_cfg.clone())?;
|
||
+
|
||
+ signer.sign(&evl_report)
|
||
+ }
|
||
+
|
||
+ pub async fn generate_challenge(&self) -> String {
|
||
+ let mut nonce: [u8; 32] = [0; 32];
|
||
+ rand::thread_rng().fill_bytes(&mut nonce);
|
||
+ base64_url::encode(&nonce)
|
||
+ }
|
||
+
|
||
+ // todo pub fun set policy
|
||
+ pub async fn set_policy(&self,
|
||
+ id: &String,
|
||
+ policy: &String,
|
||
+ policy_dir: &String,
|
||
+ ) -> Result<(), PolicyEngineError> {
|
||
+ let engine = OPA::new(policy_dir).await;
|
||
+ engine.unwrap()
|
||
+ .set_policy(id, policy)
|
||
+ .await
|
||
+ }
|
||
+ // todo pub fun get policy
|
||
+ pub async fn get_policy(&self,
|
||
+ policy_dir: &String,
|
||
+ ) -> Result<String, PolicyEngineError> {
|
||
+ let engine = OPA::new(policy_dir).await;
|
||
+ match engine.unwrap().get_all_policy().await {
|
||
+ Ok(map) => {
|
||
+ let mut json_obj: serde_json::Value = serde_json::json!({});
|
||
+ for key in map.keys() {
|
||
+ json_obj.as_object_mut()
|
||
+ .unwrap()
|
||
+ .insert(key.clone(), serde_json::json!(map[key]));
|
||
+ }
|
||
+ Ok(json_obj.to_string())
|
||
+ }
|
||
+ Err(err) => Err(err)
|
||
+ }
|
||
+ }
|
||
+ // todo pub fun import reference value
|
||
+ pub async fn register_reference(&self,
|
||
+ ref_set: &String
|
||
+ ) -> Result<(), RefOpError> {
|
||
+ let mut ops_default = ReferenceOps::default();
|
||
+ ops_default.register(ref_set)
|
||
+ }
|
||
+}
|
||
diff --git a/service/attestation/attestation-service/service/src/main.rs b/service/attestation/attestation-service/service/src/main.rs
|
||
new file mode 100644
|
||
index 0000000..1ccb152
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/service/src/main.rs
|
||
@@ -0,0 +1,76 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+/// RESTful Attestation Service
|
||
+
|
||
+use attestation_service::AttestationService;
|
||
+mod restapi;
|
||
+use restapi::{get_challenge, attestation, reference, get_policy, set_policy};
|
||
+mod session;
|
||
+use session::SessionMap;
|
||
+
|
||
+use anyhow::Result;
|
||
+use env_logger;
|
||
+use actix_web::{web, App, HttpServer};
|
||
+use std::{net::{SocketAddr, IpAddr, Ipv4Addr}, sync::Arc};
|
||
+use tokio::sync::RwLock;
|
||
+use clap::{Parser, command, arg};
|
||
+
|
||
+const DEFAULT_ASCONFIG_FILE: &str = "/etc/attestation/attestation-service/attestation-service.conf";
|
||
+const DEFAULT_SOCKETADDR: SocketAddr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
|
||
+
|
||
+#[derive(Parser, Debug)]
|
||
+#[command(version, about, long_about = None)]
|
||
+struct Cli {
|
||
+ /// Socket address to listen on
|
||
+ #[arg(short, long, default_value_t = DEFAULT_SOCKETADDR)]
|
||
+ socketaddr: SocketAddr,
|
||
+
|
||
+ /// Attestation Service config file
|
||
+ // Load `ASConfig` from a configuration file like:
|
||
+ // {
|
||
+ // "token_cfg": {
|
||
+ // "key": "/etc/attestation/attestation-service/token/private.pem",
|
||
+ // "iss": "oeas",
|
||
+ // "nbf": 0,
|
||
+ // "valid_duration": 300,
|
||
+ // "alg": "PS256"
|
||
+ // }
|
||
+ // }
|
||
+ #[arg(short, long, default_value_t = DEFAULT_ASCONFIG_FILE.to_string())]
|
||
+ config: String,
|
||
+}
|
||
+
|
||
+#[actix_web::main]
|
||
+async fn main() -> Result<()> {
|
||
+ env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
|
||
+
|
||
+ let cli = Cli::parse();
|
||
+ let server:AttestationService = AttestationService::new(Some(cli.config)).unwrap();
|
||
+ let session_map = web::Data::new(SessionMap::new());
|
||
+
|
||
+ let service = web::Data::new(Arc::new(RwLock::new(server)));
|
||
+ HttpServer::new(move || {
|
||
+ App::new()
|
||
+ .app_data(web::Data::clone(&service))
|
||
+ .app_data(web::Data::clone(&session_map))
|
||
+ .service(get_challenge)
|
||
+ .service(attestation)
|
||
+ .service(reference)
|
||
+ .service(set_policy)
|
||
+ .service(get_policy)
|
||
+ })
|
||
+ .bind((cli.socketaddr.ip().to_string(), cli.socketaddr.port()))?
|
||
+ .run()
|
||
+ .await?;
|
||
+
|
||
+ Ok(())
|
||
+}
|
||
\ No newline at end of file
|
||
diff --git a/service/attestation/attestation-service/service/src/restapi/mod.rs b/service/attestation/attestation-service/service/src/restapi/mod.rs
|
||
new file mode 100644
|
||
index 0000000..ab2ccbf
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/service/src/restapi/mod.rs
|
||
@@ -0,0 +1,139 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+use attestation_service::AttestationService;
|
||
+use attestation_service::result::{Result, Error};
|
||
+use crate::session::{Session, SessionMap};
|
||
+
|
||
+use actix_web::{ post, get, web, HttpResponse, HttpRequest};
|
||
+use serde::{Deserialize, Serialize};
|
||
+use std::sync::Arc;
|
||
+use tokio::sync::RwLock;
|
||
+use log;
|
||
+use base64_url;
|
||
+
|
||
+const DEFAULT_POLICY_DIR: &str = "/etc/attestation/attestation-service/policy";
|
||
+#[derive(Deserialize, Serialize, Debug)]
|
||
+pub struct ChallengeRequest {}
|
||
+
|
||
+#[get("/challenge")]
|
||
+pub async fn get_challenge(
|
||
+ map: web::Data<SessionMap>,
|
||
+ service: web::Data<Arc<RwLock<AttestationService>>>,
|
||
+) -> Result<HttpResponse> {
|
||
+ log::debug!("challenge request");
|
||
+
|
||
+ let challenge = service.read().await.generate_challenge().await;
|
||
+ let timeout = service.read().await.config.token_cfg.valid_duration;
|
||
+ let session = Session::new(challenge, timeout.try_into().unwrap());
|
||
+ let response = HttpResponse::Ok()
|
||
+ .cookie(session.cookie())
|
||
+ .json(session.challenge.clone());
|
||
+ map.insert(session);
|
||
+
|
||
+ Ok(response)
|
||
+}
|
||
+
|
||
+#[derive(Deserialize, Serialize, Debug)]
|
||
+pub struct AttestationRequest {
|
||
+ challenge: String,
|
||
+ evidence: String,
|
||
+ policy_id: Option<Vec<String>>,
|
||
+}
|
||
+
|
||
+#[post("/attestation")]
|
||
+pub async fn attestation(
|
||
+ request: web::Json<AttestationRequest>,
|
||
+ http_req: HttpRequest,
|
||
+ map: web::Data<SessionMap>,
|
||
+ service: web::Data<Arc<RwLock<AttestationService>>>,
|
||
+) -> Result<HttpResponse> {
|
||
+ log::debug!("attestation request is coming");
|
||
+ let request = request.0;
|
||
+ let mut challenge = request.challenge;
|
||
+ if challenge == "" {
|
||
+ let cookie = http_req.cookie("oeas-session-id").ok_or(Error::CookieMissing)?;
|
||
+ let session = map
|
||
+ .session_map
|
||
+ .get_async(cookie.value())
|
||
+ .await
|
||
+ .ok_or(Error::CookieNotFound)?;
|
||
+ if session.is_expired() {
|
||
+ return Err(Error::SessionExpired);
|
||
+ }
|
||
+ log::debug!("session challenge:{}", session.challenge);
|
||
+ challenge = session.challenge.clone();
|
||
+ }
|
||
+
|
||
+ let nonce = base64_url::decode(&challenge).expect("base64 decode nonce");
|
||
+ let evidence = base64_url::decode(&request.evidence).expect("base64 decode evidence");
|
||
+ let ids = request.policy_id;
|
||
+ let token = service.read().await.evaluate(&nonce, &evidence, &ids).await?;
|
||
+
|
||
+ Ok(HttpResponse::Ok().body(token))
|
||
+}
|
||
+
|
||
+#[derive(Deserialize, Serialize, Debug)]
|
||
+pub struct ReferenceRequest {
|
||
+ refs: String
|
||
+}
|
||
+
|
||
+#[post("/reference")]
|
||
+pub async fn reference(
|
||
+ request: web::Json<ReferenceRequest>,
|
||
+ service: web::Data<Arc<RwLock<AttestationService>>>,
|
||
+) -> Result<HttpResponse> {
|
||
+ let request = request.0;
|
||
+ log::debug!("reference request: {:?}", request);
|
||
+ match service.read().await.register_reference(&request.refs).await {
|
||
+ Ok(_) => Ok(HttpResponse::Ok().body("set reference success")),
|
||
+ Err(_err) => Ok(HttpResponse::Ok().body("set reference fail")),
|
||
+ }
|
||
+}
|
||
+
|
||
+#[derive(Deserialize, Serialize, Debug)]
|
||
+pub struct PolicyRequest {
|
||
+ tee: String,
|
||
+ id: String,
|
||
+ policy: String,
|
||
+}
|
||
+
|
||
+#[post("/policy")]
|
||
+pub async fn set_policy(
|
||
+ request: web::Json<PolicyRequest>,
|
||
+ service: web::Data<Arc<RwLock<AttestationService>>>,
|
||
+) -> Result<HttpResponse> {
|
||
+ let request = request.0;
|
||
+ log::debug!("set policy request: {:?}", request);
|
||
+ let policy_id = request.id.clone();
|
||
+ let policy = request.policy.clone();
|
||
+ let dir:String = String::from(DEFAULT_POLICY_DIR);
|
||
+ match service.read().await.set_policy(&policy_id, &policy, &dir).await {
|
||
+ Ok(_) => Ok(HttpResponse::Ok().body("set policy success")),
|
||
+ Err(err) => {
|
||
+ log::debug!("set policy error: {:?}", err);
|
||
+ Ok(HttpResponse::Ok().body("set policy fail"))
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+#[get("/policy")]
|
||
+pub async fn get_policy(
|
||
+ request: HttpRequest,
|
||
+ service: web::Data<Arc<RwLock<AttestationService>>>,
|
||
+) -> Result<HttpResponse> {
|
||
+ log::debug!("get policy request: {:?}", request);
|
||
+ let dir:String = String::from(DEFAULT_POLICY_DIR);
|
||
+ match service.read().await.get_policy(&dir).await {
|
||
+ Ok(ret) => Ok(HttpResponse::Ok().body(ret)),
|
||
+ Err(_err) => Ok(HttpResponse::Ok().body("get policy fail")),
|
||
+ }
|
||
+}
|
||
diff --git a/service/attestation/attestation-service/service/src/result/mod.rs b/service/attestation/attestation-service/service/src/result/mod.rs
|
||
new file mode 100644
|
||
index 0000000..667e80f
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/service/src/result/mod.rs
|
||
@@ -0,0 +1,55 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+use thiserror::Error;
|
||
+use actix_web::{body::BoxBody, HttpResponse, ResponseError};
|
||
+pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||
+
|
||
+#[derive(Debug, Error)]
|
||
+//#[non_exhaustive]
|
||
+//#[allow(missing_docs)]
|
||
+pub enum Error {
|
||
+ #[error("IO error: {source:?}")]
|
||
+ Io {
|
||
+ #[from]
|
||
+ source: std::io::Error,
|
||
+ },
|
||
+
|
||
+ #[error("Web error: {source:?}")]
|
||
+ Web {
|
||
+ #[from]
|
||
+ source: actix_web::error::Error,
|
||
+ },
|
||
+
|
||
+ #[error("Deserialize error: {source:?}")]
|
||
+ Deserialize {
|
||
+ #[from]
|
||
+ source: serde_json::Error,
|
||
+ },
|
||
+
|
||
+ #[error("Request cookie is missing")]
|
||
+ CookieMissing,
|
||
+
|
||
+ #[error("Request cookie is not found")]
|
||
+ CookieNotFound,
|
||
+
|
||
+ #[error("The session of request cookie is expired")]
|
||
+ SessionExpired,
|
||
+
|
||
+ #[error(transparent)]
|
||
+ Other(#[from] anyhow::Error),
|
||
+}
|
||
+
|
||
+impl ResponseError for Error {
|
||
+ fn error_response(&self) -> HttpResponse {
|
||
+ HttpResponse::InternalServerError().body(BoxBody::new(format!("{self:#?}")))
|
||
+ }
|
||
+}
|
||
diff --git a/service/attestation/attestation-service/service/src/session.rs b/service/attestation/attestation-service/service/src/session.rs
|
||
new file mode 100644
|
||
index 0000000..5f191a7
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/service/src/session.rs
|
||
@@ -0,0 +1,58 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+use actix_web::cookie::{time::{Duration, OffsetDateTime}, Cookie};
|
||
+use scc::HashMap;
|
||
+use uuid::Uuid;
|
||
+
|
||
+pub struct Session {
|
||
+ pub id: String,
|
||
+ pub challenge: String,
|
||
+ timeout: OffsetDateTime,
|
||
+}
|
||
+
|
||
+impl Session {
|
||
+ pub fn new(challenge: String, timeout_m: i64) -> Self {
|
||
+ let id = Uuid::new_v4().as_simple().to_string();
|
||
+ let timeout = OffsetDateTime::now_utc() + Duration::minutes(timeout_m);
|
||
+ Session {
|
||
+ id,
|
||
+ challenge,
|
||
+ timeout,
|
||
+ }
|
||
+ }
|
||
+ pub fn is_expired(&self) -> bool {
|
||
+ return self.timeout < OffsetDateTime::now_utc();
|
||
+ }
|
||
+ pub fn cookie(&self) -> Cookie {
|
||
+ Cookie::build("oeas-session-id", self.id.clone())
|
||
+ .expires(self.timeout.clone())
|
||
+ .finish()
|
||
+ }
|
||
+}
|
||
+
|
||
+pub struct SessionMap {
|
||
+ pub session_map: HashMap<String, Session>,
|
||
+}
|
||
+
|
||
+impl SessionMap {
|
||
+ pub fn new() -> Self {
|
||
+ SessionMap {
|
||
+ session_map: HashMap::new(),
|
||
+ }
|
||
+ }
|
||
+ pub fn insert(&self, session: Session) {
|
||
+ let _ = self.session_map.insert(session.id.clone(), session);
|
||
+ }
|
||
+ pub fn delete(&self, session: Session) {
|
||
+ let _ = self.session_map.remove(&session.id);
|
||
+ }
|
||
+}
|
||
\ No newline at end of file
|
||
diff --git a/service/attestation/attestation-service/tests/Cargo.toml b/service/attestation/attestation-service/tests/Cargo.toml
|
||
new file mode 100644
|
||
index 0000000..0fde476
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/tests/Cargo.toml
|
||
@@ -0,0 +1,9 @@
|
||
+[package]
|
||
+name = "tests"
|
||
+version = "0.1.0"
|
||
+edition = "2021"
|
||
+
|
||
+[dependencies]
|
||
+serde_json = "1.0.116"
|
||
+reqwest = {version = "0.12.5", features = ["blocking"]}
|
||
+rand = "0.8.5"
|
||
diff --git a/service/attestation/attestation-service/tests/src/lib.rs b/service/attestation/attestation-service/tests/src/lib.rs
|
||
new file mode 100644
|
||
index 0000000..abd099f
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/tests/src/lib.rs
|
||
@@ -0,0 +1,166 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+#[cfg(test)]
|
||
+mod tests {
|
||
+ use rand::{distributions::Alphanumeric, Rng};
|
||
+ use reqwest::blocking::Client;
|
||
+ use serde_json::{json, Value};
|
||
+ use std::thread;
|
||
+
|
||
+ #[test]
|
||
+ fn api_register_reference_test() {
|
||
+ let request_body = json!({
|
||
+ "refs":r#"{ "RIM": "7d2e49c8d29f18b748e658e7243ecf26bc292e5fee93f72af11ad9da9810142a",
|
||
+ "PRV": "cGFja2FnZSBhdHRlc3RhdGlvbgppbXBvcnQgcmVnby52MQpleHBlY3Rfa2V5cyA6"
|
||
+ }"#
|
||
+ });
|
||
+
|
||
+ let client = Client::new();
|
||
+ let endpoint = "http://127.0.0.1:8080/reference";
|
||
+ let res = client
|
||
+ .post(endpoint)
|
||
+ .header("Content-Type", "application/json")
|
||
+ .body(request_body.to_string())
|
||
+ .send()
|
||
+ .unwrap();
|
||
+ println!("{:?}", res);
|
||
+ assert!(res.text().unwrap().contains("success"));
|
||
+ }
|
||
+
|
||
+ #[test]
|
||
+ fn api_register_concurrently() {
|
||
+ let mut thread_all = vec![];
|
||
+ let thread_cnt = 100;
|
||
+ for _i in 0..thread_cnt {
|
||
+ thread_all.push(thread::spawn(|| {
|
||
+ let mut request_body = json!({
|
||
+ "refs":r#"{ "RIM": "7d2e49c8d29f18b748e658e7243ecf26bc292e5fee93f72af11ad9da9810142a",
|
||
+ "PRV": "cGFja2FnZSBhdHRlc3RhdGlvbgppbXBvcnQgcmVnby52MQpleHBlY3Rfa2V5cyA6"
|
||
+ }"#
|
||
+ });
|
||
+ let rng = rand::thread_rng();
|
||
+ request_body["value"] = Value::String(
|
||
+ rng.clone()
|
||
+ .sample_iter(&Alphanumeric)
|
||
+ .take(64)
|
||
+ .map(char::from)
|
||
+ .collect(),
|
||
+ );
|
||
+
|
||
+ let client = Client::new();
|
||
+ let endpoint = "http://127.0.0.1:8080/reference";
|
||
+ let res = client
|
||
+ .post(endpoint)
|
||
+ .header("Content-Type", "application/json")
|
||
+ .body(request_body.to_string())
|
||
+ .send()
|
||
+ .unwrap();
|
||
+ println!("{:?}", res);
|
||
+ assert!(res.text().unwrap().contains("success"));
|
||
+ }));
|
||
+ }
|
||
+ for hd in thread_all {
|
||
+ match hd.join() {
|
||
+ Ok(_) => {}
|
||
+ Err(_) => {
|
||
+ assert!(false)
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ #[test]
|
||
+ fn api_register_complex_reference_test() {
|
||
+ let request_body = json!({
|
||
+ "refs":r#"{"complex_ref":{"level1_1":[1,2,3],"level1_2":{"name1":"value1"}}}"#
|
||
+ }
|
||
+ );
|
||
+
|
||
+ let client = Client::new();
|
||
+ let endpoint = "http://127.0.0.1:8080/reference";
|
||
+ let res = client
|
||
+ .post(endpoint)
|
||
+ .header("Content-Type", "application/json")
|
||
+ .body(request_body.to_string())
|
||
+ .send()
|
||
+ .unwrap();
|
||
+ println!("{:?}", res);
|
||
+ assert!(res.text().unwrap().contains("success"));
|
||
+ }
|
||
+
|
||
+ #[test]
|
||
+ fn api_set_policy() {
|
||
+ let request_body = json!({
|
||
+ "tee":"KUNPENG",
|
||
+ "id": "test_policy.rego",
|
||
+ "policy":"cGFja2FnZSBhdHRlc3RhdGlvbgppbXBvcnQgcmVnby52MQpleHBlY3Rfa2V5cyA6PSBbIlJJTSIsICJSUFYiXQppbnB1dF9rZXlzIDo9IG9iamVjdC5rZXlzKGlucHV0KQpvdXRwdXRbZXhpc3RdIDo9IGlucHV0W2V4aXN0XSBpZiB7CiAgICBzb21lIGV4aXN0IGluIGV4cGVjdF9rZXlzCiAgICBleGlzdCBpbiBpbnB1dF9rZXlzCn0Kb3V0cHV0W2V4aXN0XSA6PSBudWxsIGlmIHsKICAgIHNvbWUgZXhpc3QgaW4gZXhwZWN0X2tleXMKICAgIG5vdCBleGlzdCBpbiBpbnB1dF9rZXlzCn0Kb3V0cHV0WyJPdGhlciJdIDo9ICJvdGhlciIgaWYgewogICAgInRlc3QiIGluIGlucHV0X2tleXMKfQ"
|
||
+ }
|
||
+ );
|
||
+
|
||
+ let client = Client::new();
|
||
+ let endpoint = "http://127.0.0.1:8080/policy";
|
||
+ let res = client
|
||
+ .post(endpoint)
|
||
+ .header("Content-Type", "application/json")
|
||
+ .body(request_body.to_string())
|
||
+ .send()
|
||
+ .unwrap();
|
||
+ let response = res.text().unwrap();
|
||
+ println!("set policy reponse: {}", response);
|
||
+ assert!(response.contains("success"));
|
||
+ }
|
||
+
|
||
+ #[test]
|
||
+ fn api_get_policy() {
|
||
+ let client: Client = Client::new();
|
||
+ let endpoint = "http://127.0.0.1:8080/policy";
|
||
+ let res = client
|
||
+ .get(endpoint)
|
||
+ .send()
|
||
+ .unwrap();
|
||
+ assert_eq!(res.status(), reqwest::StatusCode::OK);
|
||
+ println!("{:?}", res.text().unwrap());
|
||
+ }
|
||
+
|
||
+ #[test]
|
||
+ fn api_evaluate() {
|
||
+ let request_body = json!({
|
||
+ "policy_id":["test.rego", "test_policy.rego"],
|
||
+ "challenge":"71oZilAy6vXCgFuRUhAYNA",
|
||
+ "evidence": "eyJ0ZWUiOiJJdHJ1c3RlZSIsImV2aWRlbmNlIjoie1xuXHRcInJlcG9ydF9zaWduXCI6XHR7XG5cdFx0XCJzY2Vfbm9fYXNcIjpcdFwiQjJEUE1NbWRUT0lVN3FpNnFCc3NaOEhFN1gtRnlwQVF3Ml9zWUpjNVVoS0FIZlFUM3phZTM5cnN6TEFzaE5qOGJ5dEIyOHNTUnp2N3RXYmRPSmV3dW5Uc1pUNnJaSEFRWFFEc1k0UzloOFRIdDBPNnlnbUV6Z1MydjZkM0NpeW91MGtQanNVbzFFbHpxbU5KS0JTbFFpejNqQlVzTjZhVXo3dkM5Yllpd3FsdWpBZm9iUlBfT19OM193NGxfMmQ4T05OaWtWRHdGcE5zMjJqcVJ0ZzlxS2VvWkduZVhUSGQwYVIzMVNKTDhsRFJsOG5Ka0FLdkRFUHZ5Zl9GN0Jrd2pEYk5YM2hNdmJMLXFEWkVWT2JNdUcwYldBVGRJV0FUTDFMem9qbDRTUVNPZDNmMEc1VWg1QU9pMEJtcDZUT3ZVTG44c0FpMWkwenF3U3h3Y0U0dlJjSG56OEVwTndTcjdET2tmUXR3bkdCY21fZUZkcTYzYXAtaWN6ZWwxa2pZUFRHZXY0bEdpemt4Wm9VN3FfYTExUXJIc1dkYnppeDBaNHlpMnBWS21lUXB0TjNydmxIYXZzZXE5VTh5VXBwbkVoMnNhVzJ3QlJmS2hYSVIxRFhiTlpNOV9qUHdRNVRTanNGQXpKYTNzbWM5VkxUMlZQa2lKSzBtNzhLS19sNkQ4TVF4ZXMyU2Q1dG9fYS1hcHh1OEE2b1E5aVZXRzBkdS0xS05MUm1hbVRCcUpLZzRfQzh0Z041dUZ3ejRLMVZ2eEYtVjY5RWVEUXpRV0o0SWFQTFNCS3BzSkx1ZUZyQjk1TGNmWnhudk05OG5oQVo4QU5PQ3pFdXJSYlVlR1MwcDM2ZjUtU3BYSGlveTNSbm5rY05tYmlVb2cwbVd6T01HVTE4WTZjeFZJVGNcIlxuXHR9LFxuXHRcImFrY2VydFwiOlx0e1xuXHRcdFwic2NlX25vX2FzXCI6XHR7XG5cdFx0XHRcInNpZ25hdHVyZVwiOlx0e1xuXHRcdFx0XHRcImRya19jZXJ0XCI6XHRcIlRVbEpSV3RxUTBOQk0zRm5RWGRKUWtGblNWSkZVbU40Tnk5NE5sRkRSRnBCUmpGS2IxRkJhMkZGVFhkRVVWbEtTMjlhU1doMlkwNUJVVVZNUWxGQmQxQlVSVXhOUVd0SFFURlZSVUpvVFVOUk1EUjRSSHBCVGtKblRsWkNRVzlVUW10b01WbFlaR3hoVkVWa1RVSnpSMEV4VlVWQmVFMVZVMGhXYUdReVZuQkpSV3hWU1VaQ2VXSXlVakZaTTFGblVUQkZkMGhvWTA1TmFrMTNUbFJGTVUxVVRYbFBSRVY2VjJoalRrMTZaM2RPVkVWNFRWUk5lVTlFUlhwWGFrRTJUVkZ6ZDBOUldVUldVVkZIUlhkS1JGUnFSVkJOUVRCSFFURlZSVU5vVFVkVFNGWm9aREpXY0UxU2IzZEhRVmxFVmxGUlJFVjRSWGROYWxwSlZXdFplRTFGY3pWTlJFRTBUMVJGZUZaRVEwTkJhVWwzUkZGWlNrdHZXa2xvZG1OT1FWRkZRa0pSUVVSblowbFFRVVJEUTBGbmIwTm5aMGxDUVUxek9FTllRMXBvZGtObk1qbE1UbXRWWWxOTU9VbGljR3RaVkhSM1IyWTBLMGhYYjJoRUt6QnNPRVl3YVRGR1dIbEVibnBJYW1keU1UTnNWakp1YkZCR09XeHVVazlNWTJKSlRDdHRVVFJJYm05UVEyWXZZVXhSZFdsRFZUTkVjRXhNVG5aMFIyZFhlbXBoTW5CbFRIVlhPVWxWWkhoMGNuUlNRalV6YVdKSFRHeHdSVkl2ZEZkaVNUSk5SMmhTTldaWFptUXdSVGR4ZFM5Q1VGWnRPVlpKVDB4eFEyMVdaMWhWY0hSblVucDNPSFpIUkZSc09GSjFPRkJ4WWt0WmRucGhUbHBxYlhwek5XNUtPWGx5VW5KSksydFBaekI2TTBwWU1tTllRVzVEVVVneGVDOWlWRzlXV0dodFkybFpPVU5MTTNCR04ydDRWSGxsZUdoR1RqVXhZakp6VVdWNVEzTlhhazVsYnpsWlVGSTBTMUJ0VVVzNVJYWm5kbkpXYW5keVRuZFNMMkZwZDFKWlowbzJTelExUzNwS1FUSnNjWFZ0VmtReFUzcFlORmhtWm14RFNqRlphRkpTWTNWeWFFc3ZaRXA0UVZkUlJsa3JTV2Q0Y1V4SE9IRlFWbGM0U1RFMFpuZzJRbWxUYkZWelIzRlVRWFp5TjFWRlF6aFNWWEJWT0VsT2VUSkpaV3A1TlhOaWMyTnBSVlpsTkVkSGFqUk5PVTB2YzBaVFQzUmlOM1FyVm5GU1RsbFRTV05HYlhKVVZqVlNjbHBDVkZkU1kyeGhOM3BXVkc1NFUwNHdjR2x5UzBWWmNFOVBVV1p6UWxodU56VjNNMFpsU0RKcU5HdEpVV3RWYUVSNU9XcFhaa05sSzFONGVuQnZTMkpvVmxrd1dsbHNVRzl1U2s5QlowaEhWek5UU2tac2JpdG1Ra3BCZFdkc1RqbFphbVJ2UVZkcVZHcDRSRTB6ZFZWdGVUQjFkRFkxU20xME5uQkNkVU1yVVVwWFIySjFhQ3RFTUV0MFNVVnROVEZMVFM5RE1HUTRNME0yYm1GSk1qVmxla0o2T0ZGalpXbG5kRXg0U1UxYVprSkZWU3RYTm1aeU9HaHhWa1pDTldkRFIzVldiRzVUUTJSNmJFbDBlRXc0VldOeWVISmpRazVQWkRSMWNIZGxiMWt4ZEVOS1VUSTVXRGNyV0RjemRVVnNkVVpJUVdkTlFrRkJSMnBuV1RoM1oxbDNkMGgzV1VSV1VqQnFRa0puZDBadlFWVkZiMjh6TjFCc2VsWTVabXRuTjNFeFluZFFjek5oTkZKVWN6aDNRM2RaUkZaU01GQkNRVkZFUVdkUU5FMUdkMGREUTNOSFFWRlZSa0ozUlVKQ1JrRjNWR3BCYjBKblozSkNaMFZHUWxGamQwRnZXV05oU0ZJd1kwUnZka3g2UlhsT2VUUjNUR3BCZFUxVE9XcFpWMng2WXpOV2JFeHRhREJpVkVGcFFtZG5ja0puUlVaQ1VXTjNRVmxaVjJGSVVqQmpSRzkyVEhwRmVVNTVOSGRNYWtGMVRWUnZlVTFFVVRCTmVrRk9RbWRyY1docmFVYzVkekJDUVZGelJrRkJUME5CVVVWQlQzWlVXR1ZFWlZSVE1VNTVibEEzY0ZZMFYwdzBZMFp1UVdoaFNFbE5hbWhwYlZjM1RqbEZRblF3VTB4SFNGQXpaRGhzTTBsblYxSkZiMlpRTkZnd1JWSjFiR1J4WWxweFpucElSWEZhZG5abFZFOVNVVlZHYVdWV1ZURnJLekY0U1doRFUzbGtNMWhxVUdObGQxWkRiMFpDY0dNMlFURlFlbkJDY1dwM1kyTXpLMDFxTUdwcVNXaFpWMDAxVnl0b1VYRlJOVWhuT1RneWFWUmhNM2haTjBGc1UzaExVa2x2YkN0dU16SkpRM2hXTkUxbFQwdzNUa0ZoWVhkVlpGRkdOamh3ZHpGQk5tSmtXV2d5VjNWSWVHWXJaQ3RUWm5aVEwzZGxjVEJFV21rNGRUSnNURzFoVVZjd2JrcEVTVFUzZFcxWFEyTTNTa2d2VkZaR0wzSmlhaXRrZVhCUlJUSkJaa3h2VjNwS2JuUkRaa3d5UjI1eVlrZ3JXbkpPVHpSQ1l6RXpOWEZaU2xad1ZqTnNVSFpOYzFVeVozRk9jRFpvVmtndkt6UkJhRTQyWjFFNVYwbzFkbUprV25aT2VsZGpURlpHYzB4UlBUMFwiLFxuXHRcdFx0XHRcImRya19zaWduXCI6XHRcIkVTLUhLSUFyOTBYQ1h3ZXRfQi1pR1BmS0Uwa0VIdWczeGhnSUpRUWNRak5iSXc5bHJ4bFZaZE1XMnlIM3hfVlNMTUhfZ3RzWjVDSXRjYlpjNlVNaWhCeDRDcXVpdjZ5RG1yMlVOUmJrNGdKd2ZiNjE5em9pNkEzaThTcFpaaUctY2dQUlNGS05IdjNSV0tBbDdXaUd1SU04ZFlvdHR0eHhJT3U0bElrQWlXR2txRlpCUzJCc2JSNXVLdEl2R0hEaG1QZEh5c3JwU3lrb0loVE4wM0FpY0NNYWRFVnhzbUs5Y0pGRlFTOHg3ZlAxU0VMUHJDMFdUT0F4bjlNdFBRVXcwSnV5dzZKeXVJZU5BYjRYRV9uNjZQOU9oRXV5Y3RjajBsa3RxLUpneFFlalRVejFROHVTbVFEV2ROcUZlRTFOQWk3Zmd6YVNjd2tTc3lXa0dtckt0MFZaSnI2a0hCT0xYMFNEMElPRWJROFNRdHFieVRpRko4Uk4tMXZqb05LYjhobnpIUnpqeGJ4eGYtVW9zeWlhY3RNUVRnOUNlTi1rSVRjUDJTUVE4ZjA0OThVVkpRamFoU05OeVcxdnp6c0hXaFQyeWMtcFdwSll6YUtrXzBjdFBVT3UwazJ1VS1EbENOdFQzRkVpS0hMZWVzRmdUSUlmdFJ1Z1JoMklHSFVkcFlvTmJDVmxvRXdaLWRPY19fQ212ZTBOOVVoWWdSUUpER1VhOGRWZVhKVXZXZ2dNQXpJdEE0QUhhVlByTFRFSTRHQld5bEw5Wm1FclU5bmpXZjd3X09XcldvaUFlVm5KMl9yanhpYjVUZ3ZldXJCM3pCbEJMSTVwQ0ExbmxhT0N0aUlMM0c1dEs3N3NNXzNXeUhYSG96cFRTeTR3WG5mU0tGeHUweXROV240XCJcblx0XHRcdH0sXG5cdFx0XHRcInBheWxvYWRcIjpcdHtcblx0XHRcdFx0XCJ2ZXJzaW9uXCI6XHRcIlRFRS5SQS4xLjBcIixcblx0XHRcdFx0XCJ0aW1lc3RhbXBcIjpcdFwiOTU1ODA5NDE0MTIzNTRcIixcblx0XHRcdFx0XCJzY2VuYXJpb1wiOlx0XCJzY2Vfbm9fYXNcIixcblx0XHRcdFx0XCJzaWduX2FsZ1wiOlx0XCJQUzI1NlwiLFxuXHRcdFx0XHRcImhhc2hfYWxnXCI6XHRcIkhTMjU2XCIsXG5cdFx0XHRcdFwicXRhX2ltZ1wiOlx0XCI3V2N1ZjZLMEM0XzBUTVIwZEMyMURBdVZ2c0c2WEIySjhxZ0dtUUFHSVdzXCIsXG5cdFx0XHRcdFwicXRhX21lbVwiOlx0XCJQVXJNdENoSGRzaW1VQWZrRnZrc3Y1Z3ZHMzFSalgtbF8wb0JsLUpCZHNNXCIsXG5cdFx0XHRcdFwidGNiXCI6XHRcIlwiLFxuXHRcdFx0XHRcImFrX3B1YlwiOlx0e1xuXHRcdFx0XHRcdFwia3R5XCI6XHRcIlJTQVwiLFxuXHRcdFx0XHRcdFwiblwiOlx0XCJwUkU1OGtzdW9IeGNMMExiMWs1blNfZkt2eERZSm96anhnYlh0aVU5T0RJNFMxa1hSZ2ZBNWdiRi1BQVJkSDZZOTdzQ0t6TklBNFRZd0pVTWh0ZFlScTZZYWZrSFZLb29tUWhJYU5mSUFaMG1BWXU5V2ttS2hBNE9BRjJNX0xDMW03WVVGMlI1S2taaGxzeFUtMEo3bWwyM2Q3RFB1enI1cnJwREFDYWxqMmZtLTROVk5DMHdYTjdPaWVXSW9UdFF0Sk9DSW5aODFjZ3FWZUxhUEVUSnpmRnBtX0ZBWXgtWDlsVnlrUFVxeWNuZnVFZjJ1Tml2OVNyMHc1d1NlX1BSRmlNQmRCRGxSQllqODBsbHllbGZGb0tWeE41c1BXMENuRkJZdlA2MHV6VVNSMDZjbHZxSU4wWUotTnhEcVhMU1VrajAtZEluNHdFLTVaaU55azI1Q25mWE5tMDk1c2tOeFo4SUxYN3FIZEJkVFhKczYxQ3J1TllsUWg0S2pHZVpWZkxVWkJZaVIwZC1JbjMyWDBYakQ1QWw4OHhLNFdxWFNhdFF6N01MekhBLURKYU8ybHNKZEFxTGFqdGxnV2ZOYXBWbWd5cVB1OFFVTkhfaEhqQlhTUDRNMXEwTUFTd2JCWXlib1VVMl9CY0lSZ0pwTnNneHlRNTJibTU2aUppYVFRQ05rYU55R1FhajRxWEVodjFTQlFlYjlMSVR1dVZfRDQzLUdhS2Z3dEhWZEZiYXdvYjZFbm5yWHoxVW1fREt1dXhLU09QZ3E0MEpRanFCQllMa1J2cXJfbS1ES3BManlVb2w4MS1ldUU1bjNZejFXU1lIbG5tc1hfMEYzbFZDQktQelR5aWd5SGZvZ0hjVXNtX2tTVDFfbWdIajg2ZnZ2OVJGTFFaTFBJRVwiLFxuXHRcdFx0XHRcdFwiZVwiOlx0XCJBUUFCXCJcblx0XHRcdFx0fVxuXHRcdFx0fSxcblx0XHRcdFwiaGFuZGxlclwiOlx0XCJwcm92aXNpb25pbmctb3V0cHV0XCJcblx0XHR9XG5cdH0sXG5cdFwicGF5bG9hZFwiOlx0e1xuXHRcdFwidmVyc2lvblwiOlx0XCJURUUuUkEuMS4wXCIsXG5cdFx0XCJ0aW1lc3RhbXBcIjpcdFwiOTU1ODExNzg4NzA1MzRcIixcblx0XHRcIm5vbmNlXCI6XHRcIjcxb1ppbEF5NnZYQ2dGdVJVaEFZTkFcIixcblx0XHRcImtleVwiOlx0e1xuXHRcdFx0XCJwdWJfa2V5XCI6XHR7XG5cdFx0XHRcdFwiblwiOlx0XCJ1LUtoY1RGLTRMdVhzTUtJZ0I0a0hDMG9NbVZXOV9OUGFGRlB0ajRfSG15azd0b0psS09BMVFENFRiY0dGYUdzenk3aUhvUm9hSUphMnJ6djFuUm1tZ3JlN0lRbTZaSXN2Z3M2bm80OW4ybUJtcENOVDlacEtXemJEMjN3N2hya3VUdkY5LUlTalhMRDk4Tk1ycF9OdUxXalNCczJPV3hyWGJrdUdjU1pETXN5NjJ6dkYxclhIYXFRRmFKNXhpU3cyaGU3YS01YWFfUGpuQmtMa0VYOE1ySF9RSzBtUFI1cmFNeU85a25CVU9UT0owdUpVS3hIQ2dKSEpTU051aDY1ZUt4VlJCS3pKbzNrT0d3Z1lRdjltOEJsZUhjeWoyWlZBVUhfZFFrajRZZFQ3bjAyZ3VBam9RMVR4aHl6R250RnV1VTFTVUZDRVAwZzN3dmI5c2N4UVNmZlNacjl5WTlCMElBR1REckZ3Q0dPM3JUamF3NXJxY2FxSlprY0pkMjFIU05DNTJUOEdENHo4UWhfV05iUHNueV9sRXI5VzU5ZHBXN0FjWlJvSkZlMHhiOGhSaURwRldmOTZJdnBTT2lQZ2RNQVY5SHRjeFRoOS1pdzJiYl9kQzYtekVLS2xfTUtzWE1rSk1DdmxDazdCd3dQX1ZKVnQ2OS1pLVo0dWN3YXhOQUxCdHJfZWFQU0RPNHU2d2JDNUl1bGlPb2VvM0hEci1FQ3RwWVdWWVplVXBGTUIwZF9GU3hsT3NXQjE3QzRNT3ZzNjVtQWcwYXJNOTMyb00xeE5WUWxfYzZkS1g4alVzV1FubXpDcG5zS1JCU3UwRHFwU0JORjhNSnY4ZDR2cFRoNFU2NmhHWFhRYjVWZjdDWkpCbC05dFZRMGJHQTVhNjFfbkYxRXhtc1wiLFxuXHRcdFx0XHRcImVcIjpcdFwiQVFBQlwiLFxuXHRcdFx0XHRcImt0eVwiOlx0XCJSU0FcIlxuXHRcdFx0fSxcblx0XHRcdFwiZW5jX2tleVwiOlx0XCJWa3hVX1FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBakJBQUFBQUFBQUNBQUFBQUFBQUFBREFBQUFBQUFBQUFNQUFBQUFBQUFBQkFBQUFBQUFBQUFFQUFBQUFBQUFBRHQ1TnJxQUpocjE1N0Q5V0QyTDRCdE9nVnlXejJfQzZTLXNLc2lzb0xpakRfWmtGM29GenpnRHV1MG04XzNSRk9oc2xTV1A4MTNJTVB3SUZMRDN4MkhSOG9VbkUyYS1fT2UwY1diRGZrQUtHLUxKaS0zNzFoNWs4T25lMHV0UmhyV1lTRnBCUS1lWGlkdlJEcUg3NUFpcDlNemJ5TExYTUlvbm1LWXlXa3EzczJMWVRiX1g1UkhMSnZqVjd0cDZxS20ySnl3TzY3TUdiVGdTUzJsbzc1SGJIaVQ5dmFBZldaUFF5Y1FDMHROTlhfRUJfUUYyTWJBckpibi1UM3NaTVhya195X3RDempMYTNWZWJXS2lsUGV5aXpYWFFBb1cyc0Q5MGZybEZYazhwMU9lTGhxcWxLVml3di1rUlk0ZU5ISnJXOGVTdTZmQndLUGw4dWJUcGt3eV9ickprYmFXUVVoYVk5ZFN5Wml5bWhPZHFTTGw4dTRwRnZvd0xKdDJQanpWajhDRTNQM0M4OEtSMkdsNjRvaUF3dnk2OWVsT1E3aXRXa1VjLTZlUDR2X0paT1VOcVZUN2hCUzNKWEJ4MmxjeS0yc0lEc1FhMGhSSW10eERFZXhoendWUFJrM2xwQkFwSlljcTR6LUVHWGV6dm5Kb0NERVRHeHVMSHl3MkctRV9lY1FnTTlyVTVQOW9GTG5fSm96OFBBYkJ6ZzgwOURwc0lKM051Slp3ZkhoNkpyNHlRRXZvTHphTFhsa1VVS0IxQXhjWXphLV9tT0R6WEJnRXRHRnBpWk9UYkFoRUhSWXRzQ25paUpDbTR6Mk9nay1HY1YwdVl1cTlCZDhreGIyVnU2NTdqVG1XVGxPVVE5SnZ1bXZ4Mks3MnJyakZBdzRpVy1PSUFab2JpdmVoN1RtUjgwaFdQaThOcTlBd1BGYm9zVWZwWDUzeUNZWUNpSk15ZzUwRUpQZXBwaFduS2NReERGNUNZMjBXSkpEXzN5REJ6WEFOeDdJSmE3NUVGMGRRVm9BSXEtVW1nODJHZWMzdWdrUEtTTXl0RHZNMmZUVlRPMl9QYlJETkFlbFpoZ3VEX0hZMDhhRTVybVE0NTFPLVFlR3pnbWw3QWpNaHJBTVZwQlp1VEpneFBNRGpWaTFWZHZ2ZzlaLWVobTlwYzFENzRCQ1hwdEZFUEVFRHhjd1dvaEhkLXExVTMtczZQTVlRbUs4QzJEQ25zekUzZ0xueTZJQW9PZ19zSGJUT1Jha1dYWnlhZkNMelJ4NmxCaFlyNkZ4OHo1Y1BIOXQ1VEstcmVqVjdPTURZbWVaUFl5QmFCbE5NYzNRbVo4Tk94d0Z2bWN1MnhEMVU4cjZqSXRqR3BVdUN5RDEzRGZnZ0REVmZWem9HSldXVHROMXVLWkNfRHVQdi12blRXdFIwZjQ4cTd6dDY1WXVpVC04NFptUXQ0Q1NMTlZNb2plR2lDbXN6bVZ1Y1ExSENxcklsQVBtM243UTJDSmx6ZEdOUWZyV3FJNmU3VHo2VGg2UWVBUkZCYzM5VVI4RjlMaHpDc2dYcGI1NWRTb0E5UXVDTEswdzRWNnFjWUE5QmJkZzRlb1ZRV1J1V1ljTDlKMl92TGZON3ROSWpCa01rTmhmZDZleXk4M2JrUmpuY3p5UFh6UllKUXF2RkppN3Y3ZjhtS0lSV1VkemNPSzhDOFB6UkJvdFR6SXZqdm9XWkpfcW45OHhWVlRJTU1pYWhZRF9ldHE5cjVTeWpTR2ZHM25nVzllblJ6a2FrVDdIc1V0MjYyalpWekxITzJKUGN0X0h2T1RkVFNWeW9wZjFUYUZlY1dCSHdaMVA2NUZpcXFueFhZam9XVnZyWlc1dmg1UGgtUzBKZ2V5U0Z1NndBWldwTkhEbWtNOGhvR09PVzMzRDZUaTFYeXY1VGpvYTI3ZmlZdGdZd3RWUTBHb2RodTBYN3NxN0Y4UnVjWW55M05yYUtJSXpRaUp4ZHRlSGNzQnpxUTRDWk5JSlNuMFZzellVQkRxR0t1Y0RmeVhVXCJcblx0XHR9LFxuXHRcdFwic2NlbmFyaW9cIjpcdFwic2NlX25vX2FzXCIsXG5cdFx0XCJ1dWlkXCI6XHRcImY2OGZkNzA0LTZlYjEtNGQxNC1iMjE4LTcyMjg1MGViM2VmMFwiLFxuXHRcdFwicmVxdWVzdGVyX3V1aWRcIjpcdFwiXCIsXG5cdFx0XCJoYXNoX2FsZ1wiOlx0XCJIUzI1NlwiLFxuXHRcdFwic2lnbl9hbGdcIjpcdFwiUFMyNTZcIixcblx0XHRcInRhX21lbVwiOlx0XCJRYjkyZWUxTlRnZlVpdnlISXZuaEVZcU5IU0tLS0FPVUlzV1Y5dVl0eXA4XCIsXG5cdFx0XCJ0YV9pbWdcIjpcdFwiQW1VLXI4OHREOVhVSlMwOGhkVkJTREFySDE0WnI3UXFRQlpwLXp2WkE5a1wiLFxuXHRcdFwidGFfYXR0clwiOlx0XCJcIixcblx0XHRcInRjYlwiOlx0XCJcIlxuXHR9LFxuXHRcImhhbmRsZXJcIjpcdFwicmVwb3J0LW91dHB1dFwiXG59In0",
|
||
+ }
|
||
+ );
|
||
+ let client = Client::new();
|
||
+ let endpoint = "http://127.0.0.1:8080/attestation";
|
||
+ let res = client
|
||
+ .post(endpoint)
|
||
+ .header("Content-Type", "application/json")
|
||
+ .body(request_body.to_string())
|
||
+ .send()
|
||
+ .unwrap();
|
||
+ assert_eq!(res.status(), reqwest::StatusCode::OK);
|
||
+ println!("{:?}", res.text().unwrap());
|
||
+ }
|
||
+
|
||
+ #[test]
|
||
+ fn api_get_challenge() {
|
||
+ let client: Client = Client::new();
|
||
+ let endpoint = "http://127.0.0.1:8080/challenge";
|
||
+ let res = client
|
||
+ .get(endpoint)
|
||
+ .send()
|
||
+ .unwrap();
|
||
+ assert_eq!(res.status(), reqwest::StatusCode::OK);
|
||
+ println!("{:?}", res.text().unwrap());
|
||
+ }
|
||
+
|
||
+}
|
||
diff --git a/service/attestation/attestation-service/token/Cargo.toml b/service/attestation/attestation-service/token/Cargo.toml
|
||
new file mode 100644
|
||
index 0000000..c4b885c
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/token/Cargo.toml
|
||
@@ -0,0 +1,13 @@
|
||
+[package]
|
||
+name = "token_signer"
|
||
+version = "0.1.0"
|
||
+edition = "2021"
|
||
+
|
||
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||
+
|
||
+[dependencies]
|
||
+jsonwebtoken.workspace = true
|
||
+serde.workspace = true
|
||
+serde_json.workspace = true
|
||
+anyhow.workspace = true
|
||
+attestation-types.workspace = true
|
||
\ No newline at end of file
|
||
diff --git a/service/attestation/attestation-service/token/src/lib.rs b/service/attestation/attestation-service/token/src/lib.rs
|
||
new file mode 100644
|
||
index 0000000..ed41a4e
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/token/src/lib.rs
|
||
@@ -0,0 +1,115 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+use anyhow::{Result, bail};
|
||
+use jsonwebtoken::{encode, get_current_timestamp,
|
||
+ Algorithm, EncodingKey, Header,
|
||
+};
|
||
+use std::path::Path;
|
||
+use serde::{Deserialize, Serialize};
|
||
+use serde_json::Value;
|
||
+use attestation_types::{EvlResult, Claims};
|
||
+
|
||
+
|
||
+#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
+pub struct TokenSignConfig {
|
||
+ pub iss: String,
|
||
+ pub nbf: usize, // 生效时刻
|
||
+ pub valid_duration: usize, // 有效时间
|
||
+ pub alg: SignAlg,
|
||
+ pub key: String,
|
||
+}
|
||
+
|
||
+impl Default for TokenSignConfig {
|
||
+ fn default() -> Self {
|
||
+ TokenSignConfig {
|
||
+ iss: "oeas".to_string(),
|
||
+ nbf: 0,
|
||
+ valid_duration: 300,
|
||
+ alg: SignAlg::PS256,
|
||
+ key: "/etc/attestation/attestation-service/token/private.pem".to_string(),
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+
|
||
+#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
+pub struct EvlReport {
|
||
+ pub tee: String,
|
||
+ pub result: EvlResult,
|
||
+ pub tcb_status: Value,
|
||
+}
|
||
+
|
||
+pub type SignAlg = Algorithm;
|
||
+pub struct TokenSigner {
|
||
+ pub config: TokenSignConfig,
|
||
+}
|
||
+
|
||
+impl Default for TokenSigner {
|
||
+ fn default() -> Self {
|
||
+ TokenSigner {
|
||
+ config: TokenSignConfig::default(),
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+impl TokenSigner {
|
||
+ pub fn new(config: TokenSignConfig) -> Result<Self> {
|
||
+ Ok(TokenSigner { config })
|
||
+ }
|
||
+ fn support_rs(alg: &Algorithm) -> bool
|
||
+ {
|
||
+ if *alg == Algorithm::RS256 || *alg == Algorithm::RS384 || *alg == Algorithm::RS512{
|
||
+ return true;
|
||
+ }
|
||
+ return false;
|
||
+ }
|
||
+ fn support_ps(alg: &Algorithm) -> bool
|
||
+ {
|
||
+ if *alg == Algorithm::PS256 || *alg == Algorithm::PS384 || *alg == Algorithm::PS512 {
|
||
+ return true;
|
||
+ }
|
||
+ return false;
|
||
+ }
|
||
+ pub fn sign(&self, report: &EvlReport) -> Result<String> {
|
||
+ let alg: Algorithm = self.config.alg;
|
||
+ let mut header = Header::new(alg);
|
||
+ header.typ = Some("JWT".to_string());
|
||
+ let unix_time = get_current_timestamp();
|
||
+ let claims: Claims = Claims {
|
||
+ iss: self.config.iss.clone(),
|
||
+ iat: usize::try_from(unix_time).expect("unix time to usize error"),
|
||
+ nbf: usize::try_from(unix_time).expect("unix time to usize error"),
|
||
+ exp: usize::try_from(unix_time).expect("unix time to usize error")
|
||
+ + self.config.valid_duration,
|
||
+ evaluation_reports: report.result.clone(),
|
||
+ tee: report.tee.clone(),
|
||
+ tcb_status: report.tcb_status.clone(),
|
||
+ };
|
||
+ if !Self::support_rs(&alg) && !Self::support_ps(&alg) {
|
||
+ bail!("unknown algrithm {:?}", alg);
|
||
+ }
|
||
+ if !Path::new(&self.config.key).exists() {
|
||
+ bail!("token verfify failed, {:?} cert not exist", self.config.key);
|
||
+ }
|
||
+ let key = std::fs::read(&self.config.key).unwrap();
|
||
+ let key_value: EncodingKey = match EncodingKey::from_rsa_pem(&key) {
|
||
+ Ok(val) => val,
|
||
+ _ => bail!("get key from input error"),
|
||
+ };
|
||
+
|
||
+ let token = match encode(&header, &claims, &key_value) {
|
||
+ Ok(val) => val,
|
||
+ Err(e) => bail!("sign jwt token error {:?}", e),
|
||
+ };
|
||
+ Ok(token)
|
||
+ }
|
||
+}
|
||
\ No newline at end of file
|
||
diff --git a/service/attestation/attestation-service/verifier/Cargo.toml b/service/attestation/attestation-service/verifier/Cargo.toml
|
||
new file mode 100644
|
||
index 0000000..e870fa7
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/verifier/Cargo.toml
|
||
@@ -0,0 +1,27 @@
|
||
+[package]
|
||
+name = "verifier"
|
||
+version = "0.1.0"
|
||
+edition = "2021"
|
||
+
|
||
+[dependencies]
|
||
+anyhow.workspace = true
|
||
+serde.workspace = true
|
||
+serde_json.workspace = true
|
||
+async-trait.workspace = true
|
||
+cose-rust.workspace = true
|
||
+ciborium.workspace = true
|
||
+hex.workspace = true
|
||
+openssl.workspace = true
|
||
+log.workspace = true
|
||
+ima-measurements.workspace = true
|
||
+rand.workspace = true
|
||
+fallible-iterator.workspace = true
|
||
+attestation-types.workspace = true
|
||
+
|
||
+[dev-dependencies]
|
||
+
|
||
+[features]
|
||
+default = [ "itrustee-verifier","virtcca-verifier" ]
|
||
+itrustee-verifier = []
|
||
+virtcca-verifier = []
|
||
+no_as = []
|
||
diff --git a/service/attestation/attestation-service/verifier/src/itrustee/itrustee.rs b/service/attestation/attestation-service/verifier/src/itrustee/itrustee.rs
|
||
new file mode 100644
|
||
index 0000000..9749871
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/verifier/src/itrustee/itrustee.rs
|
||
@@ -0,0 +1,53 @@
|
||
+/* automatically generated by rust-bindgen 0.69.4 */
|
||
+
|
||
+#[repr(C)]
|
||
+#[derive(Debug, Copy, Clone)]
|
||
+pub struct buffer_data {
|
||
+ pub size: ::std::os::raw::c_uint,
|
||
+ pub buf: *mut ::std::os::raw::c_uchar,
|
||
+}
|
||
+#[test]
|
||
+fn bindgen_test_layout_buffer_data() {
|
||
+ const UNINIT: ::std::mem::MaybeUninit<buffer_data> = ::std::mem::MaybeUninit::uninit();
|
||
+ let ptr = UNINIT.as_ptr();
|
||
+ assert_eq!(
|
||
+ ::std::mem::size_of::<buffer_data>(),
|
||
+ 16usize,
|
||
+ concat!("Size of: ", stringify!(buffer_data))
|
||
+ );
|
||
+ assert_eq!(
|
||
+ ::std::mem::align_of::<buffer_data>(),
|
||
+ 8usize,
|
||
+ concat!("Alignment of ", stringify!(buffer_data))
|
||
+ );
|
||
+ assert_eq!(
|
||
+ unsafe { ::std::ptr::addr_of!((*ptr).size) as usize - ptr as usize },
|
||
+ 0usize,
|
||
+ concat!(
|
||
+ "Offset of field: ",
|
||
+ stringify!(buffer_data),
|
||
+ "::",
|
||
+ stringify!(size)
|
||
+ )
|
||
+ );
|
||
+ assert_eq!(
|
||
+ unsafe { ::std::ptr::addr_of!((*ptr).buf) as usize - ptr as usize },
|
||
+ 8usize,
|
||
+ concat!(
|
||
+ "Offset of field: ",
|
||
+ stringify!(buffer_data),
|
||
+ "::",
|
||
+ stringify!(buf)
|
||
+ )
|
||
+ );
|
||
+}
|
||
+
|
||
+#[link(name = "teeverifier")]
|
||
+extern "C" {
|
||
+ pub fn tee_verify_report(
|
||
+ data_buf: *mut buffer_data,
|
||
+ nonce: *mut buffer_data,
|
||
+ type_: ::std::os::raw::c_int,
|
||
+ filename: *mut ::std::os::raw::c_char,
|
||
+ ) -> ::std::os::raw::c_int;
|
||
+}
|
||
diff --git a/service/attestation/attestation-service/verifier/src/itrustee/mod.rs b/service/attestation/attestation-service/verifier/src/itrustee/mod.rs
|
||
new file mode 100644
|
||
index 0000000..67c857a
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/verifier/src/itrustee/mod.rs
|
||
@@ -0,0 +1,76 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+
|
||
+//! itrustee verifier plugin
|
||
+
|
||
+use super::*;
|
||
+use log;
|
||
+use serde_json::json;
|
||
+use std::path::Path;
|
||
+use std::ops::Add;
|
||
+
|
||
+mod itrustee;
|
||
+
|
||
+const ITRUSTEE_REF_VALUE_FILE: &str = "/etc/attestation/attestation-service/verifier/itrustee/basevalue.txt";
|
||
+
|
||
+#[derive(Debug, Default)]
|
||
+pub struct ItrusteeVerifier {}
|
||
+
|
||
+impl ItrusteeVerifier {
|
||
+ pub async fn evaluate(&self, user_data: &[u8], evidence: &[u8]) -> Result<TeeClaim> {
|
||
+ return evalute_wrapper(user_data, evidence);
|
||
+ }
|
||
+}
|
||
+
|
||
+fn evalute_wrapper(user_data: &[u8], evidence: &[u8]) -> Result<TeeClaim> {
|
||
+ let mut in_data = user_data.to_vec();
|
||
+ let mut in_evidence = evidence.to_vec();
|
||
+ let mut data_buf: itrustee::buffer_data = itrustee::buffer_data {
|
||
+ size: in_evidence.len() as ::std::os::raw::c_uint,
|
||
+ buf: in_evidence.as_mut_ptr() as *mut ::std::os::raw::c_uchar,
|
||
+ };
|
||
+ let mut nonce = itrustee::buffer_data {
|
||
+ size: in_data.len() as ::std::os::raw::c_uint,
|
||
+ buf: in_data.as_mut_ptr() as *mut ::std::os::raw::c_uchar,
|
||
+ };
|
||
+ log::info!("input nonce:{:?}", nonce);
|
||
+ let policy: std::os::raw::c_int = 1;
|
||
+ if !Path::new(ITRUSTEE_REF_VALUE_FILE).exists() {
|
||
+ log::error!("itrustee verify report {} not exists", ITRUSTEE_REF_VALUE_FILE);
|
||
+ bail!("itrustee verify report {} not exists", ITRUSTEE_REF_VALUE_FILE);
|
||
+ }
|
||
+ let ref_file = String::from(ITRUSTEE_REF_VALUE_FILE);
|
||
+ let mut file = ref_file.add("\0");
|
||
+ let basevalue = file.as_mut_ptr() as *mut ::std::os::raw::c_char;
|
||
+ unsafe {
|
||
+ let ret = itrustee::tee_verify_report(&mut data_buf, &mut nonce, policy, basevalue);
|
||
+ if ret != 0 {
|
||
+ log::error!("itrustee verify report failed ret:{}", ret);
|
||
+ bail!("itrustee verify report failed ret:{}", ret);
|
||
+ }
|
||
+ }
|
||
+ let js_evidence: serde_json::Value = serde_json::from_slice(evidence)?;
|
||
+ let payload = json!({
|
||
+ "itrustee.nonce": js_evidence["payload"]["nonce"].clone(),
|
||
+ "itrustee.hash_alg": js_evidence["payload"]["hash_alg"].clone(),
|
||
+ "itrustee.key": js_evidence["payload"]["key"].clone(),
|
||
+ "itrustee.ta_img": js_evidence["payload"]["ta_img"].clone(),
|
||
+ "itrustee.ta_mem": js_evidence["payload"]["ta_mem"].clone(),
|
||
+ "itrustee.uuid": js_evidence["payload"]["uuid"].clone(),
|
||
+ "itrustee.version": js_evidence["payload"]["version"].clone(),
|
||
+ });
|
||
+ let claim = json!({
|
||
+ "tee": "itrustee",
|
||
+ "payload" : payload,
|
||
+ });
|
||
+ Ok(claim as TeeClaim)
|
||
+}
|
||
diff --git a/service/attestation/attestation-service/verifier/src/lib.rs b/service/attestation/attestation-service/verifier/src/lib.rs
|
||
new file mode 100644
|
||
index 0000000..58df3bd
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/verifier/src/lib.rs
|
||
@@ -0,0 +1,80 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+
|
||
+//! Unified tee verifier
|
||
+//!
|
||
+//! This crate provides unified APIs to verify TEE evidence.
|
||
+
|
||
+use anyhow::*;
|
||
+use serde_json;
|
||
+use async_trait::async_trait;
|
||
+
|
||
+use attestation_types::{Evidence, TeeType};
|
||
+
|
||
+#[cfg(feature = "itrustee-verifier")]
|
||
+pub mod itrustee;
|
||
+
|
||
+#[cfg(feature = "virtcca-verifier")]
|
||
+pub mod virtcca;
|
||
+
|
||
+pub type TeeClaim = serde_json::Value;
|
||
+
|
||
+#[derive(Debug, Default)]
|
||
+pub struct Verifier {}
|
||
+
|
||
+#[async_trait]
|
||
+pub trait VerifierAPIs {
|
||
+ async fn verify_evidence(&self, user_data: &[u8], evidence: &[u8]) -> Result<TeeClaim>;
|
||
+ async fn verify_ima(&self,
|
||
+ evidence: &[u8],
|
||
+ claim: &serde_json::Value,
|
||
+ ) -> Result<()>;
|
||
+}
|
||
+
|
||
+const MAX_CHALLENGE_LEN: usize = 64;
|
||
+
|
||
+#[async_trait]
|
||
+impl VerifierAPIs for Verifier {
|
||
+ async fn verify_evidence(&self, user_data: &[u8], evidence: &[u8]) -> Result<TeeClaim> {
|
||
+ let len = user_data.len();
|
||
+ if len <= 0 || len > MAX_CHALLENGE_LEN {
|
||
+ log::error!("challenge len is error, expecting 0 < len <= {}, got {}", MAX_CHALLENGE_LEN, len);
|
||
+ bail!("challenge len is error, expecting 0 < len <= {}, got {}", MAX_CHALLENGE_LEN, len);
|
||
+ }
|
||
+ let aa_evidence: Evidence = serde_json::from_slice(evidence)?;
|
||
+ let tee_type = aa_evidence.tee;
|
||
+ let evidence = aa_evidence.evidence.as_bytes();
|
||
+ match tee_type {
|
||
+ #[cfg(feature = "itrustee-verifier")]
|
||
+ TeeType::Itrustee => itrustee::ItrusteeVerifier::default().evaluate(user_data, evidence).await,
|
||
+ #[cfg(feature = "virtcca-verifier")]
|
||
+ TeeType::Virtcca => virtcca::VirtCCAVerifier::default().evaluate(user_data, evidence).await,
|
||
+ _ => bail!("unsupported tee type:{:?}", tee_type),
|
||
+ }
|
||
+ }
|
||
+ async fn verify_ima(&self,
|
||
+ evidence: &[u8],
|
||
+ claim: &serde_json::Value,
|
||
+ ) -> Result<()> {
|
||
+ let aa_evidence: Evidence = serde_json::from_slice(evidence)?;
|
||
+ let tee_type = aa_evidence.tee;
|
||
+ let digest_list_file = "/etc/attestation/attestation-service/verifier/digest_list_file".to_string();
|
||
+ match tee_type {
|
||
+ #[cfg(feature = "virtcca-verifier")]
|
||
+ TeeType::Virtcca => virtcca::ima::ImaVerify::default().ima_verify(evidence, claim, digest_list_file),
|
||
+ _ => {
|
||
+ log::info!("unsupported ima type:{:?}", tee_type);
|
||
+ Ok(())
|
||
+ },
|
||
+ }
|
||
+ }
|
||
+}
|
||
diff --git a/service/attestation/attestation-service/verifier/src/virtcca/ima.rs b/service/attestation/attestation-service/verifier/src/virtcca/ima.rs
|
||
new file mode 100644
|
||
index 0000000..44292e8
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/verifier/src/virtcca/ima.rs
|
||
@@ -0,0 +1,91 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+use anyhow::{Result, bail};
|
||
+use ima_measurements::{Event, EventData, Parser};
|
||
+use fallible_iterator::FallibleIterator;
|
||
+use std::fs;
|
||
+use std::process::Command;
|
||
+use serde_json::Value;
|
||
+use rand::Rng;
|
||
+
|
||
+use attestation_types::{Evidence, VirtccaEvidence};
|
||
+
|
||
+#[derive(Debug)]
|
||
+pub struct ImaVerify {
|
||
+ log_path: String,
|
||
+}
|
||
+
|
||
+impl Default for ImaVerify {
|
||
+ fn default() -> Self {
|
||
+ let mut rng = rand::thread_rng();
|
||
+ let n: u64 = rng.gen();
|
||
+ ImaVerify {
|
||
+ // log_path: format!("/tmp/attestation-service/ima-log-{}", n), // todo fs::write depends attestation-service dir exist
|
||
+ log_path: format!("/tmp/ima-log-{}", n),
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+impl ImaVerify {
|
||
+ // todo return detail verify result list with policy
|
||
+ pub fn ima_verify(&self, evidence: &[u8], claim: &Value, digest_list_file: String) -> Result<()> {
|
||
+ let aa_evidence: Evidence = serde_json::from_slice(evidence)?;
|
||
+ let evidence = aa_evidence.evidence.as_bytes();
|
||
+ let virtcca_ev: VirtccaEvidence = serde_json::from_slice(evidence)?;
|
||
+ let ima_log = match virtcca_ev.ima_log {
|
||
+ Some(ima_log) => ima_log,
|
||
+ _ => {log::info!("no ima log"); return Ok(())},
|
||
+ };
|
||
+
|
||
+ fs::write(&self.log_path, &ima_log).expect("write img log failed");
|
||
+ let f = fs::File::open(&self.log_path).expect("ima log file not found");
|
||
+
|
||
+ let claim_ima_log_hash = claim["payload"]["cvm"]["rem"][0].clone();
|
||
+ let mut parser = Parser::new(f);
|
||
+
|
||
+ let mut events: Vec<Event> = Vec::new();
|
||
+ while let Some(event) = parser.next()? {
|
||
+ events.push(event);
|
||
+ }
|
||
+
|
||
+ let pcr_values = parser.pcr_values();
|
||
+ let pcr_10 = pcr_values.get(&10).expect("PCR 10 not measured");
|
||
+ let string_pcr_sha256 = hex::encode(pcr_10.sha256);
|
||
+
|
||
+ if Value::String(string_pcr_sha256.clone()) != claim_ima_log_hash {
|
||
+ log::error!("ima log verify failed string_pcr_sha256 {}, string_claim_ima_log_hash {}", string_pcr_sha256, claim_ima_log_hash);
|
||
+ bail!("ima log hash verify failed");
|
||
+ }
|
||
+
|
||
+ // parser each file digest in ima log, and compare with reference base value
|
||
+ for event in events {
|
||
+ let file_digest = match event.data {
|
||
+ EventData::ImaNg{digest, name} => {drop(name); digest.digest},
|
||
+ _ => bail!("Inalid event {:?}", event),
|
||
+ };
|
||
+ let hex_str_digest = hex::encode(file_digest);
|
||
+ //log::info!("hex_str_digest {}", hex_str_digest);
|
||
+ let output = Command::new("grep")
|
||
+ .arg("-E")
|
||
+ .arg("-i")
|
||
+ .arg(&hex_str_digest)
|
||
+ .arg(&digest_list_file)
|
||
+ .output()?;
|
||
+ if output.stdout.is_empty() {
|
||
+ log::error!("there is no refernce base value of file digest {:?}", hex_str_digest);
|
||
+ }
|
||
+ }
|
||
+
|
||
+ Ok(())
|
||
+ }
|
||
+}
|
||
+
|
||
diff --git a/service/attestation/attestation-service/verifier/src/virtcca/mod.rs b/service/attestation/attestation-service/verifier/src/virtcca/mod.rs
|
||
new file mode 100644
|
||
index 0000000..3ececb7
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-service/verifier/src/virtcca/mod.rs
|
||
@@ -0,0 +1,427 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+
|
||
+//! virtcca verifier plugin
|
||
+use super::TeeClaim;
|
||
+
|
||
+use anyhow::{Result, bail, anyhow};
|
||
+use cose::keys::CoseKey;
|
||
+use cose::message::CoseMessage;
|
||
+use ciborium;
|
||
+use ciborium::Value;
|
||
+use openssl::rsa;
|
||
+use openssl::pkey::Public;
|
||
+use openssl::x509;
|
||
+use openssl::pkey::PKey;
|
||
+use log;
|
||
+use serde_json::json;
|
||
+
|
||
+pub use attestation_types::VirtccaEvidence;
|
||
+pub mod ima;
|
||
+
|
||
+#[cfg(not(feature = "no_as"))]
|
||
+const VIRTCCA_ROOT_CERT: &str = "/etc/attestation/attestation-service/verifier/virtcca/Huawei Equipment Root CA.pem";
|
||
+#[cfg(not(feature = "no_as"))]
|
||
+const VIRTCCA_SUB_CERT: &str = "/etc/attestation/attestation-service/verifier/virtcca/Huawei IT Product CA.pem";
|
||
+
|
||
+// attestation agent local reference
|
||
+#[cfg(feature = "no_as")]
|
||
+const VIRTCCA_REF_VALUE_FILE: &str = "/etc/attestation/attestation-agent/local_verifier/virtcca/ref_value.json";
|
||
+#[cfg(feature = "no_as")]
|
||
+const VIRTCCA_ROOT_CERT: &str = "/etc/attestation/attestation-agent/local_verifier/virtcca/Huawei Equipment Root CA.pem";
|
||
+#[cfg(feature = "no_as")]
|
||
+const VIRTCCA_SUB_CERT: &str = "/etc/attestation/attestation-agent/local_verifier/virtcca/Huawei IT Product CA.pem";
|
||
+
|
||
+#[derive(Debug, Default)]
|
||
+pub struct VirtCCAVerifier {}
|
||
+
|
||
+impl VirtCCAVerifier {
|
||
+ pub async fn evaluate(&self, user_data: &[u8], evidence: &[u8]) -> Result<TeeClaim> {
|
||
+ return Evidence::verify(user_data, evidence);
|
||
+ }
|
||
+}
|
||
+
|
||
+const CBOR_TAG: u64 = 399;
|
||
+const CVM_LABEL: i128 = 44241;
|
||
+
|
||
+const CVM_CHALLENGE_LABEL: i128 = 10;
|
||
+const CVM_RPV_LABEL: i128 = 44235;
|
||
+const CVM_RIM_LABEL: i128 = 44238;
|
||
+const CVM_REM_LABEL: i128 = 44239;
|
||
+const CVM_HASH_ALG_LABEL: i128 = 44236;
|
||
+const CVM_PUB_KEY_LABEL: i128 = 44237;
|
||
+const CVM_PUB_KEY_HASH_ALG_LABEL: i128 = 44240;
|
||
+
|
||
+const CVM_CHALLENGE_SIZE: usize = 64;
|
||
+const CVM_RPV_SIZE: usize = 64;
|
||
+const CVM_REM_ARR_SIZE: usize = 4;
|
||
+const CVM_PUB_KEY_SIZE: usize = 550;
|
||
+
|
||
+#[derive(Debug)]
|
||
+pub struct CvmToken {
|
||
+ pub challenge: [u8; CVM_CHALLENGE_SIZE], // 10 => bytes .size 64
|
||
+ pub rpv: [u8; CVM_RPV_SIZE], // 44235 => bytes .size 64
|
||
+ pub rim: Vec<u8>, // 44238 => bytes .size {32,48,64}
|
||
+ pub rem: [Vec<u8>; CVM_REM_ARR_SIZE], // 44239 => [ 4*4 bytes .size {32,48,64} ]
|
||
+ pub hash_alg: String, // 44236 => text
|
||
+ pub pub_key: [u8; CVM_PUB_KEY_SIZE], // 44237 => bytes .size 550
|
||
+ pub pub_key_hash_alg: String, // 44240 => text
|
||
+}
|
||
+
|
||
+pub struct Evidence {
|
||
+ /// COSE Sign1 envelope for cvm_token
|
||
+ pub cvm_envelop: CoseMessage,
|
||
+ /// Decoded cvm token
|
||
+ pub cvm_token: CvmToken,
|
||
+}
|
||
+
|
||
+impl Evidence {
|
||
+ pub fn new() -> Self {
|
||
+ Self {
|
||
+ cvm_envelop: CoseMessage::new_sign(),
|
||
+ cvm_token: CvmToken::new(),
|
||
+ }
|
||
+ }
|
||
+ pub fn verify(user_data: &[u8], evidence: &[u8]) -> Result<TeeClaim> {
|
||
+ let virtcca_ev: VirtccaEvidence = serde_json::from_slice(evidence)?;
|
||
+ let evidence = virtcca_ev.evidence;
|
||
+ let dev_cert = virtcca_ev.dev_cert;
|
||
+ let mut evidence = Evidence::decode(evidence)?;
|
||
+
|
||
+ // verify platform token
|
||
+ evidence.verify_platform_token(&dev_cert)?;
|
||
+
|
||
+ // verify cvm token
|
||
+ evidence.verify_cvm_token(user_data)?;
|
||
+
|
||
+ // todo parsed TeeClaim
|
||
+ evidence.parse_claim_from_evidence()
|
||
+ }
|
||
+ fn parse_claim_from_evidence(&self) -> Result<TeeClaim> {
|
||
+ let payload = json!({
|
||
+ "vcca.cvm.challenge": hex::encode(self.cvm_token.challenge.clone()),
|
||
+ "vcca.cvm.rpv": hex::encode(self.cvm_token.rpv.clone()),
|
||
+ "vcca.cvm.rim": hex::encode(self.cvm_token.rim.clone()),
|
||
+ "vcca.cvm.rem.0": hex::encode(self.cvm_token.rem[0].clone()),
|
||
+ "vcca.cvm.rem.1": hex::encode(self.cvm_token.rem[1].clone()),
|
||
+ "vcca.cvm.rem.2": hex::encode(self.cvm_token.rem[2].clone()),
|
||
+ "vcca.cvm.rem.3": hex::encode(self.cvm_token.rem[3].clone()),
|
||
+ "vcca.platform": "",
|
||
+ });
|
||
+ let claim = json!({
|
||
+ "tee": "vcca",
|
||
+ "payload" : payload,
|
||
+ });
|
||
+ Ok(claim as TeeClaim)
|
||
+ }
|
||
+ fn verify_platform_token(&mut self, dev_cert: &[u8]) -> Result<()> {
|
||
+ // todo verify platform COSE_Sign1 by dev_cert, virtCCA report has no platform token now
|
||
+
|
||
+ // verify dev_cet by cert chain
|
||
+ Evidence::verify_dev_cert_chain(dev_cert)?;
|
||
+
|
||
+ Ok(())
|
||
+ }
|
||
+ // todo verify cert chain, now only verify signature
|
||
+ fn verify_dev_cert_chain(dev_cert: &[u8]) -> Result<()> {
|
||
+ let dev_cert = x509::X509::from_der(dev_cert)?;
|
||
+ let sub_cert_file = std::fs::read(VIRTCCA_SUB_CERT)?;
|
||
+ let sub_cert = x509::X509::from_pem(&sub_cert_file)?;
|
||
+ let root_cert_file = std::fs::read(VIRTCCA_ROOT_CERT)?;
|
||
+ let root_cert = x509::X509::from_pem(&root_cert_file)?;
|
||
+
|
||
+ // verify dev_cert by sub_cert
|
||
+ let ret = dev_cert.verify(&(sub_cert.public_key()? as PKey<Public>))?;
|
||
+ if !ret {
|
||
+ log::error!("verify dev cert by sub cert failed");
|
||
+ bail!("verify dev cert by sub cert failed");
|
||
+ }
|
||
+ // verify sub_cert by root_cert
|
||
+ let ret = sub_cert.verify(&(root_cert.public_key()? as PKey<Public>))?;
|
||
+ if !ret {
|
||
+ log::error!("verify sub cert by root cert failed");
|
||
+ bail!("verify sub cert by root cert failed");
|
||
+ }
|
||
+ // verify self signed root_cert
|
||
+ let ret = root_cert.verify(&(root_cert.public_key()? as PKey<Public>))?;
|
||
+ if !ret {
|
||
+ log::error!("verify self signed root cert failed");
|
||
+ bail!("verify self signed root cert failed");
|
||
+ }
|
||
+ Ok(())
|
||
+ }
|
||
+ fn verify_cvm_token(&mut self, challenge: &[u8]) -> Result<()> {
|
||
+ // verify challenge
|
||
+ let len = challenge.len();
|
||
+ let token_challenge = &self.cvm_token.challenge[0..len];
|
||
+ if challenge != token_challenge {
|
||
+ log::error!("verify cvm token challenge error, cvm_token challenge {:?}, input challenge {:?}",
|
||
+ token_challenge, challenge);
|
||
+ bail!("verify cvm token challenge error, cvm_token challenge {:?}, input challenge {:?}",
|
||
+ token_challenge, challenge);
|
||
+ }
|
||
+
|
||
+ // todo verify cvm pubkey by platform.challenge, virtCCA report has no platform token now
|
||
+
|
||
+ // verify COSE_Sign1 signature begin
|
||
+ let raw_pub_key = self.cvm_token.pub_key;
|
||
+ let mut cose_key: CoseKey = Evidence::from_raw_pub_key(&raw_pub_key)?;
|
||
+ cose_key.key_ops(vec![cose::keys::KEY_OPS_VERIFY]);
|
||
+ match self.cvm_envelop.header.alg {
|
||
+ Some(alg) => cose_key.alg(alg),
|
||
+ None => bail!("cose sign verify alg is none"),
|
||
+ }
|
||
+ self.cvm_envelop.key(&cose_key).map_err(|err| anyhow!("set cose_key to COSE_Sign1 envelop failed: {err:?}"))?;
|
||
+ self.cvm_envelop.decode(None, None).map_err(|err| anyhow!("verify COSE_Sign1 signature failed:{err:?}"))?;
|
||
+ // verify COSE_Sign1 signature end
|
||
+
|
||
+ // verfiy cvm token with reference value
|
||
+ #[cfg(feature = "no_as")]
|
||
+ self.compare_with_ref()?;
|
||
+
|
||
+ Ok(())
|
||
+ }
|
||
+ #[cfg(feature = "no_as")]
|
||
+ fn compare_with_ref(&mut self) -> Result<()> {
|
||
+ let ref_file = std::fs::read(VIRTCCA_REF_VALUE_FILE)?;
|
||
+ let js_ref = serde_json::from_slice(&ref_file)?;
|
||
+ match js_ref {
|
||
+ serde_json::Value::Object(obj) => {
|
||
+ for (k, v) in obj {
|
||
+ if k == "rim" {
|
||
+ let rim_ref = match v {
|
||
+ serde_json::Value::String(rim) => rim,
|
||
+ _ => bail!("tim ref expecting String"),
|
||
+ };
|
||
+ let rim = hex::encode(self.cvm_token.rim.clone());
|
||
+ if rim_ref != rim {
|
||
+ log::error!("expecting rim: {}, got: {}", rim_ref, rim);
|
||
+ bail!("expecting rim: {}, got: {}", rim_ref, rim);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ _ => bail!("invalid json ref value"),
|
||
+ }
|
||
+
|
||
+ Ok(())
|
||
+ }
|
||
+ fn from_raw_pub_key(raw_pub_key: &[u8]) -> Result<CoseKey> {
|
||
+ let pub_key: rsa::Rsa<Public> = rsa::Rsa::public_key_from_der(raw_pub_key)?;
|
||
+ let mut cose_key = CoseKey::new();
|
||
+ cose_key.kty(cose::keys::RSA);
|
||
+ cose_key.e(pub_key.e().to_vec());
|
||
+ cose_key.n(pub_key.n().to_vec());
|
||
+
|
||
+ Ok(cose_key)
|
||
+ }
|
||
+ pub fn decode(raw_evidence: Vec<u8>) -> Result<Evidence> {
|
||
+ let mut evidence: Evidence = Evidence::new();
|
||
+
|
||
+ // decode CBOR evidence to ciborium Value
|
||
+ let val: Value = ciborium::de::from_reader(raw_evidence.as_slice())?;
|
||
+ log::debug!("[debug] decode CBOR virtcca token to ciborium Value:{:?}", val);
|
||
+ if let Value::Tag(t, m) = val {
|
||
+ if t != CBOR_TAG {
|
||
+ log::error!("input evidence error, expecting tag {}, got {}", CBOR_TAG, t);
|
||
+ bail!("input evidence error, expecting tag {}, got {}", CBOR_TAG, t);
|
||
+ }
|
||
+ if let Value::Map(contents) = *m {
|
||
+ for (k, v) in contents.iter() {
|
||
+ if let Value::Integer(i) = k {
|
||
+ match (*i).into() {
|
||
+ CVM_LABEL => evidence.set_cvm_token(v)?,
|
||
+ err => bail!("unknown label {}", err),
|
||
+ }
|
||
+ } else {
|
||
+ bail!("expecting integer key");
|
||
+ }
|
||
+ }
|
||
+ } else {
|
||
+ bail!("expecting map type");
|
||
+ }
|
||
+ } else {
|
||
+ bail!("expecting tag type");
|
||
+ }
|
||
+
|
||
+ let ret = evidence.cvm_envelop.init_decoder(None);
|
||
+ match ret {
|
||
+ Ok(_) => log::info!("decode COSE success"),
|
||
+ Err(e) => {
|
||
+ log::error!("decode COSE failed, {:?}", e);
|
||
+ bail!("decode COSE failed");
|
||
+ },
|
||
+ }
|
||
+
|
||
+ // decode cvm CBOR payload
|
||
+ evidence.cvm_token = CvmToken::decode(&evidence.cvm_envelop.payload)?;
|
||
+ Ok(evidence)
|
||
+ }
|
||
+ fn set_cvm_token(&mut self, v: &Value) -> Result<()> {
|
||
+ let tmp = v.as_bytes();
|
||
+ if tmp.is_none() {
|
||
+ log::error!("cvm token is none");
|
||
+ bail!("cvm token is none");
|
||
+ }
|
||
+ self.cvm_envelop.bytes = tmp.unwrap().clone();
|
||
+ Ok(())
|
||
+ }
|
||
+}
|
||
+
|
||
+impl CvmToken {
|
||
+ pub fn new() -> Self {
|
||
+ Self {
|
||
+ challenge: [0; CVM_CHALLENGE_SIZE],
|
||
+ rpv: [0; CVM_RPV_SIZE],
|
||
+ rim: vec![0, 64],
|
||
+ rem: Default::default(),
|
||
+ hash_alg: String::from(""),
|
||
+ pub_key: [0; CVM_PUB_KEY_SIZE],
|
||
+ pub_key_hash_alg: String::from(""),
|
||
+ }
|
||
+ }
|
||
+ pub fn decode(raw_payload: &Vec<u8>) -> Result<CvmToken> {
|
||
+ let payload: Vec<u8> = ciborium::de::from_reader(raw_payload.as_slice())?;
|
||
+ log::debug!("After decode CBOR payload, payload {:?}", payload);
|
||
+ let payload: Value = ciborium::de::from_reader(payload.as_slice())?;
|
||
+ log::debug!("After decode CBOR payload agin, payload {:?}", payload);
|
||
+ let mut cvm_token: CvmToken = CvmToken::new();
|
||
+ if let Value::Map(contents) = payload {
|
||
+ for (k, v) in contents.iter() {
|
||
+ if let Value::Integer(i) = k {
|
||
+ match (*i).into() {
|
||
+ CVM_CHALLENGE_LABEL => cvm_token.set_challenge(v)?,
|
||
+ CVM_RPV_LABEL => cvm_token.set_rpv(v)?,
|
||
+ CVM_RIM_LABEL => cvm_token.set_rim(v)?,
|
||
+ CVM_REM_LABEL => cvm_token.set_rem(v)?,
|
||
+ CVM_HASH_ALG_LABEL => cvm_token.set_hash_alg(v)?,
|
||
+ CVM_PUB_KEY_LABEL => cvm_token.set_pub_key(v)?,
|
||
+ CVM_PUB_KEY_HASH_ALG_LABEL => cvm_token.set_pub_key_hash_alg(v)?,
|
||
+ err => bail!("cvm payload unkown label {}", err),
|
||
+ }
|
||
+ } else {
|
||
+ bail!("cvm payload expecting integer key");
|
||
+ }
|
||
+ }
|
||
+ } else {
|
||
+ bail!("expecting cvm payload map type");
|
||
+ }
|
||
+ log::debug!("cvm_token decode from raw payload, {:?}", cvm_token);
|
||
+ Ok(cvm_token)
|
||
+ }
|
||
+ fn set_challenge(&mut self, v: &Value) -> Result<()> {
|
||
+ let tmp = v.as_bytes();
|
||
+ if tmp.is_none() {
|
||
+ bail!("cvm token challenge is none");
|
||
+ }
|
||
+ let tmp = tmp.unwrap().clone();
|
||
+ if tmp.len() != CVM_CHALLENGE_SIZE {
|
||
+ bail!("cvm token challenge expecting {} bytes, got {}", CVM_CHALLENGE_SIZE,tmp.len());
|
||
+ }
|
||
+ self.challenge[..].clone_from_slice(&tmp);
|
||
+ Ok(())
|
||
+ }
|
||
+ fn set_rpv(&mut self, v: &Value) -> Result<()> {
|
||
+ let tmp = v.as_bytes();
|
||
+ if tmp.is_none() {
|
||
+ bail!("cvm token rpv is none");
|
||
+ }
|
||
+ let tmp = tmp.unwrap().clone();
|
||
+ if tmp.len() != CVM_RPV_SIZE {
|
||
+ bail!("cvm token rpv expecting {} bytes, got {}", CVM_RPV_SIZE, tmp.len());
|
||
+ }
|
||
+ self.rpv[..].clone_from_slice(&tmp);
|
||
+ Ok(())
|
||
+ }
|
||
+ fn get_measurement(v: &Value, who: &str) -> Result<Vec<u8>> {
|
||
+ let tmp = v.as_bytes();
|
||
+ if tmp.is_none() {
|
||
+ bail!("cvm token {} is none", who);
|
||
+ }
|
||
+ let tmp = tmp.unwrap().clone();
|
||
+ if !matches!(tmp.len(), 32 | 48 | 64) {
|
||
+ bail!("cvm token {} expecting 32, 48 or 64 bytes, got {}", who, tmp.len());
|
||
+ }
|
||
+ Ok(tmp)
|
||
+ }
|
||
+ fn set_rim(&mut self, v: &Value) -> Result<()> {
|
||
+ self.rim = Self::get_measurement(v, "rim")?;
|
||
+ Ok(())
|
||
+ }
|
||
+ fn set_rem(&mut self, v: &Value) -> Result<()> {
|
||
+ let tmp = v.as_array();
|
||
+ if tmp.is_none() {
|
||
+ bail!("cvm token rem is none");
|
||
+ }
|
||
+ let tmp = tmp.unwrap().clone();
|
||
+ if tmp.len() != 4 {
|
||
+ bail!("cvm token rem expecting size {}, got {}", CVM_REM_ARR_SIZE, tmp.len());
|
||
+ }
|
||
+
|
||
+ for (i, val) in tmp.iter().enumerate() {
|
||
+ self.rem[i] = Self::get_measurement(val, "rem[{i}]")?;
|
||
+ }
|
||
+ Ok(())
|
||
+ }
|
||
+ fn get_hash_alg(v: &Value, who: &str) -> Result<String> {
|
||
+ let alg = v.as_text();
|
||
+ if alg.is_none() {
|
||
+ bail!("{} hash alg must be str", who);
|
||
+ }
|
||
+ Ok(alg.unwrap().to_string())
|
||
+ }
|
||
+ fn set_hash_alg(&mut self, v: &Value) -> Result<()> {
|
||
+ self.hash_alg = Self::get_hash_alg(v, "cvm token")?;
|
||
+ Ok(())
|
||
+ }
|
||
+ fn set_pub_key(&mut self, v: &Value) -> Result<()> {
|
||
+ let tmp = v.as_bytes();
|
||
+ if tmp.is_none() {
|
||
+ bail!("cvm token pub key is none");
|
||
+ }
|
||
+ let tmp = tmp.unwrap().clone();
|
||
+ if tmp.len() != CVM_PUB_KEY_SIZE {
|
||
+ bail!("cvm token pub key len expecting {}, got {}", CVM_PUB_KEY_SIZE, tmp.len());
|
||
+ }
|
||
+ self.pub_key[..].clone_from_slice(&tmp);
|
||
+ Ok(())
|
||
+ }
|
||
+ fn set_pub_key_hash_alg(&mut self, v: &Value) -> Result<()> {
|
||
+ self.pub_key_hash_alg = Self::get_hash_alg(v, "pub key")?;
|
||
+ Ok(())
|
||
+ }
|
||
+}
|
||
+
|
||
+#[cfg(test)]
|
||
+mod tests {
|
||
+ use super::*;
|
||
+ use hex;
|
||
+
|
||
+ const TEST_VIRTCCA_TOKEN: &[u8; 2862] = include_bytes!("../../test_data/virtcca.cbor");
|
||
+ #[test]
|
||
+ fn decode_token() {
|
||
+ let token = hex::decode(TEST_VIRTCCA_TOKEN).unwrap();
|
||
+ let dev_cert = std::fs::read("./test_data/virtcca_aik_cert.der").unwrap();
|
||
+ let challenge = Vec::new();
|
||
+ let virtcca_ev = VirtccaEvidence {
|
||
+ evidence: token.to_vec(),
|
||
+ dev_cert: dev_cert,
|
||
+ ima_log: None,
|
||
+ };
|
||
+ let virtcca_ev = serde_json::to_vec(&virtcca_ev).unwrap();
|
||
+ let r = Evidence::verify(&challenge, &virtcca_ev);
|
||
+ match r {
|
||
+ Ok(claim) => println!("verify success {:?}", claim),
|
||
+ Err(e) => assert!(false, "verify failed {:?}", e),
|
||
+ }
|
||
+ }
|
||
+}
|
||
diff --git a/service/attestation/attestation-types/Cargo.toml b/service/attestation/attestation-types/Cargo.toml
|
||
new file mode 100644
|
||
index 0000000..1fcf465
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-types/Cargo.toml
|
||
@@ -0,0 +1,8 @@
|
||
+[package]
|
||
+name = "attestation-types"
|
||
+version = "0.1.0"
|
||
+edition = "2021"
|
||
+
|
||
+[dependencies]
|
||
+serde = { version = "1.0", features = ["derive"] }
|
||
+serde_json = "1.0"
|
||
\ No newline at end of file
|
||
diff --git a/service/attestation/attestation-types/src/lib.rs b/service/attestation/attestation-types/src/lib.rs
|
||
new file mode 100644
|
||
index 0000000..fcf1d3e
|
||
--- /dev/null
|
||
+++ b/service/attestation/attestation-types/src/lib.rs
|
||
@@ -0,0 +1,52 @@
|
||
+/*
|
||
+ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
|
||
+ * secGear is licensed under the Mulan PSL v2.
|
||
+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||
+ * You may obtain a copy of Mulan PSL v2 at:
|
||
+ * http://license.coscl.org.cn/MulanPSL2
|
||
+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
||
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
||
+ * PURPOSE.
|
||
+ * See the Mulan PSL v2 for more details.
|
||
+ */
|
||
+use serde::{Serialize, Deserialize};
|
||
+use serde_json::Value;
|
||
+
|
||
+#[derive(Debug, Serialize, Deserialize)]
|
||
+pub struct VirtccaEvidence {
|
||
+ pub evidence: Vec<u8>,
|
||
+ pub dev_cert: Vec<u8>,
|
||
+ pub ima_log: Option<Vec<u8>>,
|
||
+}
|
||
+
|
||
+#[derive(Debug, Serialize, Deserialize)]
|
||
+pub enum TeeType {
|
||
+ Itrustee = 1,
|
||
+ Virtcca,
|
||
+ Invalid,
|
||
+}
|
||
+
|
||
+
|
||
+#[derive(Debug, Serialize, Deserialize)]
|
||
+pub struct Evidence {
|
||
+ pub tee: TeeType,
|
||
+ pub evidence: String,
|
||
+}
|
||
+
|
||
+#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
+pub struct EvlResult {
|
||
+ pub eval_result: bool,
|
||
+ pub policy: Vec<String>,
|
||
+ pub report: Value,
|
||
+}
|
||
+
|
||
+#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
+pub struct Claims {
|
||
+ pub iss: String,
|
||
+ pub iat: usize,
|
||
+ pub nbf: usize,
|
||
+ pub exp: usize,
|
||
+ pub evaluation_reports: EvlResult,
|
||
+ pub tee: String,
|
||
+ pub tcb_status: Value,
|
||
+}
|
||
\ No newline at end of file
|
||
--
|
||
2.33.0
|
||
|