diff --git a/vts/appraisal/appraisal.go b/vts/appraisal/appraisal.go index 700b1133..21cc33d6 100644 --- a/vts/appraisal/appraisal.go +++ b/vts/appraisal/appraisal.go @@ -1,4 +1,4 @@ -// Copyright 2022-2023 Contributors to the Veraison project. +// Copyright 2022-2026 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package appraisal @@ -12,6 +12,7 @@ import ( "github.com/veraison/services/config" "github.com/veraison/services/policy" "github.com/veraison/services/proto" + "google.golang.org/protobuf/types/known/structpb" ) // Appraisal provides an appraisal context internally within the VTS (e.g. for @@ -22,6 +23,7 @@ type Appraisal struct { EvidenceContext *proto.EvidenceContext Result *ear.AttestationResult SignedEAR []byte + Endorsements []string } func New(tenantID string, nonce []byte, scheme string) *Appraisal { @@ -33,17 +35,38 @@ func New(tenantID string, nonce []byte, scheme string) *Appraisal { Result: ear.NewAttestationResult(scheme, config.Version, config.Developer), } - encodedNonce := base64.URLEncoding.EncodeToString(nonce) - appraisal.Result.Nonce = &encodedNonce - - appraisal.Result.VerifierID.Build = &config.Version - appraisal.Result.VerifierID.Developer = &config.Developer - + appraisal.setResultNonce(nonce) appraisal.InitPolicyID() return &appraisal } +func (o *Appraisal) setResultNonce(v []byte) { + encodedNonce := base64.URLEncoding.EncodeToString(v) + o.Result.Nonce = &encodedNonce +} + +func (o *Appraisal) SetTrustAnchorIDs(v []string) { + o.EvidenceContext.TrustAnchorIds = v +} + +func (o *Appraisal) SetReferenceIDs(v []string) { + o.EvidenceContext.ReferenceIds = v +} + +func (o *Appraisal) SetEvidenceClaims(v *structpb.Struct) { + o.EvidenceContext.Evidence = v +} + +func (o *Appraisal) SetEndorsements(v []string) { + o.Endorsements = v +} + +func (o *Appraisal) SetResultWithNonce(result *ear.AttestationResult, nonce []byte) { + o.Result = result + o.setResultNonce(nonce) +} + func (o Appraisal) GetContext() *proto.AppraisalContext { return &proto.AppraisalContext{ Evidence: o.EvidenceContext, @@ -51,13 +74,13 @@ func (o Appraisal) GetContext() *proto.AppraisalContext { } } -func (o Appraisal) SetAllClaims(claim ear.TrustClaim) { +func (o *Appraisal) SetAllClaims(claim ear.TrustClaim) { for _, submod := range o.Result.Submods { submod.TrustVector.SetAll(claim) } } -func (o Appraisal) AddPolicyClaim(name, claim string) { +func (o *Appraisal) AddPolicyClaim(name, claim string) { for _, submod := range o.Result.Submods { if submod.AppraisalExtensions.VeraisonPolicyClaims == nil { claimsMap := make(map[string]interface{}) @@ -82,6 +105,8 @@ func (o *Appraisal) UpdatePolicyID(pol *policy.Policy) error { return nil } +// InitPolicyID must be called before sending the appraisal to policy manager +// for evaluation. func (o *Appraisal) InitPolicyID() { for _, submod := range o.Result.Submods { policyID := fmt.Sprintf("policy:%s", o.Scheme) diff --git a/vts/policymanager/ipolicymanager.go b/vts/policymanager/ipolicymanager.go new file mode 100644 index 00000000..f7d258f6 --- /dev/null +++ b/vts/policymanager/ipolicymanager.go @@ -0,0 +1,13 @@ +// Copyright 2026 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 +package policymanager + +import ( + "context" + + "github.com/veraison/services/vts/appraisal" +) + +type IPolicyManager interface { + Evaluate(ctx context.Context, appraisal *appraisal.Appraisal) error +} diff --git a/vts/policymanager/policymanager.go b/vts/policymanager/policymanager.go index 7b013c4c..1ef0b264 100644 --- a/vts/policymanager/policymanager.go +++ b/vts/policymanager/policymanager.go @@ -1,4 +1,4 @@ -// Copyright 2022-2024 Contributors to the Veraison project. +// Copyright 2022-2026 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package policymanager @@ -32,13 +32,12 @@ func New(v *viper.Viper, store *policy.Store, logger *zap.SugaredLogger) (*Polic return pm, nil } +// XXX(tho) revisit coupling between appraisal and policy manager func (o *PolicyManager) Evaluate( ctx context.Context, - scheme string, appraisal *appraisal.Appraisal, - endorsements []string, ) error { - policyKey := o.getPolicyKey(appraisal) + policyKey := o.getPolicyKey(appraisal.EvidenceContext.TenantId, appraisal.Scheme) pol, err := o.getPolicy(policyKey) if err != nil { @@ -58,12 +57,12 @@ func (o *PolicyManager) Evaluate( evaluated, err := o.Agent.Evaluate( ctx, appraisalContext, - scheme, + appraisal.Scheme, pol, submod, submodAppraisal, appraisal.EvidenceContext, - endorsements, + appraisal.Endorsements, ) if err != nil { return err @@ -77,10 +76,10 @@ func (o *PolicyManager) Evaluate( return nil } -func (o *PolicyManager) getPolicyKey(a *appraisal.Appraisal) policy.PolicyKey { +func (o *PolicyManager) getPolicyKey(tenantID string, scheme string) policy.PolicyKey { return policy.PolicyKey{ - TenantId: a.EvidenceContext.TenantId, - Scheme: a.Scheme, + TenantId: tenantID, + Scheme: scheme, Name: o.Agent.GetBackendName(), } } diff --git a/vts/policymanager/policymanager_test.go b/vts/policymanager/policymanager_test.go index 7ccd15e4..78f57074 100644 --- a/vts/policymanager/policymanager_test.go +++ b/vts/policymanager/policymanager_test.go @@ -1,4 +1,4 @@ -// Copyright 2022-2023 Contributors to the Veraison project. +// Copyright 2022-2026 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package policymanager @@ -48,7 +48,7 @@ func TestPolicyMgr_getPolicy_not_found(t *testing.T) { pm := &PolicyManager{Store: &policy.Store{KVStore: store, Logger: log.Named("test")}, Agent: agent} - polKey := pm.getPolicyKey(appraisal) + polKey := pm.getPolicyKey(appraisal.EvidenceContext.TenantId, appraisal.Scheme) assert.Equal(t, "0:TPM_ENACTTRUST:opa", polKey.String()) pol, err := pm.getPolicy(polKey) @@ -82,7 +82,7 @@ func TestPolicyMgr_getPolicy_OK(t *testing.T) { pm := &PolicyManager{Store: &policy.Store{KVStore: store}, Agent: agent} - polKey := pm.getPolicyKey(appraisal) + polKey := pm.getPolicyKey(appraisal.EvidenceContext.TenantId, appraisal.Scheme) assert.Equal(t, "0:TPM_ENACTTRUST:opa", polKey.String()) _, err = pm.getPolicy(polKey) @@ -129,6 +129,7 @@ func TestPolicyMgr_Evaluate_OK(t *testing.T) { endorsements := []string{"h0KPxSKAPTEGXnvOPPA/5HUJZjHl4Hu9eg/eYMTPJcc="} ar := ear.NewAttestationResult("test", "test", "test") ap := &appraisal.Appraisal{EvidenceContext: ec, Result: ar, Scheme: "TPM_ENACTTRUST"} + ap.Endorsements = endorsements polID := "policy:TPM_ENACTTRUST" tier := ear.TrustTierAffirming @@ -140,7 +141,7 @@ func TestPolicyMgr_Evaluate_OK(t *testing.T) { Evaluate( context.TODO(), gomock.Any(), - "test", + "TPM_ENACTTRUST", gomock.Any(), gomock.Any(), ar.Submods["test"], @@ -153,7 +154,7 @@ func TestPolicyMgr_Evaluate_OK(t *testing.T) { Agent: agent, logger: log.Named("manager"), } - err := pm.Evaluate(context.TODO(), "test", ap, endorsements) + err := pm.Evaluate(context.TODO(), ap) require.NoError(t, err) } @@ -174,17 +175,17 @@ func TestPolicyMgr_Evaluate_NOK(t *testing.T) { } endorsements := []string{"h0KPxSKAPTEGXnvOPPA/5HUJZjHl4Hu9eg/eYMTPJcc="} ar := ear.NewAttestationResult("test", "test", "test") - ap := &appraisal.Appraisal{EvidenceContext: ec, Result: ar, Scheme: "TPM_ENACTTRUST"} + ap := &appraisal.Appraisal{EvidenceContext: ec, Result: ar, Scheme: "TPM_ENACTTRUST", Endorsements: endorsements} expectedErr := errors.New("could not evaluate policy: policy returned bad update") agent := mock_deps.NewMockIAgent(ctrl) agent.EXPECT().GetBackendName().Return("opa") - agent.EXPECT().Evaluate(context.TODO(), gomock.Any(), "test", gomock.Any(), gomock.Any(), ar.Submods["test"], ec, endorsements).Return(nil, expectedErr) + agent.EXPECT().Evaluate(context.TODO(), gomock.Any(), "TPM_ENACTTRUST", gomock.Any(), gomock.Any(), ar.Submods["test"], ec, endorsements).Return(nil, expectedErr) pm := &PolicyManager{ Store: &policy.Store{KVStore: store, Logger: log.Named("store")}, Agent: agent, logger: log.Named("manager"), } - err := pm.Evaluate(context.TODO(), "test", ap, endorsements) + err := pm.Evaluate(context.TODO(), ap) assert.ErrorIs(t, err, expectedErr) } diff --git a/vts/trustedservices/Makefile b/vts/trustedservices/Makefile index bbaa0e57..0679f05a 100644 --- a/vts/trustedservices/Makefile +++ b/vts/trustedservices/Makefile @@ -3,7 +3,17 @@ GOPKG := github.com/veraison/services/vts/trustedservices -all-hook-pre test-hook-pre lint-hook-pre: +INTERFACES += ../../plugin/imanager.go +INTERFACES += ../../plugin/ipluggable.go +INTERFACES += ../earsigner/iearsigner.go +INTERFACES += ../../handler/ievidencehandler.go +INTERFACES += ../../handler/istorehandler.go +INTERFACES += ../../kvstore/ikvstore.go +INTERFACES += ../policymanager/ipolicymanager.go + +MOCKPKG := mocks + +all-hook-pre test-hook-pre lint-hook-pre: _mocks $(MAKE) -C ../../proto protogen include ../../mk/common.mk diff --git a/vts/trustedservices/mocks/iearsigner.go b/vts/trustedservices/mocks/iearsigner.go new file mode 100644 index 00000000..78fe1a8c --- /dev/null +++ b/vts/trustedservices/mocks/iearsigner.go @@ -0,0 +1,97 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ../earsigner/iearsigner.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + jwa "github.com/lestrrat-go/jwx/v2/jwa" + jwk "github.com/lestrrat-go/jwx/v2/jwk" + ear "github.com/veraison/ear" + earsigner "github.com/veraison/services/vts/earsigner" +) + +// MockIEarSigner is a mock of IEarSigner interface. +type MockIEarSigner struct { + ctrl *gomock.Controller + recorder *MockIEarSignerMockRecorder +} + +// MockIEarSignerMockRecorder is the mock recorder for MockIEarSigner. +type MockIEarSignerMockRecorder struct { + mock *MockIEarSigner +} + +// NewMockIEarSigner creates a new mock instance. +func NewMockIEarSigner(ctrl *gomock.Controller) *MockIEarSigner { + mock := &MockIEarSigner{ctrl: ctrl} + mock.recorder = &MockIEarSignerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockIEarSigner) EXPECT() *MockIEarSignerMockRecorder { + return m.recorder +} + +// Close mocks base method. +func (m *MockIEarSigner) Close() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Close") + ret0, _ := ret[0].(error) + return ret0 +} + +// Close indicates an expected call of Close. +func (mr *MockIEarSignerMockRecorder) Close() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockIEarSigner)(nil).Close)) +} + +// GetEARSigningPublicKey mocks base method. +func (m *MockIEarSigner) GetEARSigningPublicKey() (jwa.KeyAlgorithm, jwk.Key, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetEARSigningPublicKey") + ret0, _ := ret[0].(jwa.KeyAlgorithm) + ret1, _ := ret[1].(jwk.Key) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// GetEARSigningPublicKey indicates an expected call of GetEARSigningPublicKey. +func (mr *MockIEarSignerMockRecorder) GetEARSigningPublicKey() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEARSigningPublicKey", reflect.TypeOf((*MockIEarSigner)(nil).GetEARSigningPublicKey)) +} + +// Init mocks base method. +func (m *MockIEarSigner) Init(cfg earsigner.Cfg, key []byte) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Init", cfg, key) + ret0, _ := ret[0].(error) + return ret0 +} + +// Init indicates an expected call of Init. +func (mr *MockIEarSignerMockRecorder) Init(cfg, key interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockIEarSigner)(nil).Init), cfg, key) +} + +// Sign mocks base method. +func (m *MockIEarSigner) Sign(earClaims ear.AttestationResult) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Sign", earClaims) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Sign indicates an expected call of Sign. +func (mr *MockIEarSignerMockRecorder) Sign(earClaims interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sign", reflect.TypeOf((*MockIEarSigner)(nil).Sign), earClaims) +} diff --git a/vts/trustedservices/mocks/ievidencehandler.go b/vts/trustedservices/mocks/ievidencehandler.go new file mode 100644 index 00000000..c4703246 --- /dev/null +++ b/vts/trustedservices/mocks/ievidencehandler.go @@ -0,0 +1,122 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ../../handler/ievidencehandler.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + ear "github.com/veraison/ear" + proto "github.com/veraison/services/proto" +) + +// MockIEvidenceHandler is a mock of IEvidenceHandler interface. +type MockIEvidenceHandler struct { + ctrl *gomock.Controller + recorder *MockIEvidenceHandlerMockRecorder +} + +// MockIEvidenceHandlerMockRecorder is the mock recorder for MockIEvidenceHandler. +type MockIEvidenceHandlerMockRecorder struct { + mock *MockIEvidenceHandler +} + +// NewMockIEvidenceHandler creates a new mock instance. +func NewMockIEvidenceHandler(ctrl *gomock.Controller) *MockIEvidenceHandler { + mock := &MockIEvidenceHandler{ctrl: ctrl} + mock.recorder = &MockIEvidenceHandlerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockIEvidenceHandler) EXPECT() *MockIEvidenceHandlerMockRecorder { + return m.recorder +} + +// AppraiseEvidence mocks base method. +func (m *MockIEvidenceHandler) AppraiseEvidence(ec *proto.EvidenceContext, endorsements []string) (*ear.AttestationResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AppraiseEvidence", ec, endorsements) + ret0, _ := ret[0].(*ear.AttestationResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AppraiseEvidence indicates an expected call of AppraiseEvidence. +func (mr *MockIEvidenceHandlerMockRecorder) AppraiseEvidence(ec, endorsements interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppraiseEvidence", reflect.TypeOf((*MockIEvidenceHandler)(nil).AppraiseEvidence), ec, endorsements) +} + +// ExtractClaims mocks base method. +func (m *MockIEvidenceHandler) ExtractClaims(token *proto.AttestationToken, trustAnchors []string) (map[string]interface{}, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ExtractClaims", token, trustAnchors) + ret0, _ := ret[0].(map[string]interface{}) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ExtractClaims indicates an expected call of ExtractClaims. +func (mr *MockIEvidenceHandlerMockRecorder) ExtractClaims(token, trustAnchors interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExtractClaims", reflect.TypeOf((*MockIEvidenceHandler)(nil).ExtractClaims), token, trustAnchors) +} + +// GetAttestationScheme mocks base method. +func (m *MockIEvidenceHandler) GetAttestationScheme() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAttestationScheme") + ret0, _ := ret[0].(string) + return ret0 +} + +// GetAttestationScheme indicates an expected call of GetAttestationScheme. +func (mr *MockIEvidenceHandlerMockRecorder) GetAttestationScheme() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAttestationScheme", reflect.TypeOf((*MockIEvidenceHandler)(nil).GetAttestationScheme)) +} + +// GetName mocks base method. +func (m *MockIEvidenceHandler) GetName() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetName") + ret0, _ := ret[0].(string) + return ret0 +} + +// GetName indicates an expected call of GetName. +func (mr *MockIEvidenceHandlerMockRecorder) GetName() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetName", reflect.TypeOf((*MockIEvidenceHandler)(nil).GetName)) +} + +// GetSupportedMediaTypes mocks base method. +func (m *MockIEvidenceHandler) GetSupportedMediaTypes() []string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSupportedMediaTypes") + ret0, _ := ret[0].([]string) + return ret0 +} + +// GetSupportedMediaTypes indicates an expected call of GetSupportedMediaTypes. +func (mr *MockIEvidenceHandlerMockRecorder) GetSupportedMediaTypes() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSupportedMediaTypes", reflect.TypeOf((*MockIEvidenceHandler)(nil).GetSupportedMediaTypes)) +} + +// ValidateEvidenceIntegrity mocks base method. +func (m *MockIEvidenceHandler) ValidateEvidenceIntegrity(token *proto.AttestationToken, trustAnchors, endorsementsStrings []string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ValidateEvidenceIntegrity", token, trustAnchors, endorsementsStrings) + ret0, _ := ret[0].(error) + return ret0 +} + +// ValidateEvidenceIntegrity indicates an expected call of ValidateEvidenceIntegrity. +func (mr *MockIEvidenceHandlerMockRecorder) ValidateEvidenceIntegrity(token, trustAnchors, endorsementsStrings interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ValidateEvidenceIntegrity", reflect.TypeOf((*MockIEvidenceHandler)(nil).ValidateEvidenceIntegrity), token, trustAnchors, endorsementsStrings) +} diff --git a/vts/trustedservices/mocks/ikvstore.go b/vts/trustedservices/mocks/ikvstore.go new file mode 100644 index 00000000..cab8df09 --- /dev/null +++ b/vts/trustedservices/mocks/ikvstore.go @@ -0,0 +1,150 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ../../kvstore/ikvstore.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + viper "github.com/spf13/viper" + zap "go.uber.org/zap" +) + +// MockIKVStore is a mock of IKVStore interface. +type MockIKVStore struct { + ctrl *gomock.Controller + recorder *MockIKVStoreMockRecorder +} + +// MockIKVStoreMockRecorder is the mock recorder for MockIKVStore. +type MockIKVStoreMockRecorder struct { + mock *MockIKVStore +} + +// NewMockIKVStore creates a new mock instance. +func NewMockIKVStore(ctrl *gomock.Controller) *MockIKVStore { + mock := &MockIKVStore{ctrl: ctrl} + mock.recorder = &MockIKVStoreMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockIKVStore) EXPECT() *MockIKVStoreMockRecorder { + return m.recorder +} + +// Add mocks base method. +func (m *MockIKVStore) Add(key, val string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Add", key, val) + ret0, _ := ret[0].(error) + return ret0 +} + +// Add indicates an expected call of Add. +func (mr *MockIKVStoreMockRecorder) Add(key, val interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockIKVStore)(nil).Add), key, val) +} + +// Close mocks base method. +func (m *MockIKVStore) Close() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Close") + ret0, _ := ret[0].(error) + return ret0 +} + +// Close indicates an expected call of Close. +func (mr *MockIKVStoreMockRecorder) Close() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockIKVStore)(nil).Close)) +} + +// Del mocks base method. +func (m *MockIKVStore) Del(key string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Del", key) + ret0, _ := ret[0].(error) + return ret0 +} + +// Del indicates an expected call of Del. +func (mr *MockIKVStoreMockRecorder) Del(key interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Del", reflect.TypeOf((*MockIKVStore)(nil).Del), key) +} + +// Get mocks base method. +func (m *MockIKVStore) Get(key string) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", key) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockIKVStoreMockRecorder) Get(key interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockIKVStore)(nil).Get), key) +} + +// GetKeys mocks base method. +func (m *MockIKVStore) GetKeys() ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetKeys") + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetKeys indicates an expected call of GetKeys. +func (mr *MockIKVStoreMockRecorder) GetKeys() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetKeys", reflect.TypeOf((*MockIKVStore)(nil).GetKeys)) +} + +// Init mocks base method. +func (m *MockIKVStore) Init(v *viper.Viper, logger *zap.SugaredLogger) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Init", v, logger) + ret0, _ := ret[0].(error) + return ret0 +} + +// Init indicates an expected call of Init. +func (mr *MockIKVStoreMockRecorder) Init(v, logger interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockIKVStore)(nil).Init), v, logger) +} + +// Set mocks base method. +func (m *MockIKVStore) Set(key, val string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Set", key, val) + ret0, _ := ret[0].(error) + return ret0 +} + +// Set indicates an expected call of Set. +func (mr *MockIKVStoreMockRecorder) Set(key, val interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockIKVStore)(nil).Set), key, val) +} + +// Setup mocks base method. +func (m *MockIKVStore) Setup() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Setup") + ret0, _ := ret[0].(error) + return ret0 +} + +// Setup indicates an expected call of Setup. +func (mr *MockIKVStoreMockRecorder) Setup() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Setup", reflect.TypeOf((*MockIKVStore)(nil).Setup)) +} diff --git a/vts/trustedservices/mocks/imanager.go b/vts/trustedservices/mocks/imanager.go new file mode 100644 index 00000000..ce7d55a6 --- /dev/null +++ b/vts/trustedservices/mocks/imanager.go @@ -0,0 +1,150 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ../../plugin/imanager.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + plugin "github.com/veraison/services/plugin" +) + +// MockIManager is a mock of IManager interface. +type MockIManager[I plugin.IPluggable] struct { + ctrl *gomock.Controller + recorder *MockIManagerMockRecorder[I] +} + +// MockIManagerMockRecorder is the mock recorder for MockIManager. +type MockIManagerMockRecorder[I plugin.IPluggable] struct { + mock *MockIManager[I] +} + +// NewMockIManager creates a new mock instance. +func NewMockIManager[I plugin.IPluggable](ctrl *gomock.Controller) *MockIManager[I] { + mock := &MockIManager[I]{ctrl: ctrl} + mock.recorder = &MockIManagerMockRecorder[I]{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockIManager[I]) EXPECT() *MockIManagerMockRecorder[I] { + return m.recorder +} + +// Close mocks base method. +func (m *MockIManager[I]) Close() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Close") + ret0, _ := ret[0].(error) + return ret0 +} + +// Close indicates an expected call of Close. +func (mr *MockIManagerMockRecorder[I]) Close() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockIManager[I])(nil).Close)) +} + +// GetRegisteredAttestationSchemes mocks base method. +func (m *MockIManager[I]) GetRegisteredAttestationSchemes() []string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetRegisteredAttestationSchemes") + ret0, _ := ret[0].([]string) + return ret0 +} + +// GetRegisteredAttestationSchemes indicates an expected call of GetRegisteredAttestationSchemes. +func (mr *MockIManagerMockRecorder[I]) GetRegisteredAttestationSchemes() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRegisteredAttestationSchemes", reflect.TypeOf((*MockIManager[I])(nil).GetRegisteredAttestationSchemes)) +} + +// GetRegisteredMediaTypes mocks base method. +func (m *MockIManager[I]) GetRegisteredMediaTypes() []string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetRegisteredMediaTypes") + ret0, _ := ret[0].([]string) + return ret0 +} + +// GetRegisteredMediaTypes indicates an expected call of GetRegisteredMediaTypes. +func (mr *MockIManagerMockRecorder[I]) GetRegisteredMediaTypes() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRegisteredMediaTypes", reflect.TypeOf((*MockIManager[I])(nil).GetRegisteredMediaTypes)) +} + +// Init mocks base method. +func (m *MockIManager[I]) Init(name string, ch *plugin.RPCChannel[I]) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Init", name, ch) + ret0, _ := ret[0].(error) + return ret0 +} + +// Init indicates an expected call of Init. +func (mr *MockIManagerMockRecorder[I]) Init(name, ch interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockIManager[I])(nil).Init), name, ch) +} + +// IsRegisteredMediaType mocks base method. +func (m *MockIManager[I]) IsRegisteredMediaType(mediaType string) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsRegisteredMediaType", mediaType) + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsRegisteredMediaType indicates an expected call of IsRegisteredMediaType. +func (mr *MockIManagerMockRecorder[I]) IsRegisteredMediaType(mediaType interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsRegisteredMediaType", reflect.TypeOf((*MockIManager[I])(nil).IsRegisteredMediaType), mediaType) +} + +// LookupByAttestationScheme mocks base method. +func (m *MockIManager[I]) LookupByAttestationScheme(name string) (I, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LookupByAttestationScheme", name) + ret0, _ := ret[0].(I) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// LookupByAttestationScheme indicates an expected call of LookupByAttestationScheme. +func (mr *MockIManagerMockRecorder[I]) LookupByAttestationScheme(name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LookupByAttestationScheme", reflect.TypeOf((*MockIManager[I])(nil).LookupByAttestationScheme), name) +} + +// LookupByMediaType mocks base method. +func (m *MockIManager[I]) LookupByMediaType(mediaType string) (I, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LookupByMediaType", mediaType) + ret0, _ := ret[0].(I) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// LookupByMediaType indicates an expected call of LookupByMediaType. +func (mr *MockIManagerMockRecorder[I]) LookupByMediaType(mediaType interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LookupByMediaType", reflect.TypeOf((*MockIManager[I])(nil).LookupByMediaType), mediaType) +} + +// LookupByName mocks base method. +func (m *MockIManager[I]) LookupByName(name string) (I, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LookupByName", name) + ret0, _ := ret[0].(I) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// LookupByName indicates an expected call of LookupByName. +func (mr *MockIManagerMockRecorder[I]) LookupByName(name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LookupByName", reflect.TypeOf((*MockIManager[I])(nil).LookupByName), name) +} diff --git a/vts/trustedservices/mocks/ipluggable.go b/vts/trustedservices/mocks/ipluggable.go new file mode 100644 index 00000000..59ab4041 --- /dev/null +++ b/vts/trustedservices/mocks/ipluggable.go @@ -0,0 +1,76 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ../../plugin/ipluggable.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// MockIPluggable is a mock of IPluggable interface. +type MockIPluggable struct { + ctrl *gomock.Controller + recorder *MockIPluggableMockRecorder +} + +// MockIPluggableMockRecorder is the mock recorder for MockIPluggable. +type MockIPluggableMockRecorder struct { + mock *MockIPluggable +} + +// NewMockIPluggable creates a new mock instance. +func NewMockIPluggable(ctrl *gomock.Controller) *MockIPluggable { + mock := &MockIPluggable{ctrl: ctrl} + mock.recorder = &MockIPluggableMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockIPluggable) EXPECT() *MockIPluggableMockRecorder { + return m.recorder +} + +// GetAttestationScheme mocks base method. +func (m *MockIPluggable) GetAttestationScheme() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAttestationScheme") + ret0, _ := ret[0].(string) + return ret0 +} + +// GetAttestationScheme indicates an expected call of GetAttestationScheme. +func (mr *MockIPluggableMockRecorder) GetAttestationScheme() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAttestationScheme", reflect.TypeOf((*MockIPluggable)(nil).GetAttestationScheme)) +} + +// GetName mocks base method. +func (m *MockIPluggable) GetName() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetName") + ret0, _ := ret[0].(string) + return ret0 +} + +// GetName indicates an expected call of GetName. +func (mr *MockIPluggableMockRecorder) GetName() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetName", reflect.TypeOf((*MockIPluggable)(nil).GetName)) +} + +// GetSupportedMediaTypes mocks base method. +func (m *MockIPluggable) GetSupportedMediaTypes() []string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSupportedMediaTypes") + ret0, _ := ret[0].([]string) + return ret0 +} + +// GetSupportedMediaTypes indicates an expected call of GetSupportedMediaTypes. +func (mr *MockIPluggableMockRecorder) GetSupportedMediaTypes() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSupportedMediaTypes", reflect.TypeOf((*MockIPluggable)(nil).GetSupportedMediaTypes)) +} diff --git a/vts/trustedservices/mocks/ipolicymanager.go b/vts/trustedservices/mocks/ipolicymanager.go new file mode 100644 index 00000000..35245e03 --- /dev/null +++ b/vts/trustedservices/mocks/ipolicymanager.go @@ -0,0 +1,50 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ../policymanager/ipolicymanager.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + appraisal "github.com/veraison/services/vts/appraisal" +) + +// MockIPolicyManager is a mock of IPolicyManager interface. +type MockIPolicyManager struct { + ctrl *gomock.Controller + recorder *MockIPolicyManagerMockRecorder +} + +// MockIPolicyManagerMockRecorder is the mock recorder for MockIPolicyManager. +type MockIPolicyManagerMockRecorder struct { + mock *MockIPolicyManager +} + +// NewMockIPolicyManager creates a new mock instance. +func NewMockIPolicyManager(ctrl *gomock.Controller) *MockIPolicyManager { + mock := &MockIPolicyManager{ctrl: ctrl} + mock.recorder = &MockIPolicyManagerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockIPolicyManager) EXPECT() *MockIPolicyManagerMockRecorder { + return m.recorder +} + +// Evaluate mocks base method. +func (m *MockIPolicyManager) Evaluate(ctx context.Context, appraisal *appraisal.Appraisal) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Evaluate", ctx, appraisal) + ret0, _ := ret[0].(error) + return ret0 +} + +// Evaluate indicates an expected call of Evaluate. +func (mr *MockIPolicyManagerMockRecorder) Evaluate(ctx, appraisal interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Evaluate", reflect.TypeOf((*MockIPolicyManager)(nil).Evaluate), ctx, appraisal) +} diff --git a/vts/trustedservices/mocks/istorehandler.go b/vts/trustedservices/mocks/istorehandler.go new file mode 100644 index 00000000..65ccf4f4 --- /dev/null +++ b/vts/trustedservices/mocks/istorehandler.go @@ -0,0 +1,153 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ../../handler/istorehandler.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + reflect "reflect" + + gomock "github.com/golang/mock/gomock" + handler "github.com/veraison/services/handler" + proto "github.com/veraison/services/proto" +) + +// MockIStoreHandler is a mock of IStoreHandler interface. +type MockIStoreHandler struct { + ctrl *gomock.Controller + recorder *MockIStoreHandlerMockRecorder +} + +// MockIStoreHandlerMockRecorder is the mock recorder for MockIStoreHandler. +type MockIStoreHandlerMockRecorder struct { + mock *MockIStoreHandler +} + +// NewMockIStoreHandler creates a new mock instance. +func NewMockIStoreHandler(ctrl *gomock.Controller) *MockIStoreHandler { + mock := &MockIStoreHandler{ctrl: ctrl} + mock.recorder = &MockIStoreHandlerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockIStoreHandler) EXPECT() *MockIStoreHandlerMockRecorder { + return m.recorder +} + +// GetAttestationScheme mocks base method. +func (m *MockIStoreHandler) GetAttestationScheme() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAttestationScheme") + ret0, _ := ret[0].(string) + return ret0 +} + +// GetAttestationScheme indicates an expected call of GetAttestationScheme. +func (mr *MockIStoreHandlerMockRecorder) GetAttestationScheme() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAttestationScheme", reflect.TypeOf((*MockIStoreHandler)(nil).GetAttestationScheme)) +} + +// GetName mocks base method. +func (m *MockIStoreHandler) GetName() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetName") + ret0, _ := ret[0].(string) + return ret0 +} + +// GetName indicates an expected call of GetName. +func (mr *MockIStoreHandlerMockRecorder) GetName() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetName", reflect.TypeOf((*MockIStoreHandler)(nil).GetName)) +} + +// GetRefValueIDs mocks base method. +func (m *MockIStoreHandler) GetRefValueIDs(tenantID string, trustAnchors []string, claims map[string]interface{}) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetRefValueIDs", tenantID, trustAnchors, claims) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetRefValueIDs indicates an expected call of GetRefValueIDs. +func (mr *MockIStoreHandlerMockRecorder) GetRefValueIDs(tenantID, trustAnchors, claims interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRefValueIDs", reflect.TypeOf((*MockIStoreHandler)(nil).GetRefValueIDs), tenantID, trustAnchors, claims) +} + +// GetSupportedMediaTypes mocks base method. +func (m *MockIStoreHandler) GetSupportedMediaTypes() []string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSupportedMediaTypes") + ret0, _ := ret[0].([]string) + return ret0 +} + +// GetSupportedMediaTypes indicates an expected call of GetSupportedMediaTypes. +func (mr *MockIStoreHandlerMockRecorder) GetSupportedMediaTypes() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSupportedMediaTypes", reflect.TypeOf((*MockIStoreHandler)(nil).GetSupportedMediaTypes)) +} + +// GetTrustAnchorIDs mocks base method. +func (m *MockIStoreHandler) GetTrustAnchorIDs(token *proto.AttestationToken) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetTrustAnchorIDs", token) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetTrustAnchorIDs indicates an expected call of GetTrustAnchorIDs. +func (mr *MockIStoreHandlerMockRecorder) GetTrustAnchorIDs(token interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTrustAnchorIDs", reflect.TypeOf((*MockIStoreHandler)(nil).GetTrustAnchorIDs), token) +} + +// SynthCoservQueryKeys mocks base method. +func (m *MockIStoreHandler) SynthCoservQueryKeys(tenantID, query string) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SynthCoservQueryKeys", tenantID, query) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SynthCoservQueryKeys indicates an expected call of SynthCoservQueryKeys. +func (mr *MockIStoreHandlerMockRecorder) SynthCoservQueryKeys(tenantID, query interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SynthCoservQueryKeys", reflect.TypeOf((*MockIStoreHandler)(nil).SynthCoservQueryKeys), tenantID, query) +} + +// SynthKeysFromRefValue mocks base method. +func (m *MockIStoreHandler) SynthKeysFromRefValue(tenantID string, refVal *handler.Endorsement) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SynthKeysFromRefValue", tenantID, refVal) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SynthKeysFromRefValue indicates an expected call of SynthKeysFromRefValue. +func (mr *MockIStoreHandlerMockRecorder) SynthKeysFromRefValue(tenantID, refVal interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SynthKeysFromRefValue", reflect.TypeOf((*MockIStoreHandler)(nil).SynthKeysFromRefValue), tenantID, refVal) +} + +// SynthKeysFromTrustAnchor mocks base method. +func (m *MockIStoreHandler) SynthKeysFromTrustAnchor(tenantID string, ta *handler.Endorsement) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SynthKeysFromTrustAnchor", tenantID, ta) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SynthKeysFromTrustAnchor indicates an expected call of SynthKeysFromTrustAnchor. +func (mr *MockIStoreHandlerMockRecorder) SynthKeysFromTrustAnchor(tenantID, ta interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SynthKeysFromTrustAnchor", reflect.TypeOf((*MockIStoreHandler)(nil).SynthKeysFromTrustAnchor), tenantID, ta) +} diff --git a/vts/trustedservices/trustedservices_grpc.go b/vts/trustedservices/trustedservices_grpc.go index 49c11095..3ac85643 100644 --- a/vts/trustedservices/trustedservices_grpc.go +++ b/vts/trustedservices/trustedservices_grpc.go @@ -1,4 +1,4 @@ -// Copyright 2022-2025 Contributors to the Veraison project. +// Copyright 2022-2026 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 package trustedservices @@ -70,7 +70,7 @@ type GRPC struct { EndPluginManager plugin.IManager[handler.IEndorsementHandler] StorePluginManager plugin.IManager[handler.IStoreHandler] CoservProxyPluginManager plugin.IManager[handler.ICoservProxyHandler] - PolicyManager *policymanager.PolicyManager + PolicyManager policymanager.IPolicyManager EarSigner earsigner.IEarSigner CoservSigner coservsigner.ICoservSigner CACertsPEM [][]byte @@ -89,7 +89,7 @@ func NewGRPC( endorsementPluginManager plugin.IManager[handler.IEndorsementHandler], storePluginManager plugin.IManager[handler.IStoreHandler], coservProxyPluginManager plugin.IManager[handler.ICoservProxyHandler], - policyManager *policymanager.PolicyManager, + policyManager policymanager.IPolicyManager, earSigner earsigner.IEarSigner, coservSigner coservsigner.ICoservSigner, logger *zap.SugaredLogger, @@ -395,106 +395,159 @@ func (o *GRPC) addTrustAnchor( return nil } -func (o *GRPC) GetAttestation( - ctx context.Context, +func (o *GRPC) getAttestation( token *proto.AttestationToken, -) (*proto.AppraisalContext, error) { - o.logger.Infow("get attestation", "media-type", token.MediaType, - "tenant-id", token.TenantId) +) (*appraisal.Appraisal, error) { + o.logger.Infow("get attestation", "media-type", token.MediaType, "tenant-id", token.TenantId) - handler, err := o.EvPluginManager.LookupByMediaType(token.MediaType) + // lookup evidence handler from media type + evidenceHandler, err := o.EvPluginManager.LookupByMediaType(token.MediaType) if err != nil { + // internal error appraisal := appraisal.New(token.TenantId, token.Nonce, "ERROR") - appraisal.SetAllClaims(ear.UnexpectedEvidenceClaim) + appraisal.SetAllClaims(ear.VerifierMalfunctionClaim) appraisal.AddPolicyClaim("problem", "could not resolve media type") - return o.finalize(appraisal, err) + + return appraisal, err } - scheme := handler.GetAttestationScheme() - stHandler, err := o.StorePluginManager.LookupByAttestationScheme(scheme) + attestationScheme := evidenceHandler.GetAttestationScheme() + + // lookup store handler from attestation scheme + storeHandler, err := o.StorePluginManager.LookupByAttestationScheme(attestationScheme) if err != nil { + // internal error appraisal := appraisal.New(token.TenantId, token.Nonce, "ERROR") - appraisal.SetAllClaims(ear.UnexpectedEvidenceClaim) - appraisal.AddPolicyClaim("problem", "could not resolve scheme name") - return o.finalize(appraisal, err) + appraisal.SetAllClaims(ear.VerifierMalfunctionClaim) + appraisal.AddPolicyClaim("problem", fmt.Sprintf("could not resolve scheme name %q", attestationScheme)) + + return appraisal, err } - appraisal, err := o.initEvidenceContext(stHandler, token) + // initialize appraisal context + appraisal := appraisal.New(token.TenantId, token.Nonce, attestationScheme) + + // get trust anchor IDs for this evidence + taIDs, err := storeHandler.GetTrustAnchorIDs(token) if err != nil { - return o.finalize(appraisal, err) + // XXX(tho) - this calling convention is only documented for + // IEvidenceHandler. If this is expected to be the convention for all + // kinds of handlers, it should be made explicit. + if errors.Is(err, handlermod.BadEvidenceError{}) { + appraisal.SetAllClaims(ear.CryptoValidationFailedClaim) + appraisal.AddPolicyClaim("problem", "could not establish identity from evidence") + } else { + appraisal.SetAllClaims(ear.VerifierMalfunctionClaim) + appraisal.AddPolicyClaim("problem", fmt.Sprintf("loading TA identifiers: %v", err)) + } + + return appraisal, err } - tas, err := o.getTrustAnchors(appraisal.EvidenceContext.TrustAnchorIds) + // lookup trust anchors from store + tas, err := o.getTrustAnchors(taIDs) if err != nil { if errors.Is(err, kvstore.ErrKeyNotFound) { - err = handlermod.BadEvidence("no trust anchor for %s", - appraisal.EvidenceContext.TrustAnchorIds) + err = handlermod.BadEvidence("no trust anchor for %s", taIDs) appraisal.SetAllClaims(ear.CryptoValidationFailedClaim) appraisal.AddPolicyClaim("problem", "no trust anchor for evidence") + } else { + appraisal.SetAllClaims(ear.VerifierMalfunctionClaim) + appraisal.AddPolicyClaim("problem", err.Error()) } - return o.finalize(appraisal, err) + + return appraisal, err } - claims, err := handler.ExtractClaims(token, tas) + // update appraisal context with trust anchor IDs + appraisal.SetTrustAnchorIDs(taIDs) + + // get the claims-set from the evidence + claims, err := evidenceHandler.ExtractClaims(token, tas) if err != nil { if errors.Is(err, handlermod.BadEvidenceError{}) { appraisal.AddPolicyClaim("problem", err.Error()) } - return o.finalize(appraisal, err) + // XXX(tho) early returns should set an appropriate EAR failure claim + // XXX(tho) else branch: consider setting a policy claim here + return appraisal, err } - referenceIDs, err := stHandler.GetRefValueIDs(token.TenantId, tas, claims) + referenceIDs, err := storeHandler.GetRefValueIDs(token.TenantId, tas, claims) if err != nil { - return o.finalize(appraisal, err) + // XXX(tho) early returns should set an appropriate EAR failure claim + // XXX(tho) else branch: consider setting a policy claim here + return appraisal, err } - appraisal.EvidenceContext.Evidence, err = structpb.NewStruct(claims) + appraisal.SetReferenceIDs(referenceIDs) + + claimsSet, err := structpb.NewStruct(claims) if err != nil { - err = fmt.Errorf("unserializable claims in result: %w", err) - return o.finalize(appraisal, err) + // XXX(tho) early returns should set an appropriate EAR failure claim + return appraisal, fmt.Errorf("unserializable claims in result: %w", err) } - appraisal.EvidenceContext.ReferenceIds = referenceIDs + appraisal.SetEvidenceClaims(claimsSet) o.logger.Debugw("constructed evidence context", - "software-id", appraisal.EvidenceContext.ReferenceIds, - "trust-anchor-id", appraisal.EvidenceContext.TrustAnchorIds) + "software-id", appraisal.EvidenceContext.GetReferenceIds(), + "trust-anchor-id", appraisal.EvidenceContext.GetTrustAnchorIds()) - var multEndorsements []string - for _, refvalID := range appraisal.EvidenceContext.ReferenceIds { + var endorsements []string - endorsements, err := o.EnStore.Get(refvalID) + for _, refvalID := range appraisal.EvidenceContext.ReferenceIds { + endorsement, err := o.EnStore.Get(refvalID) if err != nil && !errors.Is(err, kvstore.ErrKeyNotFound) { - return o.finalize(appraisal, err) + // XXX(tho) early returns should set an appropriate EAR failure claim + return appraisal, err } - o.logger.Debugw("obtained endorsements", "endorsements", endorsements) - multEndorsements = append(multEndorsements, endorsements...) + o.logger.Debugw("obtained endorsements", "endorsements", endorsement) + endorsements = append(endorsements, endorsement...) } - if err = handler.ValidateEvidenceIntegrity(token, tas, multEndorsements); err != nil { + appraisal.SetEndorsements(endorsements) + + if err = evidenceHandler.ValidateEvidenceIntegrity(token, tas, endorsements); err != nil { if errors.Is(err, handlermod.BadEvidenceError{}) { - var badErr handlermod.BadEvidenceError + var badEvidenceErr handlermod.BadEvidenceError claimStr := "integrity validation failed" - ok := errors.As(err, &badErr) - if ok { - claimStr += fmt.Sprintf(": %s", badErr.ToString()) + if ok := errors.As(err, &badEvidenceErr); ok { + claimStr += fmt.Sprintf(": %s", badEvidenceErr.ToString()) } appraisal.SetAllClaims(ear.CryptoValidationFailedClaim) appraisal.AddPolicyClaim("problem", claimStr) } - return o.finalize(appraisal, err) + // XXX(tho) early returns should set an appropriate EAR failure claim + // XXX(tho) else branch: consider setting a policy claim here + return appraisal, err } - appraisedResult, err := handler.AppraiseEvidence(appraisal.EvidenceContext, multEndorsements) + result, err := evidenceHandler.AppraiseEvidence(appraisal.EvidenceContext, appraisal.Endorsements) + if err != nil { + // XXX(tho) early returns should set an appropriate EAR failure claim + return appraisal, err + } + + appraisal.SetResultWithNonce(result, token.Nonce) + + return appraisal, nil +} + +func (o *GRPC) GetAttestation( + ctx context.Context, + token *proto.AttestationToken, +) (*proto.AppraisalContext, error) { + appraisal, err := o.getAttestation(token) if err != nil { return o.finalize(appraisal, err) } - appraisedResult.Nonce = appraisal.Result.Nonce - appraisal.Result = appraisedResult + + // XXX(tho) policy ID should have been already initalised in appraisal.New()... appraisal.InitPolicyID() - err = o.PolicyManager.Evaluate(ctx, handler.GetAttestationScheme(), appraisal, multEndorsements) + err = o.PolicyManager.Evaluate(ctx, appraisal) if err != nil { return o.finalize(appraisal, err) } @@ -504,23 +557,6 @@ func (o *GRPC) GetAttestation( return o.finalize(appraisal, nil) } -func (c *GRPC) initEvidenceContext( - handler handler.IStoreHandler, - token *proto.AttestationToken, -) (*appraisal.Appraisal, error) { - var err error - - appraisal := appraisal.New(token.TenantId, token.Nonce, handler.GetAttestationScheme()) - appraisal.EvidenceContext.TrustAnchorIds, err = handler.GetTrustAnchorIDs(token) - - if errors.Is(err, handlermod.BadEvidenceError{}) { - appraisal.SetAllClaims(ear.CryptoValidationFailedClaim) - appraisal.AddPolicyClaim("problem", "could not establish identity from evidence") - } - - return appraisal, err -} - func (c *GRPC) getTrustAnchors(id []string) ([]string, error) { var taValues []string //nolint @@ -772,6 +808,14 @@ func (o *GRPC) GetEndorsements(ctx context.Context, query *proto.EndorsementQuer }, nil } +// finalize prepares the final appraisal context to be returned to the client. +// The EAR is signed using the verifier private key. +// +// The error parameter indicates whether there was an error during the +// attestation process. If a non-nil error is supplied, it is classified as a +// verifier malfunction - unless it's of type "bad evidence", in which case it +// is logged and the error is cleared because we assume the relevant claim has +// been already set in the attestation result. func (o *GRPC) finalize( appraisal *appraisal.Appraisal, err error, diff --git a/vts/trustedservices/trustedservices_grpc_test.go b/vts/trustedservices/trustedservices_grpc_test.go new file mode 100644 index 00000000..3378704c --- /dev/null +++ b/vts/trustedservices/trustedservices_grpc_test.go @@ -0,0 +1,210 @@ +// Copyright 2026 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 +package trustedservices + +import ( + "context" + "errors" + "testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + "github.com/veraison/services/handler" + "github.com/veraison/services/log" + "github.com/veraison/services/proto" + + mock_deps "github.com/veraison/services/vts/trustedservices/mocks" +) + +type mocks struct { + evidencePluginManager *mock_deps.MockIManager[handler.IEvidenceHandler] + storePluginManager *mock_deps.MockIManager[handler.IStoreHandler] + endorsementPluginManager *mock_deps.MockIManager[handler.IEndorsementHandler] + coservProxyPluginManager *mock_deps.MockIManager[handler.ICoservProxyHandler] + policyManager *mock_deps.MockIPolicyManager + earSigner *mock_deps.MockIEarSigner + evidenceHandler *mock_deps.MockIEvidenceHandler + storeHandler *mock_deps.MockIStoreHandler + taStore *mock_deps.MockIKVStore + enStore *mock_deps.MockIKVStore +} + +func prepareMockGRPC(t *testing.T) (ITrustedServices, *mocks) { + ctrl := gomock.NewController(t) + + evidencePluginManager := mock_deps.NewMockIManager[handler.IEvidenceHandler](ctrl) + storePluginManager := mock_deps.NewMockIManager[handler.IStoreHandler](ctrl) + endorsementPluginManager := mock_deps.NewMockIManager[handler.IEndorsementHandler](ctrl) + coservProxyPluginManager := mock_deps.NewMockIManager[handler.ICoservProxyHandler](ctrl) + policyManager := mock_deps.NewMockIPolicyManager(ctrl) + earSigner := mock_deps.NewMockIEarSigner(ctrl) + evidenceHandler := mock_deps.NewMockIEvidenceHandler(ctrl) + storeHandler := mock_deps.NewMockIStoreHandler(ctrl) + taStore := mock_deps.NewMockIKVStore(ctrl) + enStore := mock_deps.NewMockIKVStore(ctrl) + + // TODO + + return NewGRPC( + taStore, + enStore, + evidencePluginManager, + endorsementPluginManager, + storePluginManager, + coservProxyPluginManager, + policyManager, + earSigner, + nil, // coservSigner + log.Named("test"), + ), &mocks{ + evidencePluginManager: evidencePluginManager, + storePluginManager: storePluginManager, + endorsementPluginManager: endorsementPluginManager, + coservProxyPluginManager: coservProxyPluginManager, + policyManager: policyManager, + earSigner: earSigner, + evidenceHandler: evidenceHandler, + storeHandler: storeHandler, + taStore: taStore, + enStore: enStore, + } +} + +func TestGRPC_GetAttestation_internal_error_evidence_handler_not_found(t *testing.T) { + tv := &proto.AttestationToken{ + TenantId: "tenant-abc", + Data: []byte("dummy-attestation-token-data"), + MediaType: "application/attestation-token", + Nonce: []byte("random-nonce-value"), + } + + grpc, mocks := prepareMockGRPC(t) + + mocks.evidencePluginManager. + EXPECT().LookupByMediaType("application/attestation-token").Return(nil, errors.New("evidence handler not found")) + + mocks.earSigner. + EXPECT().Sign(gomock.Any()).Return([]byte("signed-ear"), nil) + + expected, err := grpc.GetAttestation(context.Background(), tv) + + assert.EqualError(t, err, "evidence handler not found") + + assert.Equal(t, expected, &proto.AppraisalContext{ + Evidence: &proto.EvidenceContext{ + TenantId: "tenant-abc", + }, + Result: []byte("signed-ear"), + }) +} + +func TestGRPC_GetAttestation_internal_error_no_store_handler(t *testing.T) { + tv := &proto.AttestationToken{ + TenantId: "tenant-abc", + Data: []byte("dummy-attestation-token-data"), + MediaType: "application/attestation-token", + Nonce: []byte("random-nonce-value"), + } + + grpc, mocks := prepareMockGRPC(t) + + mocks.evidencePluginManager. + EXPECT().LookupByMediaType("application/attestation-token").Return(mocks.evidenceHandler, nil) + + mocks.evidenceHandler. + EXPECT().GetAttestationScheme().Return("test-scheme") + + mocks.storePluginManager. + EXPECT().LookupByAttestationScheme("test-scheme").Return(nil, errors.New("store handler not found")) + + mocks.earSigner. + EXPECT().Sign(gomock.Any()).Return([]byte("signed-ear"), nil) + + expected, err := grpc.GetAttestation(context.Background(), tv) + + assert.EqualError(t, err, "store handler not found") + + assert.Equal(t, expected, &proto.AppraisalContext{ + Evidence: &proto.EvidenceContext{ + TenantId: "tenant-abc", + }, + Result: []byte("signed-ear"), + }) +} + +func TestGRPC_GetAttestation_no_trust_anchor_ids(t *testing.T) { + tv := &proto.AttestationToken{ + TenantId: "tenant-abc", + Data: []byte("dummy-attestation-token-data"), + MediaType: "application/attestation-token", + Nonce: []byte("random-nonce-value"), + } + + grpc, mocks := prepareMockGRPC(t) + + mocks.evidencePluginManager. + EXPECT().LookupByMediaType("application/attestation-token").Return(mocks.evidenceHandler, nil) + + mocks.evidenceHandler. + EXPECT().GetAttestationScheme().Return("test-scheme") + + mocks.storePluginManager. + EXPECT().LookupByAttestationScheme("test-scheme").Return(mocks.storeHandler, nil) + + mocks.storeHandler. + EXPECT().GetTrustAnchorIDs(tv).Return(nil, errors.New("TA IDs not found")) + + mocks.earSigner. + EXPECT().Sign(gomock.Any()).Return([]byte("signed-ear"), nil) + + expected, err := grpc.GetAttestation(context.Background(), tv) + + assert.EqualError(t, err, "TA IDs not found") + + assert.Equal(t, expected, &proto.AppraisalContext{ + Evidence: &proto.EvidenceContext{ + TenantId: "tenant-abc", + }, + Result: []byte("signed-ear"), + }) +} + +func TestGRPC_GetAttestation_no_trust_anchors(t *testing.T) { + tv := &proto.AttestationToken{ + TenantId: "tenant-abc", + Data: []byte("dummy-attestation-token-data"), + MediaType: "application/attestation-token", + Nonce: []byte("random-nonce-value"), + } + + grpc, mocks := prepareMockGRPC(t) + + mocks.evidencePluginManager. + EXPECT().LookupByMediaType("application/attestation-token").Return(mocks.evidenceHandler, nil) + + mocks.evidenceHandler. + EXPECT().GetAttestationScheme().Return("test-scheme") + + mocks.storePluginManager. + EXPECT().LookupByAttestationScheme("test-scheme").Return(mocks.storeHandler, nil) + + mocks.storeHandler. + EXPECT().GetTrustAnchorIDs(tv).Return([]string{"dummy TA ID"}, nil) + + mocks.taStore. + EXPECT().Get("dummy TA ID").Return(nil, errors.New("TA not found")) + + mocks.earSigner. + EXPECT().Sign(gomock.Any()).Return([]byte("signed-ear"), nil) + + expected, err := grpc.GetAttestation(context.Background(), tv) + + assert.EqualError(t, err, "TA not found") + + assert.Equal(t, expected, &proto.AppraisalContext{ + Evidence: &proto.EvidenceContext{ + TenantId: "tenant-abc", + }, + Result: []byte("signed-ear"), + }) +}