Skip to content

Commit 541b7b9

Browse files
committed
verify: sev: validate ASK against revocation list
Make use of the CRL to verify that ASK has not been revoked. The CRL is an X509 revocation list signed by the ARK. Signed-off-by: Carlos López <clopez@suse.de>
1 parent 6872c66 commit 541b7b9

2 files changed

Lines changed: 78 additions & 2 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,9 @@ certificate(s) are as follows:
5858
ark.{der, pem}
5959
ask.{der, pem}
6060
vcek.{der, pem}
61+
clr.{der, pem} (optional)
6162
```
62-
The `fetch-vcek` subcommand can be used to fetch these certificates from AMD's Key Distribution Service (KDS).
63+
The `fetch-vcek` and `fetch-clr` subcommands can be used to fetch these certificates from AMD's Key Distribution Service (KDS).
6364
- Intel TDX: Provisioning Certification Key (PCK), PCK Issuer Chain\*.
6465
```
6566
{certs-dir} \

src/sev.rs

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
use crate::VerifyOptions;
44
use anyhow::{Context, Result};
55
use asn1_rs::{Oid, oid};
6-
use openssl::{ecdsa::EcdsaSig, sha::Sha384};
6+
use openssl::{
7+
ecdsa::EcdsaSig,
8+
sha::Sha384,
9+
x509::{X509, X509Crl},
10+
};
711
use sev::certs::snp::{Certificate, Verifiable};
812
use sev::firmware::guest::AttestationReport;
913
use sev::parser::ByteParser;
@@ -71,6 +75,8 @@ pub fn verify_report(
7175
println!("VCEK was signed by ASK");
7276
}
7377

78+
verify_revocation_list(certs_dir, &ark, &ask, &vcek, opts)?;
79+
7480
let vcek_pubkey = vcek
7581
.public_key()
7682
.context("Failed to get public key from VCEK")?
@@ -119,6 +125,75 @@ fn find_cert_in_dir(dir: &Path, name: &str) -> Result<PathBuf> {
119125
))
120126
}
121127

128+
fn load_crl(path: &Path) -> Result<X509Crl> {
129+
let data = fs::read(path).context(format!("Failed to read certificate {}", path.display()))?;
130+
let is_pem = path.extension().map(|ext| ext == "pem").unwrap_or(false);
131+
if is_pem {
132+
X509Crl::from_pem(&data).context("Failed to parse PEM certificate")
133+
} else {
134+
X509Crl::from_der(&data).context("Failed to parse DER certificate")
135+
}
136+
}
137+
138+
/// Checks that the ASK and VCEK have not been revoked by consulting the CRL
139+
fn verify_revocation_list(
140+
certs_dir: &Path,
141+
ark: &Certificate,
142+
ask: &Certificate,
143+
vcek: &Certificate,
144+
opts: &VerifyOptions,
145+
) -> Result<()> {
146+
let Ok(path) = find_cert_in_dir(certs_dir, "crl") else {
147+
eprintln!(
148+
"Warning: no CRL found in {}; ASK revocation status not checked",
149+
certs_dir.display()
150+
);
151+
return Ok(());
152+
};
153+
154+
let crl = load_crl(&path)?;
155+
156+
let ark_pubkey = ark.public_key().context("Failed to get ARK public key")?;
157+
if !crl
158+
.verify(&ark_pubkey)
159+
.context("Failed to verify CRL signature")?
160+
{
161+
return Err(anyhow::anyhow!("CRL is not signed by ARK"));
162+
}
163+
164+
let ask_serial = X509::from(ask)
165+
.serial_number()
166+
.to_bn()
167+
.context("Failed to read ASK serial number")?;
168+
let vcek_serial = X509::from(vcek)
169+
.serial_number()
170+
.to_bn()
171+
.context("Failed to read VCEK serial number")?;
172+
173+
// AMD uses TCB versioning rather than CRL entries to supersede old VCEKs, so
174+
// VCEK revocation is not expected in practice, but check anyway for completeness.
175+
if let Some(revoked) = crl.get_revoked() {
176+
for entry in revoked {
177+
let entry_serial = entry
178+
.serial_number()
179+
.to_bn()
180+
.context("Failed to read revoked entry serial number")?;
181+
if entry_serial == ask_serial {
182+
return Err(anyhow::anyhow!("ASK certificate has been revoked"));
183+
}
184+
if entry_serial == vcek_serial {
185+
return Err(anyhow::anyhow!("VCEK certificate has been revoked"));
186+
}
187+
}
188+
}
189+
190+
if !opts.quiet {
191+
println!("ASK and VCEK are not revoked");
192+
}
193+
194+
Ok(())
195+
}
196+
122197
fn load_certificate(path: &Path) -> Result<Certificate> {
123198
let data = fs::read(path).context(format!("Failed to read certificate {}", path.display()))?;
124199

0 commit comments

Comments
 (0)