Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,35 @@ snpguest fetch <SUBCOMMAND>
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will only work correctly on Turin+ correct? The attestation report in the previous architectures do not contain the processor model.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should work on pre-Turin CPUs as well. For report versions < 3, the logic is Turin-only as you said, but for report versions ≥ 3 the CPU family ID is used to determine the processor model.

snpguest/src/fetch.rs

Lines 114 to 152 in ce3c022

pub fn get_processor_model(att_report: AttestationReport) -> Result<ProcType> {
if att_report.version < 3 {
if [0u8; 64] == *att_report.chip_id {
return Err(anyhow::anyhow!(
"Attestation report version is lower than 3 and Chip ID is all 0s. Make sure MASK_CHIP_ID is set to 0 or update firmware."
));
} else {
let chip_id = *att_report.chip_id;
if chip_id[8..64] == [0; 56] {
return Ok(ProcType::Turin);
} else {
return Err(anyhow::anyhow!(
"Attestation report could be either Milan or Genoa. Update firmware to get a new version of the report."
));
}
}
}
let cpu_fam = att_report
.cpuid_fam_id
.ok_or_else(|| anyhow::anyhow!("Attestation report version 3+ is missing CPU family ID"))?;
let cpu_mod = att_report
.cpuid_mod_id
.ok_or_else(|| anyhow::anyhow!("Attestation report version 3+ is missing CPU model ID"))?;
match cpu_fam {
0x19 => match cpu_mod {
0x0..=0xF => Ok(ProcType::Milan),
0x10..=0x1F | 0xA0..0xAF => Ok(ProcType::Genoa),
_ => Err(anyhow::anyhow!("Processor model not supported")),
},
0x1A => match cpu_mod {
0x0..=0x11 => Ok(ProcType::Turin),
_ => Err(anyhow::anyhow!("Processor model not supported")),
},
_ => Err(anyhow::anyhow!("Processor family not supported")),
}
}

I have verified that it works correctly on a Milan CPU with report version 5. Since the report version is determined by the SEV firmware, upgrading the firmware raises the report version and enables the --report argument to work correctly. This is the same logic already used in the existing fetch ca and fetch vcek subcommands.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, we basically mandate that one must update their firmware to use this command properly, correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that is correct when using the --report flag to identify the processor model (rather than specifying the processor model explicitly). The same applies to the snpguest fetch ca command.

```

### 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.
Expand Down
115 changes: 115 additions & 0 deletions src/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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),
}
}

Expand Down Expand Up @@ -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<ProcType>,

/// 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<PathBuf>,

/// 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<Vec<u8>, 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<u8> =
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<u8> = 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::*;
Expand Down
Loading