Skip to content

Commit b4c7e82

Browse files
committed
verifier/tdx: resolve the PCCS URL from the DCAP QCNL config
The pure-Rust backend read the PCCS endpoint only from the PCCS_URL environment variable, defaulting otherwise to a built-in URL. That diverges from the FFI backend, whose quote provider reads /etc/sgx_default_qcnl.conf, so on a host configured for FFI the two backends would talk to different PCCS servers unless PCCS_URL was also set. Fall back to the qcnl.conf 'pccs_url' when PCCS_URL is unset, so both backends share one configuration. Both the JSON ({"pccs_url": "..."}) and legacy INI (PCCS_URL=...) forms are supported; unit-tested. Signed-off-by: Jiale Zhang <zhangjiale@linux.alibaba.com>
1 parent 26c5283 commit b4c7e82

1 file changed

Lines changed: 97 additions & 15 deletions

File tree

deps/verifier/src/tdx/verify/native.rs

Lines changed: 97 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
1-
//! Pure-Rust TDX quote verification backend. Selected by the `tdx-dcap-rust`
2-
//! feature.
1+
//! TDX quote verification backend that needs no external DCAP shared library.
2+
//! Selected by the `tdx-dcap-rust` feature.
33
//!
44
//! This backend removes the dependency on the Intel DCAP shared library
55
//! (`libsgx_dcap_quoteverify`) and its dynamically loaded quote provider. The
66
//! ECDSA quote signature, PCK certificate chain, TCB info and QE identity are
7-
//! all verified in Rust via the [`dcap_qvl`] crate; verification collateral is
8-
//! fetched over HTTPS from a PCCS using the verifier's own `reqwest` stack (we
7+
//! all verified via the [`dcap_qvl`] crate; verification collateral is fetched
8+
//! over HTTPS from a PCCS using the verifier's own `reqwest` stack (we
99
//! deliberately do *not* enable `dcap-qvl`'s `report` feature, which would pull
1010
//! in `reqwest` 0.13 / `aws-lc-sys`).
1111
//!
12-
//! Toolchain: `dcap-qvl`'s dependency tree requires a newer Rust toolchain
13-
//! (>= 1.86) than the default (FFI) build. This does not affect the default
14-
//! build: the `tdx-dcap-rust` feature is opt-in, and with it disabled Cargo
15-
//! prunes this whole subtree.
12+
//! Crypto backend: `dcap-qvl` is used with its `ring` crypto backend. `ring`
13+
//! statically links its own crypto (no external shared library), and — unlike
14+
//! the RustCrypto stack — builds on Rust 1.76, so this backend has the same
15+
//! toolchain requirement as the default (FFI) build. With the feature disabled,
16+
//! Cargo prunes this whole subtree, so it has no effect on the default build.
17+
//!
18+
//! PCCS configuration: the collateral endpoint is resolved from, in order, the
19+
//! `PCCS_URL` environment variable, the `pccs_url` / `PCCS_URL` entry in
20+
//! `/etc/sgx_default_qcnl.conf` (the same file the DCAP quote provider reads, so
21+
//! both backends share one configuration), and finally a built-in default.
1622
//!
1723
//! Scope: the collateral fetch currently supports quotes whose certification
1824
//! data embeds the PCK certificate chain (PCK cert type 5), which is what cloud
@@ -36,11 +42,14 @@ use x509_parser::prelude::*;
3642

3743
use crate::tdx::quote::{parse_tdx_quote, TcbVerificationResult};
3844

39-
/// Default PCCS base URL. Matches the Alibaba Cloud PCCS the FFI backend's
40-
/// quote provider is normally configured against (`/etc/sgx_default_qcnl.conf`).
41-
/// Overridable at run time with the `PCCS_URL` environment variable.
45+
/// Default PCCS base URL, used only when neither `PCCS_URL` nor the QCNL config
46+
/// file provide one.
4247
const DEFAULT_PCCS_URL: &str = "https://sgx-dcap-server.cn-beijing.aliyuncs.com";
4348

49+
/// QCNL config file read by the DCAP quote provider (FFI backend). We fall back
50+
/// to its `pccs_url` so both backends can share one PCCS configuration.
51+
const QCNL_CONF_PATH: &str = "/etc/sgx_default_qcnl.conf";
52+
4453
/// TEE type for TDX, matching the value the FFI backend reports in
4554
/// `TcbVerificationResult::tee_type`.
4655
const TEE_TYPE_TDX: u32 = 0x0000_0081;
@@ -51,11 +60,56 @@ const OID_SGX_TCB: &[u64] = &[1, 2, 840, 113741, 1, 13, 1, 2];
5160
const OID_SGX_PCESVN: &[u64] = &[1, 2, 840, 113741, 1, 13, 1, 2, 17];
5261
const OID_SGX_FMSPC: &[u64] = &[1, 2, 840, 113741, 1, 13, 1, 4];
5362

