kata-types: Implement Initdata Spec and Digest Calculation Logic

This commit introduces the Initdata Spec and the logic for
calculating its digest. It includes:

(1) Define a `ProtectedPlatform` enum to represent major TEE platform
types.
(2) Create an `InitData` struct to support building and serializing
initialization data in TOML format.
(3) Implement adaptation for SHA-256, SHA-384, and SHA-512 digest
algorithms.
(4) Provide a platform-specific mechanism for adjusting digest lengths
(zero-padding).
(5) Supporting the decoding and verification of base64+gzip encoded
Initdata.

The core functionality ensures the integrity of data injected by the
host through trusted algorithms, while also accommodating the
measurement requirements of different TEE platforms.

Signed-off-by: alex.lyn <alex.lyn@antgroup.com>
This commit is contained in:
alex.lyn
2025-06-13 16:29:56 +08:00
parent 2603ee66b8
commit 4ca394f4fc
5 changed files with 486 additions and 4 deletions

128
src/libs/Cargo.lock generated
View File

@@ -17,6 +17,12 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "adler2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "ahash"
version = "0.7.7"
@@ -114,7 +120,7 @@ dependencies = [
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"miniz_oxide 0.7.3",
"object",
"rustc-demangle",
]
@@ -159,6 +165,15 @@ dependencies = [
"wyz",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "borsh"
version = "1.5.2"
@@ -277,6 +292,24 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crc32fast"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam"
version = "0.8.3"
@@ -341,6 +374,16 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "darling"
version = "0.14.4"
@@ -461,6 +504,16 @@ dependencies = [
"syn 2.0.66",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "dirs-next"
version = "2.0.0"
@@ -533,6 +586,17 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d"
[[package]]
name = "flate2"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
dependencies = [
"crc32fast",
"libz-sys",
"miniz_oxide 0.8.8",
]
[[package]]
name = "fnv"
version = "1.0.7"
@@ -640,6 +704,16 @@ dependencies = [
"slab",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.2.6"
@@ -942,7 +1016,9 @@ dependencies = [
"base64",
"bitmask-enum",
"byte-unit",
"flate2",
"glob",
"hex",
"lazy_static",
"nix 0.24.2",
"num_cpus",
@@ -952,6 +1028,7 @@ dependencies = [
"serde",
"serde-enum-str",
"serde_json",
"sha2",
"slog",
"slog-scope",
"sysinfo",
@@ -973,6 +1050,17 @@ version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "libz-sys"
version = "1.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d"
dependencies = [
"cc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "linux-raw-sys"
version = "0.9.4"
@@ -1052,6 +1140,15 @@ dependencies = [
"adler",
]
[[package]]
name = "miniz_oxide"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
dependencies = [
"adler2",
]
[[package]]
name = "mio"
version = "1.0.3"
@@ -1332,6 +1429,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
[[package]]
name = "powerfmt"
version = "0.2.0"
@@ -1859,6 +1962,17 @@ dependencies = [
"syn 1.0.91",
]
[[package]]
name = "sha2"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "shim-interface"
version = "0.1.0"
@@ -2306,6 +2420,12 @@ dependencies = [
"tempfile",
]
[[package]]
name = "typenum"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
[[package]]
name = "unicode-ident"
version = "1.0.12"
@@ -2336,6 +2456,12 @@ version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.4"

View File

@@ -27,6 +27,9 @@ thiserror = "1.0"
toml = "0.5.8"
serde-enum-str = "0.4"
sysinfo = "0.34.2"
sha2 = "0.10.8"
flate2 = { version = "1.0", features = ["zlib"] }
hex = "0.4"
oci-spec = { version = "0.6.8", features = ["runtime"] }
safe-path = { path = "../safe-path" }

View File

@@ -0,0 +1,346 @@
// Copyright (c) 2025 Ant Group
//
// SPDX-License-Identifier: Apache-2.0
//
use anyhow::{anyhow, Context, Result};
use flate2::read::GzDecoder;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256, Sha384, Sha512};
use std::{collections::HashMap, io::Read};
/// Currently, initdata only supports version 0.1.0.
const INITDATA_VERSION: &str = "0.1.0";
/// supported algorithms list
const SUPPORTED_ALGORITHMS: [&str; 3] = ["sha256", "sha384", "sha512"];
/// TEE platform type
#[derive(Debug, Default, Clone, Copy)]
pub enum ProtectedPlatform {
/// Tdx platform for Intel TDX
Tdx,
/// Snp platform for AMD SEV-SNP
Snp,
/// Cca platform for ARM CCA
Cca,
/// Default with no protection
#[default]
NoProtection,
}
#[allow(clippy::doc_lazy_continuation)]
/// <https://github.com/confidential-containers/trustee/blob/47d7a2338e0be76308ac19be5c0c172c592780aa/kbs/docs/initdata.md>
/// The Initdata specification defines the key data structures and algorithms for injecting any well-defined data
/// from an untrusted host into a TEE (Trusted Execution Environment). To guarantee the integrity of the data,
/// either the hostdata capability of TEE evidence or the (v)TPM dynamic measurement capability will be utilized.
/// And its format looks like as below:
/// ```toml
/// algorithm = "sha384"
/// version = "0.1.0"
///
/// [data]
/// key1 = "value1"
/// key2 = "value2"
///```
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct InitData {
/// version of InitData Spec
version: String,
/// algorithm: sha256, sha512, sha384
algorithm: String,
/// data for specific "key:value"
data: HashMap<String, String>,
}
impl InitData {
/// new InitData
pub fn new(algorithm: impl Into<String>, version: impl Into<String>) -> Self {
Self {
version: version.into(),
algorithm: algorithm.into(),
data: HashMap::new(),
}
}
/// insert data items
pub fn insert_data(&mut self, key: impl Into<String>, value: impl Into<String>) {
self.data.insert(key.into(), value.into());
}
/// get algorithm
pub fn algorithm(&self) -> &str {
&self.algorithm
}
/// get version
pub fn version(&self) -> &str {
&self.version
}
/// get data
pub fn data(&self) -> &HashMap<String, String> {
&self.data
}
/// serialize it to Vec<u8>
pub fn to_vec(&self) -> Result<Vec<u8>> {
Ok(toml::to_vec(&self)?)
}
/// serialize config to TOML string
pub fn to_string(&self) -> Result<String> {
Ok(toml::to_string_pretty(self)?)
}
/// Validate InitData
pub fn validate(&self) -> Result<()> {
// Currently, it only supports 0.1.0
if self.version != INITDATA_VERSION {
return Err(anyhow!(
"unsupported version: {}, expected: {}",
self.version,
INITDATA_VERSION
));
}
if !SUPPORTED_ALGORITHMS
.iter()
.any(|&alg| alg == self.algorithm)
{
return Err(anyhow!(
"unsupported algorithm: {}, supported algorithms: {}",
self.algorithm,
SUPPORTED_ALGORITHMS.join(", ")
));
}
Ok(())
}
}
/// calculate initdata digest
fn calculate_digest(algorithm: &str, data: &str) -> Result<Vec<u8>> {
let digest = match algorithm {
"sha256" => {
let mut hasher = Sha256::new();
hasher.update(&data);
hasher.finalize().to_vec()
}
"sha384" => {
let mut hasher = Sha384::new();
hasher.update(&data);
hasher.finalize().to_vec()
}
"sha512" => {
let mut hasher = Sha512::new();
hasher.update(&data);
hasher.finalize().to_vec()
}
_ => return Err(anyhow!("unsupported Hash algorithm: {}", algorithm).into()),
};
Ok(digest)
}
/// Handle digest for different TEE platform
fn adjust_digest(digest: &[u8], platform: ProtectedPlatform) -> Vec<u8> {
let required_len = match platform {
ProtectedPlatform::Tdx => 48,
ProtectedPlatform::Snp => 32,
ProtectedPlatform::Cca => 64,
ProtectedPlatform::NoProtection => digest.len(),
};
let mut adjusted = Vec::with_capacity(required_len);
if digest.len() >= required_len {
adjusted.extend_from_slice(&digest[..required_len]);
} else {
adjusted.extend_from_slice(digest);
adjusted.resize(required_len, 0u8); // padding with zero
}
// Vec<u8>
adjusted
}
/// Parse initdata
fn parse_initdata(initdata_str: &str) -> Result<InitData> {
let initdata: InitData = toml::from_str(&initdata_str)?;
initdata.validate()?;
Ok(initdata)
}
/// calculate initdata digest
/// 1. Parse InitData
/// 2. Calculate Digest
/// 3. Adjust Digest with Platform
/// 4. Encode digest with base64/Standard
pub fn calculate_initdata_digest(
initdata_toml: &str,
platform: ProtectedPlatform,
) -> Result<String> {
// 1. Parse InitData
let initdata: InitData = parse_initdata(initdata_toml).context("parse initdata")?;
let algorithm: &str = &initdata.algorithm;
// 2. Calculate Digest
let digest = calculate_digest(algorithm, &initdata_toml).context("calculate digest")?;
// 3. Adjust Digest with Platform
let digest_platform = adjust_digest(&digest, platform);
// 4. Encode digest with base64/Standard
let b64encoded_digest = base64::encode_config(digest_platform, base64::STANDARD);
Ok(b64encoded_digest)
}
/// The argument `initda_annotation` is a Standard base64 encoded string containing a TOML formatted content.
/// This function decodes the base64 string, parses the TOML content into an InitData structure.
pub fn add_hypervisor_initdata_overrides(initda_annotation: &str) -> Result<String> {
// Base64 decode the annotation value
let b64_decoded =
base64::decode_config(initda_annotation, base64::STANDARD).context("base64 decode")?;
// Gzip decompress the decoded data
let mut gz_decoder = GzDecoder::new(&b64_decoded[..]);
let mut initdata_str = String::new();
gz_decoder
.read_to_string(&mut initdata_str)
.context("gz decoder failed")?;
// Parse the initdata
let initdata: InitData = parse_initdata(&initdata_str).context("parse initdata overrides")?;
// initdata within a TOML string
initdata.to_string()
}
#[cfg(test)]
mod tests {
use super::*;
use flate2::write::GzEncoder;
use flate2::Compression;
use std::io::Write;
/// Test InitData creation and serialization
#[test]
fn test_init_data() {
let mut init_data = InitData::new("sha384", "0.1.0");
init_data.insert_data("initdata_key", "initdata_value");
// Verify data insertion
assert_eq!(
init_data.data().get("initdata_key").unwrap(),
"initdata_value"
);
assert_eq!(init_data.version(), "0.1.0");
assert_eq!(init_data.algorithm(), "sha384");
// Test TOML serialization
let toml_str = init_data.to_string().unwrap();
assert!(toml_str.contains("initdata_key = 'initdata_value'\n"));
assert!(toml_str.starts_with("version = '0.1.0'"));
}
/// Test calculate_digest with different algorithms
#[test]
fn test_calculate_digest() {
let data = "test_data";
// Test SHA256
let sha256 = calculate_digest("sha256", data).unwrap();
assert_eq!(sha256.len(), 32);
// Test SHA384
let sha384 = calculate_digest("sha384", data).unwrap();
assert_eq!(sha384.len(), 48);
// Test SHA512
let sha512 = calculate_digest("sha512", data).unwrap();
assert_eq!(sha512.len(), 64);
// Test invalid algorithm
assert!(calculate_digest("md5", data).is_err());
}
/// Test digest adjustment for different platforms
#[test]
fn test_adjust_digest() {
let sample_digest = vec![0xAA; 64]; // 64-byte digest
// Test TDX platform (requires 48 bytes)
let tdx_result = adjust_digest(&sample_digest, ProtectedPlatform::Tdx);
assert_eq!(tdx_result.len(), 48);
assert_eq!(&tdx_result[..48], &sample_digest[..48]);
// Test SNP platform (requires 32 bytes)
let snp_result = adjust_digest(&sample_digest, ProtectedPlatform::Snp);
assert_eq!(snp_result.len(), 32);
// Test short digest with CCA platform (requires 64 bytes)
let short_digest = vec![0xBB; 32];
let cca_result = adjust_digest(&short_digest, ProtectedPlatform::Cca);
assert_eq!(cca_result.len(), 64);
assert_eq!(&cca_result[..32], &short_digest[..]);
assert_eq!(&cca_result[32..], vec![0u8; 32]);
}
/// Test hypervisor initdata processing with compression
#[test]
fn test_hypervisor_initdata_processing() {
// Create test initdata
let mut init_data = InitData::new("sha512", "0.1.0");
init_data.insert_data("hypervisor_key", "config_value");
// Create compressed annotation
let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
encoder
.write_all(init_data.to_string().unwrap().as_bytes())
.unwrap();
let compressed = encoder.finish().unwrap();
let b64_annotation = base64::encode(compressed);
// Test processing
let result = add_hypervisor_initdata_overrides(&b64_annotation).unwrap();
assert!(result.contains("hypervisor_key = 'config_value'\n"));
assert!(result.contains("algorithm = 'sha512'\n"));
}
/// Test input validation
#[test]
fn test_initdata_validation() {
// Valid TOML
let valid_toml = r#"
version = "0.1.0"
algorithm = "sha384"
[data]
valid_key = "valid_value"
"#;
assert!(parse_initdata(valid_toml).is_ok());
// Invalid TOML (missing version)
let invalid_toml = r#"
algorithm = "sha256"
[data]
key = "value"
"#;
assert!(parse_initdata(invalid_toml).is_err());
}
/// Test error handling for malformed inputs
#[test]
fn test_error_handling() {
// Invalid base64
assert!(add_hypervisor_initdata_overrides("invalid_base64!!").is_err());
// Invalid compression format
let invalid_data = base64::encode("raw uncompressed data");
assert!(add_hypervisor_initdata_overrides(&invalid_data).is_err());
}
}

View File

@@ -40,6 +40,10 @@ pub(crate) mod utils;
/// hypervisor capabilities
pub mod capabilities;
/// The Initdata specification defines the key data structures and algorithms for injecting
/// any well-defined data from an untrusted host into a TEE (Trusted Execution Environment).
pub mod initdata;
/// Common error codes.
#[derive(thiserror::Error, Debug)]
pub enum Error {

View File

@@ -2007,7 +2007,9 @@ dependencies = [
"base64 0.13.1",
"bitmask-enum",
"byte-unit",
"flate2",
"glob",
"hex",
"lazy_static",
"num_cpus",
"oci-spec",
@@ -2016,6 +2018,7 @@ dependencies = [
"serde",
"serde-enum-str",
"serde_json",
"sha2 0.10.9",
"slog",
"slog-scope",
"sysinfo",
@@ -2596,7 +2599,7 @@ dependencies = [
"openssl",
"serde",
"serde_json",
"sha2 0.10.7",
"sha2 0.10.9",
"tokio",
"zstd",
]
@@ -4081,9 +4084,9 @@ dependencies = [
[[package]]
name = "sha2"
version = "0.10.7"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if 1.0.0",
"cpufeatures",