Skip to content

Commit 81e4eae

Browse files
committed
fix: unify RA-TLS cert attestation format and fix onboard os_image_hash
Two changes: 1. ra-tls: use unified PHALA_RATLS_ATTESTATION OID for TDX certs instead of the legacy separate TDX_QUOTE + EVENT_LOG OIDs. The new format preserves vm_config (including os_image_hash). The reader already prefers the new format and falls back to old OIDs for backward compat. 2. kms: when the remote source KMS uses the old cert format (missing vm_config), the receiver-side onboard check fills os_image_hash from the local KMS's own value. This is safe because mrAggregated already validates OS image integrity through the RTMR measurement chain. This workaround should be removed once all source KMS instances use the new cert format.
1 parent de92515 commit 81e4eae

File tree

3 files changed

+23
-28
lines changed

3 files changed

+23
-28
lines changed

kms/src/main_service/upgrade_authority.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,19 @@ pub(crate) async fn ensure_kms_allowed(
224224
cfg: &KmsConfig,
225225
attestation: &VerifiedAttestation,
226226
) -> Result<()> {
227-
let boot_info = build_boot_info(attestation, false, "")
227+
let mut boot_info = build_boot_info(attestation, false, "")
228228
.context("failed to build KMS boot info from attestation")?;
229+
// Workaround: old source KMS instances use the legacy cert format (separate TDX_QUOTE +
230+
// EVENT_LOG OIDs) which lacks vm_config, resulting in an empty os_image_hash.
231+
// Fill it from the local KMS's own value. This is safe because mrAggregated already
232+
// validates OS image integrity transitively through the RTMR measurement chain.
233+
// TODO: remove once all source KMS instances use the unified PHALA_RATLS_ATTESTATION format.
234+
if boot_info.os_image_hash.is_empty() {
235+
let local_info = local_kms_boot_info(cfg.pccs_url.as_deref())
236+
.await
237+
.context("failed to get local KMS boot info for os_image_hash fallback")?;
238+
boot_info.os_image_hash = local_info.os_image_hash;
239+
}
229240
let response = cfg
230241
.auth_api
231242
.is_app_allowed(&boot_info, true)

ra-tls/src/cert.rs

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,11 @@ use x509_parser::x509::SubjectPublicKeyInfo;
2525

2626
use crate::oids::{
2727
PHALA_RATLS_APP_ID, PHALA_RATLS_APP_INFO, PHALA_RATLS_ATTESTATION, PHALA_RATLS_CERT_USAGE,
28-
PHALA_RATLS_EVENT_LOG, PHALA_RATLS_TDX_QUOTE,
2928
};
3029
use crate::traits::CertExt;
3130
#[cfg(feature = "quote")]
3231
use dstack_attest::attestation::QuoteContentType;
33-
use dstack_attest::attestation::{AppInfo, Attestation, AttestationQuote, VersionedAttestation};
32+
use dstack_attest::attestation::{AppInfo, Attestation, VersionedAttestation};
3433

3534
/// A CA certificate and private key.
3635
pub struct CaCert {
@@ -389,21 +388,8 @@ impl<Key> CertRequest<'_, Key> {
389388
add_ext(&mut params, PHALA_RATLS_CERT_USAGE, usage);
390389
}
391390
if let Some(ver_att) = self.attestation {
392-
let VersionedAttestation::V0 { attestation } = &ver_att;
393-
match &attestation.quote {
394-
AttestationQuote::DstackTdx(tdx_quote) => {
395-
// For backward compatibility, we serialize the quote to the classic oids.
396-
let event_log = serde_json::to_vec(&tdx_quote.event_log)
397-
.context("Failed to serialize event log")?;
398-
add_ext(&mut params, PHALA_RATLS_TDX_QUOTE, &tdx_quote.quote);
399-
add_ext(&mut params, PHALA_RATLS_EVENT_LOG, &event_log);
400-
}
401-
_ => {
402-
// The event logs are too large on GCP TDX to put in the certificate, so we strip them
403-
let attestation_bytes = ver_att.clone().into_stripped().to_scale();
404-
add_ext(&mut params, PHALA_RATLS_ATTESTATION, &attestation_bytes);
405-
}
406-
}
391+
let attestation_bytes = ver_att.clone().into_stripped().to_scale();
392+
add_ext(&mut params, PHALA_RATLS_ATTESTATION, &attestation_bytes);
407393
}
408394
if let Some(ca_level) = self.ca_level {
409395
params.is_ca = IsCa::Ca(BasicConstraints::Constrained(ca_level));
@@ -576,7 +562,7 @@ pub fn generate_ra_cert_with_app_id(
576562
#[cfg(test)]
577563
mod tests {
578564
use super::*;
579-
use dstack_attest::attestation::TdxQuote;
565+
use dstack_attest::attestation::{AttestationQuote, TdxQuote};
580566
use rcgen::PKCS_ECDSA_P256_SHA256;
581567
use scale::Encode;
582568

tests/docs/kms-self-authorization.md

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ This guide is written as a deployment-and-test runbook so an AI agent can follow
2525
> 7. KMS now always requires quote/attestation. For local development without TDX hardware, use `sdk/simulator` instead of trying to run a no-attestation KMS flow.
2626
> 8. For `auth-simple`, `kms.mrAggregated = []` is a deny-all policy for KMS. Use that as the baseline deny configuration, then add the measured KMS MR values for allow cases.
2727
> 9. **Port forwarding is simpler than gateway for testing.** Using `--gateway` requires the auth API to return a valid `gatewayAppId`, which adds unnecessary complexity. Use `--port tcp:0.0.0.0:<host-port>:8000` instead.
28-
> 10. **Remote KMS attestation has an empty `osImageHash`.** When the receiver verifies the source KMS during onboard, the `osImageHash` field in the attestation is empty (because `vm_config` is not available for the remote attestation). Auth configs for receiver-side checks must include `"0x"` in the `osImages` array to match this empty hash.
28+
> 10. **~~Remote KMS attestation has an empty `osImageHash`.~~** Fixed: RA-TLS certs now use the unified `PHALA_RATLS_ATTESTATION` format which preserves `vm_config`. For old source KMS instances that still use the legacy cert format, the receiver-side `ensure_kms_allowed` automatically fills `osImageHash` from the local KMS's own value. No special `"0x"` entry in `osImages` is needed anymore.
2929
> 11. The `source_url` in the `Onboard.Onboard` request must use an address **reachable from inside the CVM** (e.g., `https://10.0.2.2:<port>/prpc`), not `127.0.0.1` which is the CVM's own loopback.
3030
3131
---
@@ -49,13 +49,13 @@ This guide is written as a deployment-and-test runbook so an AI agent can follow
4949

5050
## 1. Why this document exists
5151

52-
PR #538 already proposes a richer `kms/e2e/` framework, but as of **2026-03-19** it is still open/draft and touches overlapping KMS files. To avoid waiting for that PR, this guide uses:
52+
This guide provides a standalone test procedure that does not depend on a dedicated e2e framework. It uses:
5353

5454
- existing KMS deploy flows
5555
- `auth-simple` as a controllable auth API
5656
- manual RPC calls via `curl`
5757

58-
This keeps the test independent from PR #538 while still exercising real deployment paths.
58+
This exercises real deployment paths with minimal dependencies.
5959

6060
---
6161

@@ -98,7 +98,7 @@ Policy responsibilities:
9898

9999
Before starting, make sure the following are available:
100100

101-
1. A branch or image containing the PR #573 KMS changes
101+
1. A KMS image built from current `master` (includes PR #573 auth checks, #579 mandatory attestation, #581 dedup refactor)
102102
2. A working `dstack-vmm` or teepod deployment target
103103
3. Two routable KMS onboard URLs
104104
4. `bun` installed on the host, because `kms/auth-simple` runs on Bun
@@ -315,12 +315,10 @@ All three values above are expected to be hex strings **without** the `0x` prefi
315315

316316
Use a wrong `mrAggregated` value while allowing the observed OS image.
317317

318-
> **Important:** include `"0x"` in `osImages` to handle remote KMS attestation during onboard receiver-side checks, where `osImageHash` is empty because `vm_config` is unavailable for the remote attestation.
319-
320318
```bash
321319
cat > /tmp/kms-self-auth/deny-by-mr.json <<'EOF'
322320
{
323-
"osImages": ["0xREPLACE_OS", "0x"],
321+
"osImages": ["0xREPLACE_OS"],
324322
"gatewayAppId": "any",
325323
"kms": {
326324
"mrAggregated": ["0x0000000000000000000000000000000000000000000000000000000000000000"],
@@ -337,7 +335,7 @@ EOF
337335
```bash
338336
cat > /tmp/kms-self-auth/allow-single.json <<'EOF'
339337
{
340-
"osImages": ["0xREPLACE_OS", "0x"],
338+
"osImages": ["0xREPLACE_OS"],
341339
"gatewayAppId": "any",
342340
"kms": {
343341
"mrAggregated": ["0xREPLACE_MR"],
@@ -354,7 +352,7 @@ EOF
354352
```bash
355353
cat > /tmp/kms-self-auth/allow-src-and-dst.json <<'EOF'
356354
{
357-
"osImages": ["0xREPLACE_SRC_OS", "0xREPLACE_DST_OS", "0x"],
355+
"osImages": ["0xREPLACE_SRC_OS", "0xREPLACE_DST_OS"],
358356
"gatewayAppId": "any",
359357
"kms": {
360358
"mrAggregated": ["0xREPLACE_SRC_MR", "0xREPLACE_DST_MR"],

0 commit comments

Comments
 (0)