diff --git a/Cargo.lock b/Cargo.lock index 1c963b159..ae60ec232 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -510,7 +510,7 @@ dependencies = [ "config", "const_format", "crypto", - "kbs-types", + "kbs-types 0.7.0", "log", "serde", "serde_json", @@ -538,7 +538,7 @@ dependencies = [ "futures", "hex", "jsonwebtoken", - "kbs-types", + "kbs-types 0.9.1", "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", "log", "occlum_dcap", "s390_pv", @@ -1327,7 +1327,7 @@ dependencies = [ "anyhow", "base64 0.22.1", "ctr", - "kbs-types", + "kbs-types 0.7.0", "rand", "rsa", "serde", @@ -2840,7 +2840,7 @@ dependencies = [ "env_logger 0.10.2", "jsonwebtoken", "jwt-simple", - "kbs-types", + "kbs-types 0.9.1", "kms", "lazy_static", "log", @@ -2894,6 +2894,17 @@ dependencies = [ "serde_json", ] +[[package]] +name = "kbs-types" +version = "0.9.1" +source = "git+https://github.com/virtee/kbs-types.git?rev=a704036#a70403665bfaa61c0984ae05654df0ad72591e96" +dependencies = [ + "base64 0.22.1", + "serde", + "serde_json", + "thiserror 2.0.3", +] + [[package]] name = "kbs_protocol" version = "0.1.0" @@ -2905,7 +2916,7 @@ dependencies = [ "base64 0.22.1", "crypto", "jwt-simple", - "kbs-types", + "kbs-types 0.7.0", "log", "reqwest 0.12.9", "resource_uri", @@ -3007,7 +3018,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -5895,7 +5906,7 @@ dependencies = [ "intel-tee-quote-verification-rs", "jsonwebkey", "jsonwebtoken", - "kbs-types", + "kbs-types 0.9.1", "log", "openssl", "reqwest 0.12.9", diff --git a/Cargo.toml b/Cargo.toml index b87b1d18b..a90282580 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ jwt-simple = { version = "0.12", default-features = false, features = [ "pure-rust", ] } kbs_protocol = { git = "https://github.com/confidential-containers/guest-components.git", rev = "e6999a3c0fd877dae9e68ea78b8b483062db32b8", default-features = false } -kbs-types = "0.7.0" +kbs-types = { git = "https://github.com/virtee/kbs-types.git", rev = "a704036" } 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" 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..f2f49454f 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); @@ -212,13 +214,13 @@ tag = base64_url::encode(authentication_tag); ``` -- `alg` +- `protected.alg` Algorithm used to encrypt the encryption key at `encrypted_key`. Since the key is encrypted using the HW-TEE public key, `alg` must be the same value as described in the [`Attestation`](#attestation)'s `tee-pubkey` field. -- `enc` +- `protected.enc` Encryption algorithm used to encrypt the output of the KBS service API. @@ -227,6 +229,12 @@ 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. +Due to [JSON Web Encryption](https://www.rfc-editor.org/rfc/rfc7516), AAD field +should be calculated by `ASCII(BASE64URL(UTF8(JWE Protected Header)))` + - `iv` The input to a cryptographic primitive is used to provide the initial state. @@ -239,6 +247,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..04d08dcb8 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,19 @@ 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 aad = protected.generate_aad().context("Generate JWE AAD")?; + + let tag = cipher + .encrypt_in_place_detached(nonce, &aad, &mut payload_data) .map_err(|e| anyhow!("AES encrypt Resource payload failed: {e}"))?; let k_mod = URL_SAFE_NO_PAD @@ -44,22 +53,16 @@ pub fn jwe(tee_pub_key: TeePubKey, payload_data: Vec) -> Result { let rsa_pub_key = RsaPublicKey::new(n, e).context("Building RSA key from modulus and exponent failed")?; let sym_key: &[u8] = aes_sym_key.as_slice(); - let wrapped_sym_key = rsa_pub_key + let encrypted_key = rsa_pub_key .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")?, - 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(), + protected, + encrypted_key, + iv: iv.into(), + ciphertext: payload_data, + aad: Some(aad), + tag: tag.to_vec(), }) }