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

Add support for publicKeyMultibase, deprecate publicKeyBase58 #86

Merged
merged 1 commit into from
Oct 9, 2023
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
47 changes: 39 additions & 8 deletions did/document.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/multiformats/go-multibase"

"github.com/nuts-foundation/go-did"

Expand Down Expand Up @@ -303,9 +304,11 @@ func (s Service) UnmarshalServiceEndpoint(target interface{}) error {

// VerificationMethod represents a DID Verification Method as specified by the DID Core specification (https://www.w3.org/TR/did-core/#verification-methods).
type VerificationMethod struct {
ID DID `json:"id"`
Type ssi.KeyType `json:"type,omitempty"`
Controller DID `json:"controller,omitempty"`
ID DID `json:"id"`
Type ssi.KeyType `json:"type,omitempty"`
Controller DID `json:"controller,omitempty"`
PublicKeyMultibase string `json:"publicKeyMultibase,omitempty"`
// PublicKeyBase58 is deprecated and should not be used anymore. Use PublicKeyMultibase or PublicKeyJwk instead.
PublicKeyBase58 string `json:"publicKeyBase58,omitempty"`
PublicKeyJwk map[string]interface{} `json:"publicKeyJwk,omitempty"`
}
Expand Down Expand Up @@ -343,8 +346,11 @@ func NewVerificationMethod(id DID, keyType ssi.KeyType, controller DID, key cryp
if !ok {
return nil, errors.New("wrong key type")
}
encodedKey := base58.Encode(ed25519Key, base58.BitcoinAlphabet)
vm.PublicKeyBase58 = encodedKey
encodedKey, err := multibase.Encode(multibase.Base58BTC, ed25519Key)
if err != nil {
return nil, err
}
vm.PublicKeyMultibase = encodedKey
}

return vm, nil
Expand All @@ -367,9 +373,20 @@ func (v VerificationMethod) PublicKey() (crypto.PublicKey, error) {
var pubKey crypto.PublicKey
switch v.Type {
case ssi.ED25519VerificationKey2018:
keyBytes, err := base58.Decode(v.PublicKeyBase58, base58.BitcoinAlphabet)
if err != nil {
return nil, err
var keyBytes []byte
var err error
if v.PublicKeyMultibase != "" {
_, keyBytes, err = multibase.Decode(v.PublicKeyMultibase)
if err != nil {
return nil, fmt.Errorf("publicKeyMultibase decode error: %w", err)
}
} else if v.PublicKeyBase58 != "" {
keyBytes, err = base58.Decode(v.PublicKeyBase58, base58.BitcoinAlphabet)
if err != nil {
return nil, fmt.Errorf("publicKeyBase58 decode error: %w", err)
}
} else {
return nil, errors.New("expected either publicKeyMultibase or publicKeyBase58 to be set")
}
return ed25519.PublicKey(keyBytes), err
case ssi.JsonWebKey2020:
Expand Down Expand Up @@ -429,6 +446,20 @@ func (v *VerificationMethod) UnmarshalJSON(bytes []byte) error {
if err != nil {
return err
}
// publicKeyJWK, publicKeyBase58 and publicKeyMultibase are all mutually exclusive
countPresent := 0
if len(tmp.PublicKeyJwk) > 0 {
countPresent++
}
if len(tmp.PublicKeyBase58) > 0 {
countPresent++
}
if len(tmp.PublicKeyMultibase) > 0 {
countPresent++
}
if countPresent > 1 {
return errors.New("only one of publicKeyJWK, publicKeyBase58 and publicKeyMultibase can be present")
}
*v = (VerificationMethod)(tmp)
return nil
}
Expand Down
42 changes: 42 additions & 0 deletions did/document_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,48 @@ func TestVerificationRelationship_UnmarshalJSON(t *testing.T) {
})
}

func TestNewVerificationMethod(t *testing.T) {
t.Run("Ed25519VerificationKey2018", func(t *testing.T) {
id, _ := ParseDID("did:example:123")
expectedKey, _, _ := ed25519.GenerateKey(rand.Reader)
vm, err := NewVerificationMethod(*id, ssi.ED25519VerificationKey2018, *id, expectedKey)
require.NoError(t, err)
assert.Equal(t, ssi.ED25519VerificationKey2018, vm.Type)
assert.NotEmpty(t, vm.PublicKeyMultibase)
assert.Empty(t, vm.PublicKeyBase58)
// Unmarshal, check it's equal to the input key
actualKey, err := vm.PublicKey()
require.NoError(t, err)
assert.Equal(t, expectedKey, actualKey)
})
}

func TestVerificationMethod_UnmarshalJSON(t *testing.T) {
t.Run("both publicKeyJWK and publicKeyMultibase present", func(t *testing.T) {
input, _ := json.Marshal(VerificationMethod{
ID: MustParseDIDURL("did:example:123#key-1"),
Controller: MustParseDIDURL("did:example:123"),
PublicKeyJwk: map[string]interface{}{"kty": "EC"},
PublicKeyMultibase: "foobar",
})
actual := VerificationMethod{}
err := json.Unmarshal(input, &actual)
assert.EqualError(t, err, "only one of publicKeyJWK, publicKeyBase58 and publicKeyMultibase can be present")
})
t.Run("all of publicKeyJWK, publicKeyMultibase and publicKeyBase58 are present", func(t *testing.T) {
input, _ := json.Marshal(VerificationMethod{
ID: MustParseDIDURL("did:example:123#key-1"),
Controller: MustParseDIDURL("did:example:123"),
PublicKeyJwk: map[string]interface{}{"kty": "EC"},
PublicKeyMultibase: "foobar",
PublicKeyBase58: "foobar",
})
actual := VerificationMethod{}
err := json.Unmarshal(input, &actual)
assert.EqualError(t, err, "only one of publicKeyJWK, publicKeyBase58 and publicKeyMultibase can be present")
})
}

func TestService_UnmarshalJSON(t *testing.T) {
t.Run("ok", func(t *testing.T) {
actual := Service{}
Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ require (
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/iter v1.0.2 // indirect
github.com/lestrrat-go/option v1.0.1 // indirect
github.com/mr-tron/base58 v1.1.0 // indirect
github.com/multiformats/go-base32 v0.0.3 // indirect
github.com/multiformats/go-base36 v0.1.0 // indirect
github.com/multiformats/go-multibase v0.2.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/crypto v0.9.0 // indirect
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ github.com/lestrrat-go/jwx v1.2.26/go.mod h1:MaiCdGbn3/cckbOFSCluJlJMmp9dmZm5hDu
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/mr-tron/base58 v1.1.0 h1:Y51FGVJ91WBqCEabAi5OPUz38eAx8DakuAm5svLcsfQ=
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI=
github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA=
github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4=
github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM=
github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=
github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk=
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 Down