54-
pub async fn ecdsa_quote_verification(quote: &[u8]) -> Result<TcbVerificationResult> {
55-
let pccs_url = std::env::var("PCCS_URL")
63+
/// Resolve the PCCS base URL: `PCCS_URL` env var, then the QCNL config file,
64+
/// then the built-in default.
65+
fn resolve_pccs_url() -> String {
66+
if let Ok(v) = std::env::var("PCCS_URL") {
67+
if !v.is_empty() {
68+
return v;
69+
}
70+
}
71+
if let Some(v) = std::fs::read_to_string(QCNL_CONF_PATH)
5672
.ok()
57-
.filter(|s| !s.is_empty())
58-
.unwrap_or_else(|| DEFAULT_PCCS_URL.to_string());
73+
.and_then(|c| parse_qcnl_pccs_url(&c))
74+
{
75+
debug!("dcap-qvl backend: using PCCS URL from {QCNL_CONF_PATH}");
76+
return v;
77+
}
78+
DEFAULT_PCCS_URL.to_string()
79+
}
80+
81+
/// Extract the PCCS URL from QCNL config file contents, supporting both the JSON
82+
/// form (`{"pccs_url": "https://..."}`, optionally with `//` comments) and the
83+
/// legacy INI form (`PCCS_URL=https://...`).
84+
fn parse_qcnl_pccs_url(content: &str) -> Option<String> {
85+
for raw in content.lines() {
86+
let line = raw.trim();
87+
if line.starts_with('#') || line.starts_with("//") {
88+
continue;
89+
}
90+
let Some(key_pos) = line.to_ascii_lowercase().find("pccs_url") else {
91+
continue;
92+
};
93+
// After the key (INI `=` or JSON `":`), take the URL up to the next
94+
// quote / comma / whitespace. This covers both config styles.
95+
let after = &line[key_pos + "pccs_url".len()..];
96+
let Some(http_pos) = after.find("http") else {
97+
continue;
98+
};
99+
let url = &after[http_pos..];
100+
let end = url
101+
.find(|c: char| c == '"' || c == ',' || c.is_whitespace())
102+
.unwrap_or(url.len());
103+
let url = &url[..end];
104+
if !url.is_empty() {
105+
return Some(url.to_string());
106+
}
107+
}
108+
None
109+
}
110+
111+
pub async fn ecdsa_quote_verification(quote: &[u8]) -> Result<TcbVerificationResult> {
112+
let pccs_url = resolve_pccs_url();
59113

60114
// The PCK certificate chain is embedded in the quote's certification data
61115
// (PCK cert type 5). Extract it and derive FMSPC / CA type for collateral.
@@ -548,3 +602,31 @@ fn find_sub(haystack: &[u8], needle: &[u8]) -> Option<usize> {
548602
fn rfind_sub(haystack: &[u8], needle: &[u8]) -> Option<usize> {
549603
haystack.windows(needle.len()).rposition(|w| w == needle)
550604
}
605+
606+
#[cfg(test)]
607+
mod tests {
608+
use super::parse_qcnl_pccs_url;
609+
610+
#[test]
611+
fn parse_qcnl_ini_form() {
612+
let conf = "# comment\nPCCS_URL=https://sgx-dcap-server.cn-hangzhou.aliyuncs.com/sgx/certification/v4/\nUSE_SECURE_CERT=false\n";
613+
assert_eq!(
614+
parse_qcnl_pccs_url(conf).as_deref(),
615+
Some("https://sgx-dcap-server.cn-hangzhou.aliyuncs.com/sgx/certification/v4/")
616+
);
617+
}
618+
619+
#[test]
620+
fn parse_qcnl_json_form() {
621+
let conf = "{\n // Intel default style\n \"pccs_url\": \"https://pccs.example.com/sgx/certification/v4/\",\n \"use_secure_cert\": true\n}\n";
622+
assert_eq!(
623+
parse_qcnl_pccs_url(conf).as_deref(),
624+
Some("https://pccs.example.com/sgx/certification/v4/")
625+
);
626+
}
627+
628+
#[test]
629+
fn parse_qcnl_missing() {
630+
assert_eq!(parse_qcnl_pccs_url("USE_SECURE_CERT=false\n"), None);
631+
}
632+
}

0 commit comments

Comments
 (0)