Skip to content

Commit 7b9e7c7

Browse files
committed
KBS: Update KBS protocol to 0.2.0 to fix JWE
Fixes confidential-containers#583. Due to RFC 7516, the JWE AEAD Auth Tag should be expcilitly be included inside the `tag` part. Before this commit, the tag is actually included as the suffix of the `ciphertext`. We fix this by expcilitly extract the tag and include it into the jwe body. Also, we fix the AAD calculation logic, s.t. derived from ProtectedHeader which is also specifiled by RFC7516. This should be align with the guest-components side. This change will make the kbs_client not able to connect to the KBS. Thus we update the KBS protocol version from 0.1.1 to 0.2.0. Signed-off-by: Xynnn007 <[email protected]>
1 parent 525610b commit 7b9e7c7

File tree

6 files changed

+65
-36
lines changed

6 files changed

+65
-36
lines changed

Diff for: Cargo.lock

+20-9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jwt-simple = { version = "0.12", default-features = false, features = [
3333
"pure-rust",
3434
] }
3535
kbs_protocol = { git = "https://github.com/confidential-containers/guest-components.git", rev = "e6999a3c0fd877dae9e68ea78b8b483062db32b8", default-features = false }
36-
kbs-types = "0.7.0"
36+
kbs-types = { git = "https://github.com/virtee/kbs-types.git", rev = "a704036" }
3737
kms = { git = "https://github.com/confidential-containers/guest-components.git", rev = "e6999a3c0fd877dae9e68ea78b8b483062db32b8", default-features = false }
3838
jsonwebtoken = { version = "9", default-features = false }
3939
log = "0.4.17"

Diff for: deps/verifier/src/snp/mod.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const LOADER_SPL_OID: Oid<'static> = oid!(1.3.6 .1 .4 .1 .3704 .1 .3 .1);
3737
const KDS_CERT_SITE: &str = "https://kdsintf.amd.com";
3838
const KDS_VCEK: &str = "/vcek/v1";
3939

40-
/// Attestation report versions supported
40+
/// Attestation report versions supported
4141
const REPORT_VERSION_MIN: u32 = 2;
4242
const REPORT_VERSION_MAX: u32 = 3;
4343

