From 8e689a7b33d30035e47ae7e968a2d3f7c5a4abf1 Mon Sep 17 00:00:00 2001
From: Lyuben Penkovski <lyuben.penkovski@vereign.com>
Date: Tue, 19 Jul 2022 09:30:07 +0300
Subject: [PATCH] Verify proofs by calling the signer service

Before this commit, the infohub was verifying VP proofs by itself.
Now this is delegated to the signer service for consistency, as the
signer service can act as both producer and verifier of proofs.
---
 cmd/infohub/main.go                           |  6 +-
 internal/clients/signer/client.go             | 46 ++++++++++-
 .../credential/{issuer.go => credentials.go}  | 34 +++-----
 .../infohub/infohubfakes/fake_signer.go       | 81 +++++++++++++++++++
 internal/service/infohub/service.go           |  8 +-
 5 files changed, 149 insertions(+), 26 deletions(-)
 rename internal/credential/{issuer.go => credentials.go} (52%)

diff --git a/cmd/infohub/main.go b/cmd/infohub/main.go
index f7c41d4..05b46df 100644
--- a/cmd/infohub/main.go
+++ b/cmd/infohub/main.go
@@ -76,7 +76,7 @@ func main() {
 	}
 
 	httpClient := httpClient()
-	credentials := credential.NewIssuer(cfg.Credential.IssuerURI, httpClient)
+	credentials := credential.New(cfg.Credential.IssuerURI, httpClient)
 
 	// create policy client
 	policy := policy.New(cfg.Policy.Addr, policy.WithHTTPClient(httpClient))
@@ -206,14 +206,14 @@ func httpClient() *http.Client {
 		Transport: &http.Transport{
 			Proxy: http.ProxyFromEnvironment,
 			DialContext: (&net.Dialer{
-				Timeout: 30 * time.Second,
+				Timeout: 10 * time.Second,
 			}).DialContext,
 			MaxIdleConns:        100,
 			MaxIdleConnsPerHost: 100,
 			TLSHandshakeTimeout: 10 * time.Second,
 			IdleConnTimeout:     60 * time.Second,
 		},
-		Timeout: 30 * time.Second,
+		Timeout: 10 * time.Second,
 	}
 }
 
diff --git a/internal/clients/signer/client.go b/internal/clients/signer/client.go
index 84b18f1..1b0da9c 100644
--- a/internal/clients/signer/client.go
+++ b/internal/clients/signer/client.go
@@ -6,13 +6,19 @@ import (
 	"encoding/json"
 	"fmt"
 	"io"
+	"io/ioutil"
 	"net/http"
 
 	"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
 	"github.com/piprate/json-gold/ld"
+
+	"code.vereign.com/gaiax/tsa/golib/errors"
 )
 
-const presentationProofPath = "/v1/presentation/proof"
+const (
+	presentationProofPath  = "/v1/presentation/proof"
+	presentationVerifyPath = "/v1/presentation/verify"
+)
 
 type Client struct {
 	addr       string
@@ -67,3 +73,41 @@ func (c *Client) PresentationProof(ctx context.Context, vp *verifiable.Presentat
 		verifiable.WithPresDisabledProofCheck(),
 	)
 }
+
+func (c *Client) VerifyPresentation(ctx context.Context, vp []byte) error {
+	req, err := http.NewRequestWithContext(ctx, "POST", c.addr+presentationVerifyPath, bytes.NewReader(vp))
+	if err != nil {
+		return err
+	}
+
+	resp, err := c.httpClient.Do(req)
+	if err != nil {
+		return err
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusOK {
+		return errors.New(errors.GetKind(resp.StatusCode), getErrorBody(resp))
+	}
+
+	var result struct {
+		Valid bool `json:"valid"`
+	}
+	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
+		return errors.New("failed to decode response", err)
+	}
+
+	if !result.Valid {
+		return errors.New("invalid presentation proof")
+	}
+
+	return nil
+}
+
+func getErrorBody(resp *http.Response) string {
+	body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 2<<20))
+	if err != nil {
+		return ""
+	}
+	return string(body)
+}
diff --git a/internal/credential/issuer.go b/internal/credential/credentials.go
similarity index 52%
rename from internal/credential/issuer.go
rename to internal/credential/credentials.go
index 84d96bd..cefe9d9 100644
--- a/internal/credential/issuer.go
+++ b/internal/credential/credentials.go
@@ -4,12 +4,8 @@ import (
 	"net/http"
 	"time"
 
-	"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite"
-	"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite/jsonwebsignature2020"
 	"github.com/hyperledger/aries-framework-go/pkg/doc/util"
 	"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
-	"github.com/hyperledger/aries-framework-go/pkg/vdr"
-	"github.com/hyperledger/aries-framework-go/pkg/vdr/web"
 	"github.com/piprate/json-gold/ld"
 )
 
