This guide explains how to create, sign, and register security policies in the TEE Attestation Service (TAS).
- Overview
- Policy Structure
- Component Policies (GPU Attestation)
- Signing Policies
- Registering Policies
- Validation Rule Types
- Best Practices
- Troubleshooting
TAS policies define the security requirements that TEE attestation evidence must meet. Policies consist of:
- Metadata: Descriptive information about the policy
- Validation Rules: Specific attestation requirements (measurements, versions, etc.)
- Digital Signature: Optional but recommended for integrity verification
Policies are stored in Redis and referenced during attestation validation to determine if TEE evidence meets the required security standards.
{
"metadata": {
"name": "SEV Example Policy",
"version": "1.0",
"description": "Policy description",
"policy_type": "SEV",
"policy_id": "my-sev-policy-001",
"key_id": "my-secret-id",
"created_date": "2024-09-09",
"last_updated": "2024-09-09"
},
"validation_rules": {
"measurement": {
"exact_match": "a1b2c3d4e5f6789..."
},
"vmpl": {
"exact_match": 0
},
"policy": {
"migrate_ma_allowed": false,
"debug_allowed": false,
"smt_allowed": true
},
"platform_info": {
"ecc_enabled": {
"boolean": true
},
"tsme_enabled": {
"boolean": true
},
"alias_check_complete": {
"boolean": true
},
"smt_enabled": {
"boolean": true
}
},
},
"signature": {
"algorithm": "SHA384",
"padding": "PSS",
"value": "base64-encoded-signature"
}
}| Field | Type | Required | Description |
|---|---|---|---|
metadata |
object | Yes | Policy metadata (must include policy_type, policy_id, and key_id) |
validation_rules |
object | Yes | Attestation validation criteria |
components |
object | No | Additional attestable component policies (e.g. GPUs). See Component Policies (GPU Attestation) |
signature |
object | No | Digital signature for integrity |
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Human-readable policy name |
policy_type |
string | Yes | TEE type (e.g. SEV, TDX) |
policy_id |
string | Yes | Unique identifier for this policy (alphanumeric, hyphens, underscores, dots only) |
key_id |
string | Yes | The secret ID this policy is used to release (must match the secret registered in KMS or HSM) |
version |
string | No | Policy version |
description |
string | No | Policy description |
created_by |
string | No | Policy creator |
created_date |
string | No | Creation date (ISO format) |
last_updated |
string | No | Last update date (ISO format) |
| Field | Type | Required | Description |
|---|---|---|---|
algorithm |
string | Yes | Algorithm used |
padding |
string | Yes | Padding (either PSS or PKCS1v15) |
value |
string | Yes | Base64 encoded signature |
Deprecation Notice: The
signed_datafield previously allowed specifying which fields are covered by the signature. This field is no longer supported. The signature must always cover all top-level fields exceptsignature. Policies usingsigned_datamust be re-signed.
The tcb item within validation_rules is a special case for TDX policies. It is used to require specific TCB levels or better for various components, along with an update field to require a minimum TCB-R freshness. See the example policy tdx_example_policy.json or the excerpt below for an example.
...
"validation_rules": {
"tcb": {
"update": "standard",
"platform_tcb": "UpToDate",
"tdx_module_tcb": "UpToDate",
"qe_tcb": "UpToDate"
},
...In addition to the CPU TEE rules in validation_rules, a policy may carry an
optional top-level components section that defines requirements for other
attestable devices alongside the CPU TEE. Today this is used for NVIDIA GPU
attestation.
The components section is optional — existing SEV/TDX policies without it
continue to work unchanged. When present, it is covered by the policy signature
just like every other top-level field.
components is keyed by component type (gpu). Each component type is keyed by
the device type reported by the agent. NVIDIA GPUs report gpu-nvidia:
{
"components": {
"gpu": {
"gpu-nvidia": {
"version": "4.0",
"authorization-rules": { ... }
}
}
}
}The object under gpu-nvidia is an NVIDIA attestation policy consumed by
nvidia_pytools. It
validates the claims returned by the NVIDIA Remote Attestation Service (NRAS)
and has two groups of rules:
overall-claims— checks applied to the overall NRAS result (e.g. the attestation succeeded overall).detached-claims— per-GPU checks (measurement result, debug status, secure boot, driver/VBIOS RIM checks, certificate-chain status, etc.).
For each claim, the actual value reported by NRAS must match the value in the policy. Nested objects (such as certificate-chain status) are matched field by field.
{
"metadata": {
"name": "AMD SEV-SNP + GPU Security Policy",
"policy_type": "SEV",
"policy_id": "sev-gpu-policy-001",
"key_id": "test-key-gpu-1",
"version": "1.0"
},
"validation_rules": {
"vmpl": { "exact_match": 0 },
"policy": { "debug_allowed": false, "smt_allowed": true }
},
"components": {
"gpu": {
"gpu-nvidia": {
"version": "4.0",
"authorization-rules": {
"type": "JWT",
"overall-claims": {
"x-nvidia-overall-att-result": true,
"x-nvidia-ver": "3.0"
},
"detached-claims": {
"measres": "success",
"dbgstat": "disabled",
"secboot": true,
"x-nvidia-gpu-arch-check": true,
"x-nvidia-gpu-attestation-report-cert-chain": {
"x-nvidia-cert-status": "valid",
"x-nvidia-cert-ocsp-status": "good"
}
}
}
}
}
},
"signature": { "...": "..." }
}Policy signing provides:
- Integrity: Ensures policy hasn't been tampered with
- Authentication: Verifies policy creator
- Non-repudiation: Prevents denial of policy authorship
Before signing or verifying, TAS canonicalizes the policy JSON using RFC 8785 (JSON Canonicalization Scheme / JCS). This ensures that logically equivalent JSON objects produce identical byte representations regardless of key ordering or whitespace. The canonicalized bytes are then signed with RSA using SHA-384 and either PSS or PKCS1v15 padding.
The signature covers all top-level fields in the policy (metadata, validation_rules, etc.) except the signature field itself. This means any change to the policy will invalidate the signature.
Note: Policies must be signed using RFC 8785 canonicalization. Any custom signing tool must canonicalize the policy to be signed with JCS before signing.
TAS includes a demo signing tool that can generate keys automatically and sign policies. You can use the provided demo_signer.py or generate your own keys.
# Navigate to the policy signing directory
cd certs/policy
# Sign your policy (this auto-generates keys if they don't exist)
python3 demo_signer.py /path/to/your-policy.json
# This creates:
# - policy_key.pem (private key with passphrase "passphrase")
# - policy_public_key.pem (public key for TAS configuration)
# - your-policy.json.sig (signature JSON to add to your policy)# Generate your own RSA key pair
openssl genrsa -out my-policy-key.pem 4096
# Generate public key
openssl rsa -in my-policy-key.pem -pubout -out my-policy-public.pem
# Secure the private key
chmod 600 my-policy-key.pemThen modify demo_signer.py to use your custom keys, or create your own signing script based on the demo implementation.
The demo signer creates a separate signature file. You need to manually add the signature to your policy:
# View the generated signature
cat your-policy.json.sig
# Example output:
# {
# "signature": {
# "algorithm": "SHA384",
# "padding": "PSS",
# "value": "base64-encoded-signature..."
# }
# }
# Add this signature object to your policy JSON file manually
# or use jq to merge them:
jq -s '.[0] * .[1]' your-policy.json your-policy.json.sig > your-policy-signed.json# Copy all your public keys and certs for policies to the default folder
cp public_key.pem /tas/certs/policy/
# Or alternatively configure TAS to trust your generated public key/cert
export TAS_POLICY_TRUST='/path/to/policy/public_key.pem'The demo signer can also generate certificates instead of just public keys:
# Generate certificate instead of public key
python3 demo_signer.py --cert your-policy.json
# This creates:
# - policy_key.pem (private key)
# - policy_cert.pem (certificate for TAS configuration)
# - your-policy.json.sig (signature JSON)
# Then add the certificate to your configured policy trust directoryTo register a policy with TAS, POST the policy JSON directly to the store endpoint. The policy_id, policy_type, and key_id are read from the policy's metadata section.
Policy registration uses the management API, which requires the X-MANAGEMENT-API-KEY header (separate from the client X-API-KEY).
The request body is the complete signed policy from Step 2 above:
{
"metadata": {
"name": "SEV Production Policy",
"version": "1.0",
"description": "Production policy for SEV attestation",
"policy_type": "SEV",
"policy_id": "my-sev-policy-001",
"key_id": "my-secret-id",
"created_date": "2024-09-09"
},
"validation_rules": {
"measurement": {
"exact_match": "a1b2c3d4e5f6789..."
},
"policy": {
"debug_allowed": false,
"migrate_ma_allowed": false
}
},
"signature": {
"algorithm": "SHA384",
"padding": "PSS",
"value": "base64-encoded-signature..."
}
}| Field | Type | Required | Description |
|---|---|---|---|
metadata.policy_type |
string | Yes | TEE type: either "SEV" or "TDX" |
metadata.policy_id |
string | Yes | Unique identifier for this policy (alphanumeric, hyphens, underscores, dots only) |
metadata.key_id |
string | Yes | The secret ID that this policy will be used to release (must match the secret registered in KMS or HSM) |
How it works: When a client requests a secret, TAS uses the policy_id to look up the policy and validates the attestation evidence. On success, the key_id from the policy metadata identifies which secret to release from the key manager. The key_id must exist in the key manager that TAS KBM is connected to.
Policy Storage Format: The policy will be stored in Redis using the key format: policy:{policy_id}
Example: policy:my-sev-policy-001
# Set your management API key
export TAS_MANAGEMENT_API_KEY="your-management-api-key-here"
# Register the signed policy (management API)
curl -X POST http://localhost:5001/management/policy/v0/store \
-H "Content-Type: application/json" \
-H "X-MANAGEMENT-API-KEY: $TAS_MANAGEMENT_API_KEY" \
-d '{
"metadata": {
"name": "My Policy",
"policy_type": "SEV",
"policy_id": "my-sev-policy-001",
"key_id": "my-secret-id"
},
"validation_rules": {...},
"signature": {...}
}'
# Expected response:
# {"message": "Policy 'policy:my-sev-policy-001' stored successfully"}Requires exact value match:
{
"measurement": {
"exact_match": "a1b2c3d4e5f6789abcdef0123456789abcdef"
}
}Numeric range validation:
{
"version": {
"min_value": 3,
"max_value": 10
}
}Boolean flag validation:
{
"debug": false,
"migrate_ma": false
}Check if value is in allowed list:
{
"processor_family": {
"allow_list": ["EPYC", "Xeon"]
}
}Ensure value is not in banned list:
{
"algorithms": {
"deny_list": [4,5]
}
}-
Always Sign Production Policies
# Never deploy unsigned policies in production export TAS_ENFORCE_SIGNED_POLICIES=true
-
Use Strong Key Management
# Protect private keys chmod 600 policies/keys/*.key # Use hardware security modules (HSM) for critical keys # Store keys separately from policies
-
Version Control Policies
# Track policy changes git add policies/ git commit -m "Add production SEV policy v1.0" git tag policy-v1.0
Error: Policy signature verification failed
Solutions:
- Verify the signing key matches the trusted keys in TAS configuration
- Check that the policy was signed correctly
- Ensure TAS is configured with the correct public keys
Error: Unsigned policies are not allowed by configuration
Solutions:
- Sign the policy before registration
- For development, disable enforcement:
export TAS_ENFORCE_SIGNED_POLICIES=false
Error: Policy must contain 'validation_rules' section
Solutions:
- Verify all required fields are present
- Check JSON syntax is valid
- Ensure policy follows the correct structure
# Verify policy syntax
python3 -m json.tool my-policy.json
# List registered policies (management API)
curl -H "X-MANAGEMENT-API-KEY: $TAS_MANAGEMENT_API_KEY" http://localhost:5001/management/policy/v0/list
# Get specific policy (management API)
curl -H "X-MANAGEMENT-API-KEY: $TAS_MANAGEMENT_API_KEY" http://localhost:5001/management/policy/v0/get/policy:SNP:my-policy-id