diff --git a/Cargo.lock b/Cargo.lock index c5371846d..eb72ae7bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -510,7 +510,7 @@ dependencies = [ "config", "const_format", "crypto", - "kbs-types", + "kbs-types 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "log", "serde", "serde_json", @@ -538,7 +538,7 @@ dependencies = [ "futures", "hex", "jsonwebtoken", - "kbs-types", + "kbs-types 0.7.0 (git+https://github.com/virtee/kbs-types.git?rev=f47881f)", "lazy_static", "log", "openssl", @@ -581,7 +581,7 @@ dependencies = [ "hex", "hyper 0.14.31", "hyper-tls 0.5.0", - "kbs-types", + "kbs-types 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "log", "occlum_dcap", "s390_pv", @@ -1372,7 +1372,7 @@ dependencies = [ "anyhow", "base64 0.22.1", "ctr", - "kbs-types", + "kbs-types 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand", "rsa 0.9.6", "serde", @@ -2943,7 +2943,7 @@ dependencies = [ "env_logger 0.10.2", "jsonwebtoken", "jwt-simple 0.11.9", - "kbs-types", + "kbs-types 0.7.0 (git+https://github.com/virtee/kbs-types.git?rev=f47881f)", "kms", "lazy_static", "log", @@ -2997,6 +2997,15 @@ dependencies = [ "serde_json", ] +[[package]] +name = "kbs-types" +version = "0.7.0" +source = "git+https://github.com/virtee/kbs-types.git?rev=f47881f#f47881f12e10bc9aee2e48b955351dcb099b248a" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "kbs_protocol" version = "0.1.0" @@ -3008,7 +3017,7 @@ dependencies = [ "base64 0.22.1", "crypto", "jwt-simple 0.12.10", - "kbs-types", + "kbs-types 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "log", "reqwest 0.12.9", "resource_uri", @@ -6173,7 +6182,7 @@ dependencies = [ "intel-tee-quote-verification-rs", "jsonwebkey", "jsonwebtoken", - "kbs-types", + "kbs-types 0.7.0 (git+https://github.com/virtee/kbs-types.git?rev=f47881f)", "log", "openssl", "reqwest 0.12.9", diff --git a/Cargo.toml b/Cargo.toml index 679e00fcd..e28a57385 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,14 +30,21 @@ ear = "0.3.0" env_logger = "0.10.0" hex = "0.4.3" jwt-simple = "0.11" -kbs_protocol = { git = "https://github.com/confidential-containers/guest-components.git", rev="e6999a3c0fd877dae9e68ea78b8b483062db32b8", default-features = false } -kbs-types = "0.7.0" -kms = { git = "https://github.com/confidential-containers/guest-components.git", rev="e6999a3c0fd877dae9e68ea78b8b483062db32b8", default-features = false } +kbs_protocol = { git = "https://github.com/confidential-containers/guest-components.git", rev = "e6999a3c0fd877dae9e68ea78b8b483062db32b8", default-features = false } +kbs-types = { git = "https://github.com/virtee/kbs-types.git", rev = "f47881f" } +kms = { git = "https://github.com/confidential-containers/guest-components.git", rev = "e6999a3c0fd877dae9e68ea78b8b483062db32b8", default-features = false } jsonwebtoken = { version = "9", default-features = false } log = "0.4.17" prost = "0.12" -regorus = { version = "0.2.6", default-features = false, features = ["regex", "base64", "time", "std" ] } -reqwest = { version = "0.12", default-features = false, features = ["default-tls"] } +regorus = { version = "0.2.6", default-features = false, features = [ + "regex", + "base64", + "time", + "std", +] } +reqwest = { version = "0.12", default-features = false, features = [ + "default-tls", +] } rstest = "0.18.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.132" @@ -47,7 +54,7 @@ sha2 = "0.10" shadow-rs = "0.19.0" strum = { version = "0.25", features = ["derive"] } thiserror = "1.0" -tokio = { version = "1", features = ["full"], default-features = false } +tokio = { version = "1", features = ["full"], default-features = false } tempfile = "3.14.0" tonic = "0.11" tonic-build = "0.11" diff --git a/deps/verifier/src/snp/mod.rs b/deps/verifier/src/snp/mod.rs index 3012d19bf..2dce3b688 100644 --- a/deps/verifier/src/snp/mod.rs +++ b/deps/verifier/src/snp/mod.rs @@ -37,7 +37,7 @@ const LOADER_SPL_OID: Oid<'static> = oid!(1.3.6 .1 .4 .1 .3704 .1 .3 .1); const KDS_CERT_SITE: &str = "https://kdsintf.amd.com"; const KDS_VCEK: &str = "/vcek/v1"; -/// Attestation report versions supported +/// Attestation report versions supported const REPORT_VERSION_MIN: u32 = 2; const REPORT_VERSION_MAX: u32 = 3; @@ -110,7 +110,9 @@ impl Verifier for Snp { // See Trustee Issue#589 https://github.com/confidential-containers/trustee/issues/589 if report.version < REPORT_VERSION_MIN || report.version > REPORT_VERSION_MAX { - return Err(anyhow!("Unexpected attestation report version. Check SNP Firmware ABI specification")); + return Err(anyhow!( + "Unexpected attestation report version. Check SNP Firmware ABI specification" + )); } if report.vmpl != 0 { diff --git a/kbs/docs/kbs_attestation_protocol.md b/kbs/docs/kbs_attestation_protocol.md index 8a77e84b8..aec33fcfa 100644 --- a/kbs/docs/kbs_attestation_protocol.md +++ b/kbs/docs/kbs_attestation_protocol.md @@ -191,6 +191,7 @@ payload that follows the [JSON Web Encryption](https://www.rfc-editor.org/rfc/rf { "protected": "$jose_header", "encrypted_key": "$encrypted_key", + "aad": "$aad", "iv": "$iv", "ciphertext": "$ciphertext", "tag": "$tag" @@ -204,6 +205,7 @@ The above JWE JSON fields are defined as follows: let jose_header_string = format!(r#"{{"alg": "{}","enc": "{}"}}"#, alg, enc); let jose_header = base64_url::encode(&jose_header_string); let encrypted_key = base64_url::encode(enc_kbs_symkey); +let aad = base64_url::encode(additional_authenticated_data); let iv = base64_url::encode(initialization_vector); let ciphertext = base64_url::encode(response_output); @@ -227,6 +229,10 @@ Encryption algorithm used to encrypt the output of the KBS service API. The output of the KBS service API. It must be encrypted with the KBS-generated ephemeral key. +- `aad` (Required if AEAD is used) + +An input to an AEAD operation that is integrity protected but not encrypted. + - `iv` The input to a cryptographic primitive is used to provide the initial state. @@ -239,6 +245,11 @@ The encrypted symmetric key is used to encrypt `ciphertext`. This key is encrypted with the HW-TEE's public key, using the algorithm defined in `alg`. +- `tag` + +The authentication tag is used to authenticate the ciphertext. If the algorithm +described by `enc` used does not need it, this field is left blank. + ## Key Format ### Public Key diff --git a/kbs/src/attestation/backend.rs b/kbs/src/attestation/backend.rs index 6e130d0e8..d76374371 100644 --- a/kbs/src/attestation/backend.rs +++ b/kbs/src/attestation/backend.rs @@ -25,8 +25,8 @@ use super::{ }; static KBS_MAJOR_VERSION: u64 = 0; -static KBS_MINOR_VERSION: u64 = 1; -static KBS_PATCH_VERSION: u64 = 1; +static KBS_MINOR_VERSION: u64 = 2; +static KBS_PATCH_VERSION: u64 = 0; lazy_static! { static ref VERSION_REQ: VersionReq = { diff --git a/kbs/src/jwe.rs b/kbs/src/jwe.rs index 44e68d1b1..da5694b86 100644 --- a/kbs/src/jwe.rs +++ b/kbs/src/jwe.rs @@ -2,18 +2,19 @@ // Licensed under the Apache License, Version 2.0, see LICENSE for details. // SPDX-License-Identifier: Apache-2.0 -use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit, Nonce}; +use std::collections::BTreeMap; + +use aes_gcm::{aead::AeadMutInPlace, Aes256Gcm, KeyInit, Nonce}; use anyhow::{anyhow, bail, Context, Result}; use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; -use kbs_types::{Response, TeePubKey}; +use kbs_types::{ProtectedHeader, Response, TeePubKey}; use rand::{rngs::OsRng, Rng}; use rsa::{BigUint, Pkcs1v15Encrypt, RsaPublicKey}; -use serde_json::json; const RSA_ALGORITHM: &str = "RSA1_5"; const AES_GCM_256_ALGORITHM: &str = "A256GCM"; -pub fn jwe(tee_pub_key: TeePubKey, payload_data: Vec) -> Result { +pub fn jwe(tee_pub_key: TeePubKey, mut payload_data: Vec) -> Result { let TeePubKey::RSA { alg, k_mod, k_exp } = tee_pub_key else { bail!("Only RSA key is support for TEE pub key") }; @@ -25,11 +26,20 @@ pub fn jwe(tee_pub_key: TeePubKey, payload_data: Vec) -> Result { let mut rng = rand::thread_rng(); let aes_sym_key = Aes256Gcm::generate_key(&mut OsRng); - let cipher = Aes256Gcm::new(&aes_sym_key); + let mut cipher = Aes256Gcm::new(&aes_sym_key); let iv = rng.gen::<[u8; 12]>(); let nonce = Nonce::from_slice(&iv); - let encrypted_payload_data = cipher - .encrypt(nonce, payload_data.as_slice()) + let protected = ProtectedHeader { + alg: RSA_ALGORITHM.to_string(), + enc: AES_GCM_256_ALGORITHM.to_string(), + other_fields: BTreeMap::new(), + }; + let protected_utf8 = + serde_json::to_string(&protected).context("serialize protected header failed")?; + let aad = URL_SAFE_NO_PAD.encode(protected_utf8); + + let tag = cipher + .encrypt_in_place_detached(nonce, aad.as_bytes(), &mut payload_data) .map_err(|e| anyhow!("AES encrypt Resource payload failed: {e}"))?; let k_mod = URL_SAFE_NO_PAD @@ -48,18 +58,12 @@ pub fn jwe(tee_pub_key: TeePubKey, payload_data: Vec) -> Result { .encrypt(&mut rng, Pkcs1v15Encrypt, sym_key) .context("RSA encrypt sym key failed")?; - let protected_header = json!( - { - "alg": RSA_ALGORITHM.to_string(), - "enc": AES_GCM_256_ALGORITHM.to_string(), - }); - Ok(Response { - protected: serde_json::to_string(&protected_header) - .context("serde protected_header failed")?, + protected, encrypted_key: URL_SAFE_NO_PAD.encode(wrapped_sym_key), iv: URL_SAFE_NO_PAD.encode(iv), - ciphertext: URL_SAFE_NO_PAD.encode(encrypted_payload_data), - tag: "".to_string(), + ciphertext: URL_SAFE_NO_PAD.encode(payload_data), + aad: Some(aad), + tag: URL_SAFE_NO_PAD.encode(tag), }) }