@@ -18,30 +14,31 @@ var defaultContexts = []string{
 	"https://w3id.org/security/suites/jws-2020/v1",
 }
 
-type Issuer struct {
+type Credentials struct {
 	issuerURI  string
 	docLoader  *ld.CachingDocumentLoader
 	httpClient *http.Client
 }
 
-func NewIssuer(issuerURI string, httpClient *http.Client) *Issuer {
+func New(issuerURI string, httpClient *http.Client) *Credentials {
 	loader := ld.NewDefaultDocumentLoader(httpClient)
 
-	return &Issuer{
+	return &Credentials{
 		issuerURI:  issuerURI,
 		docLoader:  ld.NewCachingDocumentLoader(loader),
 		httpClient: httpClient,
 	}
 }
 
-func (i *Issuer) NewCredential(contexts []string, subjectID string, subject map[string]interface{}, proof bool) (*verifiable.Credential, error) {
+// NewCredential creates a Verifiable Credential without proofs.
+func (c *Credentials) NewCredential(contexts []string, subjectID string, subject map[string]interface{}, proof bool) (*verifiable.Credential, error) {
 	jsonldContexts := defaultContexts
 	jsonldContexts = append(jsonldContexts, contexts...)
 
 	vc := &verifiable.Credential{
 		Context: jsonldContexts,
 		Types:   []string{verifiable.VCType},
-		Issuer:  verifiable.Issuer{ID: i.issuerURI},
+		Issuer:  verifiable.Issuer{ID: c.issuerURI},
 		Issued:  &util.TimeWrapper{Time: time.Now()},
 		Subject: verifiable.Subject{
 			ID:           subjectID,
@@ -52,7 +49,8 @@ func (i *Issuer) NewCredential(contexts []string, subjectID string, subject map[
 	return vc, nil
 }
 
-func (i *Issuer) NewPresentation(contexts []string, vc ...*verifiable.Credential) (*verifiable.Presentation, error) {
+// NewPresentation creates a Verifiable Presentation without proofs.
+func (c *Credentials) NewPresentation(contexts []string, vc ...*verifiable.Credential) (*verifiable.Presentation, error) {
 	jsonldContexts := defaultContexts
 	jsonldContexts = append(jsonldContexts, contexts...)
 
@@ -61,24 +59,18 @@ func (i *Issuer) NewPresentation(contexts []string, vc ...*verifiable.Credential
 		return nil, err
 	}
 	vp.Context = jsonldContexts
-	vp.ID = i.issuerURI
+	vp.ID = c.issuerURI
 	vp.Type = []string{verifiable.VPType}
 
 	return vp, nil
 }
 
-func (i *Issuer) ParsePresentation(vpBytes []byte) (*verifiable.Presentation, error) {
-	webvdr := web.New()
-	registry := vdr.New(vdr.WithVDR(webvdr))
-	fetcher := verifiable.NewVDRKeyResolver(registry)
-
+// ParsePresentation without verifying VP proofs.
+func (c *Credentials) ParsePresentation(vpBytes []byte) (*verifiable.Presentation, error) {
 	return verifiable.ParsePresentation(
 		vpBytes,
-		verifiable.WithPresPublicKeyFetcher(fetcher.PublicKeyFetcher()),
-		verifiable.WithPresEmbeddedSignatureSuites(
-			jsonwebsignature2020.New(suite.WithVerifier(jsonwebsignature2020.NewPublicKeyVerifier())),
-		),
-		verifiable.WithPresJSONLDDocumentLoader(i.docLoader),
+		verifiable.WithPresDisabledProofCheck(),
+		verifiable.WithPresJSONLDDocumentLoader(c.docLoader),
 		verifiable.WithPresStrictValidation(),
 	)
 }
diff --git a/internal/service/infohub/infohubfakes/fake_signer.go b/internal/service/infohub/infohubfakes/fake_signer.go
index 7ec6497..a42e940 100644
--- a/internal/service/infohub/infohubfakes/fake_signer.go
+++ b/internal/service/infohub/infohubfakes/fake_signer.go
@@ -24,6 +24,18 @@ type FakeSigner struct {
 		result1 *verifiable.Presentation
 		result2 error
 	}
+	VerifyPresentationStub        func(context.Context, []byte) error
+	verifyPresentationMutex       sync.RWMutex
+	verifyPresentationArgsForCall []struct {
+		arg1 context.Context
+		arg2 []byte
+	}
+	verifyPresentationReturns struct {
+		result1 error
+	}
+	verifyPresentationReturnsOnCall map[int]struct {
+		result1 error
+	}
 	invocations      map[string][][]interface{}
 	invocationsMutex sync.RWMutex
 }
@@ -93,11 +105,80 @@ func (fake *FakeSigner) PresentationProofReturnsOnCall(i int, result1 *verifiabl
 	}{result1, result2}
 }
 
+func (fake *FakeSigner) VerifyPresentation(arg1 context.Context, arg2 []byte) error {
+	var arg2Copy []byte
+	if arg2 != nil {
+		arg2Copy = make([]byte, len(arg2))
+		copy(arg2Copy, arg2)
+	}
+	fake.verifyPresentationMutex.Lock()
+	ret, specificReturn := fake.verifyPresentationReturnsOnCall[len(fake.verifyPresentationArgsForCall)]
+	fake.verifyPresentationArgsForCall = append(fake.verifyPresentationArgsForCall, struct {
+		arg1 context.Context
+		arg2 []byte
+	}{arg1, arg2Copy})
+	stub := fake.VerifyPresentationStub
+	fakeReturns := fake.verifyPresentationReturns
+	fake.recordInvocation("VerifyPresentation", []interface{}{arg1, arg2Copy})
+	fake.verifyPresentationMutex.Unlock()
+	if stub != nil {
+		return stub(arg1, arg2)
+	}
+	if specificReturn {
+		return ret.result1
+	}
+	return fakeReturns.result1
+}
+
+func (fake *FakeSigner) VerifyPresentationCallCount() int {
+	fake.verifyPresentationMutex.RLock()
+	defer fake.verifyPresentationMutex.RUnlock()
+	return len(fake.verifyPresentationArgsForCall)
+}
+
+func (fake *FakeSigner) VerifyPresentationCalls(stub func(context.Context, []byte) error) {
+	fake.verifyPresentationMutex.Lock()
+	defer fake.verifyPresentationMutex.Unlock()
+	fake.VerifyPresentationStub = stub
+}
+
+func (fake *FakeSigner) VerifyPresentationArgsForCall(i int) (context.Context, []byte) {
+	fake.verifyPresentationMutex.RLock()
+	defer fake.verifyPresentationMutex.RUnlock()
+	argsForCall := fake.verifyPresentationArgsForCall[i]
+	return argsForCall.arg1, argsForCall.arg2
+}
+
+func (fake *FakeSigner) VerifyPresentationReturns(result1 error) {
+	fake.verifyPresentationMutex.Lock()
+	defer fake.verifyPresentationMutex.Unlock()
+	fake.VerifyPresentationStub = nil
+	fake.verifyPresentationReturns = struct {
+		result1 error
+	}{result1}
+}
+
+func (fake *FakeSigner) VerifyPresentationReturnsOnCall(i int, result1 error) {
+	fake.verifyPresentationMutex.Lock()
+	defer fake.verifyPresentationMutex.Unlock()
+	fake.VerifyPresentationStub = nil
+	if fake.verifyPresentationReturnsOnCall == nil {
+		fake.verifyPresentationReturnsOnCall = make(map[int]struct {
+			result1 error
+		})
+	}
+	fake.verifyPresentationReturnsOnCall[i] = struct {
+		result1 error
+	}{result1}
+}
+
 func (fake *FakeSigner) Invocations() map[string][][]interface{} {
 	fake.invocationsMutex.RLock()
 	defer fake.invocationsMutex.RUnlock()
 	fake.presentationProofMutex.RLock()
 	defer fake.presentationProofMutex.RUnlock()
+	fake.verifyPresentationMutex.RLock()
+	defer fake.verifyPresentationMutex.RUnlock()
 	copiedInvocations := map[string][][]interface{}{}
 	for key, value := range fake.invocations {
 		copiedInvocations[key] = value
diff --git a/internal/service/infohub/service.go b/internal/service/infohub/service.go
index d5b1299..9c78fe4 100644
--- a/internal/service/infohub/service.go
+++ b/internal/service/infohub/service.go
@@ -42,6 +42,7 @@ type Credentials interface {
 
 type Signer interface {
 	PresentationProof(ctx context.Context, vp *verifiable.Presentation) (*verifiable.Presentation, error)
+	VerifyPresentation(ctx context.Context, vp []byte) error
 }
 
 type Service struct {
@@ -68,13 +69,18 @@ func New(storage Storage, policy Policy, cache Cache, cred Credentials, signer S
 func (s *Service) Import(ctx context.Context, req *infohub.ImportRequest) (res *infohub.ImportResult, err error) {
 	logger := s.logger.With(zap.String("operation", "import"))
 
+	if err := s.signer.VerifyPresentation(ctx, req.Data); err != nil {
+		logger.Error("error verifying presentation", zap.Error(err))
+		return nil, err
+	}
+
 	vp, err := s.credentials.ParsePresentation(req.Data)
 	if err != nil {
 		logger.Error("error parsing verifiable presentation", zap.Error(err))
 		return nil, err
 	}
 
-	// separate data entries can be wrapped in separate verifiable credentials;
+	// separate data entries are wrapped in separate verifiable credentials;
 	// each one of them must be placed separately in the cache
 	var importedCredentials []string
 	for _, credential := range vp.Credentials() {
-- 
GitLab