CoRIM verifier#

Note: The CoRIM verifier is experimental. It is the direction for NVIDIA’s attestation-verification workflow, but its behavior, interfaces, supported evidence, and EAR output may change.

For store, network, and verifier settings, see Configure the CoRIM verifier. The shape of the result is described in the EAR format reference.

Local appraisal#

Collect GPU evidence with a nonce, wrap it in a CMW collection, configure the store and OCSP client, then create the verifier:

use nv_attestation_sdk::{
    AiaOptions, CmwCollection, CmwFormat, CorimStore, EarSigningOptions,
    GpuEvidenceSource, LocalCorimVerifier, Nonce, NvatSdk, OcspClient,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let _sdk = NvatSdk::init_default()?;

    let nonce = Nonce::generate(32)?;
    let evidence = GpuEvidenceSource::from_nvml()?.collect(&nonce)?;
    let cmw = CmwCollection::from_gpu_evidence(&evidence, &nonce)?;

    let mut store = CorimStore::new(None, None)?;
    store.enable_in_memory_cache(16 * 1024 * 1024, 3600)?;

    let ocsp_client = OcspClient::create_aia(AiaOptions::default())?;
    let verifier = LocalCorimVerifier::new(store, Some(&ocsp_client))?;

    let signing = EarSigningOptions::new(
        &std::fs::read_to_string("/etc/nvat/ear-signing-key.pem")?,
        "https://verifier.example.com",
        "ear-signing-key-1",
    )?;

    let ear = verifier.verify_cmw_collection(&cmw, CmwFormat::Json, Some(&signing))?;
    println!("{}", ear.json);
    println!("{}", ear.jwt);
    Ok(())
}

The store is consumed by LocalCorimVerifier::new, so configure its cache, allowed locator prefixes, and rewrites before creating the verifier. Use CmwFormat::Json; CBOR input is reserved but not implemented.

EAR results and policy#

EarResult::json and EarResult::jwt represent the same appraisal. Supplying EarSigningOptions signs the JWT; omitting it produces an unsigned JWT for test use only.

A successful Rust call means the CMW was processed. It does not by itself mean that an application accepts the appraisal. Inspect ear_status and the submodule results, then evaluate the EAR JSON against the application’s policy. Policies that depend on an intended attestation context must also check ear_nvidia_purpose when it is present.

Use RelyingPartyPolicy::apply_to_ear_json to evaluate an EAR JSON object:

use nv_attestation_sdk::RelyingPartyPolicy;

let policy = RelyingPartyPolicy::from_rego(rego_source)?;
policy.apply_to_ear_json(&ear.json)?;

This evaluates the policy only; it does not alter the EAR or its ear_status.

For a signed EAR received from another process or service, authenticate it first, then apply the policy to the returned JSON:

use nv_attestation_sdk::{verify_ear, RelyingPartyPolicy};

let ear_json = verify_ear(&ear_jwt, "https://verifier.example.com")?;
let policy = RelyingPartyPolicy::from_rego(rego_source)?;
policy.apply_to_ear_json(&ear_json)?;

verify_ear fetches the verifier’s JWKS and validates the JWT before returning JSON. Use verify_ear_with_options when the relying party needs an expected nonce, custom HTTP/TLS settings, JWT clock-skew settings, or a service key for an NVIDIA-hosted JWKS endpoint.

Thread safety#

LocalCorimVerifier is Send and Sync; a service can share one configured verifier across worker threads. CorimStore is Send but not Sync and is consumed by LocalCorimVerifier::new, so configure it fully before sharing the verifier. An enabled CoRIM cache is shared by workers through that verifier.

CmwCollection and evidence collections belong to one request. Initialize NvatSdk once before starting worker threads and keep it alive for the process lifetime. OcspClient is Send and Sync and can be shared with the CoRIM verifier.

More examples#

The complete example is available in the source tree: corim_local_attestation.rs.

For concurrency guidance, see Thread Safety.