diff --git a/.github/workflows/ci-go-cover.yml b/.github/workflows/ci-go-cover.yml index 6c527509..2ad49870 100644 --- a/.github/workflows/ci-go-cover.yml +++ b/.github/workflows/ci-go-cover.yml @@ -24,7 +24,7 @@ jobs: GO111MODULE: on CI_PIPELINE: true steps: - - uses: actions/setup-go@v3 + - uses: actions/setup-go@v5 with: go-version: "1.24" - name: Checkout code diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6673b562..ecd53efa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ jobs: GO111MODULE: on CI_PIPELINE: true steps: - - uses: actions/setup-go@v3 + - uses: actions/setup-go@v5 with: go-version: "1.24" - name: Checkout code diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index dab21e3b..ecb84eb3 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -8,7 +8,7 @@ jobs: env: GO111MODULE: on steps: - - uses: actions/setup-go@v3 + - uses: actions/setup-go@v5 with: go-version: "1.24" - name: Checkout code diff --git a/.github/workflows/time-package.yml b/.github/workflows/time-package.yml index 9e2c8143..1c9d86bd 100644 --- a/.github/workflows/time-package.yml +++ b/.github/workflows/time-package.yml @@ -16,7 +16,7 @@ jobs: GO111MODULE: on CI_PIPELINE: true steps: - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: go-version: "1.24" cache: false diff --git a/Makefile b/Makefile index e117031e..9a544119 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,7 @@ SHELL = /bin/bash SUBDIR += builtin SUBDIR += config +SUBDIR += coserv SUBDIR += handler SUBDIR += kvstore SUBDIR += log @@ -49,6 +50,11 @@ IGNORE_COVERAGE += github.com/veraison/services/verification/verifier IGNORE_COVERAGE += github.com/veraison/services/vts/cmd/vts-service IGNORE_COVERAGE += github.com/veraison/services/vts/trustedservices IGNORE_COVERAGE += github.com/veraison/services/vtsclient +IGNORE_COVERAGE += github.com/veraison/services/coserv/api +IGNORE_COVERAGE += github.com/veraison/services/coserv/cmd/coserv-service +IGNORE_COVERAGE += github.com/veraison/services/scheme/amd-kds-coserv +IGNORE_COVERAGE += github.com/veraison/services/scheme/nvidia-coserv +IGNORE_COVERAGE += github.com/veraison/services/scheme/arm-cca include mk/cover.mk diff --git a/builtin/Makefile b/builtin/Makefile index 0667c4b2..9421bae8 100644 --- a/builtin/Makefile +++ b/builtin/Makefile @@ -4,9 +4,6 @@ all: -schemes.gen.go: $(shell find ../scheme/ -type f -name \*.go) - python3 ../scripts/gen-schemes ../scheme/ > schemes.gen.go - include ../mk/common.mk include ../mk/pkg.mk include ../mk/lint.mk diff --git a/builtin/README.md b/builtin/README.md index b2b687e0..c98f5a99 100644 --- a/builtin/README.md +++ b/builtin/README.md @@ -4,11 +4,7 @@ and loading (and, thus, any potential security issues associated with running external executables). Instead, plugins are "discovered" at build time by iterating over an array of -plugin implementations defined inside [schemes.gen.go](schemes.gen.go), which, -in turn, can be (re-)generated by running [gen-schemes](../scripts/gen-schemes) -script. +plugin implementations defined inside [schemes.go](schemes.go). -> **Note**: `gen-schemes` script repeatedly invokes `gopls` and `guru` in order -> to identify plugin implementations. This means that it is very slow. Because of -> this, a static version of `schemes.gen.go` is currently submitted as part of -> the source. +> **Note**: When a new plugin is added, [schemes.go](schemes.go) must be +> manually updated. diff --git a/builtin/builtin_loader.go b/builtin/builtin_loader.go index cc4bbb6e..4019fd9f 100644 --- a/builtin/builtin_loader.go +++ b/builtin/builtin_loader.go @@ -1,4 +1,4 @@ -// Copyright 2023 Contributors to the Veraison project. +// Copyright 2023-2025 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package builtin @@ -146,7 +146,7 @@ func GetBuiltinHandleByAttestationSchemeUsing[I plugin.IPluggable]( if ictx.GetAttestationScheme() != scheme { continue } - ldr.logger.Debugw("found plugin implenting scheme", + ldr.logger.Debugw("found plugin implementing scheme", "plugin", name, "scheme", scheme) impl, ok = ictx.(I) @@ -158,7 +158,7 @@ func GetBuiltinHandleByAttestationSchemeUsing[I plugin.IPluggable]( if !found { return *new(I), fmt.Errorf( // nolint:gocritic - "could not find plugin providing schdme %q and implementing interface %s", + "could not find plugin providing scheme %q and implementing interface %s", scheme, iface) } diff --git a/builtin/schemes.gen.go b/builtin/schemes.gen.go deleted file mode 100644 index b9ba786e..00000000 --- a/builtin/schemes.gen.go +++ /dev/null @@ -1,33 +0,0 @@ -package builtin - -import ( - "github.com/veraison/services/plugin" - - scheme1 "github.com/veraison/services/scheme/parsec-cca" - scheme2 "github.com/veraison/services/scheme/riot" - scheme3 "github.com/veraison/services/scheme/arm-cca" - scheme4 "github.com/veraison/services/scheme/tpm-enacttrust" - scheme5 "github.com/veraison/services/scheme/parsec-tpm" - scheme6 "github.com/veraison/services/scheme/psa-iot" - -) - -var plugins = []plugin.IPluggable{ - &scheme1.EvidenceHandler{}, - &scheme1.EndorsementHandler{}, - &scheme1.StoreHandler{}, - &scheme2.EvidenceHandler{}, - &scheme2.StoreHandler{}, - &scheme3.EvidenceHandler{}, - &scheme3.EndorsementHandler{}, - &scheme3.StoreHandler{}, - &scheme4.EvidenceHandler{}, - &scheme4.EndorsementHandler{}, - &scheme4.StoreHandler{}, - &scheme5.EvidenceHandler{}, - &scheme5.EndorsementHandler{}, - &scheme5.StoreHandler{}, - &scheme6.EvidenceHandler{}, - &scheme6.EndorsementHandler{}, - &scheme6.StoreHandler{}, -} diff --git a/builtin/schemes.go b/builtin/schemes.go new file mode 100644 index 00000000..461ee8e6 --- /dev/null +++ b/builtin/schemes.go @@ -0,0 +1,38 @@ +// Copyright 2022-2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 +package builtin + +import ( + "github.com/veraison/services/plugin" + + scheme8 "github.com/veraison/services/scheme/amd-kds-coserv" + scheme3 "github.com/veraison/services/scheme/arm-cca" + scheme7 "github.com/veraison/services/scheme/nvidia-coserv" + scheme1 "github.com/veraison/services/scheme/parsec-cca" + scheme5 "github.com/veraison/services/scheme/parsec-tpm" + scheme6 "github.com/veraison/services/scheme/psa-iot" + scheme2 "github.com/veraison/services/scheme/riot" + scheme4 "github.com/veraison/services/scheme/tpm-enacttrust" +) + +var plugins = []plugin.IPluggable{ + &scheme1.EvidenceHandler{}, + &scheme1.EndorsementHandler{}, + &scheme1.StoreHandler{}, + &scheme2.EvidenceHandler{}, + &scheme2.StoreHandler{}, + &scheme3.EvidenceHandler{}, + &scheme3.EndorsementHandler{}, + &scheme3.StoreHandler{}, + &scheme4.EvidenceHandler{}, + &scheme4.EndorsementHandler{}, + &scheme4.StoreHandler{}, + &scheme5.EvidenceHandler{}, + &scheme5.EndorsementHandler{}, + &scheme5.StoreHandler{}, + &scheme6.EvidenceHandler{}, + &scheme6.EndorsementHandler{}, + &scheme6.StoreHandler{}, + &scheme7.CoservProxyHandler{}, + &scheme8.CoservProxyHandler{}, +} diff --git a/coserv/Makefile b/coserv/Makefile new file mode 100644 index 00000000..d6090dcd --- /dev/null +++ b/coserv/Makefile @@ -0,0 +1,7 @@ +# Copyright 2025 Contributors to the Veraison project. +# SPDX-License-Identifier: Apache-2.0 + +SUBDIR := api +SUBDIR += cmd/coserv-service + +include ../mk/subdir.mk diff --git a/coserv/api/Makefile b/coserv/api/Makefile new file mode 100644 index 00000000..cb4b6fbb --- /dev/null +++ b/coserv/api/Makefile @@ -0,0 +1,9 @@ +# Copyright 2025 Contributors to the Veraison project. +# SPDX-License-Identifier: Apache-2.0 + +.DEFAULT_GOAL := test + +include ../../mk/common.mk +include ../../mk/pkg.mk +include ../../mk/lint.mk +include ../../mk/test.mk diff --git a/coserv/api/discovery.go b/coserv/api/discovery.go new file mode 100644 index 00000000..3ab22fac --- /dev/null +++ b/coserv/api/discovery.go @@ -0,0 +1,39 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 +package api + +import ( + "github.com/lestrrat-go/jwx/v2/jwk" +) + +const ( + CoservDiscoveryMediaType = "application/coserv-discovery+json" +) + +type Capability struct { + MediaType string `json:"media-type"` + ArtifactSupport ArtifactSupport `json:"artifact-support"` +} + +type ArtifactSupport []string + +type CoservWellKnownInfo struct { + Version string `json:"version,omitempty"` + Capabilities []Capability `json:"capabilities,omitempty"` + ApiEndpoints map[string]string `json:"api-endpoints,omitempty"` + ResultVerificationKeys []jwk.Key `json:"result-verification-key,omitempty"` +} + +func NewCoservWellKnownInfo( + version string, + capabilities []Capability, + apiEndpoints map[string]string, + resultVerificationKeys []jwk.Key, +) *CoservWellKnownInfo { + return &CoservWellKnownInfo{ + Version: version, + Capabilities: capabilities, + ResultVerificationKeys: resultVerificationKeys, + ApiEndpoints: apiEndpoints, + } +} diff --git a/coserv/api/handler.go b/coserv/api/handler.go new file mode 100644 index 00000000..77ff9b25 --- /dev/null +++ b/coserv/api/handler.go @@ -0,0 +1,173 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package api + +import ( + "fmt" + "net/http" + "slices" + "strings" + + "github.com/fxamacker/cbor/v2" + "github.com/gin-gonic/gin" + "github.com/lestrrat-go/jwx/v2/jwk" + "github.com/veraison/corim/coserv" + "github.com/veraison/services/config" + "github.com/veraison/services/coserv/endorsementdistributor" + "github.com/veraison/services/log" + "github.com/veraison/services/proto" + "go.uber.org/zap" +) + +var ( + tenantID = "0" + CoservMTs = []string{ + "application/coserv+cbor", + "application/coserv+cose", + } +) + +type Handler struct { + Logger *zap.SugaredLogger + EndorsementDistibutor endorsementdistributor.IEndorsementDistributor +} + +func NewHandler( + endorsementdistributor endorsementdistributor.IEndorsementDistributor, + logger *zap.SugaredLogger, +) Handler { + return Handler{ + EndorsementDistibutor: endorsementdistributor, + Logger: logger, + } +} + +func (o Handler) GetEdApiWellKnownInfo(c *gin.Context) { + acceptable := []string{CoservDiscoveryMediaType, gin.MIMEJSON} + + if c.NegotiateFormat(acceptable...) == "" { + reportProblem(c, + http.StatusNotAcceptable, + fmt.Sprintf("supported format(s): %s", + strings.Join(acceptable, ", ")), + ) + return + } + + mediaTypes, err := o.EndorsementDistibutor.SupportedMediaTypes() + if err != nil { + reportProblem(c, http.StatusInternalServerError, err.Error()) + return + } + + capabilities := make([]Capability, 0, len(mediaTypes)) // preallocate + + for _, mt := range mediaTypes { + c := Capability{ + MediaType: mt, + ArtifactSupport: []string{"collected"}, // only "collected" supported for now + } + capabilities = append(capabilities, c) + } + + k, err := o.EndorsementDistibutor.GetPublicKey() + if err != nil { + reportProblem(c, http.StatusInternalServerError, err.Error()) + return + } + + keySet, err := toJWKKeySet(k) + if err != nil { + reportProblem(c, http.StatusInternalServerError, err.Error()) + return + } + + obj := NewCoservWellKnownInfo( + config.Version, + capabilities, + publicApiMap, + keySet, + ) + + c.Header("Content-Type", CoservDiscoveryMediaType) + c.JSON(http.StatusOK, obj) +} + +func toJWKKeySet(k *proto.PublicKey) ([]jwk.Key, error) { + // if no CoSERV key is configured, return nil (omitempty will skip + // serialising the discovery object attribute) + if k == nil || k.Key == "" { + return nil, nil + } + + jwkKey, err := jwk.ParseKey([]byte(k.Key)) + if err != nil { + return nil, fmt.Errorf("parsing public key JWK: %w", err) + } + + return []jwk.Key{jwkKey}, nil +} + +func reportProblem(c *gin.Context, status int, details ...string) { + type PD struct { + Title string `cbor:"-1,keyasint,omitempty"` + Detail string `cbor:"-2,keyasint,omitempty"` + } + + prob := PD{ + Title: http.StatusText(status), + } + + // concatenate details if there are multiple + if len(details) > 0 { + prob.Detail = strings.Join(details, ", ") + } + + log.Info(log.Named("api"), prob) + + b, err := cbor.Marshal(prob) + if err != nil { + log.Error(log.Named("api"), "failed to marshal problem details to CBOR", "error", err) + c.AbortWithStatus(status) + } + + c.Data(status, "application/concise-problem-details+cbor", b) +} + +func (o Handler) CoservRequest(c *gin.Context) { + offered := c.NegotiateFormat(CoservMTs...) + if !slices.Contains(CoservMTs, offered) { + reportProblem(c, + http.StatusNotAcceptable, + fmt.Sprintf("the only supported output formats are %s", + strings.Join(CoservMTs, " or ")), + ) + return + } + + coservQuery := c.Param("query") + + var coserv coserv.Coserv + if err := coserv.FromBase64Url(coservQuery); err != nil { + reportProblem(c, http.StatusBadRequest, err.Error()) + return + } + + profile, err := coserv.Profile.Get() + if err != nil { + reportProblem(c, http.StatusBadRequest, err.Error()) + return + } + mediaType := fmt.Sprintf(`%s; profile=%q`, offered, profile) + + // Forward query to VTS + res, err := o.EndorsementDistibutor.GetEndorsements(tenantID, coservQuery, mediaType) + if err != nil { + status := http.StatusBadRequest + reportProblem(c, status, err.Error()) + return + } + + c.Data(http.StatusOK, mediaType, res) +} diff --git a/coserv/api/router.go b/coserv/api/router.go new file mode 100644 index 00000000..93f2f0c7 --- /dev/null +++ b/coserv/api/router.go @@ -0,0 +1,31 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 +package api + +import ( + "path" + + "github.com/gin-gonic/gin" +) + +const ( + edApiPath = "/endorsement-distribution/v1" +) + +var publicApiMap = make(map[string]string) + +func NewRouter(handler Handler) *gin.Engine { + router := gin.New() + + router.Use(gin.Logger()) + router.Use(gin.Recovery()) + + router.GET("/.well-known/coserv-configuration", handler.GetEdApiWellKnownInfo) + + coservEndpoint := path.Join(edApiPath, "coserv/:query") + // use URI template syntax to indicate the variable part in the discovery document + publicApiMap["CoSERVRequestResponse"] = path.Join(edApiPath, "coserv/{query}") + router.GET(coservEndpoint, handler.CoservRequest) + + return router +} diff --git a/coserv/cmd/coserv-service/Makefile b/coserv/cmd/coserv-service/Makefile new file mode 100644 index 00000000..53da3370 --- /dev/null +++ b/coserv/cmd/coserv-service/Makefile @@ -0,0 +1,16 @@ +# Copyright 2025 Contributors to the Veraison project. +# SPDX-License-Identifier: Apache-2.0 + +.DEFAULT_GOAL := all + +GOPKG := github.com/veraison/services/coserv/cmd/coserv-service +CMD := coserv-service +SRCS := main.go + +CMD_DEPS += $(wildcard ../../api/*.go) + +include ../../../mk/common.mk +include ../../../mk/cmd.mk +include ../../../mk/test.mk +include ../../../mk/lint.mk +include ../../../mk/pkg.mk diff --git a/coserv/cmd/coserv-service/config.yaml b/coserv/cmd/coserv-service/config.yaml new file mode 100644 index 00000000..1d1f791f --- /dev/null +++ b/coserv/cmd/coserv-service/config.yaml @@ -0,0 +1,15 @@ +# This config file assumes the service is being run directly from its build +# location, and that VTS is running on localhost +logging: + level: debug # valid levels: error, warning, info, debug +coserv: + listen-addr: 0.0.0.0:11443 + protocol: https + cert: ../../../deployments/docker/src/certs/verification.crt + cert-key: ../../../deployments/docker/src/certs/verification.key +vts: + server-addr: localhost:50051 + tls: true + ca-certs: + - ../../../deployments/docker/src/certs/rootCA.crt +# vim: set ft=yaml: diff --git a/coserv/cmd/coserv-service/main.go b/coserv/cmd/coserv-service/main.go new file mode 100644 index 00000000..e746432c --- /dev/null +++ b/coserv/cmd/coserv-service/main.go @@ -0,0 +1,113 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 +package main + +import ( + "context" + "errors" + + "github.com/veraison/services/config" + "github.com/veraison/services/coserv/api" + "github.com/veraison/services/coserv/endorsementdistributor" + "github.com/veraison/services/log" + "github.com/veraison/services/proto" + "github.com/veraison/services/vtsclient" + "google.golang.org/protobuf/types/known/emptypb" +) + +var ( + DefaultListenAddr = "localhost:11443" +) + +type cfg struct { + ListenAddr string `mapstructure:"listen-addr" valid:"dialstring"` + Protocol string `mapstructure:"protocol" valid:"in(http|https)"` + Cert string `mapstructure:"cert" config:"zerodefault"` + CertKey string `mapstructure:"cert-key" config:"zerodefault"` +} + +func (o cfg) Validate() error { + if o.Protocol == "https" && (o.Cert == "" || o.CertKey == "") { + return errors.New(`both cert and cert-key must be specified when protocol is "https"`) + } + + return nil +} + +func main() { + config.CmdLine() + + v, err := config.ReadRawConfig(*config.File, false) + if err != nil { + log.Fatalf("Could not read config: %v", err) + } + cfg := cfg{ + ListenAddr: DefaultListenAddr, + Protocol: "https", + } + + subs, err := config.GetSubs(v, "*coserv", "*vts", "*logging") + if err != nil { + log.Fatalf("Could not read config: %v", err) + } + + classifiers := map[string]interface{}{"service": "coserv"} + if err := log.Init(subs["logging"], classifiers); err != nil { + log.Fatalf("could not configure logging: %v", err) + } + log.InitGinWriter() // route gin output to our logger. + + log.Infow("Initializing Endorsement Distribution Service", "version", config.Version) + + loader := config.NewLoader(&cfg) + if err := loader.LoadFromViper(subs["coserv"]); err != nil { + log.Fatalf("Could not load coserv config: %v", err) + } + + log.Info("initializing VTS client") + vtsClient := vtsclient.NewGRPC() + if err := vtsClient.Init(subs["vts"], cfg.Cert, cfg.CertKey); err != nil { + log.Fatalf("Could not initialize VTS client: %v", err) + } + + vtsState, err := vtsClient.GetServiceState(context.Background(), &emptypb.Empty{}) + if err == nil { + if vtsState.Status == proto.ServiceStatus_SERVICE_STATUS_READY { + log.Infow("vts connection established", "server-version", + vtsState.ServerVersion) + } else { + log.Warnw("VTS server not ready. If you do not expect the server to be running yet, this is probably OK, otherwise it may indicate an issue with your vts.server-addr in your settings", + "server-state", vtsState.Status.String()) + } + } else { + log.Warnw("Could not connect to VTS server. If you do not expect the server to be running yet, this is probably OK, otherwise it may indicate an issue with vts.server-addr in your settings", + "error", err) + } + + log.Info("initializing endorsement distributor") + endorsementdistributor := endorsementdistributor.New(vtsClient) + + apiHandler := api.NewHandler(endorsementdistributor, log.Named("coserv")) + + if cfg.Protocol == "https" { + apiServerTLS(apiHandler, cfg.ListenAddr, cfg.Cert, cfg.CertKey) + } else { + apiServer(apiHandler, cfg.ListenAddr) + } +} + +func apiServer(apiHandler api.Handler, listenAddr string) { + log.Infow("initializing endorsement distribution API HTTP service", "address", listenAddr) + + if err := api.NewRouter(apiHandler).Run(listenAddr); err != nil { + log.Fatalf("Gin engine failed: %v", err) + } +} + +func apiServerTLS(apiHandler api.Handler, listenAddr, certFile, keyFile string) { + log.Infow("initializing endorsement distribution API HTTPS service", "address", listenAddr) + + if err := api.NewRouter(apiHandler).RunTLS(listenAddr, certFile, keyFile); err != nil { + log.Fatalf("Gin engine failed: %v", err) + } +} diff --git a/coserv/endorsementdistributor/Makefile b/coserv/endorsementdistributor/Makefile new file mode 100644 index 00000000..cb4b6fbb --- /dev/null +++ b/coserv/endorsementdistributor/Makefile @@ -0,0 +1,9 @@ +# Copyright 2025 Contributors to the Veraison project. +# SPDX-License-Identifier: Apache-2.0 + +.DEFAULT_GOAL := test + +include ../../mk/common.mk +include ../../mk/pkg.mk +include ../../mk/lint.mk +include ../../mk/test.mk diff --git a/coserv/endorsementdistributor/endorsementdistributor.go b/coserv/endorsementdistributor/endorsementdistributor.go new file mode 100644 index 00000000..b6a7e751 --- /dev/null +++ b/coserv/endorsementdistributor/endorsementdistributor.go @@ -0,0 +1,80 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package endorsementdistributor + +import ( + "context" + "errors" + "fmt" + + "github.com/veraison/services/log" + "github.com/veraison/services/proto" + "github.com/veraison/services/vtsclient" + "google.golang.org/protobuf/types/known/emptypb" +) + +type EndorsementDistributor struct { + VTSClient vtsclient.IVTSClient +} + +func New(vtsClient vtsclient.IVTSClient) IEndorsementDistributor { + return &EndorsementDistributor{ + VTSClient: vtsClient, + } +} + +func (ed *EndorsementDistributor) GetEndorsements(tenantID string, query string, mediaType string) ([]byte, error) { + req := &proto.EndorsementQueryIn{Query: query, MediaType: mediaType} + + res, err := ed.VTSClient.GetEndorsements(context.Background(), req) + if err != nil { + if errors.As(err, &vtsclient.NoConnectionError{}) { + return nil, errors.New("no connection") + } + return nil, fmt.Errorf("get endorsements failed: %w", err) + } + + if !res.GetStatus().Result { + return nil, fmt.Errorf( + "get endorsements failed: %s", + res.Status.GetErrorDetail(), + ) + } + + return res.ResultSet, nil +} + +func (ed *EndorsementDistributor) SupportedMediaTypes() ([]string, error) { + res, err := ed.VTSClient.GetSupportedCoservMediaTypes( + context.Background(), + &emptypb.Empty{}, + ) + if err != nil { + if errors.As(err, &vtsclient.NoConnectionError{}) { + return nil, errors.New("no connection") + } + return nil, fmt.Errorf("get supported endorsement profiles failed: %w", err) + } + + log.Debugw("GetSupportedCoservMediaTypes", "media types", res.MediaTypes) + + return res.MediaTypes, nil +} + +func (ed *EndorsementDistributor) GetPublicKey() (*proto.PublicKey, error) { + res, err := ed.VTSClient.GetCoservSigningPublicKey( + context.Background(), + &emptypb.Empty{}, + ) + if err != nil { + if errors.As(err, &vtsclient.NoConnectionError{}) { + return nil, errors.New("no connection") + } + return nil, fmt.Errorf("get CoSERV signing public key failed: %w", err) + } + + log.Debugw("GetPublicKey", "key", res) + + return res, nil +} diff --git a/coserv/endorsementdistributor/iendorsementdistributor.go b/coserv/endorsementdistributor/iendorsementdistributor.go new file mode 100644 index 00000000..5e7487e2 --- /dev/null +++ b/coserv/endorsementdistributor/iendorsementdistributor.go @@ -0,0 +1,25 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package endorsementdistributor + +import "github.com/veraison/services/proto" + +// IEndorsementDistributor provides the bridge between the coserv REST API +// handler and VTS gRPC service endpoints relevant to endorsement distribution. +type IEndorsementDistributor interface { + // SupportedMediaTypes returns a list of supported media types + SupportedMediaTypes() ([]string, error) + + // GetEndorsements retrieves endorsements matching the given query + // for the given tenantID, formatted as specified by mediaType + // (which must be one of the media types returned by SupportedMediaTypes()) + // + // If no endorsements can be found matching the query, an empty result set + // is returned with no error. + GetEndorsements(tenantID string, query string, mediaType string) ([]byte, error) + + // GetPublicKey returns the public key used to sign CoSERV responses + // (if any). If no signing is performed, nil is returned. + GetPublicKey() (*proto.PublicKey, error) +} diff --git a/coserv/test-harness/amd.sh b/coserv/test-harness/amd.sh new file mode 100755 index 00000000..ce10e8d4 --- /dev/null +++ b/coserv/test-harness/amd.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +set -o pipefail +set -eu + +base64url_encode() { + if [ "$(uname)" == "Darwin" ] + then + _base64="base64" + else + _base64="base64 -w0" + fi + + ${_base64} | tr '+/' '-_' | tr -d '='; +} + +# ref-value query +# impl-id: 7f454c4602010100000000000000000003003e00010000005058000000000000 +function rv_query() { +cat << EOF | diag2cbor.rb | base64url_encode +{ + 0:"tag:github.com/veraison,2023:amd_kds_coserv_proxy#1.0.0", + 1:{ + 0:1, + 1:{ + 1:[ + [ + 560(h'a4751aa669180bf5fe715cbe7afd5dff88c8a474a81694b87cb0360e0f476b4bef2c7caca24ef6adb4e41e4f0de8cb3171c0ac440794e7e429e668dcf7816c1c'), + [ + {0: 647, 1: {1: 552(15354741454542995460)}}, + {0: 648, 1: {4: 560(h'19'), 5: h'ff'}}, + {0: 649, 1: {4: 560(h'01'), 5: h'ff'}}, + {0: 650, 1: {4: 560(h'01'), 5: h'ff'}} + ] + ] + ] + }, + 2:0("2025-07-13T13:55:06+01:00"), + 3:2 + } +} +EOF +} + +q=$(rv_query) + +curl https://localhost:11443/endorsement-distribution/v1/coserv/$q -s \ + --insecure \ + --header 'Accept: application/coserv+cbor; profile="tag:github.com/veraison,2023:amd_kds_coserv_proxy#1.0.0"' \ + | cbor-edn cbor2diag diff --git a/coserv/test-harness/nvidia.sh b/coserv/test-harness/nvidia.sh new file mode 100755 index 00000000..da5f122d --- /dev/null +++ b/coserv/test-harness/nvidia.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +set -o pipefail +set -eu + +base64url_encode() { + if [ "$(uname)" == "Darwin" ] + then + _base64="base64" + else + _base64="base64 -w0" + fi + + ${_base64} | tr '+/' '-_' | tr -d '='; +} + +# ref-value query +# impl-id: 7f454c4602010100000000000000000003003e00010000005058000000000000 +function rv_query() { +cat << EOF | diag2cbor.rb | base64url_encode +{ + 0:"tag:github.com/veraison,2023:nvidia_coserv_proxy#1.0.0", + 1:{ + 0:2, + 1:{ + 0:[ + [ + { + 1:"NVIDIA", + 2:"NV_NIC_FIRMWARE_CX7_28.39.4082-LTS_MCX713104AC-ADA" + } + ] + ] + }, + 2:0("2025-07-11T17:23:31+01:00"), + 3:2 + } +} +EOF +} + +q=$(rv_query) + +curl https://localhost:11443/endorsement-distribution/v1/coserv/$q -s \ + --insecure \ + --header 'Accept: application/coserv+cbor; profile="tag:github.com/veraison,2023:nvidia_coserv_proxy#1.0.0"' \ + | cbor-edn cbor2diag diff --git a/coserv/test-harness/query.sh b/coserv/test-harness/query.sh new file mode 100755 index 00000000..34fd8053 --- /dev/null +++ b/coserv/test-harness/query.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +set -o pipefail +set -eu + +A=${A?must be set in the environment to one of rv or ta} + +base64url_encode() { + if [ "$(uname)" == "Darwin" ] + then + _base64="base64" + else + _base64="base64 -w0" + fi + + ${_base64} | tr '+/' '-_' | tr -d '='; +} + +# ref-value query +# impl-id: 7f454c4602010100000000000000000003003e00010000005058000000000000 +function rv_query() { +cat << EOF | diag2cbor.rb | base64url_encode +{ + / profile / 0: "tag:arm.com,2023:cca_platform#1.0.0", + / query / 1: { + / artifact-type / 0: 2, / reference-values / + / environment-selector / 1: { + / class / 0: [ [ + { + / class-id / 0: 600(h'7f454c4602010100000000000000000003003e00010000005058000000000000') / tagged-impl-id-type / + } + ] ] + }, + / timestamp / 2: 0("2030-12-01T18:30:01Z"), + / result-type / 3: 0 / collected material / + } +} +EOF +} + +# ta query +# inst-id: 0107060504030201000f0e0d0c0b0a090817161514131211101f1e1d1c1b1a1918 +function ta_query() { +cat << EOF | diag2cbor.rb | base64url_encode +{ + / profile / 0: "tag:arm.com,2023:cca_platform#1.0.0", + / query / 1: { + / artifact-type / 0: 1, / trust-anchors / + / environment-selector / 1: { + / instance / 1: [ + [ 550(h'0107060504030201000f0e0d0c0b0a090817161514131211101f1e1d1c1b1a1918') ] / UEID / + ] + }, + / timestamp / 2: 0("2030-12-01T18:30:01Z"), + / result-type / 3: 0 / collected material / + } +} +EOF +} + +if [ "${A}" == "rv" ]; then + q=$(rv_query) +elif [ "${A}" == "ta" ]; then + q=$(ta_query) +fi + +curl https://localhost:11443/endorsement-distribution/v1/coserv/$q -s \ + --insecure \ + --header 'Accept: application/coserv+cose; profile="tag:arm.com,2023:cca_platform#1.0.0"' \ + | cbor-edn cbor2diag diff --git a/deployments/docker/src/builder.docker b/deployments/docker/src/builder.docker index 366b1937..0d7038de 100644 --- a/deployments/docker/src/builder.docker +++ b/deployments/docker/src/builder.docker @@ -60,10 +60,10 @@ RUN go mod download &&\ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.26 &&\ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1 &&\ go install github.com/mitchellh/protoc-gen-go-json@v1.1.0 &&\ - go install github.com/veraison/cocli@8ebd64c1 &&\ - go install github.com/veraison/evcli/v2@0d3a093 &&\ + go install github.com/veraison/cocli@v1.0.0-alpha0 &&\ + go install github.com/veraison/evcli/v2@1685bf5 &&\ go install github.com/veraison/pocli@v0.2.0 &&\ - go install github.com/go-delve/delve/cmd/dlv@v1.22.1 + go install github.com/go-delve/delve/cmd/dlv@v1.23.0 ADD --chown=builder:builder builder-dispatcher . ADD --chown=builder:builder builder-bashrc /home/builder/.bashrc diff --git a/deployments/native/README.md b/deployments/native/README.md index f767a17c..46d88088 100644 --- a/deployments/native/README.md +++ b/deployments/native/README.md @@ -3,7 +3,7 @@ native deployment of Veraison on the system. ## Dependencies -To build Veraison services, you will need a Go toolchain, at least v1.22. +To build Veraison services, you will need a Go toolchain, at least v1.23. If Go is already installed, you can check the version: ```sh go version diff --git a/deployments/native/bootstrap/ubuntu.sh b/deployments/native/bootstrap/ubuntu.sh index 64f1ef33..b648e047 100755 --- a/deployments/native/bootstrap/ubuntu.sh +++ b/deployments/native/bootstrap/ubuntu.sh @@ -3,12 +3,13 @@ # SPDX-License-Identifier: Apache-2.0 arch=$(dpkg --print-architecture) +go_ver=1.24 sudo apt update -sudo apt install --yes git protobuf-compiler golang-1.22 make gettext sqlite3 openssl jq jose +sudo apt install --yes git protobuf-compiler golang-${go_ver} make gettext sqlite3 openssl jq jose -sudo ln -s /usr/lib/go-1.22/bin/go /usr/local/bin/go -sudo ln -s /usr/lib/go-1.22/bin/gofmt /usr/local/bin/gofmt +sudo ln -s /usr/lib/go-${go_ver}/bin/go /usr/local/bin/go +sudo ln -s /usr/lib/go-${go_ver}/bin/gofmt /usr/local/bin/gofmt go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2 diff --git a/deployments/native/deployment.sh b/deployments/native/deployment.sh index 2661d63e..a6dfeb12 100755 --- a/deployments/native/deployment.sh +++ b/deployments/native/deployment.sh @@ -65,14 +65,14 @@ function check_requirements() { fi if [[ "$(type -p go)" == "" ]]; then - echo -e "$_ERROR: Go toolchain (at least version 1.22) must be installed." + echo -e "$_ERROR: Go toolchain (at least version 1.23) must be installed." exit 1 fi - if ! printf '%s\n' 1.22 "$(go version | grep -o -E '[0-9.]+' | head -n1)" | \ + if ! printf '%s\n' 1.23 "$(go version | grep -o -E '[0-9.]+' | head -n1)" | \ $_sort -C -V; then - echo -e "$_ERROR: Go version must be at least 1.22." + echo -e "$_ERROR: Go version must be at least 1.23." exit 1 fi diff --git a/go.mod b/go.mod index b15b81ad..c0e6c815 100644 --- a/go.mod +++ b/go.mod @@ -34,11 +34,11 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.18.2 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 github.com/tbaehler/gin-keycloak v1.6.1 - github.com/veraison/ccatoken v1.3.1 - github.com/veraison/cmw v0.1.0 - github.com/veraison/corim v1.1.3-0.20250318170802-c37ac860ac4d + github.com/veraison/ccatoken v1.3.2-0.20250512122414-b26aba0635c4 + github.com/veraison/cmw v0.2.0 + github.com/veraison/corim v1.1.3-0.20251002172919-3c18c66c77b0 github.com/veraison/dice v0.0.1 github.com/veraison/ear v1.1.2 github.com/veraison/eat v0.0.0-20220117140849-ddaf59d69f53 @@ -75,7 +75,7 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/fxamacker/cbor/v2 v2.5.0 // indirect + github.com/fxamacker/cbor/v2 v2.8.0 github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-ini/ini v1.67.0 // indirect @@ -154,3 +154,5 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) + +require fortio.org/safecast v1.0.0 // indirect diff --git a/go.sum b/go.sum index ad43ca5a..503cdea4 100644 --- a/go.sum +++ b/go.sum @@ -597,6 +597,8 @@ cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcP dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +fortio.org/safecast v1.0.0 h1:dr3131WPX8iS1pTf76+39WeXbTrerDYLvi9s7Oi3wiY= +fortio.org/safecast v1.0.0/go.mod h1:xZmcPk3vi4kuUFf+tq4SvnlVdwViqf6ZSZl91Jr9Jdg= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -765,8 +767,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/fxamacker/cbor/v2 v2.3.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE= -github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= +github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= @@ -1186,8 +1188,9 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tbaehler/gin-keycloak v1.6.1 h1:LOXSW5P4j6eGt667/IiKQiyHSBVsECKgvMqdoHtnijA= @@ -1201,12 +1204,12 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -github.com/veraison/ccatoken v1.3.1 h1:zUHXr2mPprxMYv5Mm2mumxzQZ3I9wy7QGayXqa9Rv/E= -github.com/veraison/ccatoken v1.3.1/go.mod h1:vMqdbW4H/8A3oT+24qssuIK3Aefy06XqzTELGg+gWAg= -github.com/veraison/cmw v0.1.0 h1:vD6tBlGPROCW/HlDcG1jh+XUJi5ihrjXatKZBjrv8mU= -github.com/veraison/cmw v0.1.0/go.mod h1:WoBrlgByc6C1FeHhdze1/bQx1kv5d1sWKO5ezEf4Hs4= -github.com/veraison/corim v1.1.3-0.20250318170802-c37ac860ac4d h1:d1+U+qOtPG/XUsbApivrIuPJVHp+tGW4WRp87zc3mzk= -github.com/veraison/corim v1.1.3-0.20250318170802-c37ac860ac4d/go.mod h1:ih8kOpsI3+2iy8IvHkD6xSNryfK3oe9c05nlBE78BV0= +github.com/veraison/ccatoken v1.3.2-0.20250512122414-b26aba0635c4 h1:t2GQueIc1SrErZpprs2ll9ETaXln/nOCPVRq7OejzfQ= +github.com/veraison/ccatoken v1.3.2-0.20250512122414-b26aba0635c4/go.mod h1:vMqdbW4H/8A3oT+24qssuIK3Aefy06XqzTELGg+gWAg= +github.com/veraison/cmw v0.2.0 h1:BWEvwZnD4nn5osq6XwQpTRcGxwV+Su4t6ytdAbVXAJY= +github.com/veraison/cmw v0.2.0/go.mod h1:OiYKk1t6/Fmmg30ZpSMzi4nKr5kt3374sNTkgxC5BDs= +github.com/veraison/corim v1.1.3-0.20251002172919-3c18c66c77b0 h1:rl3ANdIHIStRQEYVhU0BaNc87j0W38iWqTwu0HYcuMA= +github.com/veraison/corim v1.1.3-0.20251002172919-3c18c66c77b0/go.mod h1:NDUWXTTPOnpwTa79HULq+o/6dkgK6XymHdSRGP9YP8M= github.com/veraison/dice v0.0.1 h1:dOm7ByDN/r4WlDsGkEUXzdPMXgTvAPTAksQ8+BwBrD4= github.com/veraison/dice v0.0.1/go.mod h1:QPMLc5LVMj08VZ+HNMYk4XxWoVYGAUBVm8Rd5V1hzxs= github.com/veraison/ear v1.1.2 h1:Xs41FqAG8IyJaceqNFcX2+nf51Et1uyhmCJV8SZqw/8= diff --git a/handler/README.md b/handler/README.md index 4f8a64d4..058d23e8 100644 --- a/handler/README.md +++ b/handler/README.md @@ -1,6 +1,8 @@ This package defines [`IEvidenceHandler`](ievidencehandler.go), -[`IEndorsementHandler`](iendorsementhandler.go) and [`IStoreHandler`](istorehandler.go) [pluggable](../plugin/README.md) -interfaces and associated RPC channels. These are used to add new attestation -scheme to Veraison services. Additionally, the package defines a [couple -of wrappers](plugin.go) around `plugin.RegisterImplementation` for registering -implementations of these three interfaces. +[`IEndorsementHandler`](iendorsementhandler.go), +[`IStoreHandler`](istorehandler.go) and [`ICoservHandler`](icoservhandler.go) +[pluggable](../plugin/README.md) interfaces and associated RPC channels. +These are used to add new attestation scheme to Veraison services. +Additionally, the package defines a [couple of wrappers](plugin.go) around +`plugin.RegisterImplementation` for registering implementations of these four +interfaces. diff --git a/handler/coservproxy_rpc.go b/handler/coservproxy_rpc.go new file mode 100644 index 00000000..d36d150e --- /dev/null +++ b/handler/coservproxy_rpc.go @@ -0,0 +1,116 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 +package handler + +import ( + "fmt" + "net/rpc" + + "github.com/veraison/services/log" + "github.com/veraison/services/plugin" +) + +var CoservProxyHandlerRPC = &plugin.RPCChannel[ICoservProxyHandler]{ + GetClient: getCoservClient, + GetServer: getCoservServer, +} + +func getCoservClient(c *rpc.Client) interface{} { + return &CoservProxyRPCClient{client: c} +} + +func getCoservServer(i ICoservProxyHandler) interface{} { + return &CoservProxyRPCServer{Impl: i} +} + +type CoservProxyRPCServer struct { + Impl ICoservProxyHandler +} + +func (s *CoservProxyRPCServer) GetName(args interface{}, resp *string) error { + *resp = s.Impl.GetName() + return nil +} + +func (s *CoservProxyRPCServer) GetAttestationScheme(args interface{}, resp *string) error { + *resp = s.Impl.GetAttestationScheme() + return nil +} + +func (s *CoservProxyRPCServer) GetSupportedMediaTypes(args interface{}, resp *[]string) error { + *resp = s.Impl.GetSupportedMediaTypes() + return nil +} + +type GetEndorsementArgs struct { + TenantID string + Query string +} + +func (s *CoservProxyRPCServer) GetEndorsements(args GetEndorsementArgs, resp *[]byte) (err error) { + *resp, err = s.Impl.GetEndorsements(args.TenantID, args.Query) + return err +} + +type CoservProxyRPCClient struct { + client *rpc.Client +} + +func (c *CoservProxyRPCClient) GetName() string { + var ( + resp string + unused interface{} + ) + + err := c.client.Call("Plugin.GetName", &unused, &resp) + if err != nil { + log.Errorf("Plugin.GetName RPC call failed: %v", err) + return "" + } + + return resp +} + +func (c *CoservProxyRPCClient) GetAttestationScheme() string { + var ( + resp string + unused interface{} + ) + + err := c.client.Call("Plugin.GetAttestationScheme", &unused, &resp) + if err != nil { + log.Errorf("Plugin.GetAttestationScheme RPC call failed: %v", err) + return "" + } + + return resp +} + +func (c *CoservProxyRPCClient) GetSupportedMediaTypes() []string { + var ( + resp []string + unused interface{} + ) + + err := c.client.Call("Plugin.GetSupportedMediaTypes", &unused, &resp) + if err != nil { + log.Errorf("Plugin.GetSupportedMediaTypes RPC call failed: %v", err) + return nil + } + + return resp +} + +func (c *CoservProxyRPCClient) GetEndorsements( + tenantID string, + query string, +) (resp []byte, err error) { + args := GetEndorsementArgs{TenantID: tenantID, Query: query} + + err = c.client.Call("Plugin.GetEndorsements", args, &resp) + if err != nil { + return nil, fmt.Errorf("Plugin.GetEndorsements RPC call failed: %w", ParseError(err)) + } + + return resp, nil +} diff --git a/handler/endorsement_rpc.go b/handler/endorsement_rpc.go index d38edbc1..7dee0ed7 100644 --- a/handler/endorsement_rpc.go +++ b/handler/endorsement_rpc.go @@ -79,6 +79,17 @@ func (s EndorsementRPCServer) Decode(args []byte, resp *[]byte) error { return nil } +type CoservRepackageArgs struct { + Query string + ResultSet []string +} + +func (s EndorsementRPCServer) CoservRepackage(args CoservRepackageArgs, resp *[]byte) (err error) { + *resp, err = s.Impl.CoservRepackage(args.Query, args.ResultSet) + + return err +} + /* RPC client (plugin caller side) @@ -182,3 +193,13 @@ func (c EndorsementRPCClient) Decode(data []byte, mediaType string, caCertPool [ return &resp, nil } + +func (c EndorsementRPCClient) CoservRepackage(query string, resultSet []string) (resp []byte, err error) { + args := CoservRepackageArgs{Query: query, ResultSet: resultSet} + + if err = c.client.Call("Plugin.CoservRepackage", args, &resp); err != nil { + return nil, fmt.Errorf("Plugin.CoservRepackage RPC call failed: %w", ParseError(err)) + } + + return resp, nil +} diff --git a/handler/icoservproxyhandler.go b/handler/icoservproxyhandler.go new file mode 100644 index 00000000..ee631bcc --- /dev/null +++ b/handler/icoservproxyhandler.go @@ -0,0 +1,17 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 +package handler + +import ( + "github.com/veraison/services/plugin" +) + +// ICoservProxyHandler defines the interface for CoSERV translation plugins +type ICoservProxyHandler interface { + plugin.IPluggable + + // GetEndorsements adds the "result set" to the input "query" CoSERV. + // The input query CoSERV is base64url-encoded. + // In case of a failure an error is returned and the CoSERV is nil. + GetEndorsements(tenantID string, query string) ([]byte, error) +} diff --git a/handler/iendorsementhandler.go b/handler/iendorsementhandler.go index be97e786..a653d163 100644 --- a/handler/iendorsementhandler.go +++ b/handler/iendorsementhandler.go @@ -24,4 +24,9 @@ type IEndorsementHandler interface { // Decode the endorsements from the provided []byte with specified media type. Decode(data []byte, mediaType string, caCertPool []byte) (*EndorsementHandlerResponse, error) + + // CoservRepackage reformats the supplied result set, appends it to the + // supplied CoSERV query and returns the resulting CoSERV as a CBOR-encoded + // byte buffer. + CoservRepackage(coservQuery string, resultSet []string) ([]byte, error) } diff --git a/handler/istorehandler.go b/handler/istorehandler.go index cbe79813..e2803f28 100644 --- a/handler/istorehandler.go +++ b/handler/istorehandler.go @@ -1,4 +1,4 @@ -// Copyright 2024 Contributors to the Veraison project. +// Copyright 2024-2025 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package handler @@ -14,9 +14,10 @@ import ( type IStoreHandler interface { plugin.IPluggable - // GetTrustAnchorIDs returns a slice of trust anchor identifiers used - // to retrieve the trust anchors associated with this token. The trust anchors may be necessary to validate the - // entire token and/or extract its claims (if it is encrypted). + // GetTrustAnchorIDs returns a slice of trust anchor identifiers used to + // retrieve the trust anchors associated with this token. The trust anchors + // may be necessary to validate the entire token and/or extract its claims + // (if it is encrypted). GetTrustAnchorIDs(token *proto.AttestationToken) ([]string, error) // GetRefValueIDs returns a slice of identifiers used to retrieve @@ -35,4 +36,8 @@ type IStoreHandler interface { // SynthKeysFromTrustAnchor synthesizes lookup key(s) for the provided // trust anchor. SynthKeysFromTrustAnchor(tenantID string, ta *Endorsement) ([]string, error) + + // SynthCoservQueryKeys synthesizes lookup keys for the supplied CoSERV + // environment selector. + SynthCoservQueryKeys(tenantID string, query string) ([]string, error) } diff --git a/handler/plugin.go b/handler/plugin.go index ba515958..56224423 100644 --- a/handler/plugin.go +++ b/handler/plugin.go @@ -1,4 +1,4 @@ -// Copyright 2022-2024 Contributors to the Veraison project. +// Copyright 2022-2025 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package handler @@ -26,3 +26,10 @@ func RegisterStoreHandler(i IStoreHandler) { panic(err) } } + +func RegisterCoservProxyHandler(i ICoservProxyHandler) { + err := plugin.RegisterImplementation("coserv-proxy-handler", i, CoservProxyHandlerRPC) + if err != nil { + panic(err) + } +} diff --git a/handler/store_rpc.go b/handler/store_rpc.go index f87e6ed3..2e511594 100644 --- a/handler/store_rpc.go +++ b/handler/store_rpc.go @@ -1,4 +1,4 @@ -// Copyright 2024 Contributors to the Veraison project. +// Copyright 2025 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package handler @@ -102,9 +102,9 @@ func (s *StoreRPCServer) GetTrustAnchorIDs(data []byte, resp *[]string) error { } type GetRefValueIDsArgs struct { - TenantID string + TenantID string TrustAnchors []string - Claims []byte + Claims []byte } func (s *StoreRPCServer) GetRefValueIDs(args GetRefValueIDsArgs, resp *[]string) error { @@ -116,9 +116,17 @@ func (s *StoreRPCServer) GetRefValueIDs(args GetRefValueIDsArgs, resp *[]string) } *resp, err = s.Impl.GetRefValueIDs(args.TenantID, args.TrustAnchors, claims) - if err != nil { - return err - } + + return err +} + +type SynthCoservQueryKeysArgs struct { + TenantID string + Query string +} + +func (s *StoreRPCServer) SynthCoservQueryKeys(args SynthCoservQueryKeysArgs, resp *[]string) (err error) { + *resp, err = s.Impl.SynthCoservQueryKeys(args.TenantID, args.Query) return err } @@ -186,7 +194,7 @@ func (c StoreRPCClient) GetSupportedMediaTypes() []string { return resp } -func (s *StoreRPCClient) SynthKeysFromRefValue(tenantID string, refVal *Endorsement) ([]string, error) { +func (c *StoreRPCClient) SynthKeysFromRefValue(tenantID string, refVal *Endorsement) ([]string, error) { var ( err error resp []string @@ -200,7 +208,7 @@ func (s *StoreRPCClient) SynthKeysFromRefValue(tenantID string, refVal *Endorsem return nil, fmt.Errorf("marshaling reference value: %w", err) } - err = s.client.Call("Plugin.SynthKeysFromRefValue", args, &resp) + err = c.client.Call("Plugin.SynthKeysFromRefValue", args, &resp) if err != nil { err = ParseError(err) return nil, fmt.Errorf("Plugin.SynthKeysFromRefValue RPC call failed: %w", err) // nolint @@ -209,7 +217,7 @@ func (s *StoreRPCClient) SynthKeysFromRefValue(tenantID string, refVal *Endorsem return resp, nil } -func (s *StoreRPCClient) SynthKeysFromTrustAnchor(tenantID string, ta *Endorsement) ([]string, error) { +func (c *StoreRPCClient) SynthKeysFromTrustAnchor(tenantID string, ta *Endorsement) ([]string, error) { var ( err error resp []string @@ -223,7 +231,7 @@ func (s *StoreRPCClient) SynthKeysFromTrustAnchor(tenantID string, ta *Endorseme return nil, fmt.Errorf("marshaling trust anchor: %w", err) } - err = s.client.Call("Plugin.SynthKeysFromTrustAnchor", args, &resp) + err = c.client.Call("Plugin.SynthKeysFromTrustAnchor", args, &resp) if err != nil { err = ParseError(err) return nil, fmt.Errorf("Plugin.SynthKeysFromTrustAnchor RPC call failed: %w", err) // nolint @@ -232,7 +240,7 @@ func (s *StoreRPCClient) SynthKeysFromTrustAnchor(tenantID string, ta *Endorseme return resp, nil } -func (s *StoreRPCClient) GetTrustAnchorIDs(token *proto.AttestationToken) ([]string, error) { +func (c *StoreRPCClient) GetTrustAnchorIDs(token *proto.AttestationToken) ([]string, error) { var ( err error data []byte @@ -244,7 +252,7 @@ func (s *StoreRPCClient) GetTrustAnchorIDs(token *proto.AttestationToken) ([]str return []string{""}, fmt.Errorf("marshaling token: %w", err) } - err = s.client.Call("Plugin.GetTrustAnchorIDs", data, &resp) + err = c.client.Call("Plugin.GetTrustAnchorIDs", data, &resp) if err != nil { err = ParseError(err) return []string{""}, fmt.Errorf("Plugin.GetTrustAnchorIDs RPC call failed: %w", err) // nolint @@ -253,18 +261,18 @@ func (s *StoreRPCClient) GetTrustAnchorIDs(token *proto.AttestationToken) ([]str return resp, nil } -func (s *StoreRPCClient) GetRefValueIDs( +func (c *StoreRPCClient) GetRefValueIDs( tenantID string, trustAnchors []string, claims map[string]interface{}, ) ([]string, error) { var ( - err error + err error resp []string ) args := GetRefValueIDsArgs{ - TenantID: tenantID, + TenantID: tenantID, TrustAnchors: trustAnchors, } @@ -273,7 +281,7 @@ func (s *StoreRPCClient) GetRefValueIDs( return nil, err } - err = s.client.Call("Plugin.GetRefValueIDs", args, &resp) + err = c.client.Call("Plugin.GetRefValueIDs", args, &resp) if err != nil { err = ParseError(err) return nil, fmt.Errorf("Plugin.GetRefValueIDs RPC call failed: %w", err) // nolint @@ -281,3 +289,16 @@ func (s *StoreRPCClient) GetRefValueIDs( return resp, nil } + +func (c *StoreRPCClient) SynthCoservQueryKeys( + tenantID string, + query string, +) (resp []string, err error) { + args := SynthCoservQueryKeysArgs{TenantID: tenantID, Query: query} + + if err = c.client.Call("Plugin.SynthCoservQueryKeys", args, &resp); err != nil { + return nil, fmt.Errorf("Plugin.SynthCoservQueryKeys RPC call failed: %w", ParseError(err)) + } + + return resp, nil +} diff --git a/integration-tests/data/claims/cca.good.json b/integration-tests/data/claims/cca.good.json index 3170771b..6cc821c5 100644 --- a/integration-tests/data/claims/cca.good.json +++ b/integration-tests/data/claims/cca.good.json @@ -1,5 +1,6 @@ { "cca-platform-token": { + "cca-platform-challenge": "5QHHS9edCpI1N1heeR7DUBI+gaqXUB34EkQCITSCxVM=", "cca-platform-profile": "http://arm.com/CCA-SSD/1.0.0", "cca-platform-implementation-id": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", "cca-platform-instance-id": "AQICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC", @@ -45,7 +46,7 @@ "Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw==" ], "cca-realm-hash-algo-id": "sha-256", - "cca-realm-public-key": "BIL70TKptcOWh5+7FTQNkFCXjlXHnVJ5oroOlYVPN+IM0vZPO3K1cLvXc+7iznaEJe31Re2+if+v4OlrvUbicPIHlsRIuY2vRqdk0nRC5ubthPjOyBfm7ManHTo959Z+zQ==", - "cca-realm-public-key-hash-algo-id": "sha-512" + "cca-realm-public-key": "pAECIAIhWDCC+9EyqbXDloefuxU0DZBQl45Vx51SeaK6DpWFTzfiDNL2TztytXC713Pu4s52hCUiWDDt9UXtvon/r+Dpa71G4nDyB5bESLmNr0anZNJ0Qubm7YT4zsgX5uzGpx06PefWfs0=", + "cca-realm-public-key-hash-algo-id": "sha-256" } } diff --git a/integration-tests/data/results/cca.end-to-end.json b/integration-tests/data/results/cca.end-to-end.json index 0ff5259c..6c9ccfbe 100644 --- a/integration-tests/data/results/cca.end-to-end.json +++ b/integration-tests/data/results/cca.end-to-end.json @@ -12,7 +12,7 @@ "storage-opaque": 2 }, "ear.veraison.annotated-evidence": { - "cca-platform-challenge": "Bea1iETGoM0ZOCBpuv2w5JRmKjrc+P3hFHjpM5Ua8XkP9d5ceOPbESPaCiB6i2ZVbgoi8Z7mS9wviZU7azJVXw==", + "cca-platform-challenge": "5QHHS9edCpI1N1heeR7DUBI+gaqXUB34EkQCITSCxVM=", "cca-platform-config": "AQID", "cca-platform-hash-algo-id": "sha-256", "cca-platform-implementation-id": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", @@ -61,6 +61,7 @@ "storage-opaque": 0 }, "ear.veraison.annotated-evidence": { + "cca-realm-profile": "tag:arm.com,2023:realm#1.0.0", "cca-realm-challenge": "byTWuWNaLIu/WOkIuU4Ewb+zroDN6+gyQkV4SZ/jF2Hn9eHYvOASGET1Sr36UobaiPU6ZXsVM1yTlrQyklS8XA==", "cca-realm-extensible-measurements": [ "Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw==", @@ -71,8 +72,8 @@ "cca-realm-hash-algo-id": "sha-256", "cca-realm-initial-measurement": "Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw==", "cca-realm-personalization-value": "QURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBRA==", - "cca-realm-public-key": "BIL70TKptcOWh5+7FTQNkFCXjlXHnVJ5oroOlYVPN+IM0vZPO3K1cLvXc+7iznaEJe31Re2+if+v4OlrvUbicPIHlsRIuY2vRqdk0nRC5ubthPjOyBfm7ManHTo959Z+zQ==", - "cca-realm-public-key-hash-algo-id": "sha-512" + "cca-realm-public-key": "pAECIAIhWDCC+9EyqbXDloefuxU0DZBQl45Vx51SeaK6DpWFTzfiDNL2TztytXC713Pu4s52hCUiWDDt9UXtvon/r+Dpa71G4nDyB5bESLmNr0anZNJ0Qubm7YT4zsgX5uzGpx06PefWfs0=", + "cca-realm-public-key-hash-algo-id": "sha-256" } } - } \ No newline at end of file + } diff --git a/integration-tests/data/results/cca.good.json b/integration-tests/data/results/cca.good.json index d2c4b3be..51a22575 100644 --- a/integration-tests/data/results/cca.good.json +++ b/integration-tests/data/results/cca.good.json @@ -13,7 +13,7 @@ }, "ear.appraisal-policy-id": "policy:ARM_CCA", "ear.veraison.annotated-evidence": { - "cca-platform-challenge": "Bea1iETGoM0ZOCBpuv2w5JRmKjrc+P3hFHjpM5Ua8XkP9d5ceOPbESPaCiB6i2ZVbgoi8Z7mS9wviZU7azJVXw==", + "cca-platform-challenge": "5QHHS9edCpI1N1heeR7DUBI+gaqXUB34EkQCITSCxVM=", "cca-platform-config": "AQID", "cca-platform-hash-algo-id": "sha-256", "cca-platform-implementation-id": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", @@ -63,6 +63,7 @@ "storage-opaque": 0 }, "ear.veraison.annotated-evidence": { + "cca-realm-profile": "tag:arm.com,2023:realm#1.0.0", "cca-realm-challenge": "byTWuWNaLIu/WOkIuU4Ewb+zroDN6+gyQkV4SZ/jF2Hn9eHYvOASGET1Sr36UobaiPU6ZXsVM1yTlrQyklS8XA==", "cca-realm-extensible-measurements": [ "Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw==", @@ -73,8 +74,8 @@ "cca-realm-hash-algo-id": "sha-256", "cca-realm-initial-measurement": "Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw==", "cca-realm-personalization-value": "QURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBRA==", - "cca-realm-public-key": "BIL70TKptcOWh5+7FTQNkFCXjlXHnVJ5oroOlYVPN+IM0vZPO3K1cLvXc+7iznaEJe31Re2+if+v4OlrvUbicPIHlsRIuY2vRqdk0nRC5ubthPjOyBfm7ManHTo959Z+zQ==", - "cca-realm-public-key-hash-algo-id": "sha-512" + "cca-realm-public-key": "pAECIAIhWDCC+9EyqbXDloefuxU0DZBQl45Vx51SeaK6DpWFTzfiDNL2TztytXC713Pu4s52hCUiWDDt9UXtvon/r+Dpa71G4nDyB5bESLmNr0anZNJ0Qubm7YT4zsgX5uzGpx06PefWfs0=", + "cca-realm-public-key-hash-algo-id": "sha-256" } } } diff --git a/integration-tests/data/results/cca.verify-challenge.json b/integration-tests/data/results/cca.verify-challenge.json index c25d176a..8706d646 100644 --- a/integration-tests/data/results/cca.verify-challenge.json +++ b/integration-tests/data/results/cca.verify-challenge.json @@ -12,7 +12,7 @@ "storage-opaque": 2 }, "ear.veraison.annotated-evidence": { - "cca-platform-challenge": "Bea1iETGoM0ZOCBpuv2w5JRmKjrc+P3hFHjpM5Ua8XkP9d5ceOPbESPaCiB6i2ZVbgoi8Z7mS9wviZU7azJVXw==", + "cca-platform-challenge": "5QHHS9edCpI1N1heeR7DUBI+gaqXUB34EkQCITSCxVM=", "cca-platform-config": "AQID", "cca-platform-hash-algo-id": "sha-256", "cca-platform-implementation-id": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", @@ -61,6 +61,7 @@ "storage-opaque": 0 }, "ear.veraison.annotated-evidence": { + "cca-realm-profile": "tag:arm.com,2023:realm#1.0.0", "cca-realm-challenge": "byTWuWNaLIu/WOkIuU4Ewb+zroDN6+gyQkV4SZ/jF2Hn9eHYvOASGET1Sr36UobaiPU6ZXsVM1yTlrQyklS8XA==", "cca-realm-extensible-measurements": [ "Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw==", @@ -71,8 +72,8 @@ "cca-realm-hash-algo-id": "sha-256", "cca-realm-initial-measurement": "Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw==", "cca-realm-personalization-value": "QURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBRA==", - "cca-realm-public-key": "BIL70TKptcOWh5+7FTQNkFCXjlXHnVJ5oroOlYVPN+IM0vZPO3K1cLvXc+7iznaEJe31Re2+if+v4OlrvUbicPIHlsRIuY2vRqdk0nRC5ubthPjOyBfm7ManHTo959Z+zQ==", - "cca-realm-public-key-hash-algo-id": "sha-512" + "cca-realm-public-key": "pAECIAIhWDCC+9EyqbXDloefuxU0DZBQl45Vx51SeaK6DpWFTzfiDNL2TztytXC713Pu4s52hCUiWDDt9UXtvon/r+Dpa71G4nDyB5bESLmNr0anZNJ0Qubm7YT4zsgX5uzGpx06PefWfs0=", + "cca-realm-public-key-hash-algo-id": "sha-256" } } } diff --git a/integration-tests/tests/test_enacttrust_badkey.tavern.yaml b/integration-tests/tests/test_enacttrust_badkey.tavern.yaml index db7345f4..fc8bf7fd 100644 --- a/integration-tests/tests/test_enacttrust_badkey.tavern.yaml +++ b/integration-tests/tests/test_enacttrust_badkey.tavern.yaml @@ -34,4 +34,4 @@ stages: status_code: 200 json: status: failed - failure-reason: 'submit endorsement returned error: submit endorsements failed: RPC server returned error: plugin "corim (TPM EnactTrust profile)" returned error: decoding failed for CoMID at index 0: error unmarshalling field "Triples": error unmarshalling field "AttestVerifKeys": cbor: cannot unmarshal map into Go struct field comid.KeyTriple.verification-keys of type comid.ICryptoKeyValue' + failure-reason: 'submit endorsement returned error: submit endorsements failed: RPC server returned error: plugin "corim (TPM EnactTrust profile)" returned error: CBOR decoding failed: did not see unsigned CoRIM tag' diff --git a/plugin/goplugin_loader.go b/plugin/goplugin_loader.go index d771a501..fe2a0afd 100644 --- a/plugin/goplugin_loader.go +++ b/plugin/goplugin_loader.go @@ -1,4 +1,4 @@ -// Copyright 2023 Contributors to the Veraison project. +// Copyright 2023-2025 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package plugin @@ -241,7 +241,7 @@ func GetGoPluginHandleByAttestationSchemeUsing[I IPluggable]( if ictx.GetAttestationScheme() != scheme { continue } - ldr.logger.Debugw("found plugin implenting scheme", + ldr.logger.Debugw("found plugin implementing scheme", "plugin", name, "scheme", scheme) ctx, ok = ictx.(*PluginContext[I]) @@ -252,7 +252,7 @@ func GetGoPluginHandleByAttestationSchemeUsing[I IPluggable]( if ctx == nil { return *new(I), fmt.Errorf( // nolint:gocritic - "could not find plugin providing schdme %q and implementing interface %s", + "could not find plugin providing scheme %q and implementing interface %s", scheme, iface) } diff --git a/proto/Makefile b/proto/Makefile index 6f26c00d..683aee10 100644 --- a/proto/Makefile +++ b/proto/Makefile @@ -8,6 +8,8 @@ PROTOSRCS += evidence.proto PROTOSRCS += appraisal_context.proto PROTOSRCS += state.proto PROTOSRCS += vts.proto +PROTOSRCS += status.proto +PROTOSRCS += endorsement_query.proto lint-hook-pre: protogen protolint lint $(PROTOSRCS) diff --git a/proto/appraisal_context.pb.go b/proto/appraisal_context.pb.go index bc04eddb..7b0d39fd 100644 --- a/proto/appraisal_context.pb.go +++ b/proto/appraisal_context.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v5.27.3 +// protoc v5.29.3 // source: appraisal_context.proto package proto diff --git a/proto/endorsement_query.pb.go b/proto/endorsement_query.pb.go new file mode 100644 index 00000000..e327e953 --- /dev/null +++ b/proto/endorsement_query.pb.go @@ -0,0 +1,237 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v5.29.3 +// source: endorsement_query.proto + +package proto + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type EndorsementQueryIn struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // media type (including profile) + MediaType string `protobuf:"bytes,1,opt,name=media_type,json=mediaType,proto3" json:"media_type,omitempty"` + // base64url-encoded CoSERV + Query string `protobuf:"bytes,2,opt,name=query,proto3" json:"query,omitempty"` +} + +func (x *EndorsementQueryIn) Reset() { + *x = EndorsementQueryIn{} + if protoimpl.UnsafeEnabled { + mi := &file_endorsement_query_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EndorsementQueryIn) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EndorsementQueryIn) ProtoMessage() {} + +func (x *EndorsementQueryIn) ProtoReflect() protoreflect.Message { + mi := &file_endorsement_query_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EndorsementQueryIn.ProtoReflect.Descriptor instead. +func (*EndorsementQueryIn) Descriptor() ([]byte, []int) { + return file_endorsement_query_proto_rawDescGZIP(), []int{0} +} + +func (x *EndorsementQueryIn) GetMediaType() string { + if x != nil { + return x.MediaType + } + return "" +} + +func (x *EndorsementQueryIn) GetQuery() string { + if x != nil { + return x.Query + } + return "" +} + +type EndorsementQueryOut struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Status *Status `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"` + ResultSet []byte `protobuf:"bytes,2,opt,name=result_set,json=resultSet,proto3" json:"result_set,omitempty"` +} + +func (x *EndorsementQueryOut) Reset() { + *x = EndorsementQueryOut{} + if protoimpl.UnsafeEnabled { + mi := &file_endorsement_query_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *EndorsementQueryOut) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EndorsementQueryOut) ProtoMessage() {} + +func (x *EndorsementQueryOut) ProtoReflect() protoreflect.Message { + mi := &file_endorsement_query_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EndorsementQueryOut.ProtoReflect.Descriptor instead. +func (*EndorsementQueryOut) Descriptor() ([]byte, []int) { + return file_endorsement_query_proto_rawDescGZIP(), []int{1} +} + +func (x *EndorsementQueryOut) GetStatus() *Status { + if x != nil { + return x.Status + } + return nil +} + +func (x *EndorsementQueryOut) GetResultSet() []byte { + if x != nil { + return x.ResultSet + } + return nil +} + +var File_endorsement_query_proto protoreflect.FileDescriptor + +var file_endorsement_query_proto_rawDesc = []byte{ + 0x0a, 0x17, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x71, 0x75, + 0x65, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x49, + 0x0a, 0x12, 0x45, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x49, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0x5b, 0x0a, 0x13, 0x45, 0x6e, 0x64, + 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x4f, 0x75, 0x74, + 0x12, 0x25, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x53, 0x65, 0x74, 0x42, 0x24, 0x5a, 0x22, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x65, 0x72, 0x61, 0x69, 0x73, 0x6f, 0x6e, 0x2f, 0x73, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_endorsement_query_proto_rawDescOnce sync.Once + file_endorsement_query_proto_rawDescData = file_endorsement_query_proto_rawDesc +) + +func file_endorsement_query_proto_rawDescGZIP() []byte { + file_endorsement_query_proto_rawDescOnce.Do(func() { + file_endorsement_query_proto_rawDescData = protoimpl.X.CompressGZIP(file_endorsement_query_proto_rawDescData) + }) + return file_endorsement_query_proto_rawDescData +} + +var file_endorsement_query_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_endorsement_query_proto_goTypes = []interface{}{ + (*EndorsementQueryIn)(nil), // 0: proto.EndorsementQueryIn + (*EndorsementQueryOut)(nil), // 1: proto.EndorsementQueryOut + (*Status)(nil), // 2: proto.Status +} +var file_endorsement_query_proto_depIdxs = []int32{ + 2, // 0: proto.EndorsementQueryOut.status:type_name -> proto.Status + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_endorsement_query_proto_init() } +func file_endorsement_query_proto_init() { + if File_endorsement_query_proto != nil { + return + } + file_status_proto_init() + if !protoimpl.UnsafeEnabled { + file_endorsement_query_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EndorsementQueryIn); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_endorsement_query_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*EndorsementQueryOut); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_endorsement_query_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_endorsement_query_proto_goTypes, + DependencyIndexes: file_endorsement_query_proto_depIdxs, + MessageInfos: file_endorsement_query_proto_msgTypes, + }.Build() + File_endorsement_query_proto = out.File + file_endorsement_query_proto_rawDesc = nil + file_endorsement_query_proto_goTypes = nil + file_endorsement_query_proto_depIdxs = nil +} diff --git a/proto/endorsement_query.pb.json.go b/proto/endorsement_query.pb.json.go new file mode 100644 index 00000000..d4322313 --- /dev/null +++ b/proto/endorsement_query.pb.json.go @@ -0,0 +1,40 @@ +// Code generated by protoc-gen-go-json. DO NOT EDIT. +// source: endorsement_query.proto + +package proto + +import ( + "google.golang.org/protobuf/encoding/protojson" +) + +// MarshalJSON implements json.Marshaler +func (msg *EndorsementQueryIn) MarshalJSON() ([]byte, error) { + return protojson.MarshalOptions{ + UseEnumNumbers: false, + EmitUnpopulated: false, + UseProtoNames: false, + }.Marshal(msg) +} + +// UnmarshalJSON implements json.Unmarshaler +func (msg *EndorsementQueryIn) UnmarshalJSON(b []byte) error { + return protojson.UnmarshalOptions{ + DiscardUnknown: false, + }.Unmarshal(b, msg) +} + +// MarshalJSON implements json.Marshaler +func (msg *EndorsementQueryOut) MarshalJSON() ([]byte, error) { + return protojson.MarshalOptions{ + UseEnumNumbers: false, + EmitUnpopulated: false, + UseProtoNames: false, + }.Marshal(msg) +} + +// UnmarshalJSON implements json.Unmarshaler +func (msg *EndorsementQueryOut) UnmarshalJSON(b []byte) error { + return protojson.UnmarshalOptions{ + DiscardUnknown: false, + }.Unmarshal(b, msg) +} diff --git a/proto/endorsement_query.proto b/proto/endorsement_query.proto new file mode 100644 index 00000000..b775d04c --- /dev/null +++ b/proto/endorsement_query.proto @@ -0,0 +1,23 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +syntax = "proto3"; + +package proto; + +import "status.proto"; + +option go_package = "github.com/veraison/services/proto"; + +message EndorsementQueryIn { + // media type (including profile) + string media_type = 1; + // base64url-encoded CoSERV query + string query = 2; +} + +message EndorsementQueryOut { + Status status = 1; + // CBOR-encoded CoSERV result set + bytes result_set = 2; +} diff --git a/proto/evidence.pb.go b/proto/evidence.pb.go index 72e27ad1..20b4bf8e 100644 --- a/proto/evidence.pb.go +++ b/proto/evidence.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v5.27.3 +// protoc v5.29.3 // source: evidence.proto package proto diff --git a/proto/state.pb.go b/proto/state.pb.go index 057baddf..124d9d72 100644 --- a/proto/state.pb.go +++ b/proto/state.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v5.27.3 +// protoc v5.29.3 // source: state.proto package proto diff --git a/proto/status.pb.go b/proto/status.pb.go new file mode 100644 index 00000000..3f71b220 --- /dev/null +++ b/proto/status.pb.go @@ -0,0 +1,153 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v5.29.3 +// source: status.proto + +package proto + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Status struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Result bool `protobuf:"varint,1,opt,name=result,proto3" json:"result,omitempty"` + ErrorDetail string `protobuf:"bytes,2,opt,name=error_detail,json=errorDetail,proto3" json:"error_detail,omitempty"` +} + +func (x *Status) Reset() { + *x = Status{} + if protoimpl.UnsafeEnabled { + mi := &file_status_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Status) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Status) ProtoMessage() {} + +func (x *Status) ProtoReflect() protoreflect.Message { + mi := &file_status_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Status.ProtoReflect.Descriptor instead. +func (*Status) Descriptor() ([]byte, []int) { + return file_status_proto_rawDescGZIP(), []int{0} +} + +func (x *Status) GetResult() bool { + if x != nil { + return x.Result + } + return false +} + +func (x *Status) GetErrorDetail() string { + if x != nil { + return x.ErrorDetail + } + return "" +} + +var File_status_proto protoreflect.FileDescriptor + +var file_status_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x43, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x16, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x5f, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x42, 0x24, 0x5a, 0x22, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x65, 0x72, 0x61, 0x69, 0x73, 0x6f, + 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_status_proto_rawDescOnce sync.Once + file_status_proto_rawDescData = file_status_proto_rawDesc +) + +func file_status_proto_rawDescGZIP() []byte { + file_status_proto_rawDescOnce.Do(func() { + file_status_proto_rawDescData = protoimpl.X.CompressGZIP(file_status_proto_rawDescData) + }) + return file_status_proto_rawDescData +} + +var file_status_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_status_proto_goTypes = []interface{}{ + (*Status)(nil), // 0: proto.Status +} +var file_status_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_status_proto_init() } +func file_status_proto_init() { + if File_status_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_status_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Status); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_status_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_status_proto_goTypes, + DependencyIndexes: file_status_proto_depIdxs, + MessageInfos: file_status_proto_msgTypes, + }.Build() + File_status_proto = out.File + file_status_proto_rawDesc = nil + file_status_proto_goTypes = nil + file_status_proto_depIdxs = nil +} diff --git a/proto/status.pb.json.go b/proto/status.pb.json.go new file mode 100644 index 00000000..04eb928e --- /dev/null +++ b/proto/status.pb.json.go @@ -0,0 +1,24 @@ +// Code generated by protoc-gen-go-json. DO NOT EDIT. +// source: status.proto + +package proto + +import ( + "google.golang.org/protobuf/encoding/protojson" +) + +// MarshalJSON implements json.Marshaler +func (msg *Status) MarshalJSON() ([]byte, error) { + return protojson.MarshalOptions{ + UseEnumNumbers: false, + EmitUnpopulated: false, + UseProtoNames: false, + }.Marshal(msg) +} + +// UnmarshalJSON implements json.Unmarshaler +func (msg *Status) UnmarshalJSON(b []byte) error { + return protojson.UnmarshalOptions{ + DiscardUnknown: false, + }.Unmarshal(b, msg) +} diff --git a/proto/status.proto b/proto/status.proto new file mode 100644 index 00000000..c6534e3a --- /dev/null +++ b/proto/status.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; +package proto; + +option go_package = "github.com/veraison/services/proto"; + +message Status { + bool result = 1; + string error_detail = 2; +} diff --git a/proto/token.pb.go b/proto/token.pb.go index 604133a5..c82e5d49 100644 --- a/proto/token.pb.go +++ b/proto/token.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v5.27.3 +// protoc v5.29.3 // source: token.proto package proto diff --git a/proto/vts.pb.go b/proto/vts.pb.go index 7f8b7d85..35ec2bef 100644 --- a/proto/vts.pb.go +++ b/proto/vts.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.28.1 -// protoc v5.27.3 +// protoc v5.29.3 // source: vts.proto package proto @@ -22,61 +22,6 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -type Status struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Result bool `protobuf:"varint,1,opt,name=result,proto3" json:"result,omitempty"` - ErrorDetail string `protobuf:"bytes,2,opt,name=error_detail,json=errorDetail,proto3" json:"error_detail,omitempty"` -} - -func (x *Status) Reset() { - *x = Status{} - if protoimpl.UnsafeEnabled { - mi := &file_vts_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Status) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Status) ProtoMessage() {} - -func (x *Status) ProtoReflect() protoreflect.Message { - mi := &file_vts_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Status.ProtoReflect.Descriptor instead. -func (*Status) Descriptor() ([]byte, []int) { - return file_vts_proto_rawDescGZIP(), []int{0} -} - -func (x *Status) GetResult() bool { - if x != nil { - return x.Result - } - return false -} - -func (x *Status) GetErrorDetail() string { - if x != nil { - return x.ErrorDetail - } - return "" -} - type Evidence struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -88,7 +33,7 @@ type Evidence struct { func (x *Evidence) Reset() { *x = Evidence{} if protoimpl.UnsafeEnabled { - mi := &file_vts_proto_msgTypes[1] + mi := &file_vts_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -101,7 +46,7 @@ func (x *Evidence) String() string { func (*Evidence) ProtoMessage() {} func (x *Evidence) ProtoReflect() protoreflect.Message { - mi := &file_vts_proto_msgTypes[1] + mi := &file_vts_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -114,7 +59,7 @@ func (x *Evidence) ProtoReflect() protoreflect.Message { // Deprecated: Use Evidence.ProtoReflect.Descriptor instead. func (*Evidence) Descriptor() ([]byte, []int) { - return file_vts_proto_rawDescGZIP(), []int{1} + return file_vts_proto_rawDescGZIP(), []int{0} } func (x *Evidence) GetValue() *structpb.Struct { @@ -136,7 +81,7 @@ type SubmitEndorsementsRequest struct { func (x *SubmitEndorsementsRequest) Reset() { *x = SubmitEndorsementsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_vts_proto_msgTypes[2] + mi := &file_vts_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -149,7 +94,7 @@ func (x *SubmitEndorsementsRequest) String() string { func (*SubmitEndorsementsRequest) ProtoMessage() {} func (x *SubmitEndorsementsRequest) ProtoReflect() protoreflect.Message { - mi := &file_vts_proto_msgTypes[2] + mi := &file_vts_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -162,7 +107,7 @@ func (x *SubmitEndorsementsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SubmitEndorsementsRequest.ProtoReflect.Descriptor instead. func (*SubmitEndorsementsRequest) Descriptor() ([]byte, []int) { - return file_vts_proto_rawDescGZIP(), []int{2} + return file_vts_proto_rawDescGZIP(), []int{1} } func (x *SubmitEndorsementsRequest) GetMediaType() string { @@ -190,7 +135,7 @@ type SubmitEndorsementsResponse struct { func (x *SubmitEndorsementsResponse) Reset() { *x = SubmitEndorsementsResponse{} if protoimpl.UnsafeEnabled { - mi := &file_vts_proto_msgTypes[3] + mi := &file_vts_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -203,7 +148,7 @@ func (x *SubmitEndorsementsResponse) String() string { func (*SubmitEndorsementsResponse) ProtoMessage() {} func (x *SubmitEndorsementsResponse) ProtoReflect() protoreflect.Message { - mi := &file_vts_proto_msgTypes[3] + mi := &file_vts_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -216,7 +161,7 @@ func (x *SubmitEndorsementsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SubmitEndorsementsResponse.ProtoReflect.Descriptor instead. func (*SubmitEndorsementsResponse) Descriptor() ([]byte, []int) { - return file_vts_proto_rawDescGZIP(), []int{3} + return file_vts_proto_rawDescGZIP(), []int{2} } func (x *SubmitEndorsementsResponse) GetStatus() *Status { @@ -237,7 +182,7 @@ type MediaTypeList struct { func (x *MediaTypeList) Reset() { *x = MediaTypeList{} if protoimpl.UnsafeEnabled { - mi := &file_vts_proto_msgTypes[4] + mi := &file_vts_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -250,7 +195,7 @@ func (x *MediaTypeList) String() string { func (*MediaTypeList) ProtoMessage() {} func (x *MediaTypeList) ProtoReflect() protoreflect.Message { - mi := &file_vts_proto_msgTypes[4] + mi := &file_vts_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -263,7 +208,7 @@ func (x *MediaTypeList) ProtoReflect() protoreflect.Message { // Deprecated: Use MediaTypeList.ProtoReflect.Descriptor instead. func (*MediaTypeList) Descriptor() ([]byte, []int) { - return file_vts_proto_rawDescGZIP(), []int{4} + return file_vts_proto_rawDescGZIP(), []int{3} } func (x *MediaTypeList) GetMediaTypes() []string { @@ -284,7 +229,7 @@ type PublicKey struct { func (x *PublicKey) Reset() { *x = PublicKey{} if protoimpl.UnsafeEnabled { - mi := &file_vts_proto_msgTypes[5] + mi := &file_vts_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -297,7 +242,7 @@ func (x *PublicKey) String() string { func (*PublicKey) ProtoMessage() {} func (x *PublicKey) ProtoReflect() protoreflect.Message { - mi := &file_vts_proto_msgTypes[5] + mi := &file_vts_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -310,7 +255,7 @@ func (x *PublicKey) ProtoReflect() protoreflect.Message { // Deprecated: Use PublicKey.ProtoReflect.Descriptor instead. func (*PublicKey) Descriptor() ([]byte, []int) { - return file_vts_proto_rawDescGZIP(), []int{5} + return file_vts_proto_rawDescGZIP(), []int{4} } func (x *PublicKey) GetKey() string { @@ -325,67 +270,79 @@ var File_vts_proto protoreflect.FileDescriptor var file_vts_proto_rawDesc = []byte{ 0x0a, 0x09, 0x76, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x61, 0x70, 0x70, 0x72, 0x61, 0x69, 0x73, 0x61, 0x6c, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, - 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0b, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x1a, 0x0b, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x22, 0x43, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x64, 0x65, 0x74, 0x61, - 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x22, 0x39, 0x0a, 0x08, 0x45, 0x76, 0x69, 0x64, 0x65, 0x6e, 0x63, - 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x22, 0x4e, 0x0a, 0x19, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, 0x6e, 0x64, 0x6f, 0x72, 0x73, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, - 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, - 0x22, 0x43, 0x0a, 0x1a, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, 0x6e, 0x64, 0x6f, 0x72, 0x73, - 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, - 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x30, 0x0a, 0x0d, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, - 0x70, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x5f, - 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x65, 0x64, - 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x73, 0x22, 0x1d, 0x0a, 0x09, 0x50, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x4b, 0x65, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x32, 0xd0, 0x03, 0x0a, 0x03, 0x56, 0x54, 0x53, 0x12, 0x3e, - 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x42, - 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x1a, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x41, 0x70, 0x70, 0x72, 0x61, 0x69, 0x73, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x74, 0x65, - 0x78, 0x74, 0x12, 0x52, 0x0a, 0x22, 0x47, 0x65, 0x74, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, - 0x65, 0x64, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, - 0x64, 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x1a, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, - 0x70, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x52, 0x0a, 0x22, 0x47, 0x65, 0x74, 0x53, 0x75, 0x70, - 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x69, - 0x6e, 0x67, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x16, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4d, 0x65, 0x64, - 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x59, 0x0a, 0x12, 0x53, 0x75, - 0x62, 0x6d, 0x69, 0x74, 0x45, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x12, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, - 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, - 0x74, 0x45, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x45, 0x41, 0x52, 0x53, - 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, - 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x24, 0x5a, 0x22, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x65, 0x72, 0x61, 0x69, 0x73, 0x6f, 0x6e, - 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x65, 0x6e, 0x64, + 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x0b, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0c, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0b, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x39, 0x0a, 0x08, 0x45, 0x76, 0x69, 0x64, 0x65, + 0x6e, 0x63, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x22, 0x4e, 0x0a, 0x19, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, 0x6e, 0x64, 0x6f, + 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1d, 0x0a, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, + 0x74, 0x61, 0x22, 0x43, 0x0a, 0x1a, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, 0x6e, 0x64, 0x6f, + 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x25, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x30, 0x0a, 0x0d, 0x4d, 0x65, 0x64, 0x69, 0x61, + 0x54, 0x79, 0x70, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x65, 0x64, 0x69, + 0x61, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x6d, + 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x73, 0x22, 0x1d, 0x0a, 0x09, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x32, 0xaf, 0x05, 0x0a, 0x03, 0x56, 0x54, 0x53, + 0x12, 0x3e, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x13, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x12, 0x42, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x1a, 0x17, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x70, 0x70, 0x72, 0x61, 0x69, 0x73, 0x61, 0x6c, 0x43, 0x6f, 0x6e, + 0x74, 0x65, 0x78, 0x74, 0x12, 0x52, 0x0a, 0x22, 0x47, 0x65, 0x74, 0x53, 0x75, 0x70, 0x70, 0x6f, + 0x72, 0x74, 0x65, 0x64, 0x56, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x4d, 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x1a, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4d, 0x65, 0x64, 0x69, 0x61, + 0x54, 0x79, 0x70, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x52, 0x0a, 0x22, 0x47, 0x65, 0x74, 0x53, + 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x69, 0x6e, 0x67, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x16, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4d, + 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x59, 0x0a, 0x12, + 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x12, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, + 0x74, 0x45, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x75, 0x62, + 0x6d, 0x69, 0x74, 0x45, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x45, 0x41, + 0x52, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, + 0x79, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x48, 0x0a, 0x0f, 0x47, + 0x65, 0x74, 0x45, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x19, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x49, 0x6e, 0x1a, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x45, 0x6e, 0x64, 0x6f, 0x72, 0x73, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x4f, 0x75, 0x74, 0x12, 0x4c, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x53, 0x75, 0x70, 0x70, + 0x6f, 0x72, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x73, 0x65, 0x72, 0x76, 0x4d, 0x65, 0x64, 0x69, 0x61, + 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x14, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x4c, + 0x69, 0x73, 0x74, 0x12, 0x45, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x73, 0x65, 0x72, 0x76, + 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, + 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x42, 0x24, 0x5a, 0x22, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x76, 0x65, 0x72, 0x61, 0x69, 0x73, 0x6f, + 0x6e, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -400,37 +357,45 @@ func file_vts_proto_rawDescGZIP() []byte { return file_vts_proto_rawDescData } -var file_vts_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_vts_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_vts_proto_goTypes = []interface{}{ - (*Status)(nil), // 0: proto.Status - (*Evidence)(nil), // 1: proto.Evidence - (*SubmitEndorsementsRequest)(nil), // 2: proto.SubmitEndorsementsRequest - (*SubmitEndorsementsResponse)(nil), // 3: proto.SubmitEndorsementsResponse - (*MediaTypeList)(nil), // 4: proto.MediaTypeList - (*PublicKey)(nil), // 5: proto.PublicKey - (*structpb.Struct)(nil), // 6: google.protobuf.Struct + (*Evidence)(nil), // 0: proto.Evidence + (*SubmitEndorsementsRequest)(nil), // 1: proto.SubmitEndorsementsRequest + (*SubmitEndorsementsResponse)(nil), // 2: proto.SubmitEndorsementsResponse + (*MediaTypeList)(nil), // 3: proto.MediaTypeList + (*PublicKey)(nil), // 4: proto.PublicKey + (*structpb.Struct)(nil), // 5: google.protobuf.Struct + (*Status)(nil), // 6: proto.Status (*emptypb.Empty)(nil), // 7: google.protobuf.Empty (*AttestationToken)(nil), // 8: proto.AttestationToken - (*ServiceState)(nil), // 9: proto.ServiceState - (*AppraisalContext)(nil), // 10: proto.AppraisalContext + (*EndorsementQueryIn)(nil), // 9: proto.EndorsementQueryIn + (*ServiceState)(nil), // 10: proto.ServiceState + (*AppraisalContext)(nil), // 11: proto.AppraisalContext + (*EndorsementQueryOut)(nil), // 12: proto.EndorsementQueryOut } var file_vts_proto_depIdxs = []int32{ - 6, // 0: proto.Evidence.value:type_name -> google.protobuf.Struct - 0, // 1: proto.SubmitEndorsementsResponse.status:type_name -> proto.Status + 5, // 0: proto.Evidence.value:type_name -> google.protobuf.Struct + 6, // 1: proto.SubmitEndorsementsResponse.status:type_name -> proto.Status 7, // 2: proto.VTS.GetServiceState:input_type -> google.protobuf.Empty 8, // 3: proto.VTS.GetAttestation:input_type -> proto.AttestationToken 7, // 4: proto.VTS.GetSupportedVerificationMediaTypes:input_type -> google.protobuf.Empty 7, // 5: proto.VTS.GetSupportedProvisioningMediaTypes:input_type -> google.protobuf.Empty - 2, // 6: proto.VTS.SubmitEndorsements:input_type -> proto.SubmitEndorsementsRequest + 1, // 6: proto.VTS.SubmitEndorsements:input_type -> proto.SubmitEndorsementsRequest 7, // 7: proto.VTS.GetEARSigningPublicKey:input_type -> google.protobuf.Empty - 9, // 8: proto.VTS.GetServiceState:output_type -> proto.ServiceState - 10, // 9: proto.VTS.GetAttestation:output_type -> proto.AppraisalContext - 4, // 10: proto.VTS.GetSupportedVerificationMediaTypes:output_type -> proto.MediaTypeList - 4, // 11: proto.VTS.GetSupportedProvisioningMediaTypes:output_type -> proto.MediaTypeList - 3, // 12: proto.VTS.SubmitEndorsements:output_type -> proto.SubmitEndorsementsResponse - 5, // 13: proto.VTS.GetEARSigningPublicKey:output_type -> proto.PublicKey - 8, // [8:14] is the sub-list for method output_type - 2, // [2:8] is the sub-list for method input_type + 9, // 8: proto.VTS.GetEndorsements:input_type -> proto.EndorsementQueryIn + 7, // 9: proto.VTS.GetSupportedCoservMediaTypes:input_type -> google.protobuf.Empty + 7, // 10: proto.VTS.GetCoservSigningPublicKey:input_type -> google.protobuf.Empty + 10, // 11: proto.VTS.GetServiceState:output_type -> proto.ServiceState + 11, // 12: proto.VTS.GetAttestation:output_type -> proto.AppraisalContext + 3, // 13: proto.VTS.GetSupportedVerificationMediaTypes:output_type -> proto.MediaTypeList + 3, // 14: proto.VTS.GetSupportedProvisioningMediaTypes:output_type -> proto.MediaTypeList + 2, // 15: proto.VTS.SubmitEndorsements:output_type -> proto.SubmitEndorsementsResponse + 4, // 16: proto.VTS.GetEARSigningPublicKey:output_type -> proto.PublicKey + 12, // 17: proto.VTS.GetEndorsements:output_type -> proto.EndorsementQueryOut + 3, // 18: proto.VTS.GetSupportedCoservMediaTypes:output_type -> proto.MediaTypeList + 4, // 19: proto.VTS.GetCoservSigningPublicKey:output_type -> proto.PublicKey + 11, // [11:20] is the sub-list for method output_type + 2, // [2:11] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name @@ -442,22 +407,12 @@ func file_vts_proto_init() { return } file_appraisal_context_proto_init() + file_endorsement_query_proto_init() file_state_proto_init() + file_status_proto_init() file_token_proto_init() if !protoimpl.UnsafeEnabled { file_vts_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Status); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_vts_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Evidence); i { case 0: return &v.state @@ -469,7 +424,7 @@ func file_vts_proto_init() { return nil } } - file_vts_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_vts_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SubmitEndorsementsRequest); i { case 0: return &v.state @@ -481,7 +436,7 @@ func file_vts_proto_init() { return nil } } - file_vts_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + file_vts_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*SubmitEndorsementsResponse); i { case 0: return &v.state @@ -493,7 +448,7 @@ func file_vts_proto_init() { return nil } } - file_vts_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + file_vts_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MediaTypeList); i { case 0: return &v.state @@ -505,7 +460,7 @@ func file_vts_proto_init() { return nil } } - file_vts_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + file_vts_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PublicKey); i { case 0: return &v.state @@ -524,7 +479,7 @@ func file_vts_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_vts_proto_rawDesc, NumEnums: 0, - NumMessages: 6, + NumMessages: 5, NumExtensions: 0, NumServices: 1, }, diff --git a/proto/vts.pb.json.go b/proto/vts.pb.json.go index c1a55d8f..61dd260f 100644 --- a/proto/vts.pb.json.go +++ b/proto/vts.pb.json.go @@ -7,22 +7,6 @@ import ( "google.golang.org/protobuf/encoding/protojson" ) -// MarshalJSON implements json.Marshaler -func (msg *Status) MarshalJSON() ([]byte, error) { - return protojson.MarshalOptions{ - UseEnumNumbers: false, - EmitUnpopulated: false, - UseProtoNames: false, - }.Marshal(msg) -} - -// UnmarshalJSON implements json.Unmarshaler -func (msg *Status) UnmarshalJSON(b []byte) error { - return protojson.UnmarshalOptions{ - DiscardUnknown: false, - }.Unmarshal(b, msg) -} - // MarshalJSON implements json.Marshaler func (msg *Evidence) MarshalJSON() ([]byte, error) { return protojson.MarshalOptions{ diff --git a/proto/vts.proto b/proto/vts.proto index b860b864..53476563 100644 --- a/proto/vts.proto +++ b/proto/vts.proto @@ -2,18 +2,15 @@ syntax = "proto3"; package proto; import "appraisal_context.proto"; +import "endorsement_query.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/struct.proto"; import "state.proto"; +import "status.proto"; import "token.proto"; option go_package = "github.com/veraison/services/proto"; -message Status { - bool result = 1; - string error_detail = 2; -} - message Evidence { google.protobuf.Struct value = 1; } @@ -51,5 +48,10 @@ service VTS { // Returns the public key used to sign evidence. rpc GetEARSigningPublicKey(google.protobuf.Empty) returns (PublicKey); -} + // endorsement distribution API + rpc GetEndorsements(EndorsementQueryIn) returns (EndorsementQueryOut); + rpc GetSupportedCoservMediaTypes(google.protobuf.Empty) returns (MediaTypeList); + // Returns the public key used to sign CoSERV results + rpc GetCoservSigningPublicKey(google.protobuf.Empty) returns (PublicKey); +} diff --git a/proto/vts_grpc.pb.go b/proto/vts_grpc.pb.go index fdc60431..729c8645 100644 --- a/proto/vts_grpc.pb.go +++ b/proto/vts_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.2.0 -// - protoc v5.27.3 +// - protoc v5.29.3 // source: vts.proto package proto @@ -33,6 +33,11 @@ type VTSClient interface { SubmitEndorsements(ctx context.Context, in *SubmitEndorsementsRequest, opts ...grpc.CallOption) (*SubmitEndorsementsResponse, error) // Returns the public key used to sign evidence. GetEARSigningPublicKey(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*PublicKey, error) + // endorsement distribution API + GetEndorsements(ctx context.Context, in *EndorsementQueryIn, opts ...grpc.CallOption) (*EndorsementQueryOut, error) + GetSupportedCoservMediaTypes(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*MediaTypeList, error) + // Returns the public key used to sign CoSERV results + GetCoservSigningPublicKey(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*PublicKey, error) } type vTSClient struct { @@ -97,6 +102,33 @@ func (c *vTSClient) GetEARSigningPublicKey(ctx context.Context, in *emptypb.Empt return out, nil } +func (c *vTSClient) GetEndorsements(ctx context.Context, in *EndorsementQueryIn, opts ...grpc.CallOption) (*EndorsementQueryOut, error) { + out := new(EndorsementQueryOut) + err := c.cc.Invoke(ctx, "/proto.VTS/GetEndorsements", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vTSClient) GetSupportedCoservMediaTypes(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*MediaTypeList, error) { + out := new(MediaTypeList) + err := c.cc.Invoke(ctx, "/proto.VTS/GetSupportedCoservMediaTypes", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *vTSClient) GetCoservSigningPublicKey(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*PublicKey, error) { + out := new(PublicKey) + err := c.cc.Invoke(ctx, "/proto.VTS/GetCoservSigningPublicKey", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // VTSServer is the server API for VTS service. // All implementations must embed UnimplementedVTSServer // for forward compatibility @@ -111,6 +143,11 @@ type VTSServer interface { SubmitEndorsements(context.Context, *SubmitEndorsementsRequest) (*SubmitEndorsementsResponse, error) // Returns the public key used to sign evidence. GetEARSigningPublicKey(context.Context, *emptypb.Empty) (*PublicKey, error) + // endorsement distribution API + GetEndorsements(context.Context, *EndorsementQueryIn) (*EndorsementQueryOut, error) + GetSupportedCoservMediaTypes(context.Context, *emptypb.Empty) (*MediaTypeList, error) + // Returns the public key used to sign CoSERV results + GetCoservSigningPublicKey(context.Context, *emptypb.Empty) (*PublicKey, error) mustEmbedUnimplementedVTSServer() } @@ -136,6 +173,15 @@ func (UnimplementedVTSServer) SubmitEndorsements(context.Context, *SubmitEndorse func (UnimplementedVTSServer) GetEARSigningPublicKey(context.Context, *emptypb.Empty) (*PublicKey, error) { return nil, status.Errorf(codes.Unimplemented, "method GetEARSigningPublicKey not implemented") } +func (UnimplementedVTSServer) GetEndorsements(context.Context, *EndorsementQueryIn) (*EndorsementQueryOut, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetEndorsements not implemented") +} +func (UnimplementedVTSServer) GetSupportedCoservMediaTypes(context.Context, *emptypb.Empty) (*MediaTypeList, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetSupportedCoservMediaTypes not implemented") +} +func (UnimplementedVTSServer) GetCoservSigningPublicKey(context.Context, *emptypb.Empty) (*PublicKey, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetCoservSigningPublicKey not implemented") +} func (UnimplementedVTSServer) mustEmbedUnimplementedVTSServer() {} // UnsafeVTSServer may be embedded to opt out of forward compatibility for this service. @@ -257,6 +303,60 @@ func _VTS_GetEARSigningPublicKey_Handler(srv interface{}, ctx context.Context, d return interceptor(ctx, in, info, handler) } +func _VTS_GetEndorsements_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(EndorsementQueryIn) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VTSServer).GetEndorsements(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/proto.VTS/GetEndorsements", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VTSServer).GetEndorsements(ctx, req.(*EndorsementQueryIn)) + } + return interceptor(ctx, in, info, handler) +} + +func _VTS_GetSupportedCoservMediaTypes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VTSServer).GetSupportedCoservMediaTypes(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/proto.VTS/GetSupportedCoservMediaTypes", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VTSServer).GetSupportedCoservMediaTypes(ctx, req.(*emptypb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _VTS_GetCoservSigningPublicKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VTSServer).GetCoservSigningPublicKey(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/proto.VTS/GetCoservSigningPublicKey", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VTSServer).GetCoservSigningPublicKey(ctx, req.(*emptypb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + // VTS_ServiceDesc is the grpc.ServiceDesc for VTS service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -288,6 +388,18 @@ var VTS_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetEARSigningPublicKey", Handler: _VTS_GetEARSigningPublicKey_Handler, }, + { + MethodName: "GetEndorsements", + Handler: _VTS_GetEndorsements_Handler, + }, + { + MethodName: "GetSupportedCoservMediaTypes", + Handler: _VTS_GetSupportedCoservMediaTypes_Handler, + }, + { + MethodName: "GetCoservSigningPublicKey", + Handler: _VTS_GetCoservSigningPublicKey_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "vts.proto", diff --git a/scheme/Makefile b/scheme/Makefile index c3336e19..3d8709b2 100644 --- a/scheme/Makefile +++ b/scheme/Makefile @@ -8,6 +8,8 @@ SUBDIR += psa-iot SUBDIR += tpm-enacttrust SUBDIR += parsec-tpm SUBDIR += parsec-cca +SUBDIR += nvidia-coserv +SUBDIR += amd-kds-coserv clean: ; $(RM) -rf ./bin diff --git a/scheme/README.md b/scheme/README.md index 79c86fc3..5bed35d1 100644 --- a/scheme/README.md +++ b/scheme/README.md @@ -2,11 +2,8 @@ This directory contains packages implementing support of specific attestation schemes. > [!NOTE] -> When adding (or removing) a scheme, please update `../builtin/scheme.gen.go` -> to include the appropriate entries. This can be done automatically using -> `../scripts/gen-schemes` script (see `../builtin/Makefile`) or by manually -> editing the file. The script takes a long time to execute, so unless multiple -> schemes are being added/moved/deleted, manual editing may be easier. +> When adding (or removing) a scheme, please update `../builtin/schemes.go` +> to include the appropriate entries. ## Current Schemes @@ -31,10 +28,14 @@ Currently the following schemes are implemented: > for how to convert them to the new framework. Supporting a new attestation scheme requires defining how to provision -endorsements (if any) by implementing [`IEndorsementHandler`](../handler/iendorsementhandler.go), -how to process evidence tokens by implementing [`IEvidenceHandler`](../handler/ievidencehandler.go) and -how to create and obtain scheme-specific keys used to store and retrieve endorsements and trust anchors -by implementing [`IStoreHandler`](../handler/istorehandler.go). +endorsements (if any) by implementing +[`IEndorsementHandler`](../handler/iendorsementhandler.go), how to process +evidence tokens by implementing +[`IEvidenceHandler`](../handler/ievidencehandler.go), how to create and obtain +scheme-specific keys used to store and retrieve endorsements and trust anchors +by implementing [`IStoreHandler`](../handler/istorehandler.go), and how to +handle CoSERV queries by implementing +[`ICoservProxyHandler`](../handler/icoservproxyhandler.go). Finally, an executable should be created that [registers](../handler/plugin.go) and serves them. @@ -65,10 +66,17 @@ type MyStoreHandler struct {} // Implementation of IStoreHandler for MyStoreHandler // ... +type MyCoservProxyHandler struct {} + +// ... +// Implementation of ICoservProxyHandler for MyCoservProxyHandler +// ... + func main() { handler.RegisterEndorsementHandler(&MyEndorsementHandler{}) handler.RegisterEvidenceHandler(&MyEvidenceHandler{}) handler.RegisterStoreHandler(&MyStoreHandler{}) + handler.RegisterCoservProxyHandler(&MyCoservProxyHandler{}) plugin.Serve() } diff --git a/scheme/amd-kds-coserv/Makefile b/scheme/amd-kds-coserv/Makefile new file mode 100644 index 00000000..77b1b57f --- /dev/null +++ b/scheme/amd-kds-coserv/Makefile @@ -0,0 +1,15 @@ +# Copyright 2025 Contributors to the Veraison project. +# SPDX-License-Identifier: Apache-2.0 + +.DEFAULT_GOAL := test + +GOPKG := github.com/veraison/services/scheme/amd-kds-coserv +SRCS := $(wildcard *.go) + +SUBDIR += plugin + +include ../../mk/common.mk +include ../../mk/lint.mk +include ../../mk/pkg.mk +include ../../mk/subdir.mk +include ../../mk/test.mk diff --git a/scheme/amd-kds-coserv/README.md b/scheme/amd-kds-coserv/README.md new file mode 100644 index 00000000..bacd5cad --- /dev/null +++ b/scheme/amd-kds-coserv/README.md @@ -0,0 +1,7 @@ +:warning: :construction: :warning: + +Please note that this code is currently in an experimental phase and has not yet been tested as part of the integration test suite. +It may stop working at any time without warning. +If you encounter an issue, please report it as a [bug](https://github.com/veraison/services/issues/new?template=bug-report.md). + +:warning: :construction: :warning: \ No newline at end of file diff --git a/scheme/amd-kds-coserv/coserv_handler.go b/scheme/amd-kds-coserv/coserv_handler.go new file mode 100644 index 00000000..73db4f97 --- /dev/null +++ b/scheme/amd-kds-coserv/coserv_handler.go @@ -0,0 +1,150 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package amdkdscoserv + +import ( + "encoding/pem" + "errors" + "fmt" + "io" + "net/http" + "time" + + "github.com/veraison/corim/comid" + "github.com/veraison/corim/coserv" +) + +type CoservProxyHandler struct{} + +var ( + dummyAuthority = []byte{0xab, 0xcd, 0xef} +) + +func constructVcekUrl(instance *coserv.StatefulInstance) string { + // TODO(paulhowardarm) - deduce the product name and TCB parameters from the + // stateful environment. Currently assuming 'Milan" and TCB=0. + return fmt.Sprintf("https://kdsintf.amd.com/vcek/v1/Milan/%x", instance.Instance.Bytes()) +} + +func getVcekForInstance(instance *coserv.StatefulInstance) ([]byte, error) { + url := constructVcekUrl(instance) + resp, err := http.Get(url) + if err != nil { + return nil, fmt.Errorf("HTTP GET error: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + return nil, fmt.Errorf("error response from AMD KDS: %d %s\nResponse body: %s", resp.StatusCode, resp.Status, string(body)) + } + + // Read certificate bytes. + certBytes, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("reading response error: %v", err) + } + + return certBytes, nil +} + +func (s CoservProxyHandler) GetName() string { + return "amd-kds-coserv-proxy-handler" +} + +func (s CoservProxyHandler) GetAttestationScheme() string { + return SchemeName +} + +func (s CoservProxyHandler) GetSupportedMediaTypes() []string { + return CoservMediaTypes +} + +func (s CoservProxyHandler) addTrustAnchorForInstance(i *coserv.StatefulInstance, results *coserv.ResultSet) error { + cert, err := getVcekForInstance(i) + + if err != nil { + return err + } + + // TODO(paulhowardarm) - This authority is a dummy value. + // We need some kind of cert here, representing this plug-in's authority to re-package from NVIDIA CoRIM + // We probably also need an NVIDIA cert in the chain + authority, err := comid.NewCryptoKeyTaggedBytes(dummyAuthority) + + if err != nil { + return fmt.Errorf("failed to make authority tagged bytes: %v", err) + } + + block := &pem.Block{ + Type: "CERTIFICATE", + Headers: map[string]string{}, + Bytes: cert, + } + + pem := pem.EncodeToMemory(block) + + triple := comid.KeyTriple{ + Environment: comid.Environment{ + Instance: i.Instance, + }, + VerifKeys: comid.CryptoKeys{ + comid.MustNewPKIXBase64Cert(string(pem)), + }, + } + + akQuad := coserv.AKQuad{ + Authorities: comid.NewCryptoKeys().Add(authority), + AKTriple: &triple, + } + + results.AddAttestationKeys(akQuad) + + return nil +} + +func (s CoservProxyHandler) GetEndorsements(tenantID string, query string) ([]byte, error) { + var q coserv.Coserv + if err := q.FromBase64Url(query); err != nil { + return nil, err + } + + if q.Query.ArtifactType != coserv.ArtifactTypeTrustAnchors { + return nil, errors.New("AMD CoSERV proxy plug-in can only provide Trust Anchor artifacts") + } + + if q.Query.EnvironmentSelector.Groups != nil { + return nil, errors.New("AMD CoSERV proxy plug-in can only provide for Instance environments, not Groups") + } + + if q.Query.EnvironmentSelector.Classes != nil { + return nil, errors.New("AMD CoSERV proxy plug-in can only provide for Instance environments, not Classes") + } + + if q.Query.EnvironmentSelector.Instances == nil || len(*q.Query.EnvironmentSelector.Instances) == 0 { + return nil, errors.New("AMD CoSERV proxy plug-in expects at least one Instance environment") + } + + // Begin with an empty result set + coservResult := *coserv.NewResultSet() + + // Loop over all of the instance environments in the query, and call the AMD KDS cloud service for each one. + for _, i := range *q.Query.EnvironmentSelector.Instances { + err := s.addTrustAnchorForInstance(&i, &coservResult) + if err != nil { + return nil, err + } + } + + // Set expiry on the results - fairly arbitrary expiry time of 1 hour + coservResult.SetExpiry(time.Now().Add(time.Hour)) + + // Add all results into the top-level CoSERV object + err := q.AddResults(coservResult) + if err != nil { + return nil, err + } + + return q.ToCBOR() +} diff --git a/scheme/amd-kds-coserv/plugin/Makefile b/scheme/amd-kds-coserv/plugin/Makefile new file mode 100644 index 00000000..730c6798 --- /dev/null +++ b/scheme/amd-kds-coserv/plugin/Makefile @@ -0,0 +1,8 @@ +ifndef COMBINED_PLUGINS + SUBDIR += coserv-handler +else + SUBDIR += combined +endif + +include ../../../mk/common.mk +include ../../../mk/subdir.mk diff --git a/scheme/amd-kds-coserv/plugin/combined/Makefile b/scheme/amd-kds-coserv/plugin/combined/Makefile new file mode 100644 index 00000000..f60402e6 --- /dev/null +++ b/scheme/amd-kds-coserv/plugin/combined/Makefile @@ -0,0 +1,11 @@ +# Copyright 2025 Contributors to the Veraison project. +# SPDX-License-Identifier: Apache-2.0 + +PLUGIN := ../../../bin/amd-kds-coserv.plugin +GOPKG := github.com/veraison/services/scheme/amd-kds-coserv +SRCS := main.go + +include ../../../../mk/common.mk +include ../../../../mk/plugin.mk +include ../../../../mk/lint.mk +include ../../../../mk/test.mk diff --git a/scheme/amd-kds-coserv/plugin/combined/main.go b/scheme/amd-kds-coserv/plugin/combined/main.go new file mode 100644 index 00000000..236c7405 --- /dev/null +++ b/scheme/amd-kds-coserv/plugin/combined/main.go @@ -0,0 +1,15 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "github.com/veraison/services/handler" + "github.com/veraison/services/plugin" + scheme "github.com/veraison/services/scheme/amd-kds-coserv" +) + +func main() { + handler.RegisterCoservProxyHandler(&scheme.CoservProxyHandler{}) + plugin.Serve() +} diff --git a/scheme/amd-kds-coserv/plugin/coserv-handler/Makefile b/scheme/amd-kds-coserv/plugin/coserv-handler/Makefile new file mode 100644 index 00000000..f60402e6 --- /dev/null +++ b/scheme/amd-kds-coserv/plugin/coserv-handler/Makefile @@ -0,0 +1,11 @@ +# Copyright 2025 Contributors to the Veraison project. +# SPDX-License-Identifier: Apache-2.0 + +PLUGIN := ../../../bin/amd-kds-coserv.plugin +GOPKG := github.com/veraison/services/scheme/amd-kds-coserv +SRCS := main.go + +include ../../../../mk/common.mk +include ../../../../mk/plugin.mk +include ../../../../mk/lint.mk +include ../../../../mk/test.mk diff --git a/scheme/amd-kds-coserv/plugin/coserv-handler/main.go b/scheme/amd-kds-coserv/plugin/coserv-handler/main.go new file mode 100644 index 00000000..236c7405 --- /dev/null +++ b/scheme/amd-kds-coserv/plugin/coserv-handler/main.go @@ -0,0 +1,15 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "github.com/veraison/services/handler" + "github.com/veraison/services/plugin" + scheme "github.com/veraison/services/scheme/amd-kds-coserv" +) + +func main() { + handler.RegisterCoservProxyHandler(&scheme.CoservProxyHandler{}) + plugin.Serve() +} diff --git a/scheme/amd-kds-coserv/scheme.go b/scheme/amd-kds-coserv/scheme.go new file mode 100644 index 00000000..26967aaf --- /dev/null +++ b/scheme/amd-kds-coserv/scheme.go @@ -0,0 +1,12 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package amdkdscoserv + +const SchemeName = "AMD_KDS_COSERV" + +var ( + CoservMediaTypes = []string{ + `application/coserv+cbor; profile="tag:github.com/veraison,2023:amd_kds_coserv_proxy#1.0.0"`, + } +) diff --git a/scheme/arm-cca/endorsement_handler.go b/scheme/arm-cca/endorsement_handler.go index 0d8676e6..2e2a76f7 100644 --- a/scheme/arm-cca/endorsement_handler.go +++ b/scheme/arm-cca/endorsement_handler.go @@ -3,10 +3,19 @@ package arm_cca import ( + "encoding/json" + "fmt" + "time" + "mime" + "github.com/veraison/corim/comid" + "github.com/veraison/corim/coserv" + "github.com/veraison/services/handler" + "github.com/veraison/services/log" "github.com/veraison/services/scheme/common" + "github.com/veraison/services/scheme/common/arm" ) type EndorsementHandler struct{} @@ -49,3 +58,85 @@ func (o EndorsementHandler) Decode(data []byte, mediaType string, caCertPool []b // Default to unsigned CoRIM decoder return common.UnsignedCorimDecoder(data, extractor) } + +func (o EndorsementHandler) CoservRepackage(query string, resultSet []string) ([]byte, error) { + var q coserv.Coserv + if err := q.FromBase64Url(query); err != nil { + return nil, err + } + + // add (dummy, for now) authority + authority, err := comid.NewCryptoKeyTaggedBytes([]byte("dummyauth")) + if err != nil { + return nil, fmt.Errorf("unable to map (dummy) authority: %w", err) + } + + // add (dummy, for now) expiry + dummyExpiry := time.Now().Add(time.Hour * 1) + + rset := coserv.NewResultSet() + rset.SetExpiry(dummyExpiry) + + log.Debugf("result set: %v", resultSet) + + for i, j := range resultSet { + var e handler.Endorsement + err := json.Unmarshal([]byte(j), &e) + if err != nil { + return nil, fmt.Errorf("unable to decode result[%d] %q to Endorsement: %w", i, j, err) + } + + switch q.Query.ArtifactType { + // reference values + case coserv.ArtifactTypeReferenceValues: + if e.Type != "reference value" { + log.Errorf("CCA query-result mismatch: want reference value, got %s", e.Type) + continue + } else if e.SubType != "platform.sw-component" { + log.Warnf("CCA reference values of sub-type %q are not currently handled", e.SubType) + continue + } + + rvt, err := arm.EndorsementToReferenceValueTriple(e) + if err != nil { + return nil, fmt.Errorf("unable to map result[%d] %q to CoRIM reference value triple: %w", i, j, err) + } + + rvq := &coserv.RefValQuad{ + Authorities: comid.NewCryptoKeys().Add(authority), + RVTriple: rvt, + } + + rset.AddReferenceValues(*rvq) + + // trust anchors + case coserv.ArtifactTypeTrustAnchors: + if e.Type != "trust anchor" { + log.Errorf("CCA query-result mismatch: want trust anchor, got %s", e.Type) + continue + } + + akt, err := arm.EndorsementToAttestationKeyTriple(e) + if err != nil { + return nil, fmt.Errorf("unable to map result[%d] %q to CoRIM attest key triple: %w", i, j, err) + } + + akq := &coserv.AKQuad{ + Authorities: comid.NewCryptoKeys().Add(authority), + AKTriple: akt, + } + + rset.AddAttestationKeys(*akq) + + default: + log.Errorf("CCA CoSERV can only deal with reference values and trust anchors at the moment") + continue + } + } + + if err := q.AddResults(*rset); err != nil { + return nil, fmt.Errorf("failure adding the translated result set: %w", err) + } + + return q.ToCBOR() +} diff --git a/scheme/arm-cca/endorsement_handler_test.go b/scheme/arm-cca/endorsement_handler_test.go index 21d62359..69d66290 100644 --- a/scheme/arm-cca/endorsement_handler_test.go +++ b/scheme/arm-cca/endorsement_handler_test.go @@ -57,11 +57,11 @@ func TestDecoder_Decode_invalid_data(t *testing.T) { invalidCbor := []byte("invalid CBOR") - expectedErr := `CBOR decoding failed: expected map (CBOR Major Type 5), found Major Type 3` + expectedErr := `CBOR decoding failed` _, err := d.Decode(invalidCbor, "", nil) - assert.EqualError(t, err, expectedErr) + assert.ErrorContains(t, err, expectedErr) } func TestDecoder_Decode_CcaSsdRefVal_OK(t *testing.T) { diff --git a/scheme/arm-cca/plugin/combined/main.go b/scheme/arm-cca/plugin/combined/main.go index b3edf9ae..f9458855 100644 --- a/scheme/arm-cca/plugin/combined/main.go +++ b/scheme/arm-cca/plugin/combined/main.go @@ -1,4 +1,4 @@ -// Copyright 2022-2024 Contributors to the Veraison project. +// Copyright 2022-2025 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package main diff --git a/scheme/arm-cca/scheme.go b/scheme/arm-cca/scheme.go index 6e4c65a5..e4c01768 100644 --- a/scheme/arm-cca/scheme.go +++ b/scheme/arm-cca/scheme.go @@ -9,6 +9,8 @@ var ( // Unsigned CoRIM profiles `application/corim-unsigned+cbor; profile="http://arm.com/cca/ssd/1"`, `application/corim-unsigned+cbor; profile="http://arm.com/cca/realm/1"`, + `application/rim+cbor; profile="tag:arm.com,2023:cca_platform#1.0.0"`, + `application/rim+cbor; profile="tag:arm.com,2023:realm#1.0.0"`, // Signed CoRIM profiles `application/rim+cose; profile="http://arm.com/cca/ssd/1"`, `application/rim+cose; profile="http://arm.com/cca/realm/1"`, @@ -17,4 +19,8 @@ var ( EvidenceMediaTypes = []string{ `application/eat-collection; profile="http://arm.com/CCA-SSD/1.0.0"`, } + + CoservMediaTypes = []string{ + `application/coserv+cbor; profile="tag:arm.com,2023:cca_platform#1.0.0"`, + } ) diff --git a/scheme/arm-cca/store_handler.go b/scheme/arm-cca/store_handler.go index 537a4fbf..b197d49b 100644 --- a/scheme/arm-cca/store_handler.go +++ b/scheme/arm-cca/store_handler.go @@ -1,12 +1,16 @@ -// Copyright 2024 Contributors to the Veraison project. +// Copyright 2024-2025 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package arm_cca import ( + "encoding/base64" + "errors" "fmt" "github.com/veraison/ccatoken" + "github.com/veraison/corim/comid" + "github.com/veraison/corim/coserv" "github.com/veraison/services/handler" "github.com/veraison/services/proto" "github.com/veraison/services/scheme/common/arm" @@ -86,3 +90,67 @@ func (s StoreHandler) GetRefValueIDs( } return append(pids, rids...), nil } + +func (s StoreHandler) SynthCoservQueryKeys(tenantID string, query string) ([]string, error) { + var q coserv.Coserv + if err := q.FromBase64Url(query); err != nil { + return nil, err + } + + var keys []string + + switch q.Query.ArtifactType { + case coserv.ArtifactTypeReferenceValues: + s := q.Query.EnvironmentSelector + + if s.Classes != nil { + for i, v := range *s.Classes { + implID, err := extractImplID(*v.Class) + if err != nil { + return nil, fmt.Errorf("creating lookup key for class[%d]: %w", i, err) + } + + keys = append(keys, arm.RefValLookupKey(SchemeName, tenantID, implID)) + } + } + case coserv.ArtifactTypeTrustAnchors: + s := q.Query.EnvironmentSelector + + if s.Instances != nil { + for i, v := range *s.Instances { + instID, err := extractInstID(*v.Instance) + if err != nil { + return nil, fmt.Errorf("creating lookup key for instance[%d]: %w", i, err) + } + + keys = append(keys, arm.TaCoservLookupKey(SchemeName, tenantID, instID)) + } + } + case coserv.ArtifactTypeEndorsedValues: + return nil, errors.New("CCA does not implement endorsed value queries") + } + + return keys, nil +} + +func extractImplID(c comid.Class) (string, error) { + if c.ClassID == nil { + return "", errors.New("missing class-id") + } + + implID, err := c.ClassID.GetImplID() + if err != nil { + return "", fmt.Errorf("could not extract implementation-id from class-id: %w", err) + } + + return implID.String(), nil +} + +func extractInstID(i comid.Instance) (string, error) { + instID, err := i.GetUEID() + if err != nil { + return "", fmt.Errorf("could not extract implementation-id from instance-id: %w", err) + } + + return base64.StdEncoding.EncodeToString(instID), nil +} diff --git a/scheme/arm-cca/test/corim/unsignedCorimCcaComidCcaRefValFour.cbor b/scheme/arm-cca/test/corim/unsignedCorimCcaComidCcaRefValFour.cbor index 9fd1f414..a9350438 100644 Binary files a/scheme/arm-cca/test/corim/unsignedCorimCcaComidCcaRefValFour.cbor and b/scheme/arm-cca/test/corim/unsignedCorimCcaComidCcaRefValFour.cbor differ diff --git a/scheme/arm-cca/test/corim/unsignedCorimCcaComidCcaRefValOne.cbor b/scheme/arm-cca/test/corim/unsignedCorimCcaComidCcaRefValOne.cbor index fc9bbf49..c04944ee 100644 Binary files a/scheme/arm-cca/test/corim/unsignedCorimCcaComidCcaRefValOne.cbor and b/scheme/arm-cca/test/corim/unsignedCorimCcaComidCcaRefValOne.cbor differ diff --git a/scheme/arm-cca/test/corim/unsignedCorimCcaNoProfileComidCcaRefValFour.cbor b/scheme/arm-cca/test/corim/unsignedCorimCcaNoProfileComidCcaRefValFour.cbor index 21acf3fc..53fb95e3 100644 Binary files a/scheme/arm-cca/test/corim/unsignedCorimCcaNoProfileComidCcaRefValFour.cbor and b/scheme/arm-cca/test/corim/unsignedCorimCcaNoProfileComidCcaRefValFour.cbor differ diff --git a/scheme/arm-cca/test/corim/unsignedCorimCcaNoProfileComidCcaRefValOne.cbor b/scheme/arm-cca/test/corim/unsignedCorimCcaNoProfileComidCcaRefValOne.cbor index 7771ef1a..f3cb1d06 100644 Binary files a/scheme/arm-cca/test/corim/unsignedCorimCcaNoProfileComidCcaRefValOne.cbor and b/scheme/arm-cca/test/corim/unsignedCorimCcaNoProfileComidCcaRefValOne.cbor differ diff --git a/scheme/arm-cca/test/corim/unsignedCorimCcaRealmComidCcaRealm.cbor b/scheme/arm-cca/test/corim/unsignedCorimCcaRealmComidCcaRealm.cbor index e921e628..88244eeb 100644 Binary files a/scheme/arm-cca/test/corim/unsignedCorimCcaRealmComidCcaRealm.cbor and b/scheme/arm-cca/test/corim/unsignedCorimCcaRealmComidCcaRealm.cbor differ diff --git a/scheme/arm-cca/test/corim/unsignedCorimCcaRealmComidCcaRealmInvalidClass.cbor b/scheme/arm-cca/test/corim/unsignedCorimCcaRealmComidCcaRealmInvalidClass.cbor index 793fe376..8a6ff73a 100644 Binary files a/scheme/arm-cca/test/corim/unsignedCorimCcaRealmComidCcaRealmInvalidClass.cbor and b/scheme/arm-cca/test/corim/unsignedCorimCcaRealmComidCcaRealmInvalidClass.cbor differ diff --git a/scheme/arm-cca/test/corim/unsignedCorimCcaRealmComidCcaRealmInvalidInstance.cbor b/scheme/arm-cca/test/corim/unsignedCorimCcaRealmComidCcaRealmInvalidInstance.cbor index 2bb4618b..ffa3d276 100644 Binary files a/scheme/arm-cca/test/corim/unsignedCorimCcaRealmComidCcaRealmInvalidInstance.cbor and b/scheme/arm-cca/test/corim/unsignedCorimCcaRealmComidCcaRealmInvalidInstance.cbor differ diff --git a/scheme/arm-cca/test/corim/unsignedCorimCcaRealmComidCcaRealmNoClass.cbor b/scheme/arm-cca/test/corim/unsignedCorimCcaRealmComidCcaRealmNoClass.cbor index 84b19e6f..0e4c85cf 100644 Binary files a/scheme/arm-cca/test/corim/unsignedCorimCcaRealmComidCcaRealmNoClass.cbor and b/scheme/arm-cca/test/corim/unsignedCorimCcaRealmComidCcaRealmNoClass.cbor differ diff --git a/scheme/arm-cca/test/corim/unsignedCorimCcaRealmComidCcaRealmNoInstance.cbor b/scheme/arm-cca/test/corim/unsignedCorimCcaRealmComidCcaRealmNoInstance.cbor index 8758a807..4d84201f 100644 Binary files a/scheme/arm-cca/test/corim/unsignedCorimCcaRealmComidCcaRealmNoInstance.cbor and b/scheme/arm-cca/test/corim/unsignedCorimCcaRealmComidCcaRealmNoInstance.cbor differ diff --git a/scheme/common/arm/coservrepackager.go b/scheme/common/arm/coservrepackager.go new file mode 100644 index 00000000..877a5bd9 --- /dev/null +++ b/scheme/common/arm/coservrepackager.go @@ -0,0 +1,129 @@ +// Copyright 2022-2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 +package arm + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/veraison/corim/comid" + "github.com/veraison/services/handler" + "github.com/veraison/swid" +) + +func EndorsementToReferenceValueTriple(e handler.Endorsement) (*comid.ValueTriple, error) { + var attrs SwAttr + + if err := json.Unmarshal(e.Attributes, &attrs); err != nil { + return nil, fmt.Errorf("unmarshalling attributes: %w", err) + } + + // mkey + + rvID, err := comid.NewPSARefValID(attrs.SignerID) + if err != nil { + return nil, fmt.Errorf("instantiating PSA reference value ID: %w", err) + } + + label := attrs.MeasurementType + if label != "" { + rvID.SetLabel(label) + } + + version := attrs.Version + if version != "" { + rvID.SetVersion(version) + } + + // mval + + m, err := comid.NewPSAMeasurement(rvID) + if err != nil { + return nil, fmt.Errorf("instantiating PSA measurement: %w", err) + } + + m.AddDigest( + swid.AlgIDFromString(attrs.MeasDesc), + attrs.MeasurementValue, + ) + + measurements := comid.NewMeasurements().Add(m) + + // env + + class := comid.NewClassImplID(comid.ImplID(attrs.ImplID)) + if class == nil { + return nil, errors.New("class identifier instantiation failed") + } + + model := attrs.Model + if model != "" { + class.SetModel(model) + } + + vendor := attrs.Vendor + if vendor != "" { + class.SetVendor(vendor) + } + + env := comid.Environment{ + Class: class, + } + + // rv triple + + return &comid.ValueTriple{ + Environment: env, + Measurements: *measurements, + }, nil +} + +func EndorsementToAttestationKeyTriple(e handler.Endorsement) (*comid.KeyTriple, error) { + var attrs TaAttr + + if err := json.Unmarshal(e.Attributes, &attrs); err != nil { + return nil, fmt.Errorf("unmarshalling attributes: %w", err) + } + + // cryptokeys + + k, err := comid.NewPKIXBase64Key(attrs.VerifKey) + if err != nil { + return nil, fmt.Errorf("crypto key instantiation failed: %w", err) + } + + ak := comid.NewCryptoKeys().Add(k) + + // env + + instance, err := comid.NewUEIDInstance(attrs.InstID) + if err != nil { + return nil, fmt.Errorf("instance identifier instantiation failed: %w", err) + } + + class := comid.NewClassImplID(comid.ImplID(attrs.ImplID)) + if class == nil { + return nil, errors.New("class identifier instantiation failed") + } + + model := attrs.Model + if model != "" { + class.SetModel(model) + } + + vendor := attrs.Vendor + if vendor != "" { + class.SetVendor(vendor) + } + + env := comid.Environment{ + Class: class, + Instance: instance, + } + + return &comid.KeyTriple{ + Environment: env, + VerifKeys: *ak, + }, nil +} diff --git a/scheme/common/arm/handlers.go b/scheme/common/arm/handlers.go index c6859eae..0d3888ab 100644 --- a/scheme/common/arm/handlers.go +++ b/scheme/common/arm/handlers.go @@ -1,4 +1,4 @@ -// Copyright 2021-2024 Contributors to the Veraison project. +// Copyright 2021-2025 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package arm @@ -91,9 +91,13 @@ func SynthKeysFromTrustAnchors(scheme string, tenantID string, return nil, fmt.Errorf("unable to synthesize trust anchor abs-path: %w", err) } - lookupKey := TaLookupKey(scheme, tenantID, implID, instID) - log.Debugf("Scheme %s Plugin TA Look Up Key= %s\n", scheme, lookupKey) - return []string{lookupKey}, nil + verificationLookupKey := TaLookupKey(scheme, tenantID, implID, instID) + log.Debugf("TA verification look up key: %s", verificationLookupKey) + + coservLookupKey := TaCoservLookupKey(scheme, tenantID, instID) + log.Debugf("TA coserv look up key: %s", coservLookupKey) + + return []string{verificationLookupKey, coservLookupKey}, nil } func GetTrustAnchorID(scheme string, tenantID string, claims psatoken.IClaims) (string, error) { diff --git a/scheme/common/arm/lookup.go b/scheme/common/arm/lookup.go index 2d86f97d..486c86f1 100644 --- a/scheme/common/arm/lookup.go +++ b/scheme/common/arm/lookup.go @@ -1,4 +1,4 @@ -// Copyright 2021-2024 Contributors to the Veraison project. +// Copyright 2021-2025 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package arm @@ -35,6 +35,16 @@ func TaLookupKey(schemeName, tenantID, implID, instID string) string { return u.String() } +func TaCoservLookupKey(schemeName, tenantID, instID string) string { + u := url.URL{ + Scheme: schemeName, + Host: tenantID, + Path: instID, + } + + return u.String() +} + func MustImplIDString(c psatoken.IClaims) string { v, err := c.GetImplID() if err != nil { diff --git a/scheme/common/unsignedcorim_decoder.go b/scheme/common/unsignedcorim_decoder.go index 9ceacae4..e17083d2 100644 --- a/scheme/common/unsignedcorim_decoder.go +++ b/scheme/common/unsignedcorim_decoder.go @@ -3,7 +3,6 @@ package common import ( - "bytes" "errors" "fmt" @@ -43,22 +42,13 @@ func UnsignedCorimDecoder( rsp := handler.EndorsementHandlerResponse{} for i, tag := range uc.Tags { - // need at least 3 bytes for the tag and 1 for the smallest bstr - if len(tag) < 3+1 { - return nil, fmt.Errorf("malformed tag at index %d", i) - } - - // split tag from data - cborTag, cborData := tag[:3], tag[3:] - - // The EnactTrust profile only knows about CoMIDs - if !bytes.Equal(cborTag, corim.ComidTag) { - return nil, fmt.Errorf("unknown CBOR tag %x detected at index %d", cborTag, i) + if tag.Number != corim.ComidTag { + return nil, fmt.Errorf("unknown CBOR tag %x detected at index %d", tag.Number, i) } var c comid.Comid - err := c.FromCBOR(cborData) + err := c.FromCBOR(tag.Content) if err != nil { return nil, fmt.Errorf("decoding failed for CoMID at index %d: %w", i, err) } diff --git a/scheme/nvidia-coserv/Makefile b/scheme/nvidia-coserv/Makefile new file mode 100644 index 00000000..49f41e73 --- /dev/null +++ b/scheme/nvidia-coserv/Makefile @@ -0,0 +1,15 @@ +# Copyright 2025 Contributors to the Veraison project. +# SPDX-License-Identifier: Apache-2.0 + +.DEFAULT_GOAL := test + +GOPKG := github.com/veraison/services/scheme/nvidia-coserv +SRCS := $(wildcard *.go) + +SUBDIR += plugin + +include ../../mk/common.mk +include ../../mk/lint.mk +include ../../mk/pkg.mk +include ../../mk/subdir.mk +include ../../mk/test.mk diff --git a/scheme/nvidia-coserv/README.md b/scheme/nvidia-coserv/README.md new file mode 100644 index 00000000..bacd5cad --- /dev/null +++ b/scheme/nvidia-coserv/README.md @@ -0,0 +1,7 @@ +:warning: :construction: :warning: + +Please note that this code is currently in an experimental phase and has not yet been tested as part of the integration test suite. +It may stop working at any time without warning. +If you encounter an issue, please report it as a [bug](https://github.com/veraison/services/issues/new?template=bug-report.md). + +:warning: :construction: :warning: \ No newline at end of file diff --git a/scheme/nvidia-coserv/coserv_handler.go b/scheme/nvidia-coserv/coserv_handler.go new file mode 100644 index 00000000..aa502e4a --- /dev/null +++ b/scheme/nvidia-coserv/coserv_handler.go @@ -0,0 +1,225 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package nvidiacoserv + +import ( + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "time" + + "github.com/veraison/cmw" + "github.com/veraison/corim/comid" + "github.com/veraison/corim/corim" + "github.com/veraison/corim/coserv" +) + +// ----- JSON Helper Types ----- + +// This struct models the API response from a call to the RIM service +type RimServiceResponse struct { + Id string `json:"id"` + Rim string `json:"rim"` + Sha256 string `json:"sha256"` + LastUpdated string `json:"last_updated"` + RimFormat string `json:"rim_format"` + RequestId string `json:"request_id"` +} + +type CoservProxyHandler struct{} + +var ( + dummyAuthority = []byte{0xab, 0xcd, 0xef} +) + +func (s CoservProxyHandler) GetName() string { + return "nvidia-coserv-proxy-handler" +} + +func (s CoservProxyHandler) GetAttestationScheme() string { + return SchemeName +} + +func (s CoservProxyHandler) GetSupportedMediaTypes() []string { + return CoservMediaTypes +} + +func callRimService(rimid *string) (*RimServiceResponse, error) { + // Create an HTTP request to the NVIDIA RIM service. + urlStr := fmt.Sprintf("https://rim.attestation.nvidia.com/v1/rim/%s", *rimid) + + resp, err := http.Get(urlStr) + if err != nil { + return nil, fmt.Errorf("HTTP GET error: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + return nil, fmt.Errorf("error response from NVIDIA RIM service: %d %s\nResponse body: %s", resp.StatusCode, resp.Status, string(body)) + } + + // Read the successful response + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("reading response error: %w", err) + } + + // Successful responses are JSON, modelled by the RimServiceResponse class, + // so deserialize into that. + var rimServiceResponse RimServiceResponse + err = json.Unmarshal(body, &rimServiceResponse) + if err != nil { + return nil, fmt.Errorf("unmarshalling response error: %w", err) + } + + return &rimServiceResponse, nil +} + +func (s CoservProxyHandler) addReferenceValuesForClass(query *coserv.Query, c *coserv.StatefulClass, results *coserv.ResultSet) error { + if *c.Class.Vendor != "NVIDIA" { + return errors.New("vendors other than NVIDIA not supported by this proxy plug-in") + } + + if c.Measurements != nil { + return errors.New("NVIDIA CoSERV proxy plug-in does not expect stateful class environments") + } + + if c.Class.Model == nil { + return errors.New("no Model field supplied in NVIDIA class environment") + } + + rimid := c.Class.Model + + rimServiceResponse, err := callRimService(rimid) + + if err != nil { + return err + } + + // The actual RIM is a base64-encoded field, so pull this out as a byte array. + rimBytes, err := base64.StdEncoding.DecodeString(rimServiceResponse.Rim) + + if err != nil { + return fmt.Errorf("failed to base64 decode the RIM byte string %s", rimServiceResponse.Rim) + } + + // If we want the raw source artifacts, add the RIM verbatim into the result set. + // We just need to switch on the RIM type (TCG or CORIM), and use a different media type + // label in each case. + if query.ResultType == coserv.ResultTypeBoth || query.ResultType == coserv.ResultTypeSourceArtifacts { + switch rimServiceResponse.RimFormat { + case "CORIM": + cmw, err := cmw.NewMonad("application/rim+cbor", rimBytes) + if err != nil { + return fmt.Errorf("failed to create CMW for CORIM source artifact: %w", err) + } + results.AddSourceArtifacts(*cmw) + case "TCG": + cmw, err := cmw.NewMonad("application/swid+xml", rimBytes) + if err != nil { + return fmt.Errorf("failed to create CMW for TCG source artifact: %w", err) + } + results.AddSourceArtifacts(*cmw) + default: + return fmt.Errorf("unexpected RIM format %s from NVIDIA RIM service", rimServiceResponse.RimFormat) + } + } + + // If we want collected artifacts, we need to parse the CORIM + if query.ResultType == coserv.ResultTypeBoth || query.ResultType == coserv.ResultTypeCollectedArtifacts { + if rimServiceResponse.RimFormat != "CORIM" { + return fmt.Errorf("CoSERV proxy plug-in cannot produce collected artifacts for non-CORIM format %s", rimServiceResponse.RimFormat) + } + + // We now know that the format is CORIM, so it should be possible to decode as such + var scorim corim.SignedCorim + err = scorim.FromCOSE(rimBytes) + if err != nil { + return fmt.Errorf("failed to parse COSE: %w", err) + } + + // Loop over the tags in the CoRIM payload. + for _, t := range scorim.UnsignedCorim.Tags { + cborTag, cborData := t.Number, t.Content + + // We'll just look at CoMID tags (and NVIDIA RIMs only contain these anyway) + if cborTag == corim.ComidTag { + var c comid.Comid + if err := c.FromCBOR(cborData); err != nil { + return fmt.Errorf("failed to populate CoMID from CBOR: %w", err) + } + + // We'll just look at reference value triples in the CoMID + for _, triple := range c.Triples.ReferenceValues.Values { + // Turn each triple into a quad + // TODO(paulhowardarm) - This authority is a dummy value. + // We need some kind of cert here, representing this plug-in's authority to re-package from NVIDIA CoRIM + // We probably also need an NVIDIA cert in the chain + authority, err := comid.NewCryptoKeyTaggedBytes(dummyAuthority) + if err != nil { + return fmt.Errorf("failed to make authority tagged bytes: %w", err) + } + + rvQuad := coserv.RefValQuad{ + Authorities: comid.NewCryptoKeys().Add(authority), + RVTriple: &triple, + } + + results.AddReferenceValues(rvQuad) + } + } + } + } + + return nil +} + +func (s CoservProxyHandler) GetEndorsements(tenantID string, query string) ([]byte, error) { + var q coserv.Coserv + if err := q.FromBase64Url(query); err != nil { + return nil, err + } + + if q.Query.ArtifactType != coserv.ArtifactTypeReferenceValues { + return nil, errors.New("NVIDIA CoSERV proxy plug-in can only provide Reference Value artifacts") + } + + if q.Query.EnvironmentSelector.Groups != nil { + return nil, errors.New("NVIDIA CoSERV proxy plug-in can only provide for Class environments, not Groups") + } + + if q.Query.EnvironmentSelector.Instances != nil { + return nil, errors.New("NVIDIA CoSERV proxy plug-in can only provide for Class environments, not Instances") + } + + if q.Query.EnvironmentSelector.Classes == nil || len(*q.Query.EnvironmentSelector.Classes) == 0 { + return nil, errors.New("NVIDIA CoSERV proxy plug-in expects at least one Class environment") + } + + // Begin with an empty result set + coservResult := *coserv.NewResultSet() + + // Loop over all of the class environments in the query, and call the NVIDIA RIM cloud service for each one. + for _, c := range *q.Query.EnvironmentSelector.Classes { + err := s.addReferenceValuesForClass(&q.Query, &c, &coservResult) + if err != nil { + return nil, err + } + } + + // Set expiry on the results - fairly arbitrary expiry time of 1 hour + coservResult.SetExpiry(time.Now().Add(time.Hour)) + + // Add all results into the top-level CoSERV object + err := q.AddResults(coservResult) + if err != nil { + return nil, err + } + + return q.ToCBOR() +} diff --git a/scheme/nvidia-coserv/plugin/Makefile b/scheme/nvidia-coserv/plugin/Makefile new file mode 100644 index 00000000..1d04f067 --- /dev/null +++ b/scheme/nvidia-coserv/plugin/Makefile @@ -0,0 +1,11 @@ +# Copyright 2025 Contributors to the Veraison project. +# SPDX-License-Identifier: Apache-2.0 + +ifndef COMBINED_PLUGINS + SUBDIR += coserv-handler +else + SUBDIR += combined +endif + +include ../../../mk/common.mk +include ../../../mk/subdir.mk diff --git a/scheme/nvidia-coserv/plugin/combined/Makefile b/scheme/nvidia-coserv/plugin/combined/Makefile new file mode 100644 index 00000000..eb9e44cf --- /dev/null +++ b/scheme/nvidia-coserv/plugin/combined/Makefile @@ -0,0 +1,11 @@ +# Copyright 2025 Contributors to the Veraison project. +# SPDX-License-Identifier: Apache-2.0 + +PLUGIN := ../../../bin/nvidia-coserv.plugin +GOPKG := github.com/veraison/services/scheme/nvidia-coserv +SRCS := main.go + +include ../../../../mk/common.mk +include ../../../../mk/plugin.mk +include ../../../../mk/lint.mk +include ../../../../mk/test.mk diff --git a/scheme/nvidia-coserv/plugin/combined/main.go b/scheme/nvidia-coserv/plugin/combined/main.go new file mode 100644 index 00000000..c7316fec --- /dev/null +++ b/scheme/nvidia-coserv/plugin/combined/main.go @@ -0,0 +1,15 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "github.com/veraison/services/handler" + "github.com/veraison/services/plugin" + scheme "github.com/veraison/services/scheme/nvidia-coserv" +) + +func main() { + handler.RegisterCoservProxyHandler(&scheme.CoservProxyHandler{}) + plugin.Serve() +} diff --git a/scheme/nvidia-coserv/plugin/coserv-handler/Makefile b/scheme/nvidia-coserv/plugin/coserv-handler/Makefile new file mode 100644 index 00000000..ebca1b20 --- /dev/null +++ b/scheme/nvidia-coserv/plugin/coserv-handler/Makefile @@ -0,0 +1,11 @@ +# Copyright 2025 Contributors to the Veraison project. +# SPDX-License-Identifier: Apache-2.0 + +PLUGIN := ../../../bin/nvidia-coserv-handler.plugin +GOPKG := github.com/veraison/services/scheme/nvidia-coserv +SRCS := main.go + +include ../../../../mk/common.mk +include ../../../../mk/plugin.mk +include ../../../../mk/lint.mk +include ../../../../mk/test.mk diff --git a/scheme/nvidia-coserv/plugin/coserv-handler/main.go b/scheme/nvidia-coserv/plugin/coserv-handler/main.go new file mode 100644 index 00000000..c7316fec --- /dev/null +++ b/scheme/nvidia-coserv/plugin/coserv-handler/main.go @@ -0,0 +1,15 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "github.com/veraison/services/handler" + "github.com/veraison/services/plugin" + scheme "github.com/veraison/services/scheme/nvidia-coserv" +) + +func main() { + handler.RegisterCoservProxyHandler(&scheme.CoservProxyHandler{}) + plugin.Serve() +} diff --git a/scheme/nvidia-coserv/scheme.go b/scheme/nvidia-coserv/scheme.go new file mode 100644 index 00000000..cbc5a7da --- /dev/null +++ b/scheme/nvidia-coserv/scheme.go @@ -0,0 +1,12 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package nvidiacoserv + +const SchemeName = "NVIDIA_COSERV" + +var ( + CoservMediaTypes = []string{ + `application/coserv+cbor; profile="tag:github.com/veraison,2023:nvidia_coserv_proxy#1.0.0"`, + } +) diff --git a/scheme/parsec-cca/endorsement_handler.go b/scheme/parsec-cca/endorsement_handler.go index 8be31718..36bf4ded 100644 --- a/scheme/parsec-cca/endorsement_handler.go +++ b/scheme/parsec-cca/endorsement_handler.go @@ -3,6 +3,7 @@ package parsec_cca import ( + "errors" "mime" "github.com/veraison/services/handler" @@ -49,3 +50,7 @@ func (o EndorsementHandler) Decode(data []byte, mediaType string, caCertPool []b // Default to unsigned CoRIM decoder return common.UnsignedCorimDecoder(data, extractor) } + +func (o EndorsementHandler) CoservRepackage(coservQuery string, resultSet []string) ([]byte, error) { + return nil, errors.New("Parsec CCA CoservRepackage not implemented") +} diff --git a/scheme/parsec-cca/endorsement_handler_test.go b/scheme/parsec-cca/endorsement_handler_test.go index 99a8e4c9..16be68fc 100644 --- a/scheme/parsec-cca/endorsement_handler_test.go +++ b/scheme/parsec-cca/endorsement_handler_test.go @@ -78,9 +78,9 @@ func TestDecoder_Decode_invalid_data(t *testing.T) { invalidCbor := []byte("invalid CBOR") - expectedErr := `CBOR decoding failed: expected map (CBOR Major Type 5), found Major Type 3` + expectedErr := `CBOR decoding failed` _, err := d.Decode(invalidCbor, "", nil) - assert.EqualError(t, err, expectedErr) + assert.ErrorContains(t, err, expectedErr) } diff --git a/scheme/parsec-cca/store_handler.go b/scheme/parsec-cca/store_handler.go index 85aca0c8..8af8cbe5 100644 --- a/scheme/parsec-cca/store_handler.go +++ b/scheme/parsec-cca/store_handler.go @@ -1,4 +1,4 @@ -// Copyright 2024 Contributors to the Veraison project. +// Copyright 2024-2025 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package parsec_cca @@ -66,3 +66,7 @@ func (s StoreHandler) GetRefValueIDs( } return arm.GetPlatformReferenceIDs(SchemeName, tenantID, platformClaimsMap) } + +func (s StoreHandler) SynthCoservQueryKeys(tenantID string, query string) ([]string, error) { + return []string{"TODO"}, nil +} diff --git a/scheme/parsec-cca/test/corim/unsignedCorimParsecCcaComidParsecCcaMultRefVal.cbor b/scheme/parsec-cca/test/corim/unsignedCorimParsecCcaComidParsecCcaMultRefVal.cbor index 0473c396..b9937db3 100644 Binary files a/scheme/parsec-cca/test/corim/unsignedCorimParsecCcaComidParsecCcaMultRefVal.cbor and b/scheme/parsec-cca/test/corim/unsignedCorimParsecCcaComidParsecCcaMultRefVal.cbor differ diff --git a/scheme/parsec-cca/test/corim/unsignedCorimParsecCcaComidParsecCcaRefValOne.cbor b/scheme/parsec-cca/test/corim/unsignedCorimParsecCcaComidParsecCcaRefValOne.cbor index 0be90b1e..f9aa8ed3 100644 Binary files a/scheme/parsec-cca/test/corim/unsignedCorimParsecCcaComidParsecCcaRefValOne.cbor and b/scheme/parsec-cca/test/corim/unsignedCorimParsecCcaComidParsecCcaRefValOne.cbor differ diff --git a/scheme/parsec-tpm/endorsement_handler.go b/scheme/parsec-tpm/endorsement_handler.go index d294e604..4228eb7f 100644 --- a/scheme/parsec-tpm/endorsement_handler.go +++ b/scheme/parsec-tpm/endorsement_handler.go @@ -3,6 +3,8 @@ package parsec_tpm import ( + "errors" + "mime" "github.com/veraison/services/handler" @@ -49,3 +51,7 @@ func (o EndorsementHandler) Decode(data []byte, mediaType string, caCertPool []b // Default to unsigned CoRIM decoder return common.UnsignedCorimDecoder(data, extractor) } + +func (o EndorsementHandler) CoservRepackage(coservQuery string, resultSet []string) ([]byte, error) { + return nil, errors.New("Parsec TPM CoservRepackage not implemented") +} diff --git a/scheme/parsec-tpm/endorsement_handler_test.go b/scheme/parsec-tpm/endorsement_handler_test.go index 386ac01e..2ecf95d3 100644 --- a/scheme/parsec-tpm/endorsement_handler_test.go +++ b/scheme/parsec-tpm/endorsement_handler_test.go @@ -138,9 +138,9 @@ func TestDecoder_Decode_invalid_data(t *testing.T) { invalidCbor := []byte("invalid CBOR") - expectedErr := `CBOR decoding failed: expected map (CBOR Major Type 5), found Major Type 3` + expectedErr := `CBOR decoding failed` _, err := d.Decode(invalidCbor, "", nil) - assert.EqualError(t, err, expectedErr) + assert.ErrorContains(t, err, expectedErr) } diff --git a/scheme/parsec-tpm/store_handler.go b/scheme/parsec-tpm/store_handler.go index 27d67cb3..25250f73 100644 --- a/scheme/parsec-tpm/store_handler.go +++ b/scheme/parsec-tpm/store_handler.go @@ -1,4 +1,4 @@ -// Copyright 2024 Contributors to the Veraison project. +// Copyright 2024-2025 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package parsec_tpm @@ -124,3 +124,6 @@ func tpmLookupKey(scope, tenantID, class, instance string) string { return u.String() } +func (s StoreHandler) SynthCoservQueryKeys(tenantID string, query string) ([]string, error) { + return []string{"TODO"}, nil +} diff --git a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyGood.cbor b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyGood.cbor index ef312de1..5257ccfc 100644 Binary files a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyGood.cbor and b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyGood.cbor differ diff --git a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyManyKeys.cbor b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyManyKeys.cbor index 2283d656..cdc57c46 100644 Binary files a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyManyKeys.cbor and b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyManyKeys.cbor differ diff --git a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyNoClass.cbor b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyNoClass.cbor index 6d456ee0..331f83f2 100644 Binary files a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyNoClass.cbor and b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyNoClass.cbor differ diff --git a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyNoClassId.cbor b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyNoClassId.cbor index bc6f5421..3f2d8741 100644 Binary files a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyNoClassId.cbor and b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyNoClassId.cbor differ diff --git a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyNoInstance.cbor b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyNoInstance.cbor index d1a2103e..aafaaa84 100644 Binary files a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyNoInstance.cbor and b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyNoInstance.cbor differ diff --git a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyUnknownClassIdType.cbor b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyUnknownClassIdType.cbor index df232ea8..e8554b70 100644 Binary files a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyUnknownClassIdType.cbor and b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyUnknownClassIdType.cbor differ diff --git a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyUnknownInstanceType.cbor b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyUnknownInstanceType.cbor index 9b3b2195..581bca43 100644 Binary files a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyUnknownInstanceType.cbor and b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmKeyUnknownInstanceType.cbor differ diff --git a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmPcrsGood.cbor b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmPcrsGood.cbor index af770496..88caca63 100644 Binary files a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmPcrsGood.cbor and b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmPcrsGood.cbor differ diff --git a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmPcrsNoClass.cbor b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmPcrsNoClass.cbor index e3c5cf19..c1a90c5f 100644 Binary files a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmPcrsNoClass.cbor and b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmPcrsNoClass.cbor differ diff --git a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmPcrsNoDigests.cbor b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmPcrsNoDigests.cbor index 85512579..bd2346c9 100644 Binary files a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmPcrsNoDigests.cbor and b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmPcrsNoDigests.cbor differ diff --git a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmPcrsNoPCR.cbor b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmPcrsNoPCR.cbor index 47b070d7..7730449a 100644 Binary files a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmPcrsNoPCR.cbor and b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmPcrsNoPCR.cbor differ diff --git a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmPcrsUnknownPCRType.cbor b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmPcrsUnknownPCRType.cbor index c8b7314f..95bbfd9c 100644 Binary files a/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmPcrsUnknownPCRType.cbor and b/scheme/parsec-tpm/test/corim/unsignedCorimMiniComidParsecTpmPcrsUnknownPCRType.cbor differ diff --git a/scheme/psa-iot/endorsement_handler.go b/scheme/psa-iot/endorsement_handler.go index f1776e42..acb61e6a 100644 --- a/scheme/psa-iot/endorsement_handler.go +++ b/scheme/psa-iot/endorsement_handler.go @@ -3,6 +3,8 @@ package psa_iot import ( + "errors" + "mime" "github.com/veraison/services/handler" @@ -49,3 +51,7 @@ func (o EndorsementHandler) Decode(data []byte, mediaType string, caCertPool []b // Default to unsigned CoRIM decoder return common.UnsignedCorimDecoder(data, extractor) } + +func (o EndorsementHandler) CoservRepackage(coservQuery string, resultSet []string) ([]byte, error) { + return nil, errors.New("PSA-IoT CoservRepackage not implemented") +} diff --git a/scheme/psa-iot/endorsement_handler_test.go b/scheme/psa-iot/endorsement_handler_test.go index 453276f3..b582a603 100644 --- a/scheme/psa-iot/endorsement_handler_test.go +++ b/scheme/psa-iot/endorsement_handler_test.go @@ -57,11 +57,11 @@ func TestDecoder_Decode_invalid_data(t *testing.T) { invalidCbor := []byte("invalid CBOR") - expectedErr := `CBOR decoding failed: expected map (CBOR Major Type 5), found Major Type 3` + expectedErr := `CBOR decoding failed` _, err := d.Decode(invalidCbor, "", nil) - assert.EqualError(t, err, expectedErr) + assert.ErrorContains(t, err, expectedErr) } func TestDecoder_Decode_OK(t *testing.T) { diff --git a/scheme/psa-iot/plugin/combined/main.go b/scheme/psa-iot/plugin/combined/main.go index e1a33e49..48b1bc26 100644 --- a/scheme/psa-iot/plugin/combined/main.go +++ b/scheme/psa-iot/plugin/combined/main.go @@ -1,4 +1,4 @@ -// Copyright 2022-2024 Contributors to the Veraison project. +// Copyright 2022-2025 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package main diff --git a/scheme/psa-iot/scheme.go b/scheme/psa-iot/scheme.go index 1bec58a4..ee57a9b0 100644 --- a/scheme/psa-iot/scheme.go +++ b/scheme/psa-iot/scheme.go @@ -19,3 +19,7 @@ var EvidenceMediaTypes = []string{ `application/eat+cwt; eat_profile="tag:psacertified.org,2023:psa#tfm"`, `application/eat+cwt; eat_profile="tag:psacertified.org,2019:psa#legacy"`, } + +var CoservMediaTypes = []string{ + `application/coserv+cbor; profile="tag:psacertified.org,2023:psa#tfm"`, +} diff --git a/scheme/psa-iot/store_handler.go b/scheme/psa-iot/store_handler.go index eb7aa000..47380840 100644 --- a/scheme/psa-iot/store_handler.go +++ b/scheme/psa-iot/store_handler.go @@ -1,4 +1,4 @@ -// Copyright 2021-2024 Contributors to the Veraison project. +// Copyright 2021-2025 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package psa_iot @@ -58,3 +58,7 @@ func (s StoreHandler) GetRefValueIDs( ) ([]string, error) { return arm.GetPlatformReferenceIDs(SchemeName, tenantID, claims) } + +func (s StoreHandler) SynthCoservQueryKeys(tenantID string, query string) ([]string, error) { + return []string{"TODO"}, nil +} diff --git a/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaIakPubNoImplID.cbor b/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaIakPubNoImplID.cbor index dbf20010..66d9c664 100644 Binary files a/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaIakPubNoImplID.cbor and b/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaIakPubNoImplID.cbor differ diff --git a/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaIakPubNoUeID.cbor b/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaIakPubNoUeID.cbor index 494a765e..5cc1c8c0 100644 Binary files a/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaIakPubNoUeID.cbor and b/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaIakPubNoUeID.cbor differ diff --git a/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaIakPubOne.cbor b/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaIakPubOne.cbor index 4e0a8f6e..83fd80d5 100644 Binary files a/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaIakPubOne.cbor and b/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaIakPubOne.cbor differ diff --git a/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaIakPubTwo.cbor b/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaIakPubTwo.cbor index b08aaf20..fd1d3698 100644 Binary files a/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaIakPubTwo.cbor and b/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaIakPubTwo.cbor differ diff --git a/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaMultIak.cbor b/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaMultIak.cbor index b5a2b144..b1c7efd5 100644 Binary files a/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaMultIak.cbor and b/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaMultIak.cbor differ diff --git a/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValMultDigest.cbor b/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValMultDigest.cbor index c1126a0a..866a05fc 100644 Binary files a/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValMultDigest.cbor and b/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValMultDigest.cbor differ diff --git a/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValNoImplID.cbor b/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValNoImplID.cbor index 98c8e00c..8c0278f8 100644 Binary files a/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValNoImplID.cbor and b/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValNoImplID.cbor differ diff --git a/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValNoMkey.cbor b/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValNoMkey.cbor index 798deef5..7af08fe7 100644 Binary files a/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValNoMkey.cbor and b/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValNoMkey.cbor differ diff --git a/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValOne.cbor b/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValOne.cbor index 58a38845..19a35e04 100644 Binary files a/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValOne.cbor and b/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValOne.cbor differ diff --git a/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValOnlyMandIDAttr.cbor b/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValOnlyMandIDAttr.cbor index ce5739cb..54b3026a 100644 Binary files a/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValOnlyMandIDAttr.cbor and b/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValOnlyMandIDAttr.cbor differ diff --git a/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValThree.cbor b/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValThree.cbor index 60b23d7b..34e54367 100644 Binary files a/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValThree.cbor and b/scheme/psa-iot/test/corim/unsignedCorimMiniComidPsaRefValThree.cbor differ diff --git a/scheme/riot/store_handler.go b/scheme/riot/store_handler.go index 7618e566..ed293354 100644 --- a/scheme/riot/store_handler.go +++ b/scheme/riot/store_handler.go @@ -1,4 +1,4 @@ -// Copyright 2021-2024 Contributors to the Veraison project. +// Copyright 2021-2025 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package riot @@ -44,3 +44,7 @@ func (s StoreHandler) SynthKeysFromRefValue(tenantID string, swComp *handler.End func (s StoreHandler) SynthKeysFromTrustAnchor(tenantID string, ta *handler.Endorsement) ([]string, error) { return nil, errors.New("TODO") } + +func (s StoreHandler) SynthCoservQueryKeys(tenantID string, query string) ([]string, error) { + return []string{"TODO"}, nil +} diff --git a/scheme/tpm-enacttrust/endorsement_handler.go b/scheme/tpm-enacttrust/endorsement_handler.go index 378d28d7..2127db5b 100644 --- a/scheme/tpm-enacttrust/endorsement_handler.go +++ b/scheme/tpm-enacttrust/endorsement_handler.go @@ -3,6 +3,8 @@ package tpm_enacttrust import ( + "errors" + "mime" "github.com/veraison/services/handler" @@ -49,3 +51,7 @@ func (o EndorsementHandler) Decode(data []byte, mediaType string, caCertPool []b // Default to unsigned CoRIM decoder return common.UnsignedCorimDecoder(data, extractor) } + +func (o EndorsementHandler) CoservRepackage(coservQuery string, resultSet []string) ([]byte, error) { + return nil, errors.New("TPM-EnactTrust CoservRepackage not implemented") +} diff --git a/scheme/tpm-enacttrust/store_handler.go b/scheme/tpm-enacttrust/store_handler.go index 09aaf5a9..e0978aaf 100644 --- a/scheme/tpm-enacttrust/store_handler.go +++ b/scheme/tpm-enacttrust/store_handler.go @@ -1,4 +1,4 @@ -// Copyright 2024 Contributors to the Veraison project. +// Copyright 2024-2025 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package tpm_enacttrust @@ -116,3 +116,7 @@ func tpmEnactTrustLookupKey(tenantID, nodeID string) string { return u.String() } + +func (s StoreHandler) SynthCoservQueryKeys(tenantID string, query string) ([]string, error) { + return []string{"TODO"}, nil +} diff --git a/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustAKBadInst.cbor b/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustAKBadInst.cbor index dde075cf..f7f93964 100644 Binary files a/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustAKBadInst.cbor and b/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustAKBadInst.cbor differ diff --git a/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustAKMult.cbor b/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustAKMult.cbor index 4f0b3221..3a201b39 100644 Binary files a/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustAKMult.cbor and b/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustAKMult.cbor differ diff --git a/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustAKOne.cbor b/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustAKOne.cbor index 59077cbc..3f5de9a2 100644 Binary files a/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustAKOne.cbor and b/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustAKOne.cbor differ diff --git a/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustBadInst.cbor b/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustBadInst.cbor index b89f0bd3..0953ca23 100644 Binary files a/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustBadInst.cbor and b/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustBadInst.cbor differ diff --git a/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustGoldenOne.cbor b/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustGoldenOne.cbor index a1628870..ebecb47e 100644 Binary files a/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustGoldenOne.cbor and b/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustGoldenOne.cbor differ diff --git a/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustGoldenTwo.cbor b/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustGoldenTwo.cbor index dd0efc1c..d0c27f53 100644 Binary files a/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustGoldenTwo.cbor and b/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustGoldenTwo.cbor differ diff --git a/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustMultDigest.cbor b/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustMultDigest.cbor index c7da9918..d18bf630 100644 Binary files a/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustMultDigest.cbor and b/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustMultDigest.cbor differ diff --git a/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustNoDigest.cbor b/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustNoDigest.cbor index 40f947b6..fd7dafdb 100644 Binary files a/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustNoDigest.cbor and b/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustNoDigest.cbor differ diff --git a/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustNoInst.cbor b/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustNoInst.cbor index 3bafc9e3..131c3cb9 100644 Binary files a/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustNoInst.cbor and b/scheme/tpm-enacttrust/test/corim/unsignedCorimMiniComidTpmEnactTrustNoInst.cbor differ diff --git a/scripts/gen-schemes b/scripts/gen-schemes deleted file mode 100755 index 456e79be..00000000 --- a/scripts/gen-schemes +++ /dev/null @@ -1,245 +0,0 @@ -#!/usr/bin/env python -# Copyright 2023 Contributors to the Veraison project. -# SPDX-License-Identifier: Apache-2.0 - -import logging -import os -import re -import shutil -import string -import sys -import subprocess - - -PLUGIN_INTERFACE = 'github.com/veraison/services/plugin.IPluggable' - -TEMPLATE = string.Template('''\ -package ${package} - -import ( - "github.com/veraison/services/plugin" - -${imports} - -) - -var plugins = []plugin.IPluggable{ -${implementations} -} -''') - - -class FoundStruct: - """ - Information of a golang struct defition, incluing the struct's name and its - location within the file that defines it. - """ - - def __init__(self, name, path, line, col): - self.name = name - self.path = path - self.line = line - self.col = col - - -def find_implementations(iface, dirpath): - """ - Walk dirpath looking for definitions of structs implementing the specified - iface. - - Returns a sequence of FoundStruct's. - """ - implementations = [] - for root, dirs, files in os.walk(dirpath): - for file in files: - if os.path.splitext(file)[1] == '.go': - filepath = os.path.join(root, file) - structs = get_structs(filepath) - for struct in structs: - if check_implements(struct, iface): - implementations.append(struct) - - return implementations - - -def get_structs(filepath): - """ - Returns a sequences of FoundStruct's with information on struct definitions - found with the file. - """ - res = subprocess.run(['gopls', 'symbols', filepath], - capture_output=True, text=True) - res.check_returncode() - - structs = [] - for n, line in enumerate(res.stdout.split('\n'), 1): - try: - if not line.strip(): - continue - - parts = line.split() - if parts[1] == 'Struct': - symbol_start = parts[2].split('-')[0] - line, col = list(map(int, symbol_start.split(':'))) - structs.append(FoundStruct(parts[0], filepath, line, col)) - except Exception as e: - logging.error(f'parse error {filepath}:{n}: {e}') - raise e - - return structs - - -def check_implements(struct, iface): - "check the specified FoundStruct implements the named interface" - offset = get_byte_offset(struct.path, struct.line, struct.col) - - res = subprocess.run(['guru', 'implements', f'{struct.path}:#{offset}'], - capture_output=True, text=True) - res.check_returncode() - - lines = res.stdout.strip().split('\n') - for line in lines[1:]: - _, implemented = line.rsplit('implements ', 1) - if implemented == iface: - return True - - return False - - -def get_byte_offset(path, line, col): - """ - Returns the byte offset of the specified line and column within the - specified file. - - This is useful for converting the ctags-like output of symbols, which - references them by line/col, into something that can be given to - golang.org/x/tools/cmd/guru. - """ - with open(path, 'rb') as fh: - buf = fh.read() - - offset = 0 - for _ in range(line - 1): - offset = buf.index(b'\n', offset+1) - - # note: col is in effect a _character_ offset within a line. This will - # break if there are multi-byte chars on the line prior to col. However, - # since this is intended for identifying symbol definition offsets for - # guru, the likelihood of that actually happening is vanishingly small. - # (the only things we'd expect to see on a line prior to the symbol name in - # a definition would be structural syntax, e.g. parens, and reserved words, - # all of which are represented in ASCII). - return offset + col - - -def get_package(path): - """ - Return the import spec for the package contianing the specified file. - - Note: this is done purely based on directory names, assuming that the - "package" directive inside the file matches its location. - """ - path = os.path.abspath(path) - - dir_path = os.path.dirname(path) - mod_path = os.path.join(dir_path, 'go.mod') - suffix = '' - while not os.path.isfile(mod_path): - if dir_path == '/': - raise ValueError(f'Path "{path}" does not appear to be inside a go module.') - dir_path, dir_name = os.path.split(dir_path) - mod_path = os.path.join(dir_path, 'go.mod') - if suffix: - suffix = '/'.join([dir_name, suffix]) - else: - suffix = dir_name - - with open(mod_path) as fh: - for line in fh: - if line.startswith('module '): - mod_name = line.strip().split()[1] - - if suffix: - suffix = '/'.join([mod_name, suffix]) - else: - suffix = mod_name - - break - else: - raise ValueError(f'"{mod_path}" does not contain a module directive.') - - return suffix - - -def gen_schemes(implementations, package): - """ - Generate the text of a go file that imports discovered schemes and creates - a plugins list that may be used to populate the builtin plugin mananger. - """ - seen_import_packages = {} - import_lines = [] - impl_lines = [] - - for impl in implementations: - import_pkg = get_package(impl.path) - - label = seen_import_packages.get(import_pkg) - if label is None: - label = 'scheme{}'.format(len(seen_import_packages) + 1) - seen_import_packages[import_pkg] = label - import_lines.append(f'\t{label} "{import_pkg}"') - - impl_lines.append(f'\t&{label}.{impl.name}{{}},') - - return TEMPLATE.substitute({ - 'package': package, - 'imports': '\n'.join(import_lines), - 'implementations': '\n'.join(impl_lines), - }) - - -def check_dependencies(): - if not shutil.which('gopls'): - print('''\ - Please install gopls using - - go install golang.org/x/tools/gopls@latest - - and make sure that it is in PATH. - ''', file=sys.stderr) - sys.exit(1) - - if not shutil.which('guru'): - print('''\ - Please install guru using - - go install golang.org/x/tools/cmd/guru@latest - - and make sure that it is in PATH. - ''', file=sys.stderr) - sys.exit(1) - - -def main(path, package): - impls = find_implementations(PLUGIN_INTERFACE, path) - print(gen_schemes(impls, package)) - - -if __name__ == '__main__': - import argparse - - logging.basicConfig( - format='%(levelname)s: %(message)s', - stream=sys.stderr, - ) - - parser = argparse.ArgumentParser() - parser.add_argument('directory', metavar='DIR', - help="directory to scan for plugin implementations.") - parser.add_argument('-p', '--package', metavar='NAME', default='builtin', - help="the name to use for the package directive in the generated go file.") - args = parser.parse_args() - - check_dependencies() - main(args.directory, args.package) - diff --git a/verification/api/handler.go b/verification/api/handler.go index a74d1ee5..d5491155 100644 --- a/verification/api/handler.go +++ b/verification/api/handler.go @@ -1,4 +1,4 @@ -// Copyright 2022-2024 Contributors to the Veraison project. +// Copyright 2022-2025 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package api @@ -336,8 +336,25 @@ func (o *Handler) SubmitEvidence(c *gin.Context) { return } - mediaType = w.GetType() - evidence = w.GetValue() + // Here we only deal with CMW records and tags. Collection CMWs are + // dealt with in the non-exceptional path. + if w.GetKind() == cmw.KindMonad { + mediaType, err = w.GetMonadType() + if err != nil { + ReportProblem(c, + http.StatusBadRequest, + fmt.Sprintf("could not extract CMW media type: %v", err), + ) + } + + evidence, err = w.GetMonadValue() + if err != nil { + ReportProblem(c, + http.StatusBadRequest, + fmt.Sprintf("could not extract CMW value: %v", err), + ) + } + } } isSupported, err := o.Verifier.IsSupportedMediaType(mediaType) @@ -506,7 +523,6 @@ func (o *Handler) getKey() (jwk.Key, error) { } return key, nil - } func (o *Handler) getVerificationMediaTypes() ([]string, error) { diff --git a/verification/api/handler_test.go b/verification/api/handler_test.go index 98815aba..d28b440e 100644 --- a/verification/api/handler_test.go +++ b/verification/api/handler_test.go @@ -1,4 +1,4 @@ -// Copyright 2022-2024 Contributors to the Veraison project. +// Copyright 2022-2025 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package api @@ -32,8 +32,8 @@ const ( ) var ( - testSupportedMediaTypeA = `application/eat_cwt; profile=http://arm.com/psa/2.0.0` - testSupportedMediaTypeB = `application/eat_cwt; profile=PSA_IOT_PROFILE_1` + testSupportedMediaTypeA = `application/eat_cwt; profile="http://arm.com/psa/2.0.0"` + testSupportedMediaTypeB = `application/eat_cwt; profile="PSA_IOT_PROFILE_1"` testSupportedMediaTypeC = `application/psa-attestation-token` testSupportedMediaTypes = []string{ testSupportedMediaTypeA, @@ -48,8 +48,8 @@ var ( "nonce": "mVubqtg3Wa5GSrx3L/2B99cQU2bMQFVYUI9aTmDYi64=", "expiry": "2022-07-13T13:50:24.520525+01:00", "accept": [ - "application/eat_cwt;profile=http://arm.com/psa/2.0.0", - "application/eat_cwt;profile=PSA_IOT_PROFILE_1", + "application/eat_cwt;profile=\"http://arm.com/psa/2.0.0\"", + "application/eat_cwt;profile=\"PSA_IOT_PROFILE_1\"", "application/psa-attestation-token" ] }` @@ -64,12 +64,12 @@ var ( "nonce": "mVubqtg3Wa5GSrx3L/2B99cQU2bMQFVYUI9aTmDYi64=", "expiry": "2022-07-13T13:50:24.520525+01:00", "accept": [ - "application/eat_cwt;profile=http://arm.com/psa/2.0.0", - "application/eat_cwt;profile=PSA_IOT_PROFILE_1", + "application/eat_cwt;profile=\"http://arm.com/psa/2.0.0\"", + "application/eat_cwt;profile=\"PSA_IOT_PROFILE_1\"", "application/psa-attestation-token" ], "evidence": { - "type":"application/eat_cwt; profile=http://arm.com/psa/2.0.0", + "type":"application/eat_cwt; profile=\"http://arm.com/psa/2.0.0\"", "value":"eyAiayI6ICJ2IiB9" } }` @@ -78,12 +78,12 @@ var ( "nonce": "mVubqtg3Wa5GSrx3L/2B99cQU2bMQFVYUI9aTmDYi64=", "expiry": "2022-07-13T13:50:24.520525+01:00", "accept": [ - "application/eat_cwt;profile=http://arm.com/psa/2.0.0", - "application/eat_cwt;profile=PSA_IOT_PROFILE_1", + "application/eat_cwt;profile=\"http://arm.com/psa/2.0.0\"", + "application/eat_cwt;profile=\"PSA_IOT_PROFILE_1\"", "application/psa-attestation-token" ], "evidence": { - "type":"application/eat_cwt; profile=http://arm.com/psa/2.0.0", + "type":"application/eat_cwt; profile=\"http://arm.com/psa/2.0.0\"", "value":"eyAiayI6ICJ2IiB9" }, "result": "{}" @@ -1111,10 +1111,9 @@ func TestHandler_GetWellKnownVerificationInfo_UnsupportedAccept(t *testing.T) { } func goodCMW(t *testing.T) []byte { - var w cmw.CMW - w.SetMediaType(testSupportedMediaTypeA) - w.SetValue([]byte(testJSONBody)) - b, err := w.Serialize(cmw.JSONArray) + w, err := cmw.NewMonad(testSupportedMediaTypeA, []byte(testJSONBody)) + require.NoError(t, err) + b, err := w.MarshalJSON() require.NoError(t, err) return b } @@ -1168,7 +1167,7 @@ func TestHandler_SubmitEvidence_bad_CMW(t *testing.T) { badCMW := []byte(`["missing value"]`) - verifierError := "could not unwrap the CMW: wrong number of entries (1) in the CMW array" + verifierError := "could not unwrap the CMW: wrong number of entries (1) in the CMW record" url := path.Join(testSessionBaseURL, testUUIDString) diff --git a/vts/cmd/vts-service/config-docker.yaml b/vts/cmd/vts-service/config-docker.yaml index 8430b506..801f256d 100644 --- a/vts/cmd/vts-service/config-docker.yaml +++ b/vts/cmd/vts-service/config-docker.yaml @@ -27,5 +27,9 @@ vts: ear-signer: alg: ES256 key: ./skey.jwk +coserv-signer: + use: true + alg: ES256 + key: ./skey.jwk logging: level: debug diff --git a/vts/cmd/vts-service/config.yaml b/vts/cmd/vts-service/config.yaml index 275f269e..ac8f2860 100644 --- a/vts/cmd/vts-service/config.yaml +++ b/vts/cmd/vts-service/config.yaml @@ -32,5 +32,9 @@ vts: ear-signer: alg: ES256 key: ./skey.jwk +coserv-signer: + use: true + alg: ES256 + key: ./skey.jwk logging: level: debug diff --git a/vts/cmd/vts-service/main.go b/vts/cmd/vts-service/main.go index c758d7b4..fd507922 100644 --- a/vts/cmd/vts-service/main.go +++ b/vts/cmd/vts-service/main.go @@ -1,4 +1,4 @@ -// Copyright 2022-2024 Contributors to the Veraison project. +// Copyright 2022-2025 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package main @@ -17,6 +17,7 @@ import ( "github.com/veraison/services/log" "github.com/veraison/services/plugin" "github.com/veraison/services/policy" + "github.com/veraison/services/vts/coservsigner" "github.com/veraison/services/vts/earsigner" "github.com/veraison/services/vts/policymanager" "github.com/veraison/services/vts/trustedservices" @@ -31,7 +32,7 @@ func main() { } subs, err := config.GetSubs(v, "ta-store", "en-store", "po-store", - "*po-agent", "plugin", "*vts", "ear-signer", "*logging") + "*po-agent", "plugin", "*vts", "ear-signer", "*coserv-signer", "*logging") if err != nil { log.Fatal(err) } @@ -67,6 +68,7 @@ func main() { var evPluginManager plugin.IManager[handler.IEvidenceHandler] var endPluginManager plugin.IManager[handler.IEndorsementHandler] var storePluginManager plugin.IManager[handler.IStoreHandler] + var coservProxyPluginManager plugin.IManager[handler.ICoservProxyHandler] psubs, err := config.GetSubs(subs["plugin"], "*go-plugin", "*builtin") if err != nil { @@ -104,6 +106,14 @@ func main() { if err != nil { log.Fatalf("could not create store PluginManagerWithLoader: %v", err) } + coservProxyPluginManager, err = plugin.CreateGoPluginManagerWithLoader( + loader, + "coserv-proxy-handler", + log.Named("plugin"), + handler.CoservProxyHandlerRPC) + if err != nil { + log.Fatalf("could not create coserv PluginManagerWithLoader: %v", err) + } } else if config.SchemeLoader == "builtin" { loader, err := builtin.CreateBuiltinLoader( psubs["builtin"].AllSettings(), @@ -129,6 +139,12 @@ func main() { if err != nil { log.Fatalf("could not create store BuiltinManagerWithLoader: %v", err) } + coservProxyPluginManager, err = builtin.CreateBuiltinManagerWithLoader[handler.ICoservProxyHandler]( + loader, log.Named("builtin"), + "coserv-handler") + if err != nil { + log.Fatalf("could not create coserv BuiltinManagerWithLoader: %v", err) + } } else { log.Panicw("invalid SchemeLoader value", "SchemeLoader", config.SchemeLoader) } @@ -143,19 +159,39 @@ func main() { log.Info("\t", mt) } + log.Info("CoSERV Proxy media types:") + for _, mt := range coservProxyPluginManager.GetRegisteredMediaTypes() { + log.Info("\t", mt) + } + log.Info("loading EAR signer") earSigner, err := earsigner.New(subs["ear-signer"], afero.NewOsFs()) if err != nil { log.Fatalf("EAR signer initialization failed: %v", err) } + var coservSigner coservsigner.ICoservSigner + + if subs["coserv-signer"].GetBool("use") { + log.Info("loading CoSERV signer") + coservSigner, err = coservsigner.New(subs["coserv-signer"], afero.NewOsFs()) + if err != nil { + log.Fatalf("CoSERV signer initialization failed: %v", err) + } + + // CoSERV media types. + log.Info("TODO CoSERV profile types:") + } + log.Info("initializing service") - // from this point onwards taStore, enStore, evPluginManager, endPluginManager, - // storePluginManager, policyManager and earSigner are owned by vts + // from this point onwards taStore, enStore, evPluginManager, + // endPluginManager, storePluginManager, coservProxyPluginManager, + // policyManager and earSigner are owned by vts vts := trustedservices.NewGRPC(taStore, enStore, - evPluginManager, endPluginManager, storePluginManager, policyManager, earSigner, log.Named("vts")) + evPluginManager, endPluginManager, storePluginManager, coservProxyPluginManager, + policyManager, earSigner, coservSigner, log.Named("vts")) - if err = vts.Init(subs["vts"], evPluginManager, endPluginManager, storePluginManager); err != nil { + if err = vts.Init(subs["vts"], evPluginManager, endPluginManager, storePluginManager, coservProxyPluginManager); err != nil { log.Fatalf("VTS initialisation failed: %v", err) } diff --git a/vts/coservsigner/Makefile b/vts/coservsigner/Makefile new file mode 100644 index 00000000..a1f9d784 --- /dev/null +++ b/vts/coservsigner/Makefile @@ -0,0 +1,10 @@ +# Copyright 2025 Contributors to the Veraison project. +# SPDX-License-Identifier: Apache-2.0 + +all-hook-pre test-hook-pre lint-hook-pre: + $(MAKE) -C ../../proto protogen + +include ../../mk/common.mk +include ../../mk/pkg.mk +include ../../mk/lint.mk +include ../../mk/test.mk diff --git a/vts/coservsigner/README.md b/vts/coservsigner/README.md new file mode 100644 index 00000000..581c0a19 --- /dev/null +++ b/vts/coservsigner/README.md @@ -0,0 +1,18 @@ +## Configuration + +`coserv-signer`is the stanza containing the configuration details about the +CoSERV result set signing process. The supported directives are: + +### `coserv-signer` configuration + +- `use`: boolean to indicate whether to use the CoSERV signer +- `alg`: the [JWS algorithm](https://www.iana.org/assignments/jose/jose.xhtml#web-signature-encryption-algorithms) +used for signing, e.g.: `ES256`, `RS512`. +- `key`: URL with the location of the private key to be used with `alg`. The +following URL schems are supported: + - `file`: URL path is the path to a local file + - `aws`: URL path is in the form `:` where `` + is an AWS region, and `` is the name under which the key is + stored in AWS Secrets Manager. + If a scheme is not specified, `file` is assumed. + The key is in [JWK format](https://datatracker.ietf.org/doc/rfc7517/). diff --git a/vts/coservsigner/coservsigner.go b/vts/coservsigner/coservsigner.go new file mode 100644 index 00000000..a5267911 --- /dev/null +++ b/vts/coservsigner/coservsigner.go @@ -0,0 +1,36 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 +package coservsigner + +import ( + "github.com/spf13/afero" + "github.com/spf13/viper" + "github.com/veraison/services/config" +) + +type Cfg struct { + Use bool `mapstructure:"use"` + Key string `mapstructure:"key"` + Alg string `mapstructure:"alg"` +} + +func New(v *viper.Viper, fs afero.Fs) (ICoservSigner, error) { + if !v.GetBool("use") { + return nil, nil + } + + cfg := Cfg{} + + configLoader := config.NewLoader(&cfg) + if err := configLoader.LoadFromViper(v); err != nil { + return nil, err + } + + cs := COSESigner{} + + if err := cs.Init(cfg, fs); err != nil { + return nil, err + } + + return &cs, nil +} diff --git a/vts/coservsigner/coservsigner_cose.go b/vts/coservsigner/coservsigner_cose.go new file mode 100644 index 00000000..d0d583a6 --- /dev/null +++ b/vts/coservsigner/coservsigner_cose.go @@ -0,0 +1,229 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 +package coservsigner + +import ( + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "math/big" + "net/url" + "strings" + + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwk" + "github.com/spf13/afero" + "github.com/veraison/corim/coserv" + "github.com/veraison/go-cose" + "github.com/veraison/services/vts/earsigner" +) + +type COSESigner struct { + // JWK is used for interchange of key material (reading from configuration + // and exporting to the discovery API) + Key jwk.Key + rawkey []byte + Alg jwa.KeyAlgorithm + + // COSE is used for the actual signing of CoSERVs + Signer cose.Signer +} + +func (o *COSESigner) Init(cfg Cfg, fs afero.Fs) error { + keyUrl, err := url.Parse(cfg.Key) + if err != nil { + return fmt.Errorf("parsing CoSERV signer key from configuration: %w", err) + } + + key, err := earsigner.NewKeyLoader(fs).Load(keyUrl) + if err != nil { + return fmt.Errorf("loading CoSERV signer key: %w", err) + } + + if err := o.parseKey(key); err != nil { + return fmt.Errorf("parsing CoSERV signer key: %w", err) + } + + if err := o.setAlg(cfg.Alg); err != nil { + return fmt.Errorf("setting CoSERV signer algorithm: %w", err) + } + + // instantiate the cose.Signer from the loaded key and alg + if err := o.initSigner(); err != nil { + return fmt.Errorf("creating COSE signer: %w", err) + } + + return nil +} + +func (o *COSESigner) Close() error { + return nil +} + +func (o COSESigner) Sign(c coserv.Coserv) ([]byte, error) { + return c.Sign(o.Signer) +} + +func (o COSESigner) GetCoservSigningPublicKey() (jwa.KeyAlgorithm, jwk.Key, error) { + key, err := o.Key.PublicKey() + if err != nil { + return nil, nil, fmt.Errorf("getting public key from CoSERV signer key: %w", err) + } + + return o.Alg, key, nil +} + +func (o *COSESigner) setAlg(alg string) error { + for _, a := range jwa.SignatureAlgorithms() { + if a.String() == alg { + o.Alg = a + return nil + } + } + + return fmt.Errorf( + "%q is not a valid value for %q. Supported algorithms: %s", + alg, "coserv-signer.alg", algList(), + ) +} + +func algList() string { + var l []string + + for _, a := range jwa.SignatureAlgorithms() { + l = append(l, string(a)) + } + + return strings.Join(l, ", ") +} + +func (o *COSESigner) parseKey(raw []byte) error { + k, err := jwk.ParseKey(raw) + if err != nil { + return err + } + + if _, ok := k.(jwk.AsymmetricKey); !ok { + return fmt.Errorf("expected asymmetric key, got %T", k) + } + + if !k.(jwk.AsymmetricKey).IsPrivate() { + return errors.New("expected private key, got public key") + } + + o.Key = k + o.rawkey = raw + + return nil +} + +func jwkToMap(k []byte) (map[string]string, error) { + var x map[string]interface{} + + if err := json.Unmarshal(k, &x); err != nil { + return nil, fmt.Errorf("getting key map from CoSERV signer key: %w", err) + } + + y := make(map[string]string, len(x)) + + for k, v := range x { + if s, ok := v.(string); ok { + y[k] = s + } else { + return nil, fmt.Errorf("cannot convert value for k %q: expecting string, got %T", k, v) + } + } + + return y, nil +} + +func (o *COSESigner) initSigner() error { + k, err := jwkToMap(o.rawkey) + if err != nil { + return fmt.Errorf("internalising JWK: %w", err) + } + + cryptoSigner, err := getCryptoSigner(k) + if err != nil { + return fmt.Errorf("mapping JWK to crypto.Signer: %w", err) + } + + alg, err := getAlg(o.Alg) + if err != nil { + return fmt.Errorf("mapping JWK to crypto.Signer: %w", err) + } + + coseSigner, err := cose.NewSigner(alg, cryptoSigner) + if err != nil { + return fmt.Errorf("mapping JWK to crypto.Signer: %w", err) + } + + o.Signer = coseSigner + + return nil +} + +func getAlg(alg jwa.KeyAlgorithm) (cose.Algorithm, error) { + switch alg { + case jwa.ES256: + return cose.AlgorithmES256, nil + case jwa.ES384: + return cose.AlgorithmES384, nil + case jwa.ES512: + return cose.AlgorithmES512, nil + } + return 0, fmt.Errorf("unsupported signing algorithm: %s", alg) +} + +func getCryptoSigner(k map[string]string) (crypto.Signer, error) { + if k["kty"] == "EC" { + var c elliptic.Curve + switch k["crv"] { + case "P-256": + c = elliptic.P256() + case "P-384": + c = elliptic.P384() + case "P-521": + c = elliptic.P521() + default: + return nil, errors.New("unsupported EC curve: " + k["crv"]) + } + + x, err := base64ToBigInt(k["x"]) + if err != nil { + return nil, fmt.Errorf("decoding x coordinate: %w", err) + } + y, err := base64ToBigInt(k["y"]) + if err != nil { + return nil, fmt.Errorf("decoding y coordinate: %w", err) + } + d, err := base64ToBigInt(k["d"]) + if err != nil { + return nil, fmt.Errorf("decoding d coordinate: %w", err) + } + + pkey := &ecdsa.PrivateKey{ + PublicKey: ecdsa.PublicKey{ + X: x, + Y: y, + Curve: c, + }, + D: d, + } + return pkey, nil + } + + return nil, errors.New("unsupported key type: " + k["kty"]) +} + +func base64ToBigInt(s string) (*big.Int, error) { + val, err := base64.RawURLEncoding.DecodeString(s) + if err != nil { + return nil, err + } + return new(big.Int).SetBytes(val), nil +} diff --git a/vts/coservsigner/coservsigner_cose_test.go b/vts/coservsigner/coservsigner_cose_test.go new file mode 100644 index 00000000..52cedb5b --- /dev/null +++ b/vts/coservsigner/coservsigner_cose_test.go @@ -0,0 +1,149 @@ +package coservsigner + +import ( + "testing" + + "github.com/spf13/afero" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var testKey = []byte(`{ + "kty":"EC", + "crv":"P-256", + "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", + "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", + "d":"870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE" +}`) + +var testPubKey = []byte(`{ + "kty":"EC", + "crv":"P-256", + "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", + "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM" +}`) + +var testBadKey = []byte(`{ + "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", + "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM" +}`) + +var testSymmetricKey = []byte(`{ + "kty":"oct", + "alg":"A128KW", + "k":"GawgguFyGrWKav7AX4VKUg" +}`) + +var testRSAKey = []byte(`{ + "kty":"RSA", + "n":"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + "e":"AQAB", + "d":"X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9M7dx5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqijwp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBznbJSzFHK66jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q", + "p":"83i-7IvMGXoMXCskv73TKr8637FiO7Z27zv8oj6pbWUQyLPQBQxtPVnwD20R-60eTDmD2ujnMt5PoqMrm8RfmNhVWDtjjMmCMjOpSXicFHj7XOuVIYQyqVWlWEh6dN36GVZYk93N8Bc9vY41xy8B9RzzOGVQzXvNEvn7O0nVbfs", + "q":"3dfOR9cuYq-0S-mkFLzgItgMEfFzB2q3hWehMuG0oCuqnb3vobLyumqjVZQO1dIrdwgTnCdpYzBcOfW5r370AFXjiWft_NGEiovonizhKpo9VVS78TzFgxkIdrecRezsZ-1kYd_s1qDbxtkDEgfAITAG9LUnADun4vIcb6yelxk", + "dp":"G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq":"s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "qi":"GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "alg":"RS256", + "kid":"2011-04-29" +}`) + +func makeFS(t *testing.T) afero.Fs { + fs := afero.NewMemMapFs() + + err := fs.MkdirAll("testdata", 0700) + require.NoError(t, err) + + err = afero.WriteFile(fs, "testdata/ec256key.jwk", testKey, 0600) + require.NoError(t, err) + + err = afero.WriteFile(fs, "testdata/ec256key-pub.jwk", testPubKey, 0600) + require.NoError(t, err) + + err = afero.WriteFile(fs, "testdata/bad-key.json", testBadKey, 0600) + require.NoError(t, err) + + err = afero.WriteFile(fs, "testdata/symmetric-key.json", testSymmetricKey, 0600) + require.NoError(t, err) + + err = afero.WriteFile(fs, "testdata/rsa.jwk", testRSAKey, 0600) + require.NoError(t, err) + + return fs +} + +func TestCOSESigner_Init_OK(t *testing.T) { + cfg := Cfg{ + Key: "testdata/ec256key.jwk", + Alg: "ES256", + } + fs := makeFS(t) + + var o COSESigner + err := o.Init(cfg, fs) + assert.NoError(t, err) + + assert.Equal(t, testKey, o.rawkey) + assert.Equal(t, "ES256", o.Alg.String()) + assert.NotNil(t, o.Signer) + assert.NotNil(t, o.Key) +} + +func TestCOSESigner_Init_KO_not_a_private_key(t *testing.T) { + cfg := Cfg{ + Key: "testdata/ec256key-pub.jwk", + Alg: "ES256", + } + fs := makeFS(t) + + var o COSESigner + err := o.Init(cfg, fs) + assert.EqualError(t, err, "parsing CoSERV signer key: expected private key, got public key") +} + +func TestCOSESigner_Init_KO_bad_key(t *testing.T) { + cfg := Cfg{ + Key: "testdata/bad-key.json", + Alg: "ES256", + } + fs := makeFS(t) + + var o COSESigner + err := o.Init(cfg, fs) + assert.EqualError(t, err, "parsing CoSERV signer key: invalid key type from JSON ()") +} + +func TestCOSESigner_Init_KO_alg_not_set(t *testing.T) { + cfg := Cfg{ + Key: "testdata/ec256key.jwk", + } + fs := makeFS(t) + + var o COSESigner + err := o.Init(cfg, fs) + assert.ErrorContains(t, err, "setting CoSERV signer algorithm") +} + +func TestCOSESigner_Init_KO_symmetric_key(t *testing.T) { + cfg := Cfg{ + Key: "testdata/symmetric-key.json", + Alg: "A128KW", + } + fs := makeFS(t) + + var o COSESigner + err := o.Init(cfg, fs) + assert.EqualError(t, err, "parsing CoSERV signer key: expected asymmetric key, got *jwk.symmetricKey") +} + +func TestCOSESigner_Init_KO_rsa_key(t *testing.T) { + cfg := Cfg{ + Key: "testdata/rsa.jwk", + Alg: "RS256", + } + fs := makeFS(t) + + var o COSESigner + err := o.Init(cfg, fs) + assert.EqualError(t, err, "creating COSE signer: mapping JWK to crypto.Signer: unsupported key type: RSA") +} diff --git a/vts/coservsigner/icoservsigner.go b/vts/coservsigner/icoservsigner.go new file mode 100644 index 00000000..56b139b1 --- /dev/null +++ b/vts/coservsigner/icoservsigner.go @@ -0,0 +1,15 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 +package coservsigner + +import ( + "github.com/lestrrat-go/jwx/v2/jwa" + "github.com/lestrrat-go/jwx/v2/jwk" + "github.com/veraison/corim/coserv" +) + +type ICoservSigner interface { + Sign(coserv coserv.Coserv) ([]byte, error) + GetCoservSigningPublicKey() (jwa.KeyAlgorithm, jwk.Key, error) + Close() error +} diff --git a/vts/trustedservices/itrustedservices.go b/vts/trustedservices/itrustedservices.go index e01f7946..b9d995c6 100644 --- a/vts/trustedservices/itrustedservices.go +++ b/vts/trustedservices/itrustedservices.go @@ -1,4 +1,4 @@ -// Copyright 2022-2024 Contributors to the Veraison project. +// Copyright 2022-2025 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package trustedservices @@ -12,9 +12,10 @@ import ( type ITrustedServices interface { Init( cfg *viper.Viper, - evm plugin.IManager[handler.IEvidenceHandler], - endm plugin.IManager[handler.IEndorsementHandler], - stm plugin.IManager[handler.IStoreHandler], + evidenceManager plugin.IManager[handler.IEvidenceHandler], + endorsementManager plugin.IManager[handler.IEndorsementHandler], + storeManager plugin.IManager[handler.IStoreHandler], + coservProxyManager plugin.IManager[handler.ICoservProxyHandler], ) error Close() error Run() error diff --git a/vts/trustedservices/trustedservices_grpc.go b/vts/trustedservices/trustedservices_grpc.go index fe876708..49c11095 100644 --- a/vts/trustedservices/trustedservices_grpc.go +++ b/vts/trustedservices/trustedservices_grpc.go @@ -22,6 +22,7 @@ import ( "google.golang.org/protobuf/types/known/emptypb" "google.golang.org/protobuf/types/known/structpb" + "github.com/veraison/corim/coserv" "github.com/veraison/ear" "github.com/veraison/services/config" "github.com/veraison/services/handler" @@ -30,6 +31,7 @@ import ( "github.com/veraison/services/plugin" "github.com/veraison/services/proto" "github.com/veraison/services/vts/appraisal" + "github.com/veraison/services/vts/coservsigner" "github.com/veraison/services/vts/earsigner" "github.com/veraison/services/vts/policymanager" ) @@ -62,14 +64,16 @@ func NewGRPCConfig() *GRPCConfig { type GRPC struct { ServerAddress string - TaStore kvstore.IKVStore - EnStore kvstore.IKVStore - EvPluginManager plugin.IManager[handler.IEvidenceHandler] - EndPluginManager plugin.IManager[handler.IEndorsementHandler] - StorePluginManager plugin.IManager[handler.IStoreHandler] - PolicyManager *policymanager.PolicyManager - EarSigner earsigner.IEarSigner - CACertsPEM [][]byte + TaStore kvstore.IKVStore + EnStore kvstore.IKVStore + EvPluginManager plugin.IManager[handler.IEvidenceHandler] + EndPluginManager plugin.IManager[handler.IEndorsementHandler] + StorePluginManager plugin.IManager[handler.IStoreHandler] + CoservProxyPluginManager plugin.IManager[handler.ICoservProxyHandler] + PolicyManager *policymanager.PolicyManager + EarSigner earsigner.IEarSigner + CoservSigner coservsigner.ICoservSigner + CACertsPEM [][]byte Server *grpc.Server Socket net.Listener @@ -81,22 +85,26 @@ type GRPC struct { func NewGRPC( taStore, enStore kvstore.IKVStore, - evpluginManager plugin.IManager[handler.IEvidenceHandler], - endpluginManager plugin.IManager[handler.IEndorsementHandler], - storepluginManager plugin.IManager[handler.IStoreHandler], + evidencePluginManager plugin.IManager[handler.IEvidenceHandler], + endorsementPluginManager plugin.IManager[handler.IEndorsementHandler], + storePluginManager plugin.IManager[handler.IStoreHandler], + coservProxyPluginManager plugin.IManager[handler.ICoservProxyHandler], policyManager *policymanager.PolicyManager, earSigner earsigner.IEarSigner, + coservSigner coservsigner.ICoservSigner, logger *zap.SugaredLogger, ) ITrustedServices { return &GRPC{ - TaStore: taStore, - EnStore: enStore, - EvPluginManager: evpluginManager, - EndPluginManager: endpluginManager, - StorePluginManager: storepluginManager, - PolicyManager: policyManager, - EarSigner: earSigner, - logger: logger, + TaStore: taStore, + EnStore: enStore, + EvPluginManager: evidencePluginManager, + EndPluginManager: endorsementPluginManager, + StorePluginManager: storePluginManager, + CoservProxyPluginManager: coservProxyPluginManager, + PolicyManager: policyManager, + EarSigner: earSigner, + CoservSigner: coservSigner, + logger: logger, } } @@ -111,15 +119,16 @@ func (o *GRPC) Run() error { func (o *GRPC) Init( v *viper.Viper, - evm plugin.IManager[handler.IEvidenceHandler], - endm plugin.IManager[handler.IEndorsementHandler], - stm plugin.IManager[handler.IStoreHandler], + evidenceManager plugin.IManager[handler.IEvidenceHandler], + endorsementManager plugin.IManager[handler.IEndorsementHandler], + storeManager plugin.IManager[handler.IStoreHandler], + coservProxyManager plugin.IManager[handler.ICoservProxyHandler], ) error { var err error cfg := GRPCConfig{ ServerAddress: DefaultVTSAddr, - UseTLS: true, + UseTLS: true, } loader := config.NewLoader(&cfg) @@ -127,9 +136,10 @@ func (o *GRPC) Init( return err } - o.EvPluginManager = evm - o.EndPluginManager = endm - o.StorePluginManager = stm + o.EvPluginManager = evidenceManager + o.EndPluginManager = endorsementManager + o.StorePluginManager = storeManager + o.CoservProxyPluginManager = coservProxyManager if cfg.ListenAddress != "" { o.ServerAddress = cfg.ListenAddress @@ -193,6 +203,10 @@ func (o *GRPC) Close() error { o.logger.Errorf("store plugin manager shutdown failed: %v", err) } + if err := o.CoservProxyPluginManager.Close(); err != nil { + o.logger.Errorf("coserv plugin manager shutdown failed: %v", err) + } + if err := o.TaStore.Close(); err != nil { o.logger.Errorf("trust anchor store closure failed: %v", err) } @@ -205,6 +219,12 @@ func (o *GRPC) Close() error { o.logger.Errorf("EAR signer closure failed: %v", err) } + if o.CoservSigner != nil { + if err := o.CoservSigner.Close(); err != nil { + o.logger.Errorf("CoSERV signer closure failed: %v", err) + } + } + return nil } @@ -502,8 +522,8 @@ func (c *GRPC) initEvidenceContext( } func (c *GRPC) getTrustAnchors(id []string) ([]string, error) { - var taValues []string //nolint + for _, taID := range id { values, err := c.TaStore.Get(taID) if err != nil { @@ -530,6 +550,53 @@ func (c *GRPC) GetSupportedProvisioningMediaTypes(context.Context, *emptypb.Empt return &proto.MediaTypeList{MediaTypes: mts}, nil } +func (c *GRPC) assembleCoservMediaTypes(mts []string, filter string) []string { + mediaTypes := make([]string, 0, 10) + + for _, mt := range mts { + t, p, err := mime.ParseMediaType(mt) + if err != nil || t != filter { + continue + } + profile := p["profile"] + if profile == "" { + continue + } + + mediaType := fmt.Sprintf(`application/coserv+cbor; profile=%q`, profile) + mediaTypes = append(mediaTypes, mediaType) + + if c.CoservSigner != nil { + mediaType = fmt.Sprintf(`application/coserv+cose; profile=%q`, profile) + mediaTypes = append(mediaTypes, mediaType) + } + } + + return mediaTypes +} + +func (c *GRPC) GetSupportedCoservMediaTypes(context.Context, *emptypb.Empty) (*proto.MediaTypeList, error) { + var mediaTypes []string + + corimDerived := c.assembleCoservMediaTypes( + c.EndPluginManager.GetRegisteredMediaTypes(), + "application/rim+cbor", + ) + + mediaTypes = append(mediaTypes, corimDerived...) + + coservProxyDerived := c.assembleCoservMediaTypes( + c.CoservProxyPluginManager.GetRegisteredMediaTypes(), + "application/coserv+cbor", + ) + + mediaTypes = append(mediaTypes, coservProxyDerived...) + + c.logger.Debugw("GetSupportedCoservMediaTypes", "media types", mediaTypes) + + return &proto.MediaTypeList{MediaTypes: mediaTypes}, nil +} + func (o *GRPC) GetEARSigningPublicKey(context.Context, *emptypb.Empty) (*proto.PublicKey, error) { alg, key, err := o.EarSigner.GetEARSigningPublicKey() if err != nil { @@ -553,6 +620,158 @@ func (o *GRPC) GetEARSigningPublicKey(context.Context, *emptypb.Empty) (*proto.P }, nil } +func (o *GRPC) GetCoservSigningPublicKey(context.Context, *emptypb.Empty) (*proto.PublicKey, error) { + // If CoSERV is not enabled, return an empty key. + if o.CoservSigner == nil { + return &proto.PublicKey{Key: ""}, nil + } + + alg, key, err := o.CoservSigner.GetCoservSigningPublicKey() + if err != nil { + return nil, err + } + + err = key.Set("alg", alg.String()) + if err != nil { + return nil, err + } + + b, err := json.Marshal(key) + if err != nil { + return nil, err + } + + bstring := string(b) + + return &proto.PublicKey{ + Key: bstring, + }, nil +} + +func getEndorsementsError(err error) *proto.EndorsementQueryOut { + return &proto.EndorsementQueryOut{ + Status: &proto.Status{ + Result: false, + ErrorDetail: fmt.Sprintf("%v", err), + }, + } +} + +func (o *GRPC) getEndorsementsFromStores(query *proto.EndorsementQueryIn) ([]byte, error) { + var q coserv.Coserv + if err := q.FromBase64Url(query.Query); err != nil { + return nil, err + } + + // select store based on the requested artefact type + var ( + store kvstore.IKVStore + storeName string + ) + switch q.Query.ArtifactType { + case coserv.ArtifactTypeEndorsedValues: + return nil, errors.New("endorsed value queries are not supported") + case coserv.ArtifactTypeReferenceValues: + storeName = "reference-value" + store = o.EnStore + case coserv.ArtifactTypeTrustAnchors: + storeName = "trust-anchors" + store = o.TaStore + } + + profile, err := q.Profile.Get() + if err != nil { + return nil, err + } + + // Look up a matching endorsement plugin + endorsementHandler, err := o.EndPluginManager.LookupByMediaType(fmt.Sprintf(`application/rim+cbor; profile=%q`, profile)) + if err != nil { + return nil, err + } + + scheme := endorsementHandler.GetAttestationScheme() + + storeHandler, err := o.StorePluginManager.LookupByAttestationScheme(scheme) + if err != nil { + return nil, err + } + + queryKeys, err := storeHandler.SynthCoservQueryKeys(DummyTenantID, query.Query) + if err != nil { + return nil, err + } + + var resultSet []string + for _, key := range queryKeys { + res, err := store.Get(key) + if err != nil && !errors.Is(err, kvstore.ErrKeyNotFound) { + return nil, fmt.Errorf("lookup %q in %s failed: %w", key, storeName, err) + } + + resultSet = append(resultSet, res...) + } + + coservResults, err := endorsementHandler.CoservRepackage(query.Query, resultSet) + if err != nil { + return nil, err + } + + return coservResults, nil +} + +func (o *GRPC) getEndorsementsFromProxy(handlerPlugin handler.ICoservProxyHandler, query *proto.EndorsementQueryIn) ([]byte, error) { + return handlerPlugin.GetEndorsements(DummyTenantID, query.Query) +} + +func (o *GRPC) GetEndorsements(ctx context.Context, query *proto.EndorsementQueryIn) (*proto.EndorsementQueryOut, error) { + o.logger.Debugw("GetEndorsements", "media-type", query.MediaType) + + var ( + err error + out []byte + handlerPlugin handler.ICoservProxyHandler + ) + + // First, check to see if we have a CoSERV proxy plugin that can handle this query + handlerPlugin, err = o.CoservProxyPluginManager.LookupByMediaType(query.MediaType) + if err == nil { + // No error means we have a proxy plugin, so delegate to that. + out, err = o.getEndorsementsFromProxy(handlerPlugin, query) + } else { + // There was no proxy plugin, so assume we can obtain from own stores + out, err = o.getEndorsementsFromStores(query) + } + + if err != nil { + return getEndorsementsError(err), nil + } + + // If we have a CoSERV signer configured and the client requested a COSE + // response, sign the result here. + if strings.HasPrefix(query.MediaType, "application/coserv+cose") { + if o.CoservSigner != nil { + var tmp coserv.Coserv + err = tmp.FromCBOR(out) + if err != nil { + return getEndorsementsError(fmt.Errorf("could not parse CoSERV response: %w", err)), nil + } + + out, err = o.CoservSigner.Sign(tmp) + if err != nil { + return getEndorsementsError(fmt.Errorf("could not sign CoSERV response: %w", err)), nil + } + } else { + return getEndorsementsError(errors.New("no CoSERV signer configured")), nil + } + } + + return &proto.EndorsementQueryOut{ + Status: &proto.Status{Result: true}, + ResultSet: out, + }, nil +} + func (o *GRPC) finalize( appraisal *appraisal.Appraisal, err error, diff --git a/vtsclient/vtsclient_grpc.go b/vtsclient/vtsclient_grpc.go index 6d011182..aafdabae 100644 --- a/vtsclient/vtsclient_grpc.go +++ b/vtsclient/vtsclient_grpc.go @@ -1,4 +1,4 @@ -// Copyright 2022-2024 Contributors to the Veraison project. +// Copyright 2022-2025 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package vtsclient @@ -195,6 +195,54 @@ func (o *GRPC) GetEARSigningPublicKey(ctx context.Context, in *emptypb.Empty, op return c.GetEARSigningPublicKey(ctx, in, opts...) } +func (o *GRPC) GetCoservSigningPublicKey(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*proto.PublicKey, error) { + if err := o.EnsureConnection(); err != nil { + return nil, NewNoConnectionError("GetCoservSigningPublicKey", err) + } + + c := o.GetProvisionerClient() + if c == nil { + return nil, ErrNoClient + } + + return c.GetCoservSigningPublicKey(ctx, in, opts...) +} + +func (o *GRPC) GetEndorsements( + ctx context.Context, in *proto.EndorsementQueryIn, opts ...grpc.CallOption, +) (*proto.EndorsementQueryOut, error) { + if err := o.EnsureConnection(); err != nil { + return nil, NewNoConnectionError("GetEndorsements", err) + } + + c := o.GetProvisionerClient() + if c == nil { + return nil, ErrNoClient + } + + return c.GetEndorsements(ctx, in, opts...) +} + +func (o *GRPC) GetSupportedCoservMediaTypes( + ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption, +) (*proto.MediaTypeList, error) { + if err := o.EnsureConnection(); err != nil { + return nil, NewNoConnectionError("GetSupportedCoservMediaTypes", err) + } + + c := o.GetProvisionerClient() + if c == nil { + return nil, ErrNoClient + } + + mts, err := c.GetSupportedCoservMediaTypes(ctx, in, opts...) + if err != nil { + return nil, err + } + + return mts, nil +} + func normalizeMediaTypeList(mts *proto.MediaTypeList) *proto.MediaTypeList { var nmts []string // nolint:prealloc