-
Notifications
You must be signed in to change notification settings - Fork 7
attesters: add NVIDIA GPU evidence plugin #74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 5 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
00fd037
attesters: add NVIDIA GPU evidence plugin
cowbon 7f9b7e2
Align GPU evidence encoding with CDDL
cowbon 9c6f7e7
Update Nvidia GPU evidence media type
cowbon 2ea0941
Remove Nvidia GPU evidence CBOR media type
cowbon 02899ba
Remove GPU evidence CBOR helpers
cowbon d969280
Address NVIDIA GPU attester review feedback
cowbon 55c1eb4
Address NVIDIA GPU attester review feedback
cowbon 9441a2b
Use value receivers in NVIDIA GPU plugin
cowbon File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ | |
|
|
||
| SUBDIR := tsm | ||
| SUBDIR += mocktsm | ||
| SUBDIR += gpu | ||
|
|
||
| clean: ; $(RM) -rf ./bin | ||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| # Copyright 2026 Contributors to the Veraison project. | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| .DEFAULT_GOAL := test | ||
|
|
||
| GOPKG := github.com/veraison/ratsd/attesters/gpu | ||
| SRCS := $(wildcard *.go) | ||
|
|
||
| SUBDIR += plugin | ||
|
|
||
| include ../../mk/subdir.mk |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,203 @@ | ||
| // Copyright 2026 Contributors to the Veraison project. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| package gpu | ||
|
|
||
| import ( | ||
| "encoding/json" | ||
| "fmt" | ||
|
|
||
| "github.com/NVIDIA/go-nvml/pkg/nvml" | ||
| nvgpu "github.com/confidentsecurity/go-nvtrust/pkg/gonvtrust/gpu" | ||
| "github.com/veraison/ratsd/proto/compositor" | ||
| "github.com/veraison/ratsd/tokens" | ||
| ) | ||
|
|
||
| const ( | ||
| ApplicationvndVeraisonNvGpuEvidenceJSON = tokens.GPUEvidenceMediaTypeJSON | ||
| gpuNonceSize = nvml.CC_GPU_CEC_NONCE_SIZE | ||
| ) | ||
|
|
||
| var ( | ||
| sid = &compositor.SubAttesterID{ | ||
| Name: "nv-gpu-evidence", | ||
| Version: "1.0.0", | ||
| } | ||
|
|
||
| supportedFormats = []*compositor.Format{ | ||
| { | ||
| ContentType: ApplicationvndVeraisonNvGpuEvidenceJSON, | ||
| NonceSize: gpuNonceSize, | ||
| }, | ||
| } | ||
|
|
||
| statusSucceeded = &compositor.Status{Result: true, Error: ""} | ||
| ) | ||
|
|
||
| type evidenceCollector interface { | ||
| CollectEvidence(nonce []byte) ([]nvgpu.GPUDevice, error) | ||
| Shutdown() error | ||
| } | ||
|
|
||
| type collectorFactory func() (evidenceCollector, error) | ||
|
|
||
| type GPUPlugin struct { | ||
| newCollector collectorFactory | ||
| } | ||
|
|
||
| func NewPlugin() *GPUPlugin { | ||
| return &GPUPlugin{newCollector: defaultCollectorFactory} | ||
| } | ||
|
|
||
| func defaultCollectorFactory() (evidenceCollector, error) { | ||
| return nvgpu.NewNvmlGPUAdmin(nil) | ||
| } | ||
|
|
||
| func getEvidenceError(e error) *compositor.EvidenceOut { | ||
| return &compositor.EvidenceOut{ | ||
| Status: &compositor.Status{ | ||
| Result: false, | ||
| Error: e.Error(), | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| func (g *GPUPlugin) GetOptions() *compositor.OptionsOut { | ||
| return &compositor.OptionsOut{ | ||
| Options: []*compositor.Option{}, | ||
| Status: statusSucceeded, | ||
| } | ||
| } | ||
|
|
||
| func (g *GPUPlugin) GetSubAttesterID() *compositor.SubAttesterIDOut { | ||
| return &compositor.SubAttesterIDOut{ | ||
| SubAttesterID: sid, | ||
| Status: statusSucceeded, | ||
| } | ||
| } | ||
|
|
||
| func (g *GPUPlugin) GetSupportedFormats() *compositor.SupportedFormatsOut { | ||
| collector, err := g.newCollector() | ||
|
cowbon marked this conversation as resolved.
Outdated
|
||
| if err != nil { | ||
| return &compositor.SupportedFormatsOut{ | ||
| Status: &compositor.Status{ | ||
| Result: false, | ||
| Error: fmt.Sprintf("GPU evidence collection is not available: %s", err.Error()), | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| if err := collector.Shutdown(); err != nil { | ||
| return &compositor.SupportedFormatsOut{ | ||
| Status: &compositor.Status{ | ||
| Result: false, | ||
| Error: fmt.Sprintf("GPU evidence collection is not available: %s", err.Error()), | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| return &compositor.SupportedFormatsOut{ | ||
| Status: statusSucceeded, | ||
| Formats: supportedFormats, | ||
| } | ||
| } | ||
|
|
||
| func (g *GPUPlugin) GetEvidence(in *compositor.EvidenceIn) *compositor.EvidenceOut { | ||
| if uint32(len(in.Nonce)) != gpuNonceSize { | ||
| errMsg := fmt.Errorf( | ||
| "nonce size of the GPU attester should be %d, got %d", | ||
| gpuNonceSize, uint32(len(in.Nonce)), | ||
| ) | ||
| return getEvidenceError(errMsg) | ||
| } | ||
|
|
||
| if err := validateOptions(in.Options); err != nil { | ||
| return getEvidenceError(err) | ||
| } | ||
|
|
||
| if !supportsFormat(in.ContentType) { | ||
| return getEvidenceError(fmt.Errorf("no supported format in gpu plugin matches the requested format")) | ||
| } | ||
|
|
||
| collector, err := g.newCollector() | ||
| if err != nil { | ||
| return getEvidenceError(fmt.Errorf("failed to initialize GPU evidence collector: %v", err)) | ||
| } | ||
|
|
||
| devices, collectErr := collector.CollectEvidence(in.Nonce) | ||
| shutdownErr := collector.Shutdown() | ||
|
|
||
| if collectErr != nil { | ||
| return getEvidenceError(fmt.Errorf("failed to collect GPU evidence: %v", collectErr)) | ||
| } | ||
| if shutdownErr != nil { | ||
| return getEvidenceError(fmt.Errorf("failed to shutdown GPU evidence collector: %v", shutdownErr)) | ||
| } | ||
|
|
||
| encodedEvidence, err := encodeEvidence(in.ContentType, in.Nonce, devices) | ||
| if err != nil { | ||
| return getEvidenceError(err) | ||
| } | ||
|
|
||
| return &compositor.EvidenceOut{ | ||
| Status: statusSucceeded, | ||
| Evidence: encodedEvidence, | ||
| } | ||
| } | ||
|
|
||
| func supportsFormat(contentType string) bool { | ||
| for _, format := range supportedFormats { | ||
| if format.ContentType == contentType { | ||
| return true | ||
| } | ||
| } | ||
|
|
||
| return false | ||
| } | ||
|
|
||
| func validateOptions(options []byte) error { | ||
| if len(options) == 0 || string(options) == "null" { | ||
| return nil | ||
| } | ||
|
|
||
| var parsed map[string]json.RawMessage | ||
| if err := json.Unmarshal(options, &parsed); err != nil { | ||
| return fmt.Errorf("failed to parse %s: %v", options, err) | ||
| } | ||
|
|
||
| if len(parsed) > 0 { | ||
| return fmt.Errorf("gpu attester does not support options") | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func encodeEvidence(contentType string, nonce []byte, devices []nvgpu.GPUDevice) ([]byte, error) { | ||
| token := &tokens.GPUEvidence{ | ||
| Devices: make([]tokens.GPUDeviceEvidence, len(devices)), | ||
| } | ||
|
|
||
| for i, device := range devices { | ||
| certChain, err := device.Certificate().EncodeBase64() | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to encode GPU certificate chain for device %d: %v", i, err) | ||
| } | ||
|
|
||
| token.Devices[i] = tokens.GPUDeviceEvidence{ | ||
| Nonce: nonce, | ||
| Arch: device.Arch(), | ||
| AttestationReport: device.AttestationReport(), | ||
| CertificateChain: certChain, | ||
| } | ||
| } | ||
|
|
||
| switch contentType { | ||
| case ApplicationvndVeraisonNvGpuEvidenceJSON: | ||
| encodedEvidence, err := token.ToJSON() | ||
| if err != nil { | ||
| return nil, fmt.Errorf("failed to JSON encode GPU evidence: %v", err) | ||
| } | ||
| return encodedEvidence, nil | ||
| default: | ||
| return nil, fmt.Errorf("no supported format in gpu plugin matches the requested format") | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.