Quick Start Guide#
Overview#
The C API of nvat
is located in <nvat.h>.
This document follows the terminology and architectural concepts defined in the Remote Attestation Procedures Architecture (RATS) RFC.
The API is divided into high level and low level APIs.
The high level API offers a pluggable interface to perform end-to-end system attestation,
which includes collecting evidence from an attester,
submitting the evidence to a verifier to obtain attestation results,
and applying appraisal policies to attestation results.
The entrypoint for the high level APIs is nvat_attest_system()
.
The SDK also provides low-level APIs for advanced or custom attestation workflows.
These APIs allow you to directly collect evidence, verify evidence, and evaluate attestation results as needed.
For example, you can use nvat_gpu_evidence_collect()
to gather GPU evidence from a system.
This function is useful if you need to submit evidence to a custom verifier or save it for auditing.
Similarly, nvat_verify_gpu_evidence()
can be used to verify evidence that was collected elsewhere or at a different time.
Check API groups to see all the api’s, categorized by their functionality.
Conventions#
Allocations:
All structures besides return codes, primitive flags, and bitmaps are opaque and cannot be allocated on the stack. For example, nvat_sdk_opts_t is defined as typedef nvat_sdk_opts_st* nvat_sdk_opts_t. nvat_sdk_opts_st is forward declared and cannot be directly used outside the SDK. The rest of this document will omit this detail for brevity.
All structures are allocated on the heap using functions named
nvat_<type>_create
and released usingnvat_<type>_destroy
.Most objects internally implement reference counting, so the user must free the objects created by SDK. Any exceptions will be called out in the API documentation.
Error handling:
All fallible errors return nvat_rc_t (NVAT_RC_OK to indicate the success case) .
Return values are communicated to callers using out parameters which are always the last parameter of a function signature.
Getting Started#
The NVIDIA Attestation SDK provides a simple C API for performing hardware attestation of NVIDIA GPUs and switches. This guide covers the most common use cases with step-by-step examples.
Basic Workflow#
Initialize the SDK with configuration options
See here for common configuration options and the defaults for them
Create an attestation context for your target device type
Perform attestation to collect and verify evidence
Process the results and handle any errors
Clean up resources when finished
Basic GPU Attestation Example#
This example demonstrates how to perform basic GPU attestation using the NVIDIA Attestation SDK.
Overview#
This is the most common use case - verifying the integrity and authenticity of NVIDIA GPU hardware and firmware using a complete end-to-end attestation workflow with a relying party policy.
#include <stdio.h>
#include <nvat.h>
/*
The high level API to attest NVIDIA GPU and/or NVSwitches is `nvat_attest_system`. This examples uses that API to perform local attestation for a GPU.
Using that API involves creating an attestation context (`nvat_attestation_ctx_t`), which can be used to configure the behavior of `nvat_attest_system`.
Please refer to API documentation of `nvat_attestation_ctx_create` to see the default options that will be used, which can changed.
The `nvat_attest_system` function returns 3 things: the detached EAT produced as the result of attestation, the claims set (as a JSON string) that are part of the EAT, and the result of applying relying party policy to the claims set.
The steps involved in this example are:
* Create SDK options and initialize the SDK
* Create the attestation context and configure it:
* Set verifier type to local (if not set default is local)
* Set the device(s) to be attested (if not set default is GPU)
* Set the relying part policy (optional, see `nvat_attest_system` for more info)
* Call `nvat_attest_system`
* Print the detached EAT for inspection
*/
/*
This is a sample rego policy which is used to show how a custom relying party policy can be created using rego.
*/
const char* REGO_RP_POLICY =
"package policy\n"
"import future.keywords.every\n"
"default nv_match := false\n"
"nv_match {\n"
" every result in input {\n"
" result[\"x-nvidia-device-type\"] == \"gpu\"\n"
" result.secboot\n"
" result.dbgstat == \"disabled\"\n"
" }\n"
"}\n";
/*
This is a helper function which is called from main. It creates the attestation context, sets some options on it to show how the context can be modified to configure the attestation process and then finally calls `nvat_attest_system` to perform the attestation.
*/
nvat_rc_t attest(nvat_attestation_ctx_t* ctx) {
nvat_rc_t err;
// Step 1: Create and configure SDK options
nvat_sdk_opts_t opts;
err = nvat_sdk_opts_create(&opts);
if (err != NVAT_RC_OK) {
return err;
}
// Step 2: Initialize the SDK with the options defined above.
// This must be called before any other SDK operations to initialize global
// dependencies such as the logger
err = nvat_sdk_initialize(&opts);
if (err != NVAT_RC_OK) {
nvat_sdk_opts_free(&opts);
return err;
}
// Step 3: Create attestation context for GPU devices.
// This context holds configuration for evidence collection, verification, and policy evaluation
err = nvat_attestation_ctx_create(ctx);
if (err != NVAT_RC_OK) {
nvat_sdk_opts_free(&opts);
nvat_sdk_shutdown();
return err;
}
// Step 4: Define trust requirements by setting up a relying party policy.
nvat_relying_party_policy_t rp_policy;
err = nvat_relying_party_policy_create_rego_from_str(&rp_policy, REGO_RP_POLICY);
if (err != NVAT_RC_OK) {
nvat_attestation_ctx_free(ctx);
nvat_sdk_opts_free(&opts);
nvat_sdk_shutdown();
return err;
}
// Set the devices to be attested. Default is GPU.
err = nvat_attestation_ctx_set_devices(*ctx, NVAT_DEVICE_GPU);
if (err != NVAT_RC_OK) {
nvat_attestation_ctx_free(ctx);
nvat_sdk_opts_free(&opts);
nvat_sdk_shutdown();
return err;
}
// Set the context to use policy handle that we created above
nvat_attestation_ctx_set_relying_party_policy(*ctx, rp_policy);
// all the handles are opaque pointers, which are refcounted. So this needs to be
// free'ed by the client and when the ctx is free'ed, the relying party policy
// handle will finally be free'd
nvat_relying_party_policy_free(&rp_policy);
nvat_attestation_ctx_set_verifier_type(*ctx, NVAT_VERIFY_LOCAL);
// Step 5: Perform system attestation
// nvat_attest_system performs the complete attestation workflow - collecting evidence from GPUs,
// verifying it against trusted references, and evaluating results against the relying party policy
// returns NVAT_RC_OK is system is trustworthy according to RP policy or NVAT_RC_RP_POLICY_MISMATCH if the policy rejected the results. (if RP policy is not provided, this return code indicates that the overall result in the detached EAT is true)
// it can also optionally return the claims set that is part of the EAT
nvat_claims_t* claims;
nvat_str_t detached_eat;
err = nvat_attest_system(*ctx, NULL, &detached_eat, &claims);
if (err != NVAT_RC_OK) {
nvat_attestation_ctx_free(ctx);
nvat_sdk_opts_free(&opts);
nvat_sdk_shutdown();
return err;
}
// Step 6: Serialize and display results
// The detached EAT is a serialized JSON object. We will get the string data and print it
char* eat_data;
err = nvat_str_get_data(detached_eat, &eat_data);
if (err != NVAT_RC_OK) {
nvat_attestation_ctx_free(ctx);
nvat_sdk_opts_free(&opts);
nvat_sdk_shutdown();
return err;
}
printf("Detached EAT: %s\n", eat_data);
// Cleanup resources
nvat_str_free(&detached_eat);
nvat_attestation_ctx_free(ctx);
nvat_sdk_opts_free(&opts);
nvat_sdk_shutdown();
return NVAT_RC_OK;
}
int main(void) {
nvat_attestation_ctx_t ctx;
nvat_rc_t rc = attest(&ctx);
if (ctx != NULL) {
nvat_attestation_ctx_free(&ctx);
}
nvat_sdk_shutdown();
switch(rc) {
case NVAT_RC_OK:
return 0;
case NVAT_RC_RP_POLICY_MISMATCH:
fprintf(stderr, "attestation results did not match relying party policy (nvat code: %03d)\n", rc);
return 2;
case NVAT_RC_OVERALL_RESULT_FALSE:
fprintf(stderr, "overall attestation result fail (nvat code: %03d)\n", rc);
return 3;
default:
fprintf(stderr, "system attestation failed: %s (nvat code: %03d)\n",
nvat_rc_to_string(rc), rc);
return 1;
}
}
API References#
Complete attestation workflow:
nvat_attest_system()
(related: attestation context APIs)Error handling:
return_codes()
SDK initialization:
nvat_sdk_init()
Attestation context:
nvat_attestation_ctx_create()
Evidence Collection Example#
Understand how to collect attestation evidence and perform verification using lower-level APIs.
Overview#
This example demonstrates how to manually collect GPU evidence, set up verification components, and process attestation results.
This approach provides more control over the attestation process compared to the simplified nvat_attest_system()
workflow.
Prerequisites#
System with supported NVIDIA GPU
NVML library available
Network access for RIM service and OCSP validation
Code#
/*
This struct is used in the example to make memory management easier.
In case of an error, we can just call teardown with this struct which
will always free all the elements, even if they have not been allocated
yet. The free functions are no-op if the argument is NULL and we initialize
them to NULL in main
*/
typedef struct context {
nvat_sdk_opts_t opts;
nvat_logger_t logger;
nvat_gpu_evidence_source_t evidence_source;
nvat_nonce_t nonce;
nvat_gpu_evidence_t* evidences;
uint8_t num_evidence;
nvat_gpu_verifier_t verifier;
nvat_evidence_policy_t evidence_policy;
nvat_claims_t* claims;
nvat_ocsp_client_t ocsp_client;
nvat_rim_store_t rim_store;
nvat_str_t nonce_str;
nvat_str_t evidence_str;
} context_t;
void teardown(context_t ctx) {
nvat_sdk_opts_free(&ctx.opts);
nvat_logger_free(&ctx.logger);
nvat_gpu_evidence_source_free(&ctx.evidence_source);
nvat_nonce_free(&ctx.nonce);
nvat_gpu_evidence_collection_free(&ctx.evidence_collection);
nvat_gpu_verifier_free(&ctx.verifier);
nvat_claims_collection_free(&ctx.claims);
nvat_evidence_policy_free(&ctx.evidence_policy);
nvat_rim_store_free(&ctx.rim_store);
nvat_ocsp_client_free(&ctx.ocsp_client);
nvat_str_free(&ctx.nonce_str);
nvat_str_free(&ctx.evidence_str);
nvat_sdk_shutdown();
}
nvat_rc_t attest(void) {
context_t ctx = {0};
nvat_rc_t err;
// Step 1: Initialize SDK with logging
// Similar to previous examples, but includes setting up an SPDLog logger for detailed
// debugging output during evidence collection and verification
err = nvat_sdk_opts_create(&ctx.opts);
if (err != NVAT_RC_OK) {
teardown(ctx);
return err;
}
err = nvat_logger_spdlog_create(&ctx.logger, "nvat_collect_evidence", NVAT_LOG_LEVEL_DEBUG);
if (err != NVAT_RC_OK) {
teardown(ctx);
return err;
}
nvat_sdk_opts_set_logger(ctx.opts, &ctx.logger);
err = nvat_sdk_initialize(&ctx.opts);
if (err != NVAT_RC_OK) {
teardown(ctx);
return err;
}
// Step 2: Set up evidence source and nonce
// nvat_gpu_evidence_source_nvml_create creates an evidence source that uses NVML
// to communicate with NVIDIA GPUs and collect hardware attestation data
err = nvat_gpu_evidence_source_nvml_create(&ctx.evidence_source);
if (err != NVAT_RC_OK) {
teardown(ctx);
return err;
}
// nvat_nonce_create generates a cryptographically secure random nonce (32 bytes)
// that gets included in the attestation evidence to prevent replay attacks
err = nvat_nonce_create(&ctx.nonce, 32);
if (err != NVAT_RC_OK) {
teardown(ctx);
return err;
}
err = nvat_nonce_hex_string(ctx.nonce, &ctx.nonce_str);
if (err != NVAT_RC_OK) {
teardown(ctx);
return err;
}
char* buf;
err = nvat_str_get_data(ctx.nonce_str, &buf);
if (err != NVAT_RC_OK) {
teardown(ctx);
return err;
}
fprintf(stderr, "nonce is %s\n", buf);
// Step 3: Collect GPU evidence
// nvat_gpu_evidence_collect gathers attestation evidence from the GPU, including
// measurements, certificates, and other cryptographic proofs
// The evidence is then serialized to JSON format for inspection
err = nvat_gpu_evidence_collect(ctx.evidence_source, ctx.nonce, &ctx.evidences, &ctx.num_evidence);
if (err != NVAT_RC_OK) {
teardown(ctx);
return err;
}
err = nvat_gpu_evidence_serialize_json(ctx.evidences, ctx.num_evidence, &ctx.evidence_str);
if (err != NVAT_RC_OK) {
teardown(ctx);
return err;
}
err = nvat_str_get_data(ctx.evidence_str, &buf);
if (err != NVAT_RC_OK) {
teardown(ctx);
return err;
}
fprintf(stdout, "gpu evidence: %s\n", buf);
// Step 4: Set up verification components
// The code creates the components needed for local verification:
// - nvat_rim_store_create_remote: Creates a RIM (Reference Integrity Measurements) store
// that connects to NVIDIA's remote service
// - nvat_ocsp_client_create_default: Sets up OCSP client for certificate revocation checking
// - nvat_gpu_local_verifier_create: Combines RIM store and OCSP client into a local verifier
err = nvat_rim_store_create_remote(&ctx.rim_store, NULL, NULL);
if (err != NVAT_RC_OK) {
teardown(ctx);
return err;
}
err = nvat_ocsp_client_create_default(&ctx.ocsp_client, NULL, NULL);
if (err != NVAT_RC_OK) {
teardown(ctx);
return err;
}
nvat_gpu_local_verifier_t local_verifier;
err = nvat_gpu_local_verifier_create(&local_verifier, ctx.rim_store, ctx.ocsp_client);
if (err != NVAT_RC_OK) {
teardown(ctx);
return err;
}
ctx.verifier = nvat_gpu_local_verifier_upcast(local_verifier);
err = nvat_evidence_policy_create_default(&ctx.evidence_policy);
if (err != NVAT_RC_OK) {
teardown(ctx);
return err;
}
// Step 5: Verify evidence and produce claims
// nvat_verify_gpu_evidence verifies the collected evidence against trusted references
// and produces attestation claims. The verification process includes:
// - Certificate chain validation
// - Signature verification
// - Measurement comparison against RIMs
// - Certificate revocation checking via OCSP
// This produces a claims set, which will be used to create a detached EAT
err = nvat_verify_gpu_evidence(ctx.verifier, ctx.evidences, ctx.num_evidence, ctx.evidence_policy, &ctx.claims);
if (err != NVAT_RC_OK) {
teardown(ctx);
return err;
}
nvat_str_t detached_eat;
err = nvat_get_detached_eat(&ctx.claims, ctx.num_evidence, &detached_eat);
if (err != NVAT_RC_OK)
{
fprintf(stderr, "Failed to get detached EAT: %s (nvat code: %03d)\n",
nvat_rc_to_string(err), err);
teardown(ctx);
return err;
}
char* eat_data;
err = nvat_str_get_data(detached_eat, &eat_data);
if (err != NVAT_RC_OK) {
printf("Failed to get detached EAT: %s (nvat code: %03d)\n",
nvat_rc_to_string(err), err);
nvat_str_free(&detached_eat);
teardown(ctx);
return err;
}
fprintf(stdout, "detached EAT: %s\n", eat_data);
teardown(ctx);
return NVAT_RC_OK;
}
int main(void) {
nvat_rc_t rc = attest();
if (rc != NVAT_RC_OK) {
fprintf(stderr, "collect_gpu_evidence failed: %s (nvat code: %03d)\n", nvat_rc_to_string(rc), rc);
return 1;
}
return 0;
}
API References#
Evidence collection:
nvat_gpu_evidence_collect()
Evidence sources:
nvat_gpu_evidence_source_nvml_create()
Nonce generation:
nvat_nonce_create()
Verification:
nvat_verify_gpu_evidence()
Local verifier:
nvat_gpu_local_verifier_create()
Custom Logger Example#
Learn how to integrate custom logging functionality with the attestation SDK.
Overview#
The SDK provides flexible logging capabilities that can be customized to integrate with your application’s logging system. You can define custom callbacks for log message handling, filtering, and flushing.
Code#
#include <stdio.h>
#include <nvat.h>
// Custom callback to determine whether a log message should be processed
bool should_log_cb(nvat_log_level_t level, const char* filename, const char* function, int line_number, void* user_data) {
// Allow all messages at TRACE level and above
return level >= NVAT_LOG_LEVEL_TRACE;
}
// Custom callback to handle log messages
void log_cb(nvat_log_level_t level, const char* message, const char* filename, const char* function, int line_number, void* user_data) {
char* level_str = "";
if (level == NVAT_LOG_LEVEL_ERROR) {
level_str = " [ERROR]";
}
// Format: [user_data] [filename:line function][LEVEL]: message
fprintf(stdout, "[%s] [%s:%d %s]%s: %s\n", (char*) user_data, filename, line_number, function, level_str, message);
}
// Custom callback for flushing buffered log messages
void flush_cb(void* user_data) {
fprintf(stdout, "invoked flush() on custom logger\n");
}
nvat_rc_t run(void) {
// Step 1: Create SDK options
nvat_sdk_opts_t opts;
nvat_rc_t err = nvat_sdk_opts_create(&opts);
if (err != NVAT_RC_OK) {
return err;
}
// Step 2: Create custom logger with callbacks
// Define three callback functions:
// - should_log_cb: Filters messages based on log level, filename, function, and line number
// - log_cb: Handles the actual log message formatting and output
// - flush_cb: Handles requests to flush any buffered log messages
// nvat_logger_callback_create creates a custom logger backend with your callback functions
// The user_data parameter allows passing custom context to all callbacks
nvat_logger_t logger;
err = nvat_logger_callback_create(
&logger,
log_cb, // Log message callback
should_log_cb, // Log filtering callback
flush_cb, // Flush callback
"custom-logger-data" // User data passed to callbacks
);
if (err != NVAT_RC_OK) {
return err;
}
// Step 3: Set the custom logger in SDK options
// nvat_sdk_opts_set_logger connects your custom logger to the SDK options
// The logger becomes active when nvat_sdk_init is called
nvat_sdk_opts_set_logger(opts, &logger);
// Step 4: Initialize SDK with custom logger
err = nvat_sdk_init(&opts);
if (err != NVAT_RC_OK) {
return err;
}
// Step 5: Trigger a log message (intentional error for demonstration)
// When the SDK generates log messages, your callbacks are invoked:
// - should_log_cb first determines if the message should be processed
// - If allowed, log_cb handles formatting and output
// - flush_cb is called when buffered messages should be flushed
// The example intentionally triggers an error message by passing NULL to a function
// that requires a valid pointer, demonstrating how SDK errors are logged through your custom system
nvat_http_options_create_default(NULL); // This will log an error
return NVAT_RC_OK;
}
int main(void) {
nvat_rc_t rc = run();
nvat_sdk_shutdown();
if (rc != NVAT_RC_OK) {
fprintf(stderr, "custom-logger failed: %s (nvat code: %03d)\n", nvat_rc_to_string(rc), rc);
return 1;
}
return 0;
}
API References#
Custom logger creation:
nvat_logger_callback_create()
Logger configuration:
nvat_sdk_opts_set_logger()
SDK initialization:
nvat_sdk_init()
Error handling:
return_codes()
Common Patterns#
Error Handling#
All SDK functions return error codes that should be checked:
nvat_rc_t rc = nvat_sdk_init(&opts);
if (rc != NVAT_RC_OK) {
fprintf(stderr, "SDK initialization failed: %s\n", nvat_rc_to_string(rc));
// Handle error appropriately
return 1;
}
Resource Management#
Always clean up SDK resources:
// Clean up in reverse order of creation
nvat_attestation_ctx_free(&ctx);
nvat_sdk_opts_free(&opts);
nvat_sdk_shutdown();
Next Steps#
Explore the Full API#
Complete API Reference: Browse the API groups for detailed descriptions of all SDK functions, types, and constants
Advanced Configuration: See here to learn more about configuring the attestation process