diff --git a/README.md b/README.md index 5ed0138..9b94f0f 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,35 @@ snpguest fetch snpguest fetch vcek pem ./certs-kds attestation-report.bin ``` +3. `crl` + + Requests the Certificate Revocation List (CRL) from the KDS. It takes the same set of arguments as `snpguest fetch ca`. The user needs to specify the encoding to store the CRL in (PEM or DER). Currently, only PEM and DER encodings are supported. The user must specify their host processor model. The user also needs to provide the path to the directory where the CRL will be stored. If the CRL already exists in the provided directory, it will be overwritten. The `--endorser` argument specifies the type of attestation signing key (defaults to VCEK). + + **Usage** + ```bash + # Fetch CRL of the user-provided processor model + snpguest fetch crl $ENCODING $CERTS_DIR $PROCESSOR_MODEL --endorser $ENDORSER + # Fetch CRL of the processor-model written in the attestation report + snpguest fetch crl $ENCODING $CERTS_DIR --report $ATT_REPORT_PATH --endorser $ENDORSER + ``` + + **Arguments** + | Argument | Description | Default | + | :-- | :-- | :-- | + | `$ENCODING` | Specifies the encoding to store the CRL in (PEM or DER). | required | + | `$CERTS_DIR` | Specifies the directory to store the CRL in. | required | + | `$PROCESSOR_MODEL` | Specifies the host processor model (conflict with `--report`). | required | + | `-r, --report $ATT_REPORT_PATH` | Specifies the attestation report to detect the host processor model (conflict with `$PROCESSOR_MODEL`) | — | + | `-e, --endorser $ENDORSER` | Specifies the endorser type, possible values: "vcek", "vlek". | "vcek" | + + Example + ```bash + snpguest fetch crl der ./certs-kds milan -e vlek + ``` + ```bash + snpguest fetch crl pem ./certs-kds -r attestation-report.bin -e vcek + ``` + ### 5. `key` Creates the derived key based on input parameters and stores it. `$KEY_PATH` is the path to store the derived key. `$ROOT_KEY_SELECT` is the root key from which to derive the key (either "vcek" or "vmrk"). The `--guest_field_select` option specifies which Guest Field Select bits to enable as a binary string of length 6 or 7. Each of the bits from *right to left* correspond to Guest Policy, Image ID, Family ID, Measurement, SVN and TCB Version, Launch Mitigation Vector, respectively. For each bit, 0 denotes off, and 1 denotes on. The `--guest_svn` option specifies the guest SVN to mix into the key, the `--tcb_version` option specifies the TCB version to mix into the derived key, and the `--launch_mit_vector` option specifies the launch mitigation vector value to mix into the derived key. The `--vmpl` option specifies the VMPL level the Guest is running on and defaults to 1. diff --git a/src/fetch.rs b/src/fetch.rs index db549ca..82f1912 100644 --- a/src/fetch.rs +++ b/src/fetch.rs @@ -23,6 +23,9 @@ pub enum FetchCmd { /// Fetch the VCEK from the KDS. Vcek(vcek::Args), + + /// Fetch the CRL from the KDS. + Crl(crl::Args), } #[derive(ValueEnum, Debug, Clone, PartialEq, Eq)] @@ -152,6 +155,7 @@ pub fn cmd(cmd: FetchCmd) -> Result<()> { match cmd { FetchCmd::CA(args) => cert_authority::fetch_ca(args), FetchCmd::Vcek(args) => vcek::fetch_vcek(args), + FetchCmd::Crl(args) => crl::fetch_crl(args), } } @@ -396,6 +400,117 @@ mod vcek { Ok(()) } } + +mod crl { + use super::*; + use openssl::x509::X509Crl; + use reqwest::StatusCode; + use std::io::Write; + + #[derive(Parser)] + pub struct Args { + /// Specify encoding to use for the CRL. + #[arg(value_name = "encoding", required = true, ignore_case = true)] + pub encoding: CertFormat, + + /// Directory to store the CRL in. + #[arg(value_name = "certs-dir", required = true)] + pub certs_dir: PathBuf, + + /// Specify the processor model for the desired CRL. + #[arg( + value_name = "processor-model", + required_unless_present = "att_report", + conflicts_with = "att_report", + ignore_case = true + )] + pub processor_model: Option, + + /// Attestation Report to get processor model from (V3 of report needed). + #[arg( + short = 'r', + long = "report", + value_name = "att-report", + conflicts_with = "processor_model", + ignore_case = true + )] + pub att_report: Option, + + /// Specify which endorsement CRL to pull, either VCEK or VLEK. + #[arg(short, long, value_name = "endorser", default_value_t = Endorsement::Vcek, ignore_case = true)] + pub endorser: Endorsement, + } + + // Function to build kds request for CRL and return a CRL + pub fn request_crl_kds( + processor_model: ProcType, + endorser: &Endorsement, + ) -> Result, anyhow::Error> { + const KDS_CRL_SITE: &str = "https://kdsintf.amd.com"; + const KDS_CRL: &str = "crl"; + + // Should make -> https://kdsintf.amd.com/vcek/v1/{SEV_PROD_NAME}/crl + let url: String = format!( + "{KDS_CRL_SITE}/{}/v1/{}/{KDS_CRL}", + endorser.to_string().to_lowercase(), + processor_model.to_kds_url() + ); + + // CRL in DER format + let crl_rsp: Response = get(url).context("unable to send request for CRL to URL")?; + + match crl_rsp.status() { + StatusCode::OK => { + let crl_rsp_bytes: Vec = + crl_rsp.bytes().context("unable to parse CRL")?.to_vec(); + Ok(crl_rsp_bytes) + } + status => Err(anyhow::anyhow!("unable to fetch CRL from URL: {status:?}")), + } + } + + // Fetch the CRL from the kds and write it into the certs directory + pub fn fetch_crl(args: Args) -> Result<()> { + let proc_model = if let Some(processor_model) = args.processor_model { + processor_model + } else if let Some(att_report) = args.att_report { + let report = + report::read_report(att_report).context("could not open attestation report")?; + get_processor_model(report)? + } else { + return Err(anyhow::anyhow!("attestation report is missing or invalid, or the user did not specify a processor model")); + }; + + // Request CRL + let crl_der = request_crl_kds(proc_model, &args.endorser)?; + let crl = X509Crl::from_der(&crl_der)?; + + // Convert encoding + let bytes: Vec = match args.encoding { + CertFormat::Pem => crl.to_pem()?, + CertFormat::Der => crl.to_der()?, + }; + + // Write CRL into directory + if !args.certs_dir.exists() { + fs::create_dir(&args.certs_dir).context("could not create certs folder")?; + } + + let crl_path: PathBuf = args.certs_dir.join(format!("crl.{}", args.encoding)); + let mut file = std::fs::OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(&crl_path) + .context("unable to create or overwrite CRL")?; + + file.write(&bytes) + .context(format!("unable to write data to file {:?}", file))?; + + Ok(()) + } +} + #[cfg(test)] mod tests { use super::*;