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
           https://gitee.com/openeuler/secGear/commit/1fbc825bd34e859f3bc641f6b1b14c106be23433
           https://gitee.com/openeuler/secGear/commit/97f78a21040443796d137ce1739861b66451c7dd
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,42 @@
+[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 = []
+with_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
+
+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,73 @@
+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 = match block_on(fut) {
+        Ok(_) => return 0,
+        Err(e) => {
+            println!("verify report failed {:?}", e);
+            return 1;
+        }
+    };
+}
+
+#[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,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.
+ */
+
+//! 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<()>;
+}
+
+const MAX_CHALLENGE_LEN: usize = 64;
+
+#[async_trait]
+impl VerifierAPIs for Verifier {
+    async fn verify_evidence(&self, user_data: &[u8], evidence: &[u8]) -> Result<()> {
+        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),
+        }
+    }
+}
\ 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
+
+        // verify 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
