diff --git a/cmd/infohub/main.go b/cmd/infohub/main.go index f7c41d475f3c47301d775238214dc779656afe09..05b46dfab25578cda4fc4275fff9c0aea504b261 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 84b18f190aa44b34f34201c4548f649bc7eb8ae3..1b0da9cbd2e6ffa75dd9d42b95abf2a893ca1aa4 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 84d96bd3892493fd36dd3be92198f5a46c58d5ee..cefe9d96dc90b62a2ed421bd3810431140c6b6ab 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 7ec6497e70fe60c5b0bd13e9e17b005b93bd51a2..a42e9402409ae7ab9c3abc5c141be3980ba416b5 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 d5b1299653112b5148504e7fe96195aaf0227390..9c78fe450401d93a9ede15b16eb23a9e394d6282 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() {