secGear/backport-init-attestation.patch
2024-06-04 22:27:59 +08:00

1689 lines
57 KiB
Diff

From d06b6beab9ae13898870297e8ef2ae806cd8d6d0 Mon Sep 17 00:00:00 2001
From: houmingyong <houmingyong@huawei.com>
Date: Tue, 28 May 2024 10:25:41 +0800
Subject: [PATCH] init attestation
The current patch incorporates the following commit points:
Reference: https://gitee.com/openeuler/secGear/commit/d06b6beab9ae13898870297e8ef2ae806cd8d6d0
https://gitee.com/openeuler/secGear/commit/b90e039631f1031a485ef038174c0bef831223a5
https://gitee.com/openeuler/secGear/commit/dad056809c5e94b50c47063d728d5f1e47800512
https://gitee.com/openeuler/secGear/commit/ce4c7b6a8c013cd208004a3cec13a15fff100b1a
https://gitee.com/openeuler/secGear/commit/8e02b257d9bec81bc557d6431e90448522ad6270
https://gitee.com/openeuler/secGear/commit/980d0a89d3b1b1a6d280846d6edddabdfd57a635
Conflict:no
---
.../attestation/attestation-agent/Cargo.toml | 30 ++
.../attestation/attestation-agent/README.md | 5 +
.../attestation-agent/agent/Cargo.toml | 38 ++
.../agent/attestation-agent.example.toml | 1 +
.../attestation-agent/agent/src/agent.rs | 144 +++++++
.../agent/src/bin/aa-test/main.rs | 68 ++++
.../agent/src/bin/generate-headers/main.rs | 4 +
.../attestation-agent/agent/src/lib.rs | 84 ++++
.../attestation-agent/attester/Cargo.toml | 24 ++
.../attester/src/itrustee/itrustee.rs | 51 +++
.../attester/src/itrustee/mod.rs | 130 ++++++
.../attestation-agent/attester/src/lib.rs | 90 +++++
.../attester/src/virtcca/mod.rs | 88 +++++
.../attester/src/virtcca/virtcca.rs | 108 +++++
.../rust_attestation_agent.h | 56 +++
.../attestation-service/Cargo.toml | 22 ++
.../attestation-service/verifier/Cargo.toml | 27 ++
.../verifier/src/itrustee/itrustee.rs | 53 +++
.../verifier/src/itrustee/mod.rs | 58 +++
.../attestation-service/verifier/src/lib.rs | 51 +++
.../verifier/src/virtcca/mod.rs | 373 ++++++++++++++++++
21 files changed, 1505 insertions(+)
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.example.toml
create mode 100644 service/attestation/attestation-agent/agent/src/agent.rs
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/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/rust_attestation_agent.h
create mode 100644 service/attestation/attestation-service/Cargo.toml
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/mod.rs
diff --git a/service/attestation/attestation-agent/Cargo.toml b/service/attestation/attestation-agent/Cargo.toml
new file mode 100644
index 0000000..7a424e6
--- /dev/null
+++ b/service/attestation/attestation-agent/Cargo.toml
@@ -0,0 +1,30 @@
+[workspace]
+resolver = "2"
+members = [
+ "agent",
+ "attester",
+]
+
+[workspace.dependencies]
+serde = "=1.0.203"
+half = "=1.6"
+ciborium-ll = "=0.2.0"
+ciborium-io = "=0.2.0"
+ciborium = "=0.2.0"
+anyhow = "1.0"
+serde_json = "1.0"
+async-trait = "=0.1.78"
+cose-rust = "=0.1.7"
+hex = "0.4"
+openssl = "=0.10.64"
+log = "=0.4.14"
+
+config = "=0.13.0"
+rand = "=0.8.5"
+base64-url = "=3.0.0"
+tokio = "1.0"
+env_logger = "=0.4.0"
+safer-ffi = "=0.1.8"
+futures = "=0.3.30"
+
+verifier = {path = "../attestation-service/verifier", default-features = false}
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..66919d9
--- /dev/null
+++ b/service/attestation/attestation-agent/agent/Cargo.toml
@@ -0,0 +1,39 @@
+[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 = [ "verifier" ]
+with_as = []
+all = ["itrustee", "virtcca"]
+itrustee = ["attester/itrustee-attester", "verifier/itrustee-verifier"]
+virtcca = ["attester/virtcca-attester", "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
+
+attester = { path = "../attester" }
+verifier = { workspace = true, optional = true }
diff --git a/service/attestation/attestation-agent/agent/attestation-agent.example.toml b/service/attestation/attestation-agent/agent/attestation-agent.example.toml
new file mode 100644
index 0000000..4ba5ef2
--- /dev/null
+++ b/service/attestation/attestation-agent/agent/attestation-agent.example.toml
@@ -0,0 +1 @@
+svr_url = "http://127.0.0.1:8888"
diff --git a/service/attestation/attestation-agent/agent/src/agent.rs b/service/attestation/attestation-agent/agent/src/agent.rs
new file mode 100644
index 0000000..9aec2bb
--- /dev/null
+++ b/service/attestation/attestation-agent/agent/src/agent.rs
@@ -0,0 +1,145 @@
+/*
+ * 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;
+use log;
+use serde::{Serialize, Deserialize};
+use async_trait::async_trait;
+
+use attester::{Attester, AttesterAPIs};
+
+#[cfg(feature = "no_as")]
+use verifier::{Verifier, VerifierAPIs};
+
+const DEFAULT_AA_CONF_PATH: &str = "/etc/attestation/attestation-agent.toml";
+
+pub use attester::EvidenceRequest;
+
+#[async_trait]
+pub trait AttestationAgentAPIs {
+ /// `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]) -> Result<()>;
+}
+
+#[async_trait]
+impl AttestationAgentAPIs for AttestationAgent {
+ 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]) -> Result<()> {
+ #[cfg(feature = "no_as")]
+ let _ret = Verifier::default().verify_evidence(challenge, evidence).await?;
+ #[cfg(feature = "with_as")]
+ let _ret = request_as(challenge, evidence);
+
+ return Ok(_ret);
+ }
+}
+
+fn request_as(challenge: &[u8], evidence: &[u8]) -> Result<()> {
+ let _ = challenge;
+ let _ = evidence;
+ // todo send request to attestation service
+ Ok(())
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+struct AAConfig {
+ svr_url: String, // Attestation Service url
+}
+
+impl Default for AAConfig {
+ fn default() -> Self {
+ Self {
+ svr_url: String::from(""),
+ }
+ }
+}
+
+impl TryFrom<&str> for AAConfig {
+ type Error = config::ConfigError;
+ fn try_from(config_path: &str) -> Result<Self, Self::Error> {
+ let c = config::Config::builder()
+ .add_source(config::File::with_name(config_path))
+ .build()?;
+ let cfg = c.try_deserialize()?;
+ Ok(cfg)
+ }
+}
+
+#[derive(Debug)]
+pub struct AttestationAgent {
+ _config: AAConfig,
+}
+
+impl Default for AttestationAgent {
+ fn default() -> Self {
+ if let Ok(_config) = AAConfig::try_from(DEFAULT_AA_CONF_PATH) {
+ log::info!("attestation agent construct with default config file");
+ return AttestationAgent { _config };
+ }
+ log::warn!("The default conf file {} is missing", DEFAULT_AA_CONF_PATH);
+ Self {
+ _config: AAConfig::default(),
+ }
+ }
+}
+
+#[allow(dead_code)]
+impl AttestationAgent {
+ pub fn new(conf_path: Option<&str>) -> Result<Self> {
+ let _config = match conf_path {
+ Some(conf_path) => {
+ log::info!("Attestation Agent config file:{conf_path}");
+ AAConfig::try_from(conf_path)?
+ }
+ None => {
+ log::warn!("No Attestation Agent config file specified. Using a default config");
+ AAConfig::default()
+ }
+ };
+ Ok(AttestationAgent {_config})
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::agent::*;
+
+ #[test]
+ fn aa_default_conf_file() {
+ let aa = AttestationAgent::default();
+ assert_eq!(aa._config.svr_url, "http://127.0.0.1:8000");
+ }
+
+ #[test]
+ fn aa_new_no_conf_path() {
+ let aa = AttestationAgent::new(None).unwrap();
+ assert_eq!(aa._config.svr_url, "");
+ }
+
+ #[test]
+ fn aa_new_with_example_conf() {
+ let aa = AttestationAgent::new(Some("attestation-agent.example.toml")).unwrap();
+ assert_eq!(aa._config.svr_url, "http://127.0.0.1:8888");
+ }
+}
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..5cedfc8
--- /dev/null
+++ b/service/attestation/attestation-agent/agent/src/bin/aa-test/main.rs
@@ -0,0 +1,68 @@
+/*
+ * 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 rand::{self, RngCore};
+use tokio;
+use env_logger;
+
+const TEST_THREAD_NUM: i64 = 1; // multi thread num
+
+//mod agent;
+use attestation_agent::agent::*;
+
+#[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 {attestation_proc(i).await;});
+ handles.push(t);
+ }
+
+ for handle in handles {
+ let _ = tokio::join!(handle);
+ }
+ println!("main stop");
+}
+
+async fn attestation_proc(i: i64) {
+ println!("attestation_proc {} start", i);
+ let aa: AttestationAgent = AttestationAgent::default();
+
+ let mut nonce: [u8; 16] = [0; 16];
+ rand::thread_rng().fill_bytes(&mut nonce);
+
+ // Step1: construct input param
+ let user_data: attester::EvidenceRequest = attester::EvidenceRequest {
+ uuid: String::from("f68fd704-6eb1-4d14-b218-722850eb3ef0"),
+ challenge: nonce.to_vec(),
+ };
+
+ // Step2: get tee evidence
+ let evidence = aa.get_evidence(user_data).await;
+ match evidence {
+ Ok(evidence) => {
+ println!("get evidence success");
+ // Step3: verify evidence
+ let ret = aa.verify_evidence(&nonce, &evidence).await;
+ match ret {
+ Ok(_) => println!("verify evidence success"),
+ Err(_) =>println!("verify evidence failed"),
+ }
+ },
+ Err(e) => println!("get evidence failed: {}", e),
+ }
+ 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..3eb4334
--- /dev/null
+++ b/service/attestation/attestation-agent/agent/src/bin/generate-headers/main.rs
@@ -0,0 +1,4 @@
+use attestation_agent;
+fn main() -> ::std::io::Result<()> {
+ attestation_agent::generate_headers()
+}
\ No newline at end of file
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..0f1efc2
--- /dev/null
+++ b/service/attestation/attestation-agent/agent/src/lib.rs
@@ -0,0 +1,72 @@
+use agent::*;
+pub mod agent;
+
+// c interface
+use safer_ffi::prelude::*;
+use futures::executor::block_on;
+use attester::EvidenceRequest;
+
+#[ffi_export]
+pub fn get_report(c_uuid: Option<&repr_c::String>, c_challenge: Option<&repr_c::Vec<u8>>) -> repr_c::Vec<u8> {
+ let uuid = match c_uuid {
+ None => {println!("uuid is null"); return Vec::new().into();},
+ Some(uuid) => uuid.clone().to_string(),
+ };
+ let challenge = match c_challenge {
+ None => {println!("challenge is null"); return Vec::new().into();},
+ Some(cha) => cha.clone().to_vec(),
+ };
+
+ let input: EvidenceRequest = EvidenceRequest {
+ uuid: uuid,
+ challenge: challenge,
+ };
+
+ let fut = async {
+ agent::AttestationAgent::default().get_evidence(input).await
+ };
+ let report: Vec<u8> = match block_on(fut) {
+ Ok(report) => report,
+ Err(e) => {
+ println!("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>>) -> safer_ffi::libc::c_int {
+ let challenge = match c_challenge {
+ None => {println!("challenge is null"); return 1;},
+ Some(cha) => cha.clone().to_vec(),
+ };
+
+ let report = match report {
+ None => {println!("report is null"); return 1;},
+ Some(report) => report.clone().to_vec(),
+ };
+
+ let fut = async {agent::AttestationAgent::default().verify_evidence(
+ &challenge, &report).await};
+ let ret = block_on(fut);
+ if ret.is_err() {
+ println!("verfiy report failed");
+ return 1;
+ }
+ return 0;
+}
+
+#[ffi_export]
+pub fn free_report(report: repr_c::Vec<u8>) {
+ drop(report);
+}
+
+// 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("rust_attestation_agent.h")?
+ .generate()
+}
\ No newline at end of file
diff --git a/service/attestation/attestation-agent/attester/Cargo.toml b/service/attestation/attestation-agent/attester/Cargo.toml
new file mode 100644
index 0000000..575e63f
--- /dev/null
+++ b/service/attestation/attestation-agent/attester/Cargo.toml
@@ -0,0 +1,24 @@
+[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
+
+[dev-dependencies]
+
+
+
+
+
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..28bf33c
--- /dev/null
+++ b/service/attestation/attestation-agent/attester/src/lib.rs
@@ -0,0 +1,90 @@
+/*
+ * 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 serde::{Serialize, Deserialize};
+use async_trait::async_trait;
+use log;
+
+#[cfg(feature = "itrustee-attester")]
+mod itrustee;
+
+#[cfg(feature = "virtcca-attester")]
+pub mod virtcca;
+
+#[derive(Debug)]
+pub struct EvidenceRequest {
+ pub uuid: String,
+ pub challenge: Vec<u8>,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct Evidence {
+ pub tee: TeeType,
+ pub evidence: String,
+}
+
+#[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 {}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub enum TeeType {
+ Itrustee = 1,
+ Virtcca,
+ Invalid,
+}
+
+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..dfb3aef
--- /dev/null
+++ b/service/attestation/attestation-agent/attester/src/virtcca/mod.rs
@@ -0,0 +1,88 @@
+/*
+ * 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 serde::{Serialize, Deserialize};
+use log;
+
+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.challenge)?;
+ let evidence = serde_json::to_string(&evidence)?;
+ Ok(evidence)
+ }
+}
+
+pub fn detect_platform() -> bool {
+ Path::new("/dev/tsi").exists()
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct VirtccaEvidence {
+ pub evidence: Vec<u8>,
+ pub dev_cert: Vec<u8>,
+}
+
+fn virtcca_get_token(challenge: &[u8]) -> Result<VirtccaEvidence> {
+ unsafe {
+ let ctx = tsi_new_ctx();
+
+ let mut challenge = 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 evidence = VirtccaEvidence {
+ evidence: token,
+ dev_cert: dev_cert,
+ };
+ 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..65da1d8
--- /dev/null
+++ b/service/attestation/attestation-agent/attester/src/virtcca/virtcca.rs
@@ -0,0 +1,108 @@
+/* automatically generated by rust-bindgen 0.69.4 */
+#[allow(non_camel_case_types)]
+pub type wchar_t = ::std::os::raw::c_int;
+#[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/rust_attestation_agent.h b/service/attestation/attestation-agent/rust_attestation_agent.h
new file mode 100644
index 0000000..e06e61e
--- /dev/null
+++ b/service/attestation/attestation-agent/rust_attestation_agent.h
@@ -0,0 +1,56 @@
+/*! \file */
+/*******************************************
+ * *
+ * File auto-generated by `::safer_ffi`. *
+ * *
+ * Do not manually edit this file. *
+ * *
+ *******************************************/
+
+#ifndef __RUST_ATTESTATION_AGENT__
+#define __RUST_ATTESTATION_AGENT__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#include <stddef.h>
+#include <stdint.h>
+
+/** \brief
+ * Same as [`Vec<T>`][`rust::Vec`], but with guaranteed `#[repr(C)]` layout
+ */
+typedef struct Vec_uint8 {
+ /** <No documentation available> */
+ uint8_t * ptr;
+
+ /** <No documentation available> */
+ size_t len;
+
+ /** <No documentation available> */
+ size_t cap;
+} Vec_uint8_t;
+
+/** <No documentation available> */
+void
+free_report (
+ Vec_uint8_t report);
+
+/** <No documentation available> */
+Vec_uint8_t
+get_report (
+ Vec_uint8_t const * c_uuid,
+ Vec_uint8_t const * c_challenge);
+
+/** <No documentation available> */
+int32_t
+verify_report (
+ Vec_uint8_t const * c_challenge,
+ Vec_uint8_t const * report);
+
+
+#ifdef __cplusplus
+} /* extern \"C\" */
+#endif
+
+#endif /* __RUST_ATTESTATION_AGENT__ */
diff --git a/service/attestation/attestation-service/Cargo.toml b/service/attestation/attestation-service/Cargo.toml
new file mode 100644
index 0000000..e244637
--- /dev/null
+++ b/service/attestation/attestation-service/Cargo.toml
@@ -0,0 +1,22 @@
+[workspace]
+resolver = "2"
+members = [
+ #"service",
+ "verifier",
+]
+
+[workspace.dependencies]
+serde = "=1.0.203"
+half = "=1.6"
+ciborium-ll = "=0.2.0"
+ciborium-io = "=0.2.0"
+ciborium = "=0.2.0"
+anyhow = "1.0"
+serde_json = "1.0"
+async-trait = "=0.1.78"
+cose-rust = "=0.1.7"
+hex = "0.4"
+openssl = "=0.10.64"
+log = "=0.4.14"
+
+attester = {path = "../attestation-agent/attester"}
diff --git a/service/attestation/attestation-service/verifier/Cargo.toml b/service/attestation/attestation-service/verifier/Cargo.toml
new file mode 100644
index 0000000..d5f2874
--- /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
+ciborium-ll.workspace = true
+ciborium-io.workspace = true
+ciborium.workspace = true
+half.workspace = true
+serde.workspace = true
+serde_json.workspace = true
+async-trait.workspace = true
+cose-rust.workspace = true
+hex.workspace = true
+openssl.workspace = true
+log.workspace = true
+
+attester.workspace = true
+
+[dev-dependencies]
+
+[features]
+default = [ "itrustee-verifier","virtcca-verifier" ]
+itrustee-verifier = []
+virtcca-verifier = ["attester/virtcca-attester"]
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..f8038ba
--- /dev/null
+++ b/service/attestation/attestation-service/verifier/src/itrustee/mod.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.
+ */
+
+//! itrustee verifier plugin
+
+use super::*;
+use log;
+use std::path::Path;
+
+mod itrustee;
+
+const ITRUSTEE_REF_VALUE_FILE: &str = "/etc/attestation/itrustee/basevalue.txt";
+
+#[derive(Debug, Default)]
+pub struct ItrusteeVerifier {}
+
+impl ItrusteeVerifier {
+ pub async fn evaluate(&self, user_data: &[u8], evidence: &[u8]) -> Result<()> {
+ return evalute_wrapper(user_data, evidence);
+ }
+}
+
+fn evalute_wrapper(user_data: &[u8], evidence: &[u8]) -> Result<()> {
+ 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,
+ };
+ 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 mut ref_file = String::from(ITRUSTEE_REF_VALUE_FILE);
+ let basevalue = ref_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);
+ }
+ }
+ Ok(())
+}
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..f3c9157
--- /dev/null
+++ b/service/attestation/attestation-service/verifier/src/lib.rs
@@ -0,0 +1,51 @@
+/*
+ * 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 attester::{Evidence, TeeType};
+
+#[cfg(feature = "itrustee-verifier")]
+mod itrustee;
+
+#[cfg(feature = "virtcca-verifier")]
+mod virtcca;
+
+#[derive(Debug, Default)]
+pub struct Verifier {}
+
+#[async_trait]
+pub trait VerifierAPIs {
+ async fn verify_evidence(&self, user_data: &[u8], evidence: &[u8]) -> Result<()>;
+}
+
+#[async_trait]
+impl VerifierAPIs for Verifier {
+ async fn verify_evidence(&self, user_data: &[u8], evidence: &[u8]) -> Result<()> {
+ 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),
+ }
+ }
+}
\ No newline at end of file
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..3994743
--- /dev/null
+++ b/service/attestation/attestation-service/verifier/src/virtcca/mod.rs
@@ -0,0 +1,373 @@
+/*
+ * 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 anyhow::{Result, bail, anyhow};
+use cose::keys::CoseKey;
+use cose::message::CoseMessage;
+use ciborium;
+use ciborium::value::Value;
+use openssl::rsa;
+use openssl::pkey::Public;
+use openssl::x509;
+use openssl::pkey::PKey;
+use log;
+
+use attester::virtcca::VirtccaEvidence;
+
+const VIRTCCA_ROOT_CERT: &str = "/etc/attestation/virtcca/Huawei Equipment Root CA.pem";
+const VIRTCCA_SUB_CERT: &str = "/etc/attestation/virtcca/Huawei IT Product CA.pem";
+const VIRTCCA_REF_VALUE_FILE: &str = "/etc/attestation/virtcca/ref_value.json";
+
+#[derive(Debug, Default)]
+pub struct VirtCCAVerifier {}
+
+impl VirtCCAVerifier {
+ pub async fn evaluate(&self, user_data: &[u8], evidence: &[u8]) -> Result<()> {
+ 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<()> {
+ 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)?;
+
+ Ok(())
+ }
+ fn verify_platform_token(&mut self, dev_cert: &[u8]) -> Result<()> {
+ // todo verify platform COSE_Sign1 by dev_cert
+
+ // 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
+
+ // 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
+ self.compare_with_ref()?;
+
+ Ok(())
+ }
+
+ 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);
+ println!("[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(())
+ }
+}
+
--
2.33.0