Skip to content
Snippets Groups Projects
Commit 8e689a7b authored by Lyuben Penkovski's avatar Lyuben Penkovski
Browse files

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.
parent 5e368f8d
No related branches found
No related tags found
1 merge request!13Verify proofs by calling the signer service
Pipeline #52792 passed with stages
in 1 minute and 26 seconds
......@@ -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,
}
}
......
......@@ -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)
}
......@@ -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(),
)
}
......@@ -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
......
......@@ -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() {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment