Skip to content

Commit

Permalink
New version of Attestation SDK and Local Verifier. (#23)
Browse files Browse the repository at this point in the history
* 1. Added new version of Attestation SDK and Local GPU Verifier 2. Updated RIM documentation
* 1. Patched Local verifier's logic in comparing vBIOS version numbers 2. Added 1.2.0 packages for Attestation SDK
* Removed vBIOS RIMs from rims directory
  • Loading branch information
thisiskarthikj authored Nov 5, 2023
1 parent 6296b7a commit 8db7fb5
Show file tree
Hide file tree
Showing 45 changed files with 358 additions and 3,200 deletions.
4 changes: 2 additions & 2 deletions guest_tools/attestation_sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ The Attestation SDK offers developers easy-to-use APIs for implementing attestat
- Local GPU Attestation (using NVIDIA NVML based Python libraries)
- Remote GPU Attestation (using NVIDIA Remote Attestation Service)

Note: SDK v1.1.0 is still in Early Access Release (beta), and the APIs may undergo changes until the GA release.
Note: SDK v1.2.0 is still in Early Access Release (beta), and the APIs may undergo changes until the GA release.

## Install Attestation SDK

Expand Down Expand Up @@ -107,7 +107,7 @@ Please refer to the [sample implementation](tests/RemoteGPUTest.py)
## Version Info
SDK latest version - 1.1.0
SDK latest version - 1.2.0
## Future Roadmap
Expand Down
Binary file not shown.
Binary file not shown.
4 changes: 2 additions & 2 deletions guest_tools/attestation_sdk/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "nv-attestation-sdk"
version = "1.1.0"
version = "1.2.0"
authors = [
{name = "NVIDIA"},
]
Expand All @@ -14,7 +14,7 @@ classifiers = [
]
dependencies = [
'pyjwt ~= 2.7.0',
'verifier == 1.1.0',
'verifier == 1.2.0',
'requests ~= 2.31.0'
]
keywords = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
from enum import IntFlag
from enum import IntEnum
from datetime import datetime
from nv_attestation_sdk.gpu import attest_gpu
from nv_attestation_sdk.gpu import attest_gpu_local
from nv_attestation_sdk.gpu import attest_gpu_remote
from nv_attestation_sdk.attestation import *
import secrets
import jwt
Expand Down Expand Up @@ -93,6 +94,7 @@ def get_verifiers(cls) -> list:
"""
return cls._verifiers


@classmethod
def attest(cls) -> bool:
"""
Expand All @@ -117,13 +119,13 @@ def attest(cls) -> bool:
sdk_nonce_for_attestation = cls._generate_nonce()

if verifier[VerifierFields.DEVICE] == Devices.GPU and verifier[VerifierFields.ENVIRONMENT] == Environment.LOCAL:
this_result, jwt_token = attest_gpu.attest_gpu_local(sdk_nonce_for_attestation)
this_result, jwt_token = attest_gpu_local.attest(sdk_nonce_for_attestation)

# save the token with the verifier
verifier[VerifierFields.JWT_TOKEN] = jwt_token
attest_result = attest_result and this_result
elif verifier[VerifierFields.DEVICE] == Devices.GPU and verifier[VerifierFields.ENVIRONMENT] == Environment.REMOTE:
this_result, jwt_token = attest_gpu.attest_gpu_remote(sdk_nonce_for_attestation, verifier[VerifierFields.URL])
this_result, jwt_token = attest_gpu_remote.attest(sdk_nonce_for_attestation, verifier[VerifierFields.URL])

# save the token with the verifier
verifier[VerifierFields.JWT_TOKEN] = jwt_token
Expand Down Expand Up @@ -235,9 +237,9 @@ def _validate_token_internal(cls, policy:str, eat_token: str) -> bool:
jwt_token = eat_claims[verifier_name]
verifier = cls.get_verifier_by_name(verifier_name)
if verifier_name == "LOCAL_GPU_CLAIMS":
this_result = attest_gpu.validate_gpu_token_local(verifier, jwt_token, policy)
this_result = attest_gpu_local.validate_gpu_token(verifier, jwt_token, policy)
elif verifier_name == "REMOTE_GPU_CLAIMS":
this_result = attest_gpu.validate_gpu_token_remote(verifier, jwt_token, policy)
this_result = attest_gpu_remote.validate_gpu_token(verifier, jwt_token, policy)
elif verifier_name == "TEST_CPU_CLAIMS":
claims = jwt.decode( jwt_token, "notasecret", algorithms="HS256")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#
# Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
import json
import jwt
import requests
import base64
from cryptography.hazmat.primitives import serialization
from cryptography.x509 import load_der_x509_certificate
from cryptography.hazmat.backends import default_backend
from nv_attestation_sdk import attestation
from urllib.parse import urlparse
from nv_attestation_sdk.gpu import gpu_utils

def validate_gpu_token(verifier, gpu_token: str, policy: str):
if policy == "" or gpu_token == "":
return False
decoded_token = jwt.decode(gpu_token, algorithms='HS256', verify=False, key="secret")
auth_rules = gpu_utils.get_auth_rules(policy)
return gpu_utils.validate_gpu_token_with_policy(decoded_token, auth_rules)

def attest(nonce):
attestation_result = False
from verifier import cc_admin
jwt_token = ""
try:
params = {"verbose": False,
"test_no_gpu": False,
"driver_rim": None,
"vbios_rim": None,
"user_mode": True,
'rim_root_cert': None,
'rim_service_url': None,
'allow_hold_cert': True,
'nonce': nonce}
attestation_result, jwt_token = cc_admin.attest(params)
except Exception as e:
print("\tException: ", e)
jwt_token = get_err_eat_token()
return attestation_result, jwt_token

def get_err_eat_token(errCode=1, errMsg="GPU_ATTESTATION_ERR"):
errJson = {'x-nv-err-message': errMsg, 'x-nv-err-code': errCode}
return jwt.encode(errJson,
'secret',
"HS256")

def build_payload(nonce, evidence, cert_chain):
data = dict()
data['nonce'] = nonce
encoded_evidence_bytes = evidence.encode("ascii")
encoded_evidence = base64.b64encode(encoded_evidence_bytes)
encoded_evidence = encoded_evidence.decode('utf-8')
data['evidence'] = encoded_evidence
data['arch'] = 'HOPPER'
data['certificate'] = str(cert_chain)
payload = json.dumps(data)
return payload

Original file line number Diff line number Diff line change
Expand Up @@ -10,40 +10,18 @@
from cryptography.hazmat.backends import default_backend
from nv_attestation_sdk import attestation
from urllib.parse import urlparse
from nv_attestation_sdk.gpu import gpu_utils

def validate_gpu_token_local(verifier, gpu_token: str, policy: str):
if policy == "" or gpu_token == "":
return False
decoded_token = jwt.decode(gpu_token, algorithms='HS256', verify=False, key="secret")
auth_rules = get_auth_rules(policy)
return validate_gpu_token_with_policy(decoded_token, auth_rules)

def validate_gpu_token_with_policy(token: str, policy: str):
for key in policy:
if key in token:
if type(policy[key]) is dict:
return validate_gpu_token_with_policy(token[key], policy[key])
else:
if token[key] != policy[key]:
print("\t[ERROR] Invalid token. Authorized claims does not match the appraisal policy: ", key)
return False
else:
print("\t[ERROR] Invalid token. Authorized claims does not match the appraisal policy: ", key)
return False
return True

def get_auth_rules(policy: str):
if policy == "":
return None
policy_obj = json.loads(policy)
return policy_obj['authorization-rules']
def attest(nonce, verifierUrl):
gpu_evidence_list = generate_evidence(nonce)
return verify_evidence(nonce, gpu_evidence_list, verifierUrl)

def create_jwks_url(verifier_url:str):
parsed_url = urlparse(verifier_url)
jwks_url = parsed_url.scheme + "://" + parsed_url.netloc + "/" + ".well-known/jwks.json"
return jwks_url

def validate_gpu_token_remote(verifier, gpu_token: str, policy: str):
def validate_gpu_token(verifier, gpu_token: str, policy: str):
verifier_url = verifier[attestation.VerifierFields.URL]
jwks_url = create_jwks_url(verifier_url)
print("***** Validating Signature using JWKS endpont " + jwks_url + " ****** ")
Expand Down Expand Up @@ -84,8 +62,8 @@ def validate_gpu_token_remote(verifier, gpu_token: str, policy: str):
json_formatted_str = json.dumps(decoded_token, indent=2)
print("Decoded Token " , str(json_formatted_str))
print("***** JWT token signature is valid. *****")
auth_rules = get_auth_rules(policy)
return validate_gpu_token_with_policy(decoded_token, auth_rules)
auth_rules = gpu_utils.get_auth_rules(policy)
return gpu_utils.validate_gpu_token_with_policy(decoded_token, auth_rules)
except jwt.ExpiredSignatureError:
print("JWT token has expired.")
except jwt.InvalidTokenError as e:
Expand All @@ -94,33 +72,19 @@ def validate_gpu_token_remote(verifier, gpu_token: str, policy: str):
print("No matching key or x5c key found for the provided kid.")
return False

def attest_gpu_local(nonce):
attestation_result = False
def generate_evidence(nonce=""):
print("generate_evidence")
from verifier import cc_admin
jwt_token = ""
try:
params = {"verbose": True,
"test_no_gpu": False,
"driver_rim": "/usr/share/nvidia/rim/RIM_GH100PROD.swidtag",
"vbios_rim": None,
"user_mode": True,
'nonce': nonce}
attestation_result, jwt_token = cc_admin.attest(params)
except Exception as e:
print("\tException: ", e)
jwt_token = get_err_eat_token()
return attestation_result, jwt_token
gpu_evidence_list = cc_admin.collect_gpu_evidence(nonce)
return gpu_evidence_list

def attest_gpu_remote(nonce, verifierUrl):
from verifier import cc_admin
def verify_evidence(nonce, gpu_evidence_list, verifierUrl="https://nras.attestation.nvidia.com/v1/attest/gpu"):
attestation_result = False
jwt_token = ""
headers = {
'Content-Type': 'application/json'
}
try:
print("attest_gpu_remote")
gpu_evidence_list = cc_admin.collect_gpu_evidence(nonce)
for i , gpu_evidence in enumerate(gpu_evidence_list):
gpu_evidence = gpu_evidence_list[i]
current_gpu_status = False
Expand All @@ -135,7 +99,7 @@ def attest_gpu_remote(nonce, verifierUrl):
current_gpu_status = True
else:
print("**** Attestation Failed ****")
print("received NRAS response code: ", response.status_code)
print("received NRAS response: ", reponse_json)
#jwt_token = get_err_eat_token(reponse_json['errorCode'], reponse_json['message'])
if i == 0:
attestation_result = current_gpu_status
Expand All @@ -145,12 +109,6 @@ def attest_gpu_remote(nonce, verifierUrl):
print("\tException: ", e)
return attestation_result, jwt_token

def get_err_eat_token(errCode=1, errMsg="GPU_ATTESTATION_ERR"):
errJson = {'x-nv-err-message': errMsg, 'x-nv-err-code': errCode}
return jwt.encode(errJson,
'secret',
"HS256")

def build_payload(nonce, evidence, cert_chain):
data = dict()
data['nonce'] = nonce
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import json

def validate_gpu_token_with_policy(token: str, auth_rules: str):
for key in auth_rules:
if key in token:
if type(auth_rules[key]) is dict:
return validate_gpu_token_with_policy(token[key], auth_rules[key])
else:
if token[key] != auth_rules[key]:
print("\t[ERROR] Invalid token. Authorized claims does not match the appraisal policy: ", key)
return False
else:
print("\t[ERROR] Invalid token. Authorized claims does not match the appraisal policy: ", key)
return False
return True

def get_auth_rules(policy: str):
if policy == "":
return None
policy_obj = json.loads(policy)
return policy_obj['authorization-rules']
2 changes: 2 additions & 0 deletions guest_tools/attestation_sdk/tests/RemoteGPUTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@

print ("[RemoteGPUTest] call validate_token() - expecting True")
print(client.validate_token(remote_att_result_policy))


15 changes: 15 additions & 0 deletions guest_tools/attestation_sdk/tests/RemoteGPUTest2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
from nv_attestation_sdk.gpu import attest_gpu_remote
import secrets
nonce = secrets.token_bytes(32).hex()

evidence = attest_gpu_remote.generate_evidence(nonce)
print(evidence)

verify_result = attest_gpu_remote.verify_evidence(nonce,evidence, "https://nras.attestation.nvidia.com/v1/attest/gpu")

print(verify_result)
4 changes: 4 additions & 0 deletions guest_tools/gpu_verifiers/local_gpu_verifier/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ To run the cc_admin module, use the following command:
--user_mode Runs the gpu attestation in user mode.
--allow_hold_cert If the user wants to continue the attestation in case of the OCSP revocation status of the certificate in the RIM files is 'certificate_hold'
--nonce Specify a Nonce for Attestation Report
--rim_root_cert RIM_ROOT_CERT
The absolute path to the root certificate to be used for verifying the certificate chain of the driver and vBIOS RIM certificate chain
--rim_service_url RIM_SERVICE_URL
The URL to be used for fetching driver and vBIOS RIM files. eg: https://rim.nvidia.com/rims/

If you need information about any function, use

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "verifier"
version = "1.1.0"
version = "1.2.0"
description = "A Python-based tool that validates GPU measurements by comparing GPU runtime measurements with authenticated golden measurements"
authors = [
{name = "NVIDIA"}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"__copyright__",
]

__version__ = "1.1.0"
__version__ = "1.2.0"

__author__ = "NVIDIA CORPORATION"
__copyright__ = f"Copyright (c) 2021-2023 {__author__}"
Loading

0 comments on commit 8db7fb5

Please sign in to comment.