@@ -110,7 +110,9 @@ impl Verifier for Snp {
110110

111111
// See Trustee Issue#589 https://github.com/confidential-containers/trustee/issues/589
112112
if report.version < REPORT_VERSION_MIN || report.version > REPORT_VERSION_MAX {
113-
return Err(anyhow!("Unexpected attestation report version. Check SNP Firmware ABI specification"));
113+
return Err(anyhow!(
114+
"Unexpected attestation report version. Check SNP Firmware ABI specification"
115+
));
114116
}
115117

116118
if report.vmpl != 0 {

Diff for: kbs/docs/kbs_attestation_protocol.md

+15-2
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ payload that follows the [JSON Web Encryption](https://www.rfc-editor.org/rfc/rf
191191
{
192192
"protected": "$jose_header",
193193
"encrypted_key": "$encrypted_key",
194+
"aad": "$aad",
194195
"iv": "$iv",
195196
"ciphertext": "$ciphertext",
196197
"tag": "$tag"
@@ -204,6 +205,7 @@ The above JWE JSON fields are defined as follows:
204205
let jose_header_string = format!(r#"{{"alg": "{}","enc": "{}"}}"#, alg, enc);
205206
let jose_header = base64_url::encode(&jose_header_string);
206207
let encrypted_key = base64_url::encode(enc_kbs_symkey);
208+
let aad = base64_url::encode(additional_authenticated_data);
207209
let iv = base64_url::encode(initialization_vector);
208210
let ciphertext = base64_url::encode(response_output);
209211

@@ -212,13 +214,13 @@ tag = base64_url::encode(authentication_tag);
212214

213215
```
214216

215-
- `alg`
217+
- `protected.alg`
216218

217219
Algorithm used to encrypt the encryption key at `encrypted_key`.
218220
Since the key is encrypted using the HW-TEE public key, `alg` must be the same
219221
value as described in the [`Attestation`](#attestation)'s `tee-pubkey` field.
220222

221-
- `enc`
223+
- `protected.enc`
222224

223225
Encryption algorithm used to encrypt the output of the KBS service API.
224226

@@ -227,6 +229,12 @@ Encryption algorithm used to encrypt the output of the KBS service API.
227229
The output of the KBS service API. It must be encrypted with the KBS-generated
228230
ephemeral key.
229231

232+
- `aad` (Required if AEAD is used)
233+
234+
An input to an AEAD operation that is integrity protected but not encrypted.
235+
Due to [JSON Web Encryption](https://www.rfc-editor.org/rfc/rfc7516), AAD field
236+
should be calculated by `ASCII(BASE64URL(UTF8(JWE Protected Header)))`
237+
230238
- `iv`
231239

232240
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`.
239247
This key is encrypted with the HW-TEE's public key, using the algorithm defined
240248
in `alg`.
241249

250+
- `tag`
251+
252+
The authentication tag is used to authenticate the ciphertext. If the algorithm
253+
described by `enc` used does not need it, this field is left blank.
254+
242255
## Key Format
243256

244257
### Public Key

Diff for: kbs/src/attestation/backend.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ use super::{
2525
};
2626

2727
static KBS_MAJOR_VERSION: u64 = 0;
28-
static KBS_MINOR_VERSION: u64 = 1;
29-
static KBS_PATCH_VERSION: u64 = 1;
28+
static KBS_MINOR_VERSION: u64 = 2;
29+
static KBS_PATCH_VERSION: u64 = 0;
3030

3131
lazy_static! {
3232
static ref VERSION_REQ: VersionReq = {

Diff for: kbs/src/jwe.rs

+23-20
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,19 @@
22
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
33
// SPDX-License-Identifier: Apache-2.0
44

5-
use aes_gcm::{aead::Aead, Aes256Gcm, KeyInit, Nonce};
5+
use std::collections::BTreeMap;
6+
7+
use aes_gcm::{aead::AeadMutInPlace, Aes256Gcm, KeyInit, Nonce};
68
use anyhow::{anyhow, bail, Context, Result};
79
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
8-
use kbs_types::{Response, TeePubKey};
10+
use kbs_types::{ProtectedHeader, Response, TeePubKey};
911
use rand::{rngs::OsRng, Rng};
1012
use rsa::{BigUint, Pkcs1v15Encrypt, RsaPublicKey};
11-
use serde_json::json;
1213

1314
const RSA_ALGORITHM: &str = "RSA1_5";
1415
const AES_GCM_256_ALGORITHM: &str = "A256GCM";
1516

16-
pub fn jwe(tee_pub_key: TeePubKey, payload_data: Vec<u8>) -> Result<Response> {
17+
pub fn jwe(tee_pub_key: TeePubKey, mut payload_data: Vec<u8>) -> Result<Response> {
1718
let TeePubKey::RSA { alg, k_mod, k_exp } = tee_pub_key else {
1819
bail!("Only RSA key is support for TEE pub key")
1920
};
@@ -25,11 +26,19 @@ pub fn jwe(tee_pub_key: TeePubKey, payload_data: Vec<u8>) -> Result<Response> {
2526
let mut rng = rand::thread_rng();
2627

2728
let aes_sym_key = Aes256Gcm::generate_key(&mut OsRng);
28-
let cipher = Aes256Gcm::new(&aes_sym_key);
29+
let mut cipher = Aes256Gcm::new(&aes_sym_key);
2930
let iv = rng.gen::<[u8; 12]>();
3031
let nonce = Nonce::from_slice(&iv);
31-
let encrypted_payload_data = cipher
32-
.encrypt(nonce, payload_data.as_slice())
32+
let protected = ProtectedHeader {
33+
alg: RSA_ALGORITHM.to_string(),
34+
enc: AES_GCM_256_ALGORITHM.to_string(),
35+
other_fields: BTreeMap::new(),
36+
};
37+
38+
let aad = protected.generate_aad().context("Generate JWE AAD")?;
39+
40+
let tag = cipher
41+
.encrypt_in_place_detached(nonce, &aad, &mut payload_data)
3342
.map_err(|e| anyhow!("AES encrypt Resource payload failed: {e}"))?;
3443

3544
let k_mod = URL_SAFE_NO_PAD
@@ -44,22 +53,16 @@ pub fn jwe(tee_pub_key: TeePubKey, payload_data: Vec<u8>) -> Result<Response> {
4453
let rsa_pub_key =
4554
RsaPublicKey::new(n, e).context("Building RSA key from modulus and exponent failed")?;
4655
let sym_key: &[u8] = aes_sym_key.as_slice();
47-
let wrapped_sym_key = rsa_pub_key
56+
let encrypted_key = rsa_pub_key
4857
.encrypt(&mut rng, Pkcs1v15Encrypt, sym_key)
4958
.context("RSA encrypt sym key failed")?;
5059

51-
let protected_header = json!(
52-
{
53-
"alg": RSA_ALGORITHM.to_string(),
54-
"enc": AES_GCM_256_ALGORITHM.to_string(),
55-
});
56-
5760
Ok(Response {
58-
protected: serde_json::to_string(&protected_header)
59-
.context("serde protected_header failed")?,
60-
encrypted_key: URL_SAFE_NO_PAD.encode(wrapped_sym_key),
61-
iv: URL_SAFE_NO_PAD.encode(iv),
62-
ciphertext: URL_SAFE_NO_PAD.encode(encrypted_payload_data),
63-
tag: "".to_string(),
61+
protected,
62+
encrypted_key,
63+
iv: iv.into(),
64+
ciphertext: payload_data,
65+
aad: Some(aad),
66+
tag: tag.to_vec(),
6467
})
6568
}

0 commit comments

Comments
 (0)