Skip to content

Commit

Permalink
Update to go-did v0.10.0
Browse files Browse the repository at this point in the history
  • Loading branch information
reinkrul committed Dec 1, 2023
1 parent 79ded21 commit 5a66734
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 17 deletions.
3 changes: 1 addition & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ require (
github.com/nats-io/nats-server/v2 v2.10.5
github.com/nats-io/nats.go v1.31.0
github.com/nuts-foundation/crypto-ecies v0.0.0-20211207143025-5b84f9efce2b
github.com/nuts-foundation/go-did v0.9.0
github.com/nuts-foundation/go-did v0.10.0
github.com/nuts-foundation/go-leia/v4 v4.0.1
github.com/nuts-foundation/go-stoabs v1.9.0
// check the oapi-codegen tool version in the makefile when upgrading the runtime
Expand Down Expand Up @@ -106,7 +106,6 @@ require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jinzhu/gorm v1.9.16 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.17.2 // indirect
github.com/kr/text v0.2.0 // indirect
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,12 @@ github.com/nuts-foundation/crypto-ecies v0.0.0-20211207143025-5b84f9efce2b h1:80
github.com/nuts-foundation/crypto-ecies v0.0.0-20211207143025-5b84f9efce2b/go.mod h1:6YUioYirD6/8IahZkoS4Ypc8xbeJW76Xdk1QKcziNTM=
github.com/nuts-foundation/go-did v0.9.0 h1:JBz1cYaMxplKZ31QyWierrR3Yt2RIpaxZTt8KFm4Ph4=
github.com/nuts-foundation/go-did v0.9.0/go.mod h1:L39mh6SBsuenqeZw2JxARx4a/bwdARwchG2x3zPMTjc=
github.com/nuts-foundation/go-did v0.9.1-0.20231130131753-940e6a204184 h1:FpmffpdwFe927vT8eIlgWlXOkMCXXoQz1KpIToFX4xU=
github.com/nuts-foundation/go-did v0.9.1-0.20231130131753-940e6a204184/go.mod h1:lIoZWplLKE5wHejFVCs3pVdT1u659ZOnTRNvF1Q8lqE=
github.com/nuts-foundation/go-did v0.9.1-0.20231201081127-fe64a8cc4906 h1:5Y5NBQpBeEGzvciWlqtaWKT31la1LdCDqo0LskVQe1g=
github.com/nuts-foundation/go-did v0.9.1-0.20231201081127-fe64a8cc4906/go.mod h1:lIoZWplLKE5wHejFVCs3pVdT1u659ZOnTRNvF1Q8lqE=
github.com/nuts-foundation/go-did v0.10.0 h1:PWfmK0zQu8TwMChl4bW+sZdjSemtCNhcqVa2LQLq2+U=
github.com/nuts-foundation/go-did v0.10.0/go.mod h1:lIoZWplLKE5wHejFVCs3pVdT1u659ZOnTRNvF1Q8lqE=
github.com/nuts-foundation/go-leia/v4 v4.0.1 h1:+Sbk3Bew1QnRUqRXSOwomMw3nIZgncmTX425J7U5Q34=
github.com/nuts-foundation/go-leia/v4 v4.0.1/go.mod h1:eaZuWIolpU61TMvTMcen85+SOEOnHiALdg5SxqLXzz8=
github.com/nuts-foundation/go-stoabs v1.9.0 h1:zK+ugfolaJYyBvGwsRuavLVdycXk4Yw/1gI+tz17lWQ=
Expand Down
44 changes: 41 additions & 3 deletions vcr/signature/proof/proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ package proof
import (
"crypto"
"encoding/json"
"errors"
nutsCrypto "github.com/nuts-foundation/nuts-node/crypto"
"github.com/nuts-foundation/nuts-node/vcr/signature"
"reflect"
)

// Document represents the document to sign. It does not contain proofs or signatures
Expand Down Expand Up @@ -58,13 +60,37 @@ func (d SignedDocument) DocumentWithoutProof() Document {
return docWithoutProof
}

// UnmarshalProofValue unmarshalls the signature of the document in the provided target
// UnmarshalProofValue unmarshalls the signature of the document into the provided target.
// JSON-LD allows proof it be a compacted array (proof as JSON object instead of JSON array with objects).
// It handles this gracefully: if the target is a slice, it will unmarshal the proof as a slice.
// If the target is not a slice it will unmarshal the first element of the proof as the target,
// or return an error if the proof to be unmarshalled contains more than one element.
func (d SignedDocument) UnmarshalProofValue(target interface{}) error {
asJSON, err := json.Marshal(d["proof"])
src := d["proof"]
// if target is a slice, make sure the proof is unmarshalled as a slice
if isPtrSlice(target) {
// target is a slice
if _, ok := d["proof"].([]interface{}); !ok {
// unmarshal target is a slice, but proof is not. Make it a slice.
src = []interface{}{d["proof"]}
}
} else {
if srcAsSlice, ok := d["proof"].([]interface{}); ok {
if len(srcAsSlice) > 1 {
return errors.New("tried to unmarshal multiple JSON-LD proofs into a single value, which is impossible")
}
if len(srcAsSlice) == 0 {
return errors.New("no proof")
}
// just take first element
src = srcAsSlice[0]
}
}
asJSON, err := json.Marshal(src)
if err != nil {
return err
}
return json.Unmarshal(asJSON, target)
return json.Unmarshal(asJSON, &target)
}

// Proof is the interface that defines a set of methods which a proof should implement.
Expand All @@ -78,3 +104,15 @@ type ProofVerifier interface {
// Verify verifies the Document with the provided public key. If the document is valid, it returns no error.
Verify(document Document, suite signature.Suite, key crypto.PublicKey) error
}

func isPtrSlice(i interface{}) bool {
// Taken from https://stackoverflow.com/questions/69675420/how-to-check-if-interface-is-a-a-pointer-to-a-slice
if i == nil {
return false
}
v := reflect.ValueOf(i)
if v.Kind() != reflect.Ptr {
return false
}
return v.Elem().Kind() == reflect.Slice
}
89 changes: 78 additions & 11 deletions vcr/signature/proof/proof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,13 @@ import (
ssi "github.com/nuts-foundation/go-did"
"github.com/nuts-foundation/go-did/vc"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
)

func TestSignedDocument_UnmarshalProofValue(t *testing.T) {

t.Run("unmarshal signed doc into SignedDocument struct", func(t *testing.T) {

jsonldDocument := `{
t.Run("compacted proof array", func(t *testing.T) {
const jsonldDocument = `{
"@context": [
{"title": "https://schema.org#title"},
"https://w3id.org/security/suites/ed25519-2020/v1"
Expand All @@ -44,14 +43,82 @@ func TestSignedDocument_UnmarshalProofValue(t *testing.T) {
"proofValue": "z4oey5q2M3XKaxup3tmzN4DRFTLVqpLMweBrSxMY2xHX5XTYVQeVbY8nQAVHMrXFkXJpmEcqdoDwLWxaqA3Q1geV6"
}
}`
document := SignedDocument{}
err := json.Unmarshal([]byte(jsonldDocument), &document)
assert.NoError(t, err)

t.Run("single unmarshal target", func(t *testing.T) {
proof := vc.JSONWebSignature2020Proof{}
assert.NoError(t, document.UnmarshalProofValue(&proof))
assert.Equal(t, ssi.ProofType("Ed25519Signature2020"), proof.Type)
})
t.Run("slice unmarshal target", func(t *testing.T) {
var proof []vc.JSONWebSignature2020Proof
assert.NoError(t, document.UnmarshalProofValue(&proof))
require.Len(t, proof, 1)
assert.Equal(t, ssi.ProofType("Ed25519Signature2020"), proof[0].Type)
})
})
t.Run("uncompacted proof array", func(t *testing.T) {
const jsonldDocument = `{
"@context": [
{"title": "https://schema.org#title"},
"https://w3id.org/security/suites/ed25519-2020/v1"
],
"title": "Hello world!",
"proof": [{
"type": "Ed25519Signature2020",
"created": "2020-11-05T19:23:24Z",
"verificationMethod": "https://ldi.example/issuer#z6MkjLrk3gKS2nnkeWcmcxiZPGskmesDpuwRBorgHxUXfxnG",
"proofPurpose": "assertionMethod",
"proofValue": "z4oey5q2M3XKaxup3tmzN4DRFTLVqpLMweBrSxMY2xHX5XTYVQeVbY8nQAVHMrXFkXJpmEcqdoDwLWxaqA3Q1geV6"
}]
}`
document := SignedDocument{}
err := json.Unmarshal([]byte(jsonldDocument), &document)
assert.NoError(t, err)

signedDoc := SignedDocument{}
err := json.Unmarshal([]byte(jsonldDocument), &signedDoc)
t.Run("single unmarshal target", func(t *testing.T) {
proof := vc.JSONWebSignature2020Proof{}
assert.NoError(t, document.UnmarshalProofValue(&proof))
assert.Equal(t, ssi.ProofType("Ed25519Signature2020"), proof.Type)
})
t.Run("slice unmarshal target", func(t *testing.T) {
var proof []vc.JSONWebSignature2020Proof
assert.NoError(t, document.UnmarshalProofValue(&proof))
require.Len(t, proof, 1)
assert.Equal(t, ssi.ProofType("Ed25519Signature2020"), proof[0].Type)
})
})
t.Run("multiple proofs", func(t *testing.T) {
const jsonldDocument = `{
"@context": [
{"title": "https://schema.org#title"},
"https://w3id.org/security/suites/ed25519-2020/v1"
],
"title": "Hello world!",
"proof": [{
"type": "Ed25519Signature2020",
"created": "2020-11-05T19:23:24Z",
"verificationMethod": "https://ldi.example/issuer#z6MkjLrk3gKS2nnkeWcmcxiZPGskmesDpuwRBorgHxUXfxnG",
"proofPurpose": "assertionMethod",
"proofValue": "z4oey5q2M3XKaxup3tmzN4DRFTLVqpLMweBrSxMY2xHX5XTYVQeVbY8nQAVHMrXFkXJpmEcqdoDwLWxaqA3Q1geV6"
}, {}]
}`
document := SignedDocument{}
err := json.Unmarshal([]byte(jsonldDocument), &document)
assert.NoError(t, err)
proof := vc.JSONWebSignature2020Proof{}
assert.NoError(t, signedDoc.UnmarshalProofValue(&proof))
assert.Equal(t, ssi.ProofType("Ed25519Signature2020"), proof.Type)
assert.Equal(t, "Hello world!", signedDoc["title"])
t.Logf("%#v", signedDoc)

t.Run("single unmarshal target (error)", func(t *testing.T) {
proof := vc.JSONWebSignature2020Proof{}
err := document.UnmarshalProofValue(&proof)
assert.EqualError(t, err, "tried to unmarshal multiple JSON-LD proofs into a single value, which is impossible")
})
t.Run("slice unmarshal target", func(t *testing.T) {
var proof []vc.JSONWebSignature2020Proof
assert.NoError(t, document.UnmarshalProofValue(&proof))
require.Len(t, proof, 2)
assert.Equal(t, ssi.ProofType("Ed25519Signature2020"), proof[0].Type)
})
})
}
2 changes: 1 addition & 1 deletion vcr/verifier/verifier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ func Test_verifier_Validate(t *testing.T) {

err := instance.Validate(vc2, nil)

assert.ErrorContains(t, err, "unable to extract ldproof from signed document: json: cannot unmarshal array into Go value of type proof.LDProof")
assert.ErrorContains(t, err, "unable to extract ldproof from signed document: no proof")
})

t.Run("error - wrong jws in proof", func(t *testing.T) {
Expand Down

0 comments on commit 5a66734

Please sign in to comment.