From 54e221867a12e30e703c79639c76fd251f83c78f Mon Sep 17 00:00:00 2001 From: Tyler Fanelli Date: Sun, 15 Dec 2024 01:29:39 -0500 Subject: [PATCH] attest: Decrypt secret from proxy Use the TEE key to decrypt the secret received from the proxy. Co-developed-by: Stefano Garzarella Signed-off-by: Tyler Fanelli --- Cargo.lock | 30 +++++++++++++++++++ Cargo.toml | 1 + kernel/Cargo.toml | 3 +- kernel/src/attest.rs | 69 +++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 98 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d5245a6b4..920df82b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -448,6 +448,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", + "pem-rfc7468", "zeroize", ] @@ -478,6 +479,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", "subtle", ] @@ -509,7 +511,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", + "digest", "elliptic-curve", + "rfc6979", "signature", "spki", ] @@ -535,6 +539,7 @@ dependencies = [ "generic-array", "group", "hkdf", + "pem-rfc7468", "pkcs8", "rand_core", "sec1", @@ -1426,6 +1431,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", + "sha2", ] [[package]] @@ -1443,6 +1449,15 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1571,6 +1586,9 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] [[package]] name = "range_map_vec" @@ -1633,6 +1651,16 @@ dependencies = [ "windows-registry", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + [[package]] name = "ring" version = "0.17.8" @@ -1857,6 +1885,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ + "digest", "rand_core", ] @@ -1947,6 +1976,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" name = "svsm" version = "0.1.0" dependencies = [ + "aes", "aes-gcm", "base64", "bitfield-struct 0.6.2", diff --git a/Cargo.toml b/Cargo.toml index 4c7bf56a8..9b1720746 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ userlib = { path = "user/lib" } userinit = { path = "user/init" } # crates.io +aes = "0.8.4" aes-gcm = { version = "0.10.3", default-features = false } arbitrary = "1.3.0" base64 = { version = "0.22.1", default-features = false, features = ["alloc"] } diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 001436f50..ef8aa366a 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -25,6 +25,7 @@ libaproxy = { workspace = true, optional = true } elf.workspace = true syscall.workspace = true +aes = { workspace = true, optional = true } aes-gcm = { workspace = true, features = ["aes", "alloc"] } base64 = { workspace = true, optional = true } bitfield-struct.workspace = true @@ -55,7 +56,7 @@ verify_external = { workspace = true, optional = true} test.workspace = true [features] -attest = ["dep:base64", "dep:kbs-types", "dep:libaproxy", "dep:p384", +attest = ["dep:aes", "dep:base64", "dep:kbs-types", "dep:libaproxy", "dep:p384", "dep:rand_chacha", "dep:rdrand", "dep:serde", "dep:serde_json", "dep:sha2"] default = [] diff --git a/kernel/src/attest.rs b/kernel/src/attest.rs index 6a6dcb94f..054f4e2d3 100644 --- a/kernel/src/attest.rs +++ b/kernel/src/attest.rs @@ -16,16 +16,18 @@ use crate::{ io::{Read, Write, DEFAULT_IO_DRIVER}, serial::SerialPort, }; +use aes::{cipher::BlockDecrypt, Aes128}; +use aes_gcm::KeyInit; use alloc::{string::ToString, vec, vec::Vec}; use base64::prelude::*; -use core::fmt; +use core::{cmp::min, fmt}; use kbs_types::Tee; use libaproxy::*; -use p384::{ecdh, NistP384}; +use p384::{ecdh, NistP384, PublicKey}; use rand_chacha::{rand_core::SeedableRng, ChaChaRng}; use rdrand::RdSeed; use serde::Serialize; -use sha2::{Digest, Sha384, Sha512}; +use sha2::{Digest, Sha256, Sha384, Sha512}; use zerocopy::{FromBytes, IntoBytes}; /// The attestation driver that communicates with the proxy via some communication channel (serial @@ -95,7 +97,15 @@ impl AttestationDriver<'_> { self.write(request)?; - todo!(); + let payload = self.read()?; + let response: AttestationResponse = + serde_json::from_slice(&payload).or(Err(AttestationError::AttestationDeserialize))?; + + if !response.success { + return Err(AttestationError::Failed); + } + + self.secret_decrypt(response, &key) } /// Read attestation data from the serial port. @@ -233,6 +243,53 @@ impl AttestationDriver<'_> { Ok(sha.finalize().to_vec()) } + + /// Decrypt a secret from the attestation server with the TEE private key. + fn secret_decrypt( + &self, + resp: AttestationResponse, + key: &TeeKey, + ) -> Result, AttestationError> { + let secret = resp.secret.ok_or(AttestationError::SecretMissing)?; + + match key { + TeeKey::Ecdh384Sha256Aes128(ec) => { + // Get the shared ECDH secret between the client/server EC keys. + let shared = { + let s = resp.pub_key.ok_or(AttestationError::SecretDecrypt)?; + let pub_key = + PublicKey::from_sec1_bytes(&s).or(Err(AttestationError::SecretDecrypt))?; + + ec.diffie_hellman(&pub_key) + }; + + // Extract the HKDF bytes and use to build an AES-128 symmetric key. + let mut sha_bytes = [0u8; 16]; + let empty: [u8; 0] = []; + + let hkdf = shared.extract::(None); + hkdf.expand(&empty, &mut sha_bytes) + .or(Err(AttestationError::SecretDecrypt))?; + let aes = + Aes128::new_from_slice(&sha_bytes).or(Err(AttestationError::SecretDecrypt))?; + + // Decrypt each 16-byte block of the ciphertext with the symmetric key. + let mut ptr = 0; + let len = secret.len(); + let mut vec: Vec = Vec::new(); + while ptr < len { + let remain = min(16, len - ptr); + let mut arr: [u8; 16] = [0u8; 16]; + arr[..remain].copy_from_slice(&secret[ptr..ptr + remain]); + aes.decrypt_block((&mut arr).into()); + vec.append(&mut arr.to_vec()); + ptr += remain; + } + + Ok(vec) + } + } + } } /// TEE key used to decrypt secrets sent from the attestation server. @@ -305,6 +362,10 @@ pub enum AttestationError { ProxyRead, /// Error writing over the attestation proxy transport channel. ProxyWrite, + /// Attestation successful, but unable to decrypt secret. + SecretDecrypt, + /// Attestation successful, but no secret found. + SecretMissing, /// Error fetching the SEV-SNP attestation report. SnpGetReport, /// Error encoding the TEE public key to JSON.