aproxy: decouple KBS backend and shift runtime_data hashing from kernel#1122
aproxy: decouple KBS backend and shift runtime_data hashing from kernel#1122armenon-rh wants to merge 2 commits into
Conversation
luigix25
left a comment
There was a problem hiding this comment.
I did a quick review.
Don't forget to run make clippy cargo fmt and make test before submitting :)
| #[serde(rename = "attestation_report")] | ||
| snp_report: sev::firmware::guest::AttestationReport, | ||
| #[serde(rename = "cert_chain")] | ||
| certs_buf: Option<Vec<u8>>, |
There was a problem hiding this comment.
is this parameter used? I see it is set to None
There was a problem hiding this comment.
Our implementation checks against the KBS verifier SnpEvidence
I recall I was getting this error when these fields were not in place.
Indeed its set to None and sent by the aproxy, so maybe the KBS verifier fetches the certificate automatically. I am not entirely sure.
Currently the attestation flow only works against the mock attestation
server. Against the production-ready Trustee KBS (CoCo), attestation
fails immediately because the cryptographic hashes do not match.
The RCAR (Request-Challenge-Attestation-Response) handshake forms the
basis of secret retrieval from KBS. This has 2 parts, negotiation and
attestation. Negotiation phase typically only allows the client to get a
nonce from the KBS, and this nonce will be used in the attestation
request in the subsequent step. For SVSM, the client is SVSM + aproxy.
The current negotiation request:
SVSM sends only version and tee to aproxy. SVSM public key is not sent.
The current negotiation response:
aproxy fetches the challenge nonce from KBS and send it back to SVSM.
The current hashing in svsm:
SVSM does the hashing locally in the kernel by concatenating the raw
bytes of the nonce and the pub_key coordinates, hashing them with
SHA-512.
Report data = sha512(nonce + pub_key_x + pub_key_y).
This is signed by the AMD processor.
The real Trustee KBS does not expect a raw concatenation of bytes hashed
with SHA-512. Instead, the KBS verifier expects the runtime data to be a
JWS-formatted JSON object containing specific parameters, hashed with
SHA-384, see below.
{
"nonce": kbs-nonce,
"additional-evidence": "",
"tee-pubkey": {
"kty": "EC",
"crv": "P-521",
"x": "..."
"y": "..."
}
}
Although the hashing and serializing can be done in SVSM, it is not wise
to add/implement the JSON serializer in the kernel. We would also
require a base64url encoder. Besides, if SVSM constructs the
KBS-specific JSON, SVSM becomes tightly coupled to the specific API
implementation of a single KBS (Trustee).
For those reasons, the job of serialising and hashing should be done by
the aproxy. Because the baremetal SVSM kernel cannot do complex JSON
formatting, serialization, JWS mapping and base64 encoding, SVSM could
not compute this JSON-based hash. As a result the hash SVSM signed in the report
never matches the hash that KBS expected, and the attestation failed.
This commit allows aproxy to have the public key of svsm ready to use
during the negotiation phase itself. By sending SVSM's public key during
negotiation, aproxy can compute the SHA-384 of the constructed JSON first,
and pass it back to SVSM. SVSM can pass that hash to the AMD CPU to be
hardware-signed.
SVSM APROXY Trustee KBS
| | |
| | |
| 1. Negotiation | |
+---------------->| |
| Request | |
|(tee, ver, pk) |2. GET/auth |
| +--------------->|
| | 3. Returns |
| |<---------------+
| | challenge nonce|
| | |
| | |
| | |
| |4. Precomputes |
| | KBS hashing. |
| |a. Constructs runtime_data JSON
| | (with nonce + SVSM key)
| |b. Computes sha-384 of this JSON
| | This is called challenge hash
| | |
| | |
| | |
| 5. Negotiation | |
|<----------------+ |
| Response | |
| | |
| | |
| | |
|6. Requests Hardware Report |
| Passes Challenge hash as |
| REPORT_DATA. AMD CPU signs |
| Challenge Hash| |
| | |
| | |
| | |
| | |
| 7. Attestation | |
+---------------->| |
| Request | |
| Sends TEE report| |
| + SVSM pubkey | |
| | 8. POST/attest |
| +--------------->|
| | -Sends TEE rep |
| | -Sends runtime_|
| | data JSON with|
| | (nonce + pubkey)
| | |
| | |
| | | 9.Verification at KBS
| | | a. Compute sha384 of runtime
| | | data (covers nonce+pubkey)
| | | b. Reads REPORT_DATA from TEE
| | | Report. Contains (challenge Hash)
| | | c. Compares both. Attestation
| | | accepted. Issue attestation token.
v v v
A subsequent commit will move the JSON construction and hashing
responsibility out of the kernel, and put it into the aproxy.
Signed-off-by: Arun Menon <armenon@redhat.com>
Running SVSM against a trustee KBS service to verify the attestation report and get the attestation token back fails with the error "Deserialize SNP Evidence failed" [1] On further inspection, here are a few problems identified with the current attestation flow: 1. The trustee KBS service depends on the virtee sev crate AttestationReport structure [2] and it does not match with the structure of AttestationReport that SVSM sends to aproxy. As a result the deserialization fails. 2. The additional_evidence is not passed in the report at all. 3. Runtime data is hashed using sha512 whereas KBS service expects to use Sha384 hash function. 4. nonce should be stored from the negotiation response, so that it can be sent back in the attestation request. In this commit, we add the official 'sev' crate (from virtee, with snp and serde features enabled) to aproxy's dependencies, and implement a decoupled KBS backend in tools/aproxy. [3] The aproxy KBS backend parses the raw report bytes received from SVSM using sev::firmware::guest::AttestationReport. This completely decouples the SVSM kernel from KBS serialization format requirements. The standard parser utilized by the KBS evidence verifier is used in the aproxy, resolving previous deserialization failures. aproxy backend now constructs the complete, KBS-compliant runtime_data JSON object (which contains the nonce and the tee-pubkey formatted with JWS parameters like alg: 'ECDH-ES+A256KW' and crv: 'P-521'). aproxy serializes this entire JSON object, computes its SHA-384 hash, and returns this combined hash to SVSM as the NegotiationResponse challenge. SVSM then simply hashes this single challenge into its SEV-SNP report. Update the hash function in kernel/src/attest.rs to directly copy the 48 byte challenge into a zero padded 64 byte array. Hashing is actually performed in the aproxy and the hash function used is Sha384 Casting SNP report bytes into sev crate's AttestationReport structure via read_unaligned causes undefined behaviour due to optimized Option enums. Replace unsafe cast with sev::parser::Decoder to safely decode the report byte-by-byte into the correct memory layout. Adding resource_id: Option<String> to KbsProtocol prevents the Protocol enum from implementing Copy. Therefore pattern matching on self.protocol directly inside HttpClient method now borrows self, and that conflicts with subsequently passing self as mutable reference to the backend method calls like negotiation and attestation. Use idiomatic mem::replace pattern to avoid this. Since sev crate introduces std, make clippy --all-features fails. Invoking clippy with CARGO_HACK=1 succeeds. make clippy CARGO_HACK=1 [1] https://github.com/confidential-containers/trustee/blob/main/deps/verifier/src/snp/mod.rs#L351 [2] https://github.com/confidential-containers/trustee/blob/main/deps/verifier/Cargo.toml#L96 [3] https://docs.rs/crate/sev/8.0.0/source/src/firmware/guest/types/snp.rs#255 Signed-off-by: Arun Menon <armenon@redhat.com>
a39bf21 to
bc5b94e
Compare
|
@armenon-rh please resolve all the conversation that no longer applies. I run not sure why, i didn't investigate, but reverting your changes it passes |
| Note: Make sure to add the option --pcd PcdUninstallMemAttrProtocol=TRUE while building edk2 for Fedora 42 images | ||
|
|
||
| ```bash | ||
| export PYTHON3_ENABLE=TRUE | ||
| export PYTHON_COMMAND=python3 | ||
| make -j16 -C BaseTools/ | ||
| source ./edksetup.sh --reconfig | ||
| build -p OvmfPkg/OvmfPkgX64.dsc -a X64 \ | ||
| -b DEBUG -t GCC \ | ||
| -D DEBUG_ON_SERIAL_PORT \ | ||
| -D DEBUG_VERBOSE \ | ||
| -D TPM2_ENABLE | ||
| --pcd PcdUninstallMemAttrProtocol=TRUE | ||
| ``` No newline at end of file |
There was a problem hiding this comment.
we already have a section where we talk about building edk2 in the file installation/INSTALL.md.
Can we just link it here somehow? If building instruction change, we'd need to two update two things
| "EcPublicKeyBytes", | ||
| "Challenge" | ||
| ] | ||
|
|
There was a problem hiding this comment.
nit: i think this blank like can be dropped
| /// Take 48 byte negotiation challenge nonce from aproxy into 64 byte array required for the TEE attestation evidence report | ||
| fn prepare_report_data(n: &NegotiationResponse) -> Result<Vec<u8>, AttestationError> { | ||
| let mut report_data = [0u8; 64]; | ||
| report_data[..48].copy_from_slice(&n.challenge); |
There was a problem hiding this comment.
should we check for n.challenge size?
There was a problem hiding this comment.
yes. I shall add the check in v3.
| init_data: None, | ||
| runtime_data: RuntimeData { | ||
| nonce: BASE64_STANDARD.encode(request.challenge), | ||
| nonce: self.original_nonce.take().unwrap_or_default(), |
There was a problem hiding this comment.
should this be treated as an error? IIUC nonce is mandatory, right? I'm not an expert
There was a problem hiding this comment.
yes, it should be treated as an error. I will change it in v3
Hi Luigi, |
This PR fixes the SEV-SNP attestation flow against a trustee KBS server.
Verified that the attestation token is successfully issued by the trustee KBS instance, see kbs container logs below