mirror of
https://github.com/ultravioletrs/cocos.git
synced 2026-06-23 04:10:25 +00:00
COCOS-591: Add support for GPU CC attestation (#592)
CI / lint (push) Has been cancelled
CI / test (agent) (push) Has been cancelled
CI / test (cli) (push) Has been cancelled
CI / test (cmd) (push) Has been cancelled
CI / test (internal) (push) Has been cancelled
CI / test (manager, true) (push) Has been cancelled
CI / test (pkg) (push) Has been cancelled
CI / upload-coverage (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (agent) (push) Has been cancelled
CI / test (cli) (push) Has been cancelled
CI / test (cmd) (push) Has been cancelled
CI / test (internal) (push) Has been cancelled
CI / test (manager, true) (push) Has been cancelled
CI / test (pkg) (push) Has been cancelled
CI / upload-coverage (push) Has been cancelled
* Added GPU evidence collection * Added GPU evidence verification * Added make command for nvattest helper * Added command for installing all services * changed attestion-service.service so it knows where the helper is * Possible IGVM script bug * Possible bug * Bug * bug * Revert "bug" This reverts commitd81d67e73d. * Revert "Bug" This reverts commit5e566d53c1. * Revert "Possible bug" This reverts commit47d13fe583. * Revert "Possible IGVM script bug" This reverts commit3fb1b79537. * Revert "changed attestion-service.service so it knows where the helper is" This reverts commitf9f11ed183. * Revert "Added command for installing all services" This reverts commit5dcf7a5c0a. * NOISSUE - Enforce binding label check (#589) * NOISSUE - Implement extensible resource downloader framework with support for S3, GCS, and OCI sources (#590) * feat: implement extensible resource downloader framework with support for S3, GCS, and OCI sources Signed-off-by: SammyOina <sammyoina@gmail.com> * refactor: improve resource URL parsing and add support for bare OCI image references Signed-off-by: Sammy Oina <sammyoina@gmail.com> * fix: add empty string check and slash requirement for OCI image inference, and update python unit tests with event mock expectations Signed-off-by: Sammy Oina <sammyoina@gmail.com> * refactor: introduce OCIClient interface, add test coverage for decryption, and improve resource download error handling Signed-off-by: Sammy Oina <sammyoina@gmail.com> * chore: remove trailing whitespace in OCI downloader and HTTP tests Signed-off-by: Sammy Oina <sammyoina@gmail.com> --------- Signed-off-by: SammyOina <sammyoina@gmail.com> Signed-off-by: Sammy Oina <sammyoina@gmail.com> * Refactored baed on comments * Added GPU evidence collection * Added GPU evidence verification * Added make command for nvattest helper * Added command for installing all services * changed attestion-service.service so it knows where the helper is * Possible IGVM script bug * Possible bug * Bug * bug * Revert "bug" This reverts commitd81d67e73d. * Revert "Bug" This reverts commit5e566d53c1. * Revert "Possible bug" This reverts commit47d13fe583. * Revert "Possible IGVM script bug" This reverts commit3fb1b79537. * Revert "changed attestion-service.service so it knows where the helper is" This reverts commitf9f11ed183. * Revert "Added command for installing all services" This reverts commit5dcf7a5c0a. * Refactored baed on comments * fixed lint error * fixed tests * Fixed according to comments * COCOS-584 - Support multiple kbs (#587) * feat: Implement per-resource KBS configuration, allowing algorithms and datasets to specify individual KBS URLs. Signed-off-by: Sammy Oina <sammyoina@gmail.com> * refactor: Encapsulate CLI error handling and CVM certificate paths within the CLI struct, and add algorithm type to agent's algorithm structure. Signed-off-by: Sammy Oina <sammyoina@gmail.com> * style: Remove blank lines and fix indentation in CLI commands. Signed-off-by: Sammy Oina <sammyoina@gmail.com> * refactor: Update downloadAndDecryptGenericResource to accept KBS URL as a parameter and adjust related tests Signed-off-by: Sammy Oina <sammyoina@gmail.com> * refactor: group CLI configuration into structured types and simplify skopeo decryption key handling Signed-off-by: Sammy Oina <sammyoina@gmail.com> --------- Signed-off-by: Sammy Oina <sammyoina@gmail.com> * Added GPU evidence collection * Added GPU evidence verification * Added make command for nvattest helper * Added command for installing all services * changed attestion-service.service so it knows where the helper is * Possible IGVM script bug * Possible bug * Bug * bug * Revert "bug" This reverts commitd81d67e73d. * Revert "Bug" This reverts commit5e566d53c1. * Revert "Possible bug" This reverts commit47d13fe583. * Revert "Possible IGVM script bug" This reverts commit3fb1b79537. * Revert "changed attestion-service.service so it knows where the helper is" This reverts commitf9f11ed183. * Revert "Added command for installing all services" This reverts commit5dcf7a5c0a. * Refactored baed on comments * Added GPU evidence collection * Added GPU evidence verification * Added make command for nvattest helper * Added command for installing all services * changed attestion-service.service so it knows where the helper is * Possible IGVM script bug * Possible bug * Bug * bug * Revert "bug" This reverts commitd81d67e73d. * Revert "Bug" This reverts commit5e566d53c1. * Revert "Possible bug" This reverts commit47d13fe583. * Revert "Possible IGVM script bug" This reverts commit3fb1b79537. * Revert "changed attestion-service.service so it knows where the helper is" This reverts commitf9f11ed183. * Revert "Added command for installing all services" This reverts commit5dcf7a5c0a. * Refactored baed on comments * fixed lint error * fixed tests * Fixed according to comments --------- Signed-off-by: SammyOina <sammyoina@gmail.com> Signed-off-by: Sammy Oina <sammyoina@gmail.com> Co-authored-by: Danko Miladinovic <72250944+danko-miladinovic@users.noreply.github.com> Co-authored-by: Sammy Kerata Oina <44265300+SammyOina@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
attestationpb "github.com/ultravioletrs/cocos/internal/proto/attestation/v1"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/eat"
|
||||
attestationgpu "github.com/ultravioletrs/cocos/pkg/attestation/gpu"
|
||||
)
|
||||
|
||||
func newGPUCollector(cfg config) (attestationgpu.Collector, error) {
|
||||
if strings.TrimSpace(cfg.GPUHelperPath) == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return attestationgpu.NewCommandCollector(cfg.GPUHelperPath, cfg.GPUHelperTimeout)
|
||||
}
|
||||
|
||||
func (s *service) claimOptions(ctx context.Context, req *attestationpb.AttestationRequest, platformType attestation.PlatformType) ([]eat.ClaimsOption, error) {
|
||||
var opts []eat.ClaimsOption
|
||||
|
||||
if s.gpuCollector != nil && shouldCollectGPU(platformType) {
|
||||
sessionNonce := requestNonce(req)
|
||||
gpuNonce := deriveComponentNonce(sessionNonce, "gpu")
|
||||
|
||||
evidence, err := s.gpuCollector.Collect(ctx, gpuNonce)
|
||||
if err != nil {
|
||||
// GPU evidence is opportunistic: if no supported CC-capable GPU is
|
||||
// attached, or the helper cannot collect evidence, we continue with
|
||||
// the root CPU/TEE attestation instead of failing the whole request.
|
||||
s.logger.Warn(fmt.Sprintf("[ATTESTATION-SERVICE] Skipping optional GPU evidence collection: %s", err))
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
s.logger.Info(fmt.Sprintf("[ATTESTATION-SERVICE] Collected GPU evidence: format=%s bytes=%d",
|
||||
evidence.EvidenceFormat, len(evidence.RawEvidence)))
|
||||
|
||||
opts = append(opts, eat.WithGPU(&eat.GPUExtensions{
|
||||
Vendor: evidence.Vendor,
|
||||
EvidenceFormat: evidence.EvidenceFormat,
|
||||
Nonce: gpuNonce,
|
||||
EvidenceJSON: evidence.RawEvidence,
|
||||
}))
|
||||
}
|
||||
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
func shouldCollectGPU(platformType attestation.PlatformType) bool {
|
||||
switch platformType {
|
||||
case attestation.SNP, attestation.SNPvTPM, attestation.TDX, attestation.Azure:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func requestNonce(req *attestationpb.AttestationRequest) []byte {
|
||||
if len(req.Nonce) > 0 {
|
||||
return append([]byte(nil), req.Nonce...)
|
||||
}
|
||||
|
||||
return append([]byte(nil), req.ReportData...)
|
||||
}
|
||||
|
||||
func deriveComponentNonce(sessionNonce []byte, component string) []byte {
|
||||
digest := sha256.Sum256(append(append([]byte(nil), sessionNonce...), []byte(":"+component)...))
|
||||
return digest[:]
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"log/slog"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
attestationpb "github.com/ultravioletrs/cocos/internal/proto/attestation/v1"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation"
|
||||
attestationgpu "github.com/ultravioletrs/cocos/pkg/attestation/gpu"
|
||||
)
|
||||
|
||||
func TestRequestNonce(t *testing.T) {
|
||||
req := &attestationpb.AttestationRequest{
|
||||
ReportData: []byte("report"),
|
||||
Nonce: []byte("nonce"),
|
||||
}
|
||||
|
||||
assert.Equal(t, []byte("nonce"), requestNonce(req))
|
||||
|
||||
req.Nonce = nil
|
||||
assert.Equal(t, []byte("report"), requestNonce(req))
|
||||
}
|
||||
|
||||
func TestDeriveComponentNonce(t *testing.T) {
|
||||
sessionNonce := []byte("session-nonce")
|
||||
|
||||
gpuNonce := deriveComponentNonce(sessionNonce, "gpu")
|
||||
gpuNonceAgain := deriveComponentNonce(sessionNonce, "gpu")
|
||||
teeNonce := deriveComponentNonce(sessionNonce, "tee")
|
||||
|
||||
assert.Len(t, gpuNonce, 32)
|
||||
assert.Equal(t, gpuNonce, gpuNonceAgain)
|
||||
assert.NotEqual(t, gpuNonce, teeNonce)
|
||||
}
|
||||
|
||||
func TestShouldCollectGPU(t *testing.T) {
|
||||
assert.True(t, shouldCollectGPU(attestation.SNP))
|
||||
assert.True(t, shouldCollectGPU(attestation.SNPvTPM))
|
||||
assert.True(t, shouldCollectGPU(attestation.TDX))
|
||||
assert.False(t, shouldCollectGPU(attestation.VTPM))
|
||||
assert.False(t, shouldCollectGPU(attestation.NoCC))
|
||||
}
|
||||
|
||||
func TestNewGPUCollector(t *testing.T) {
|
||||
collector, err := newGPUCollector(config{})
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, collector)
|
||||
|
||||
collector, err = newGPUCollector(config{
|
||||
GPUHelperPath: "/tmp/helper",
|
||||
GPUHelperTimeout: 0,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, collector)
|
||||
}
|
||||
|
||||
func TestClaimOptions_SkipsOptionalGPUFailure(t *testing.T) {
|
||||
svc := &service{
|
||||
logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
|
||||
gpuCollector: failingCollector{},
|
||||
}
|
||||
|
||||
req := &attestationpb.AttestationRequest{
|
||||
ReportData: []byte("report-data"),
|
||||
Nonce: []byte("nonce-data"),
|
||||
}
|
||||
|
||||
opts, err := svc.claimOptions(context.Background(), req, attestation.TDX)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, opts)
|
||||
}
|
||||
|
||||
type failingCollector struct{}
|
||||
|
||||
func (failingCollector) Collect(context.Context, []byte) (*attestationgpu.Evidence, error) {
|
||||
return nil, assert.AnError
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
mglog "github.com/absmach/supermq/logger"
|
||||
"github.com/caarlos0/env/v11"
|
||||
@@ -22,6 +23,7 @@ import (
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/azure"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/ccaa"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/eat"
|
||||
attestationgpu "github.com/ultravioletrs/cocos/pkg/attestation/gpu"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/tdx"
|
||||
"github.com/ultravioletrs/cocos/pkg/attestation/vtpm"
|
||||
logclient "github.com/ultravioletrs/cocos/pkg/clients/grpc/log"
|
||||
@@ -35,16 +37,29 @@ const (
|
||||
)
|
||||
|
||||
type config struct {
|
||||
LogLevel string `env:"ATTESTATION_LOG_LEVEL" envDefault:"debug"`
|
||||
Vmpl int `env:"ATTESTATION_VMPL" envDefault:"2"`
|
||||
AgentMaaURL string `env:"AGENT_MAA_URL" envDefault:"https://sharedeus2.eus2.attest.azure.net"`
|
||||
AgentOSBuild string `env:"AGENT_OS_BUILD" envDefault:"UVC"`
|
||||
AgentOSDistro string `env:"AGENT_OS_DISTRO" envDefault:"UVC"`
|
||||
AgentOSType string `env:"AGENT_OS_TYPE" envDefault:"UVC"`
|
||||
EATFormat string `env:"ATTESTATION_EAT_FORMAT" envDefault:"CBOR"` // JWT or CBOR
|
||||
EATIssuer string `env:"ATTESTATION_EAT_ISSUER" envDefault:"cocos-attestation-service"`
|
||||
UseCCAttestationAgent bool `env:"USE_CC_ATTESTATION_AGENT" envDefault:"false"`
|
||||
CCAgentAddress string `env:"CC_AGENT_ADDRESS" envDefault:"127.0.0.1:50002"`
|
||||
LogLevel string `env:"ATTESTATION_LOG_LEVEL" envDefault:"debug"`
|
||||
Vmpl int `env:"ATTESTATION_VMPL" envDefault:"2"`
|
||||
AgentMaaURL string `env:"AGENT_MAA_URL" envDefault:"https://sharedeus2.eus2.attest.azure.net"`
|
||||
AgentOSBuild string `env:"AGENT_OS_BUILD" envDefault:"UVC"`
|
||||
AgentOSDistro string `env:"AGENT_OS_DISTRO" envDefault:"UVC"`
|
||||
AgentOSType string `env:"AGENT_OS_TYPE" envDefault:"UVC"`
|
||||
EATFormat string `env:"ATTESTATION_EAT_FORMAT" envDefault:"CBOR"` // JWT or CBOR
|
||||
EATIssuer string `env:"ATTESTATION_EAT_ISSUER" envDefault:"cocos-attestation-service"`
|
||||
UseCCAttestationAgent bool `env:"USE_CC_ATTESTATION_AGENT" envDefault:"false"`
|
||||
CCAgentAddress string `env:"CC_AGENT_ADDRESS" envDefault:"127.0.0.1:50002"`
|
||||
GPUHelperPath string `env:"ATTESTATION_GPU_HELPER_PATH" envDefault:""`
|
||||
GPUHelperTimeout time.Duration `env:"ATTESTATION_GPU_HELPER_TIMEOUT" envDefault:"30s"`
|
||||
|
||||
// Future KBS Integration Configuration
|
||||
// When KBS support is added, these fields will enable:
|
||||
// - Remote attestation verification via KBS
|
||||
// - Encrypted algorithm/dataset retrieval
|
||||
// - Per-computation secret provisioning
|
||||
//
|
||||
// Example future fields:
|
||||
// KBSEndpoint string `env:"KBS_ENDPOINT" envDefault:""` // Optional KBS URL
|
||||
// KBSEnabled bool `env:"KBS_ENABLED" envDefault:"false"`
|
||||
// KBSTimeout int `env:"KBS_TIMEOUT_SECONDS" envDefault:"30"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
@@ -218,13 +233,24 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
gpuCollector, err := newGPUCollector(cfg)
|
||||
if err != nil {
|
||||
logger.Error(fmt.Sprintf("failed to configure GPU attestation collector: %s", err))
|
||||
exitCode = 1
|
||||
return
|
||||
}
|
||||
if gpuCollector != nil {
|
||||
logger.Info(fmt.Sprintf("[ATTESTATION-SERVICE] GPU evidence collection enabled via helper %s", cfg.GPUHelperPath))
|
||||
}
|
||||
|
||||
grpcServer := grpc.NewServer()
|
||||
svc := &service{
|
||||
provider: provider,
|
||||
logger: logger,
|
||||
signingKey: signingKey,
|
||||
eatFormat: cfg.EATFormat,
|
||||
eatIssuer: cfg.EATIssuer,
|
||||
provider: provider,
|
||||
logger: logger,
|
||||
signingKey: signingKey,
|
||||
eatFormat: cfg.EATFormat,
|
||||
eatIssuer: cfg.EATIssuer,
|
||||
gpuCollector: gpuCollector,
|
||||
}
|
||||
attestationpb.RegisterAttestationServiceServer(grpcServer, svc)
|
||||
|
||||
@@ -256,11 +282,12 @@ func main() {
|
||||
|
||||
type service struct {
|
||||
attestationpb.UnimplementedAttestationServiceServer
|
||||
provider attestation.Provider
|
||||
logger *slog.Logger
|
||||
signingKey *ecdsa.PrivateKey
|
||||
eatFormat string
|
||||
eatIssuer string
|
||||
provider attestation.Provider
|
||||
logger *slog.Logger
|
||||
signingKey *ecdsa.PrivateKey
|
||||
eatFormat string
|
||||
eatIssuer string
|
||||
gpuCollector attestationgpu.Collector
|
||||
}
|
||||
|
||||
func (s *service) FetchAttestation(ctx context.Context, req *attestationpb.AttestationRequest) (*attestationpb.AttestationResponse, error) {
|
||||
@@ -319,12 +346,14 @@ func (s *service) FetchAttestation(ctx context.Context, req *attestationpb.Attes
|
||||
}
|
||||
|
||||
// Create EAT claims from binary report
|
||||
nonce := req.ReportData
|
||||
if len(req.Nonce) > 0 {
|
||||
nonce = req.Nonce
|
||||
nonce := requestNonce(req)
|
||||
|
||||
claimOpts, err := s.claimOptions(ctx, req, platformType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
claims, err := eat.NewEATClaims(binaryReport, nonce, platformType)
|
||||
claims, err := eat.NewEATClaims(binaryReport, nonce, platformType, claimOpts...)
|
||||
if err != nil {
|
||||
s.logger.Error(fmt.Sprintf("failed to create EAT claims: %s", err))
|
||||
return nil, fmt.Errorf("failed to create EAT claims: %w", err)
|
||||
|
||||
Reference in New Issue
Block a user