Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: json-ld data integrity #69

Merged
merged 7 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,11 @@ linters-settings:
allow-cuddle-declarations: false
godot:
check-all: false
gomoddirectives:
replace-local: true

linters:
enable-all: true
disable:
- gomoddirectives
- nolintlint
- varnamelen
- tenv
Expand Down
21 changes: 15 additions & 6 deletions dataintegrity/suite/ecdsa2019/ecdsa2019.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,26 +311,35 @@ func canonicalize(data map[string]interface{}, loader ld.DocumentLoader) ([]byte
return out, nil
}

func hashData(transformedDoc, confData []byte, h hash.Hash) []byte {
h.Write(transformedDoc)
func hashData(docData, proofData []byte, h hash.Hash) []byte {
h.Write(docData)
docHash := h.Sum(nil)

h.Reset()
h.Write(confData)
result := h.Sum(docHash)
h.Write(proofData)
proofHash := h.Sum(nil)

return result
return append(proofHash, docHash...)
}

func proofConfig(docCtx interface{}, opts *models.ProofOptions) map[string]interface{} {
return map[string]interface{}{
proof := map[string]interface{}{
ldCtxKey: docCtx,
"type": models.DataIntegrityProof,
"cryptosuite": opts.SuiteType,
"verificationMethod": opts.VerificationMethodID,
"created": opts.Created.Format(models.DateTimeFormat),
"proofPurpose": opts.Purpose,
}

if opts.Challenge != "" {
proof["challenge"] = opts.Challenge
}
if opts.Domain != "" {
proof["domain"] = opts.Domain
}

return proof
}

func sign(sigBase []byte, key *jwk.JWK, signerGetter SignerGetter) ([]byte, error) {
Expand Down
55 changes: 40 additions & 15 deletions dataintegrity/suite/eddsa2022/eddsa2022.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ const (
// implementing eddsa signatures with RDF canonicalization as per this
// spec:https://w3c.github.io/vc-di-eddsa/#verify-proof-eddsa-rdfc-2022
SuiteType = "eddsa-rdfc-2022"

// SuiteType2 "eddsa-2022" is the data integrity Type identifier for the suite. Alias (vc playground).
SuiteType2 = "eddsa-2022"
)

// SignerGetter returns a Signer, which must sign with the private key matching
Expand Down Expand Up @@ -126,7 +129,7 @@ func (i initializer) Verifier() (suite.Verifier, error) {
// Type private, implements suite.SignerInitializer and
// suite.VerifierInitializer.
func (i initializer) Type() []string {
return []string{SuiteType}
return []string{SuiteType, SuiteType2}
}

// SignerInitializerOptions provides options for a SignerInitializer.
Expand Down Expand Up @@ -200,6 +203,7 @@ func (s *Suite) CreateProof(doc []byte, opts *models.ProofOptions) (*models.Proo
return p, nil
}

// nolint:gocyclo
func (s *Suite) transformAndHash(doc []byte, opts *models.ProofOptions) ([]byte, *pubkey.PublicKey, Verifier, error) {
docData := make(map[string]interface{})

Expand All @@ -208,11 +212,6 @@ func (s *Suite) transformAndHash(doc []byte, opts *models.ProofOptions) ([]byte,
return nil, nil, nil, fmt.Errorf("eddsa-2022 suite expects JSON-LD payload: %w", err)
}

vmKey := opts.VerificationMethod.JSONWebKey()
if vmKey == nil {
return nil, nil, nil, errors.New("verification method needs JWK")
}

var (
keyType kms.KeyType
h hash.Hash
Expand All @@ -221,11 +220,22 @@ func (s *Suite) transformAndHash(doc []byte, opts *models.ProofOptions) ([]byte,

verifier = s.eD25519Verifier
keyType = kms.ED25519Type

finalKey := &pubkey.PublicKey{Type: keyType, JWK: opts.VerificationMethod.JSONWebKey()}
if finalKey.JWK == nil && len(opts.VerificationMethod.Value) > 0 {
finalKey.BytesKey = &pubkey.BytesKey{Bytes: opts.VerificationMethod.Value}
}

if finalKey.JWK == nil && finalKey.BytesKey == nil {
return nil, nil, nil, errors.New("verification method needs JWK")
}

h = sha256.New()

confData := proofConfig(docData[ldCtxKey], opts)

if opts.ProofType != "DataIntegrityProof" || opts.SuiteType != SuiteType {
if opts.ProofType != "DataIntegrityProof" || (opts.SuiteType != SuiteType &&
opts.SuiteType != SuiteType2) {
return nil, nil, nil, suite.ErrProofTransformation
}

Expand All @@ -241,7 +251,7 @@ func (s *Suite) transformAndHash(doc []byte, opts *models.ProofOptions) ([]byte,

docHash := hashData(canonDoc, canonConf, h)

return docHash, &pubkey.PublicKey{Type: keyType, JWK: vmKey}, verifier, nil
return docHash, finalKey, verifier, nil
}

// VerifyProof implements the eddsa-2022 cryptographic suite for CheckJWTProof Proof.
Expand Down Expand Up @@ -279,26 +289,41 @@ func canonicalize(data map[string]interface{}, loader ld.DocumentLoader) ([]byte
return out, nil
}

func hashData(transformedDoc, confData []byte, h hash.Hash) []byte {
h.Write(transformedDoc)
func hashData(docData, proofData []byte, h hash.Hash) []byte {
h.Write(docData)
docHash := h.Sum(nil)

h.Reset()
h.Write(confData)
result := h.Sum(docHash)
h.Write(proofData)
proofHash := h.Sum(nil)

return result
return append(proofHash, docHash...)
}

func proofConfig(docCtx interface{}, opts *models.ProofOptions) map[string]interface{} {
return map[string]interface{}{
suiteType := SuiteType
if opts.SuiteType != "" {
suiteType = opts.SuiteType
}

proof := map[string]interface{}{
ldCtxKey: docCtx,
"type": models.DataIntegrityProof,
"cryptosuite": SuiteType,
"cryptosuite": suiteType,
"verificationMethod": opts.VerificationMethodID,
"created": opts.Created.Format(models.DateTimeFormat),
"proofPurpose": opts.Purpose,
}

if opts.Challenge != "" {
proof["challenge"] = opts.Challenge
}

if opts.Domain != "" {
proof["domain"] = opts.Domain
}

return proof
}

func sign(sigBase []byte, key *jwk.JWK, signerGetter SignerGetter) ([]byte, error) {
Expand Down
14 changes: 1 addition & 13 deletions dataintegrity/suite/eddsa2022/eddsa2022_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func TestNew(t *testing.T) {
require.NotNil(t, signer)
require.False(t, signer.RequiresCreated())

require.EqualValues(t, []string{"eddsa-rdfc-2022"}, sigInit.Type())
require.EqualValues(t, []string{"eddsa-rdfc-2022", "eddsa-2022"}, sigInit.Type())
})

t.Run("verifier success", func(t *testing.T) {
Expand Down Expand Up @@ -250,18 +250,6 @@ func TestSharedFailures(t *testing.T) {
testSign(t, tc)
})

t.Run("no jwk in vm", func(t *testing.T) {
tc := successCase(t)

tc.proofOpts.VerificationMethod = &did.VerificationMethod{
ID: tc.proofOpts.VerificationMethodID,
Value: []byte(fooBar),
}
tc.errStr = "verification method needs JWK"

testSign(t, tc)
})

t.Run("invalid proof/suite type", func(t *testing.T) {
tc := successCase(t)

Expand Down
6 changes: 4 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ require (
github.com/multiformats/go-multibase v0.1.1
github.com/piprate/json-gold v0.5.1-0.20230111113000-6ddbe6e6f19f
github.com/samber/lo v1.47.0
github.com/stretchr/testify v1.8.2
github.com/stretchr/testify v1.8.3
github.com/tidwall/gjson v1.14.3
github.com/tidwall/sjson v1.1.4
github.com/trustbloc/bbs-signature-go v1.0.2
github.com/trustbloc/did-go v1.3.1-0.20240910130808-bf0188fdfe70
github.com/trustbloc/did-go v1.3.1-0.20241021165331-5721a3ff7396
github.com/trustbloc/kms-go v1.1.2
github.com/veraison/go-cose v1.1.1-0.20240126165338-2300d5c96dbd
github.com/xeipuuv/gojsonschema v1.2.0
Expand Down Expand Up @@ -67,3 +67,5 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect
rsc.io/tmplfunc v0.0.3 // indirect
)

replace github.com/piprate/json-gold v0.5.1-0.20230111113000-6ddbe6e6f19f => github.com/trustbloc/json-gold v0.5.1
16 changes: 6 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,6 @@ github.com/multiformats/go-multibase v0.1.1/go.mod h1:ZEjHE+IsUrgp5mhlEAYjMtZwK1
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/piprate/json-gold v0.5.1-0.20230111113000-6ddbe6e6f19f h1:HlPa7RcxTCrva5izPfTEfvYecO7LTahgmMRD1Qp13xg=
github.com/piprate/json-gold v0.5.1-0.20230111113000-6ddbe6e6f19f/go.mod h1:WZ501QQMbZZ+3pXFPhQKzNwS1+jls0oqov3uQ2WasLs=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand All @@ -111,14 +109,10 @@ github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/teserakt-io/golang-ed25519 v0.0.0-20210104091850-3888c087a4c8 h1:RBkacARv7qY5laaXGlF4wFB/tk5rnthhPb8oIBGoagY=
github.com/teserakt-io/golang-ed25519 v0.0.0-20210104091850-3888c087a4c8/go.mod h1:9PdLyPiZIiW3UopXyRnPYyjUXSpiQNHRLu8fOsR3o8M=
github.com/tidwall/gjson v1.6.7/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI=
Expand All @@ -134,8 +128,10 @@ github.com/tidwall/sjson v1.1.4 h1:bTSsPLdAYF5QNLSwYsKfBKKTnlGbIuhqL3CpRsjzGhg=
github.com/tidwall/sjson v1.1.4/go.mod h1:wXpKXu8CtDjKAZ+3DrKY5ROCorDFahq8l0tey/Lx1fg=
github.com/trustbloc/bbs-signature-go v1.0.2 h1:gepEsbLiZHv/vva9FKG5gF38mGtOIyGez7desZxiI1o=
github.com/trustbloc/bbs-signature-go v1.0.2/go.mod h1:xYotcXHAbcE0TO+SteW0J6XI3geQaXq4wdnXR2k+XCU=
github.com/trustbloc/did-go v1.3.1-0.20240910130808-bf0188fdfe70 h1:5GOoXZcKYDTsobaTCeFUEBjbognYbjBh38fCjkvClU4=
github.com/trustbloc/did-go v1.3.1-0.20240910130808-bf0188fdfe70/go.mod h1:packTRoBoo8DrwOE7QKsI98xXS3Vf6ovUXYD4FUAcB4=
github.com/trustbloc/did-go v1.3.1-0.20241021165331-5721a3ff7396 h1:z9x5gLgDeUtcPUS8uQgHD/KQ/PL5VK2QV9oENYjsWbU=
github.com/trustbloc/did-go v1.3.1-0.20241021165331-5721a3ff7396/go.mod h1:L5m4TVlPwe7VN5FRrANPMg6EJN8wIlthC8CvossDZVI=
github.com/trustbloc/json-gold v0.5.1 h1:0HHf0ildMnN4rUr7Rgxwnm1CO116JoGMrgoWIFngM1U=
github.com/trustbloc/json-gold v0.5.1/go.mod h1:RVhE35veDX19r5gfUAR+IYHkAUuPwJO8Ie/qVeFaIzw=
github.com/trustbloc/kms-go v1.1.2 h1:nAlhDoHkSyX1eQFRz/sJsdgmJuNadyX7FJEy/9ROwys=
github.com/trustbloc/kms-go v1.1.2/go.mod h1:OKOtsLbE6W5s4mpjWkvk8XEqcmt9vTgVmDNkHELpWO0=
github.com/veraison/go-cose v1.1.1-0.20240126165338-2300d5c96dbd h1:QhdCHSW1/oosJbzBTEYLU6xcKxXbQzzqFnhCtW2UWbA=
Expand Down
6 changes: 6 additions & 0 deletions proof/ldproofs/ed25519signature2020/proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ func New() *Proof {
JWKKeyType: JWKKeyType,
JWKCurve: JWKCurve,
},
{
VerificationMethodType: "Ed25519VerificationKey2018",
KMSKeyType: kms.ED25519Type,
JWKKeyType: JWKKeyType,
JWKCurve: JWKCurve,
},
{
VerificationMethodType: "JsonWebKey2020",
KMSKeyType: kms.ED25519Type,
Expand Down
66 changes: 66 additions & 0 deletions verifiable/data_integrity_proof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,27 @@ SPDX-License-Identifier: Apache-2.0
package verifiable

import (
_ "embed"
"net/http"
"testing"
"time"

"github.com/piprate/json-gold/ld"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/trustbloc/did-go/doc/did"
"github.com/trustbloc/did-go/method/jwk"
"github.com/trustbloc/did-go/method/key"
vdrpkg "github.com/trustbloc/did-go/vdr"
vdrapi "github.com/trustbloc/did-go/vdr/api"
kmsapi "github.com/trustbloc/kms-go/spi/kms"

"github.com/trustbloc/vc-go/dataintegrity"
"github.com/trustbloc/vc-go/dataintegrity/suite/ecdsa2019"
"github.com/trustbloc/vc-go/dataintegrity/suite/eddsa2022"
"github.com/trustbloc/vc-go/internal/testutil/kmscryptoutil"
"github.com/trustbloc/vc-go/proof/defaults"
"github.com/trustbloc/vc-go/vermethod"
)

func Test_DataIntegrity_SignVerify(t *testing.T) {
Expand Down Expand Up @@ -178,6 +188,62 @@ func Test_DataIntegrity_SignVerify(t *testing.T) {
})
}

//go:embed testdata/example_presentation.jsonld
var examplePresentation []byte

//go:embed testdata/example_presentation_2.json
var examplePresentation2 []byte

//go:embed testdata/context/credential_v2.jsonld
var credentialV2Context []byte

func TestCanParseRDFC2022Presentation(t *testing.T) {
vdr := vdrpkg.New(vdrpkg.WithVDR(jwk.New()), vdrpkg.WithVDR(key.New()))

loader := ld.NewDefaultDocumentLoader(http.DefaultClient)
verifier, err := dataintegrity.NewVerifier(&dataintegrity.Options{
DIDResolver: vdr,
}, eddsa2022.NewVerifierInitializer(&eddsa2022.VerifierInitializerOptions{
LDDocumentLoader: loader,
}))

resp, err := ParsePresentation(examplePresentation,
WithPresDataIntegrityVerifier(verifier),
WithPresJSONLDDocumentLoader(loader),
WithPresExpectedDataIntegrityFields("authentication",
"github.com/w3c/vc-data-model-2.0-test-suite", "ubXbWYV5hUDu1VCy2b75qKg"),
)
require.NoError(t, err)
assert.NotNil(t, resp)
}

func TestCanParsePlaygroundPresentation(t *testing.T) {
vdr := vdrpkg.New(vdrpkg.WithVDR(jwk.New()), vdrpkg.WithVDR(key.New()))

loader := ld.NewDefaultDocumentLoader(http.DefaultClient)
verifier, err := dataintegrity.NewVerifier(&dataintegrity.Options{
DIDResolver: vdr,
}, eddsa2022.NewVerifierInitializer(&eddsa2022.VerifierInitializerOptions{
LDDocumentLoader: loader,
}), ecdsa2019.NewVerifierInitializer(&ecdsa2019.VerifierInitializerOptions{
LDDocumentLoader: loader,
}))

proofChecker := defaults.NewDefaultProofChecker(vermethod.NewVDRResolver(vdr))

resp, err := ParsePresentation(examplePresentation2,
WithPresDataIntegrityVerifier(verifier),
WithPresJSONLDDocumentLoader(loader),
WithPresProofChecker(proofChecker),
WithPresExpectedDataIntegrityFields("authentication",
"https://playground.chapi.io",
"3779e883a51a8086039db1d4e773aec26faeb3ee99643706345c572cddded857",
),
)
require.NoError(t, err)
assert.NotNil(t, resp)
}

type resolveFunc func(id string) (*did.DocResolution, error)

func (f resolveFunc) Resolve(id string, opts ...vdrapi.DIDMethodOption) (*did.DocResolution, error) {
Expand Down
Loading
Loading