Skip to content

Commit 6872c66

Browse files
committed
cli: allow fetching AMD's Certificate Revocation List (CRL)
AMD publishes a Certificate Revocation List (CRL) via their KDS. The CRL is signed by the ARK, and can be used to revoke ASK certificates. Add a `fetch-crl` command to fetch the revocation list. A future commit will add support to use the CRL to verify that an ASK has not been revoked. Signed-off-by: Carlos López <clopez@suse.de>
1 parent 891c7eb commit 6872c66

3 files changed

Lines changed: 77 additions & 0 deletions

File tree

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,15 @@ cvmtool fetch-vcek --certs-dir certs/
8282
cvmtool fetch-pck --certs-dir certs/
8383
```
8484

85+
### Fetch Revocation List (SEV-SNP only, optional)
86+
87+
To fetch the certificate revocation list for verification, use the `fetch-clr` command from within the CVM:
88+
89+
```bash
90+
# Fetch CLR from AMD KDS
91+
cvmtool fetch-clr --certs-dir certs/
92+
```
93+
8594
## Azure CVM Support
8695

8796
When built with the `azure` feature, cvmtool can retrieve attestation reports from Azure Confidential VMs via the vTPM (Virtual TPM). This works on Azure CVMs using either SNP or TDX isolation.

src/main.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,12 @@ enum Commands {
114114
#[arg(short, long, default_value = ".")]
115115
certs_dir: PathBuf,
116116
},
117+
/// Fetch VCEK Certificate Revocation List from AMD KDS (for SEV-SNP)
118+
FetchCrl {
119+
/// Output directory for the CRL
120+
#[arg(short, long, default_value = ".")]
121+
certs_dir: PathBuf,
122+
},
117123
}
118124

119125
/// Options for attestation verification beyond cryptographic checks
@@ -291,6 +297,32 @@ fn main() -> Result<()> {
291297
println!("Saved certificates to {:?}", certs_dir);
292298
}
293299
}
300+
Commands::FetchCrl { certs_dir } => {
301+
if cli.verbose {
302+
println!("Generating SEV attestation report...");
303+
}
304+
305+
let report = report(Some("sev"), [0; 64])?;
306+
let report = sev::parse_report(&report)?;
307+
308+
let processor = vcek::get_processor_model(&report)?;
309+
if cli.verbose {
310+
println!("Detected processor: {processor}");
311+
}
312+
313+
if cli.verbose {
314+
println!("Fetching CRL from AMD KDS...");
315+
}
316+
let crl_der = vcek::fetch_crl(&processor)?;
317+
if cli.verbose {
318+
println!("CRL retrieved ({} bytes)", crl_der.len());
319+
}
320+
321+
vcek::save_crl(&crl_der, &certs_dir, cli.verbose)?;
322+
if !cli.quiet {
323+
println!("Saved CRL to {:?}", certs_dir);
324+
}
325+
}
294326
}
295327

296328
Ok(())

src/vcek.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,3 +237,39 @@ pub fn save_certificates(
237237

238238
Ok(())
239239
}
240+
241+
pub fn fetch_crl(processor: &ProcType) -> Result<Vec<u8>> {
242+
let url = format!("{}/vcek/v1/{}/crl", KDS_BASE_URL, processor.to_kds_url());
243+
244+
let client = reqwest::blocking::Client::new();
245+
let response = client
246+
.get(&url)
247+
.send()
248+
.context("Failed to send request to AMD KDS for CRL")?;
249+
250+
response
251+
.error_for_status_ref()
252+
.context("AMD KDS returned error for CRL request")?;
253+
254+
Ok(response
255+
.bytes()
256+
.context("Failed to read CRL from response")?
257+
.to_vec())
258+
}
259+
260+
pub fn save_crl(crl_der: &[u8], output_dir: &Path, verbose: bool) -> Result<()> {
261+
fs::create_dir_all(output_dir).context(format!(
262+
"Failed to create output directory {}",
263+
output_dir.display()
264+
))?;
265+
266+
let crl = openssl::x509::X509Crl::from_der(crl_der).context("Failed to parse CRL DER")?;
267+
let pem = crl.to_pem().context("Failed to convert CRL to PEM")?;
268+
let path = output_dir.join("crl.pem");
269+
fs::write(&path, &pem).context(format!("Failed to write CRL to {}", path.display()))?;
270+
if verbose {
271+
println!("Saved CRL to {}", path.display());
272+
}
273+
274+
Ok(())
275+
}

0 commit comments

Comments
 (0)