From 875c07db66dc320051abdff0e2dbc89ea491e23a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Tue, 24 Jun 2025 18:10:36 +0200 Subject: [PATCH 1/2] add support for the Multikey verification method --- crypto/{internal => _helpers}/multibase.go | 0 crypto/{internal => _testsuite}/testsuite.go | 2 +- crypto/ed25519/key_test.go | 8 +- crypto/ed25519/public.go | 2 +- crypto/p256/key_test.go | 8 +- crypto/p256/public.go | 2 +- crypto/x25519/key_test.go | 8 +- crypto/x25519/public.go | 2 +- interfaces.go | 2 +- methods/did-key/key.go | 71 ++++------ utilities.go | 2 +- verifications/ed25519/VerificationKey2020.go | 8 +- .../ed25519/VerificationKey2020_test.go | 7 +- verifications/json.go | 3 + verifications/multikey/multikey.go | 126 ++++++++++++++++++ verifications/multikey/multikey_test.go | 28 ++++ verifications/x25519/KeyAgreementKey2020.go | 4 +- 17 files changed, 214 insertions(+), 69 deletions(-) rename crypto/{internal => _helpers}/multibase.go (100%) rename crypto/{internal => _testsuite}/testsuite.go (99%) create mode 100644 verifications/multikey/multikey.go create mode 100644 verifications/multikey/multikey_test.go diff --git a/crypto/internal/multibase.go b/crypto/_helpers/multibase.go similarity index 100% rename from crypto/internal/multibase.go rename to crypto/_helpers/multibase.go diff --git a/crypto/internal/testsuite.go b/crypto/_testsuite/testsuite.go similarity index 99% rename from crypto/internal/testsuite.go rename to crypto/_testsuite/testsuite.go index 6a217cc..a2ae9ae 100644 --- a/crypto/internal/testsuite.go +++ b/crypto/_testsuite/testsuite.go @@ -1,4 +1,4 @@ -package helpers +package testsuite import ( "fmt" diff --git a/crypto/ed25519/key_test.go b/crypto/ed25519/key_test.go index 88e1cb8..fe8e3a9 100644 --- a/crypto/ed25519/key_test.go +++ b/crypto/ed25519/key_test.go @@ -3,10 +3,10 @@ package ed25519 import ( "testing" - "github.com/INFURA/go-did/crypto/internal" + "github.com/INFURA/go-did/crypto/_testsuite" ) -var harness = helpers.TestHarness[PublicKey, PrivateKey]{ +var harness = testsuite.TestHarness[PublicKey, PrivateKey]{ Name: "ed25519", GenerateKeyPair: GenerateKeyPair, PublicKeyFromBytes: PublicKeyFromBytes, @@ -23,9 +23,9 @@ var harness = helpers.TestHarness[PublicKey, PrivateKey]{ } func TestSuite(t *testing.T) { - helpers.TestSuite(t, harness) + testsuite.TestSuite(t, harness) } func BenchmarkSuite(b *testing.B) { - helpers.BenchSuite(b, harness) + testsuite.BenchSuite(b, harness) } diff --git a/crypto/ed25519/public.go b/crypto/ed25519/public.go index e0f24bc..76e64ba 100644 --- a/crypto/ed25519/public.go +++ b/crypto/ed25519/public.go @@ -10,7 +10,7 @@ import ( "golang.org/x/crypto/cryptobyte" "github.com/INFURA/go-did/crypto" - "github.com/INFURA/go-did/crypto/internal" + "github.com/INFURA/go-did/crypto/_helpers" ) var _ crypto.SigningPublicKey = &PublicKey{} diff --git a/crypto/p256/key_test.go b/crypto/p256/key_test.go index 94b5d08..59029a0 100644 --- a/crypto/p256/key_test.go +++ b/crypto/p256/key_test.go @@ -3,10 +3,10 @@ package p256 import ( "testing" - "github.com/INFURA/go-did/crypto/internal" + "github.com/INFURA/go-did/crypto/_testsuite" ) -var harness = helpers.TestHarness[*PublicKey, *PrivateKey]{ +var harness = testsuite.TestHarness[*PublicKey, *PrivateKey]{ Name: "p256", GenerateKeyPair: GenerateKeyPair, PublicKeyFromBytes: PublicKeyFromBytes, @@ -23,9 +23,9 @@ var harness = helpers.TestHarness[*PublicKey, *PrivateKey]{ } func TestSuite(t *testing.T) { - helpers.TestSuite(t, harness) + testsuite.TestSuite(t, harness) } func BenchmarkSuite(b *testing.B) { - helpers.BenchSuite(b, harness) + testsuite.BenchSuite(b, harness) } diff --git a/crypto/p256/public.go b/crypto/p256/public.go index e794970..6dcb8d7 100644 --- a/crypto/p256/public.go +++ b/crypto/p256/public.go @@ -10,7 +10,7 @@ import ( "math/big" "github.com/INFURA/go-did/crypto" - helpers "github.com/INFURA/go-did/crypto/internal" + helpers "github.com/INFURA/go-did/crypto/_helpers" ) var _ crypto.SigningPublicKey = (*PublicKey)(nil) diff --git a/crypto/x25519/key_test.go b/crypto/x25519/key_test.go index c8c7f20..95ec9e8 100644 --- a/crypto/x25519/key_test.go +++ b/crypto/x25519/key_test.go @@ -5,11 +5,11 @@ import ( "github.com/stretchr/testify/require" + "github.com/INFURA/go-did/crypto/_testsuite" "github.com/INFURA/go-did/crypto/ed25519" - "github.com/INFURA/go-did/crypto/internal" ) -var harness = helpers.TestHarness[*PublicKey, *PrivateKey]{ +var harness = testsuite.TestHarness[*PublicKey, *PrivateKey]{ Name: "x25519", GenerateKeyPair: GenerateKeyPair, PublicKeyFromBytes: PublicKeyFromBytes, @@ -26,11 +26,11 @@ var harness = helpers.TestHarness[*PublicKey, *PrivateKey]{ } func TestSuite(t *testing.T) { - helpers.TestSuite(t, harness) + testsuite.TestSuite(t, harness) } func BenchmarkSuite(b *testing.B) { - helpers.BenchSuite(b, harness) + testsuite.BenchSuite(b, harness) } func TestEd25519ToX25519(t *testing.T) { diff --git a/crypto/x25519/public.go b/crypto/x25519/public.go index 62da6bc..18e02c3 100644 --- a/crypto/x25519/public.go +++ b/crypto/x25519/public.go @@ -8,8 +8,8 @@ import ( "math/big" "github.com/INFURA/go-did/crypto" + helpers "github.com/INFURA/go-did/crypto/_helpers" "github.com/INFURA/go-did/crypto/ed25519" - helpers "github.com/INFURA/go-did/crypto/internal" ) var _ crypto.PublicKey = (*PublicKey)(nil) diff --git a/interfaces.go b/interfaces.go index c3241b0..60eb450 100644 --- a/interfaces.go +++ b/interfaces.go @@ -102,7 +102,7 @@ type VerificationMethodSignature interface { VerificationMethod // Verify checks that 'sig' is a valid signature of 'data'. - Verify(data []byte, sig []byte) bool + Verify(data []byte, sig []byte) (bool, error) } // VerificationMethodKeyAgreement is a VerificationMethod implementing a shared key agreement. diff --git a/methods/did-key/key.go b/methods/did-key/key.go index c99a2d0..a15658f 100644 --- a/methods/did-key/key.go +++ b/methods/did-key/key.go @@ -4,15 +4,14 @@ import ( "fmt" "strings" - mbase "github.com/multiformats/go-multibase" - "github.com/multiformats/go-varint" - "github.com/INFURA/go-did" "github.com/INFURA/go-did/crypto" + "github.com/INFURA/go-did/crypto/_helpers" "github.com/INFURA/go-did/crypto/ed25519" "github.com/INFURA/go-did/crypto/p256" "github.com/INFURA/go-did/crypto/x25519" "github.com/INFURA/go-did/verifications/ed25519" + "github.com/INFURA/go-did/verifications/multikey" "github.com/INFURA/go-did/verifications/x25519" ) @@ -34,66 +33,54 @@ func Decode(identifier string) (did.DID, error) { const keyPrefix = "did:key:" if !strings.HasPrefix(identifier, keyPrefix) { - return nil, fmt.Errorf("must start with 'did:key'") + return nil, fmt.Errorf("%w: must start with 'did:key'", did.ErrInvalidDid) } msi := identifier[len(keyPrefix):] - baseCodec, bytes, err := mbase.Decode(msi) - if err != nil { - return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err) - } - // the specification enforces that encoding - if baseCodec != mbase.Base58BTC { - return nil, fmt.Errorf("%w: not Base58BTC encoded", did.ErrInvalidDid) - } - code, read, err := varint.FromUvarint(bytes) + code, bytes, err := helpers.PublicKeyMultibaseDecode(msi) if err != nil { return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err) } - switch code { - case ed25519.MultibaseCode: - pub, err := ed25519.PublicKeyFromBytes(bytes[read:]) - if err != nil { - return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err) - } - return FromPublicKey(pub) - case p256.MultibaseCode: - pub, err := p256.PublicKeyFromBytes(bytes[read:]) - if err != nil { - return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err) - } - return FromPublicKey(pub) - - // case Secp256k1: // TODO - // case RSA: // TODO + decoder, ok := map[uint64]func(b []byte) (crypto.PublicKey, error){ + ed25519.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return ed25519.PublicKeyFromBytes(b) }, + p256.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return p256.PublicKeyFromBytes(b) }, + x25519.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return x25519.PublicKeyFromBytes(b) }, + }[code] + if !ok { + return nil, fmt.Errorf("%w: unsupported did:key multicodec: 0x%x", did.ErrInvalidDid, code) } - return nil, fmt.Errorf("%w: unsupported did:key multicodec: 0x%x", did.ErrInvalidDid, code) + pub, err := decoder(bytes) + if err != nil { + return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err) + } + d, err := FromPublicKey(pub) + if err != nil { + return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err) + } + return d, nil } func FromPublicKey(pub crypto.PublicKey) (did.DID, error) { - var err error switch pub := pub.(type) { case ed25519.PublicKey: d := DidKey{msi: pub.ToPublicKeyMultibase()} - d.signature, err = ed25519vm.NewVerificationKey2020(fmt.Sprintf("did:key:%s#%s", d.msi, d.msi), pub, d) + d.signature = ed25519vm.NewVerificationKey2020(fmt.Sprintf("did:key:%s#%s", d.msi, d.msi), pub, d) + xpub, err := x25519.PublicKeyFromEd25519(pub) if err != nil { return nil, err } - xpub, err := x25519.PublicKeyFromEd25519(pub) - if err != nil { - return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err) - } xmsi := xpub.ToPublicKeyMultibase() - d.keyAgreement, err = x25519vm.NewKeyAgreementKey2020(fmt.Sprintf("did:key:%s#%s", d.msi, xmsi), xpub, d) - if err != nil { - return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err) - } + d.keyAgreement = x25519vm.NewKeyAgreementKey2020(fmt.Sprintf("did:key:%s#%s", d.msi, xmsi), xpub, d) + return d, nil + case *p256.PublicKey: + d := DidKey{msi: pub.ToPublicKeyMultibase()} + mk := multikey.NewMultiKey(fmt.Sprintf("did:key:%s#%s", d.msi, d.msi), pub, d) + d.signature = mk + d.keyAgreement = mk return d, nil - // case *p256.PublicKey: - // d := DidKey{msi: pub.ToPublicKeyMultibase()} default: return nil, fmt.Errorf("unsupported public key: %T", pub) diff --git a/utilities.go b/utilities.go index 1591c35..0835e24 100644 --- a/utilities.go +++ b/utilities.go @@ -11,7 +11,7 @@ import ( // If no method verifies the signature, it returns false and nil. func TryAllVerify(methods []VerificationMethodSignature, data []byte, sig []byte) (bool, VerificationMethodSignature) { for _, method := range methods { - if method.Verify(data, sig) { + if valid, err := method.Verify(data, sig); err == nil && valid { return true, method } } diff --git a/verifications/ed25519/VerificationKey2020.go b/verifications/ed25519/VerificationKey2020.go index 0a1732e..7526102 100644 --- a/verifications/ed25519/VerificationKey2020.go +++ b/verifications/ed25519/VerificationKey2020.go @@ -24,12 +24,12 @@ type VerificationKey2020 struct { controller string } -func NewVerificationKey2020(id string, pubkey ed25519.PublicKey, controller did.DID) (*VerificationKey2020, error) { +func NewVerificationKey2020(id string, pubkey ed25519.PublicKey, controller did.DID) *VerificationKey2020 { return &VerificationKey2020{ id: id, pubkey: pubkey, controller: controller.String(), - }, nil + } } func (v VerificationKey2020) MarshalJSON() ([]byte, error) { @@ -91,6 +91,6 @@ func (v VerificationKey2020) JsonLdContext() string { return JsonLdContext } -func (v VerificationKey2020) Verify(data []byte, sig []byte) bool { - return v.pubkey.VerifyBytes(data, sig) +func (v VerificationKey2020) Verify(data []byte, sig []byte) (bool, error) { + return v.pubkey.VerifyBytes(data, sig), nil } diff --git a/verifications/ed25519/VerificationKey2020_test.go b/verifications/ed25519/VerificationKey2020_test.go index 2325852..af0c2dd 100644 --- a/verifications/ed25519/VerificationKey2020_test.go +++ b/verifications/ed25519/VerificationKey2020_test.go @@ -40,8 +40,7 @@ func TestSignature(t *testing.T) { contDid := "did:key:" + pk.ToPublicKeyMultibase() controller := did.MustParse(contDid) - vk, err := ed25519vm.NewVerificationKey2020("foo", pk, controller) - require.NoError(t, err) + vk := ed25519vm.NewVerificationKey2020("foo", pk, controller) for _, tc := range []struct { name string @@ -78,7 +77,9 @@ func TestSignature(t *testing.T) { }, } { t.Run(tc.name, func(t *testing.T) { - require.Equal(t, tc.valid, vk.Verify(tc.data, tc.signature)) + valid, err := vk.Verify(tc.data, tc.signature) + require.NoError(t, err) + require.Equal(t, tc.valid, valid) }) } } diff --git a/verifications/json.go b/verifications/json.go index ec40b8b..361fbf5 100644 --- a/verifications/json.go +++ b/verifications/json.go @@ -6,6 +6,7 @@ import ( "github.com/INFURA/go-did" "github.com/INFURA/go-did/verifications/ed25519" + "github.com/INFURA/go-did/verifications/multikey" "github.com/INFURA/go-did/verifications/x25519" ) @@ -21,6 +22,8 @@ func UnmarshalJSON(data []byte) (did.VerificationMethod, error) { switch aux.Type { case ed25519vm.Type: res = &ed25519vm.VerificationKey2020{} + case multikey.Type: + res = &multikey.MultiKey{} case x25519vm.Type: res = &x25519vm.KeyAgreementKey2020{} default: diff --git a/verifications/multikey/multikey.go b/verifications/multikey/multikey.go new file mode 100644 index 0000000..082b518 --- /dev/null +++ b/verifications/multikey/multikey.go @@ -0,0 +1,126 @@ +package multikey + +import ( + "encoding/json" + "errors" + "fmt" + + "github.com/INFURA/go-did" + "github.com/INFURA/go-did/crypto" + helpers "github.com/INFURA/go-did/crypto/_helpers" + "github.com/INFURA/go-did/crypto/ed25519" + "github.com/INFURA/go-did/crypto/p256" + "github.com/INFURA/go-did/crypto/x25519" +) + +// Specification: https://www.w3.org/TR/cid-1.0/#Multikey + +const ( + JsonLdContext = "https://www.w3.org/ns/cid/v1" + Type = "Multikey" +) + +var _ did.VerificationMethodSignature = &MultiKey{} +var _ did.VerificationMethodKeyAgreement = &MultiKey{} + +type MultiKey struct { + id string + pubkey crypto.PublicKey + controller string +} + +func NewMultiKey(id string, pubkey crypto.PublicKey, controller did.DID) *MultiKey { + return &MultiKey{ + id: id, + pubkey: pubkey, + controller: controller.String(), + } +} + +func (m MultiKey) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + ID string `json:"id"` + Type string `json:"type"` + Controller string `json:"controller"` + PublicKeyMultibase string `json:"publicKeyMultibase"` + }{ + ID: m.ID(), + Type: m.Type(), + Controller: m.Controller(), + PublicKeyMultibase: m.pubkey.ToPublicKeyMultibase(), + }) +} + +func (m *MultiKey) UnmarshalJSON(bytes []byte) error { + aux := struct { + ID string `json:"id"` + Type string `json:"type"` + Controller string `json:"controller"` + PublicKeyMultibase string `json:"publicKeyMultibase"` + }{} + err := json.Unmarshal(bytes, &aux) + if err != nil { + return err + } + if aux.Type != m.Type() { + return errors.New("invalid type") + } + m.id = aux.ID + if len(m.id) == 0 { + return errors.New("invalid id") + } + + code, pubBytes, err := helpers.PublicKeyMultibaseDecode(aux.PublicKeyMultibase) + if err != nil { + return fmt.Errorf("invalid publicKeyMultibase: %w", err) + } + decoder, ok := map[uint64]func(b []byte) (crypto.PublicKey, error){ + ed25519.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return ed25519.PublicKeyFromBytes(b) }, + p256.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return p256.PublicKeyFromBytes(b) }, + x25519.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return x25519.PublicKeyFromBytes(b) }, + }[code] + if !ok { + return fmt.Errorf("unsupported publicKeyMultibase code: %d", code) + } + m.pubkey, err = decoder(pubBytes) + if err != nil { + return fmt.Errorf("invalid publicKeyMultibase: %w", err) + } + + m.controller = aux.Controller + if !did.HasValidDIDSyntax(m.controller) { + return errors.New("invalid controller") + } + return nil +} + +func (m MultiKey) ID() string { + return m.id +} + +func (m MultiKey) Type() string { + return Type +} + +func (m MultiKey) Controller() string { + return m.controller +} + +func (m MultiKey) JsonLdContext() string { + return JsonLdContext +} + +func (m MultiKey) Verify(data []byte, sig []byte) (bool, error) { + if pub, ok := m.pubkey.(crypto.SigningPublicKey); ok { + return pub.VerifyBytes(data, sig), nil + } + return false, errors.New("not a signing public key") +} + +func (m MultiKey) PrivateKeyIsCompatible(local crypto.KeyExchangePrivateKey) bool { + return local.PublicKeyIsCompatible(m.pubkey) +} + +func (m MultiKey) KeyExchange(local crypto.KeyExchangePrivateKey) ([]byte, error) { + return local.KeyExchange(m.pubkey) +} diff --git a/verifications/multikey/multikey_test.go b/verifications/multikey/multikey_test.go new file mode 100644 index 0000000..84383a3 --- /dev/null +++ b/verifications/multikey/multikey_test.go @@ -0,0 +1,28 @@ +package multikey_test + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" + + _ "github.com/INFURA/go-did/methods/did-key" + "github.com/INFURA/go-did/verifications/multikey" +) + +func TestJsonRoundTrip(t *testing.T) { + data := `{ + "id": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK", + "type": "Multikey", + "controller": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK", + "publicKeyMultibase": "z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK" + }` + + var mk multikey.MultiKey + err := json.Unmarshal([]byte(data), &mk) + require.NoError(t, err) + + bytes, err := json.Marshal(mk) + require.NoError(t, err) + require.JSONEq(t, data, string(bytes)) +} diff --git a/verifications/x25519/KeyAgreementKey2020.go b/verifications/x25519/KeyAgreementKey2020.go index efab589..fc8307b 100644 --- a/verifications/x25519/KeyAgreementKey2020.go +++ b/verifications/x25519/KeyAgreementKey2020.go @@ -25,12 +25,12 @@ type KeyAgreementKey2020 struct { controller string } -func NewKeyAgreementKey2020(id string, pubkey *x25519.PublicKey, controller did.DID) (*KeyAgreementKey2020, error) { +func NewKeyAgreementKey2020(id string, pubkey *x25519.PublicKey, controller did.DID) *KeyAgreementKey2020 { return &KeyAgreementKey2020{ id: id, pubkey: pubkey, controller: controller.String(), - }, nil + } } func (k KeyAgreementKey2020) MarshalJSON() ([]byte, error) { From 0026cba0ac7869121cc9995216c4a62ec32203dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Tue, 24 Jun 2025 18:49:46 +0200 Subject: [PATCH 2/2] avoid allocations for a static map --- methods/did-key/key.go | 13 +++++++------ verifications/multikey/multikey.go | 12 +++++++----- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/methods/did-key/key.go b/methods/did-key/key.go index a15658f..1091560 100644 --- a/methods/did-key/key.go +++ b/methods/did-key/key.go @@ -43,15 +43,10 @@ func Decode(identifier string) (did.DID, error) { return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err) } - decoder, ok := map[uint64]func(b []byte) (crypto.PublicKey, error){ - ed25519.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return ed25519.PublicKeyFromBytes(b) }, - p256.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return p256.PublicKeyFromBytes(b) }, - x25519.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return x25519.PublicKeyFromBytes(b) }, - }[code] + decoder, ok := decoders[code] if !ok { return nil, fmt.Errorf("%w: unsupported did:key multicodec: 0x%x", did.ErrInvalidDid, code) } - pub, err := decoder(bytes) if err != nil { return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err) @@ -63,6 +58,12 @@ func Decode(identifier string) (did.DID, error) { return d, nil } +var decoders = map[uint64]func(b []byte) (crypto.PublicKey, error){ + ed25519.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return ed25519.PublicKeyFromBytes(b) }, + p256.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return p256.PublicKeyFromBytes(b) }, + x25519.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return x25519.PublicKeyFromBytes(b) }, +} + func FromPublicKey(pub crypto.PublicKey) (did.DID, error) { switch pub := pub.(type) { case ed25519.PublicKey: diff --git a/verifications/multikey/multikey.go b/verifications/multikey/multikey.go index 082b518..513b723 100644 --- a/verifications/multikey/multikey.go +++ b/verifications/multikey/multikey.go @@ -74,11 +74,7 @@ func (m *MultiKey) UnmarshalJSON(bytes []byte) error { if err != nil { return fmt.Errorf("invalid publicKeyMultibase: %w", err) } - decoder, ok := map[uint64]func(b []byte) (crypto.PublicKey, error){ - ed25519.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return ed25519.PublicKeyFromBytes(b) }, - p256.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return p256.PublicKeyFromBytes(b) }, - x25519.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return x25519.PublicKeyFromBytes(b) }, - }[code] + decoder, ok := decoders[code] if !ok { return fmt.Errorf("unsupported publicKeyMultibase code: %d", code) } @@ -94,6 +90,12 @@ func (m *MultiKey) UnmarshalJSON(bytes []byte) error { return nil } +var decoders = map[uint64]func(b []byte) (crypto.PublicKey, error){ + ed25519.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return ed25519.PublicKeyFromBytes(b) }, + p256.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return p256.PublicKeyFromBytes(b) }, + x25519.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return x25519.PublicKeyFromBytes(b) }, +} + func (m MultiKey) ID() string { return m.id }