diff --git a/crypto/_allkeys/allkeys.go b/crypto/_allkeys/allkeys.go deleted file mode 100644 index d7c45bc..0000000 --- a/crypto/_allkeys/allkeys.go +++ /dev/null @@ -1,38 +0,0 @@ -package allkeys - -import ( - "fmt" - - "github.com/MetaMask/go-did-it/crypto" - "github.com/MetaMask/go-did-it/crypto/ed25519" - helpers "github.com/MetaMask/go-did-it/crypto/internal" - "github.com/MetaMask/go-did-it/crypto/p256" - "github.com/MetaMask/go-did-it/crypto/p384" - "github.com/MetaMask/go-did-it/crypto/p521" - "github.com/MetaMask/go-did-it/crypto/rsa" - "github.com/MetaMask/go-did-it/crypto/secp256k1" - "github.com/MetaMask/go-did-it/crypto/x25519" -) - -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) }, - p384.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return p384.PublicKeyFromBytes(b) }, - p521.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return p521.PublicKeyFromBytes(b) }, - rsa.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return rsa.PublicKeyFromPKCS1DER(b) }, - secp256k1.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return secp256k1.PublicKeyFromBytes(b) }, - x25519.MultibaseCode: func(b []byte) (crypto.PublicKey, error) { return x25519.PublicKeyFromBytes(b) }, -} - -// PublicKeyFromPublicKeyMultibase decodes the public key from its PublicKeyMultibase form -func PublicKeyFromPublicKeyMultibase(multibase string) (crypto.PublicKey, error) { - code, pubBytes, err := helpers.PublicKeyMultibaseDecode(multibase) - if err != nil { - return nil, fmt.Errorf("invalid publicKeyMultibase: %w", err) - } - decoder, ok := decoders[code] - if !ok { - return nil, fmt.Errorf("unsupported publicKeyMultibase code: %d", code) - } - return decoder(pubBytes) -} diff --git a/crypto/all/all.go b/crypto/all/all.go new file mode 100644 index 0000000..5b38ad9 --- /dev/null +++ b/crypto/all/all.go @@ -0,0 +1,34 @@ +// Package all registers every key algorithm supported by this module into the crypto.DefaultKeySet KeySet. +// +// Import it for its side effect when you want the global default to accept everything, typically in +// tests or kitchen-sink tools: +// +// import _ "github.com/MetaMask/go-did-it/crypto/all" +// +// It pulls in every algorithm package, so only this package pays that binary-size cost. For finer +// control, register a subset yourself with crypto.Register(ed25519.KeyType(), ...) or build an explicit +// crypto.KeySet. +package all + +import ( + "github.com/MetaMask/go-did-it/crypto" + "github.com/MetaMask/go-did-it/crypto/ed25519" + "github.com/MetaMask/go-did-it/crypto/p256" + "github.com/MetaMask/go-did-it/crypto/p384" + "github.com/MetaMask/go-did-it/crypto/p521" + "github.com/MetaMask/go-did-it/crypto/rsa" + "github.com/MetaMask/go-did-it/crypto/secp256k1" + "github.com/MetaMask/go-did-it/crypto/x25519" +) + +func init() { + crypto.Register( + ed25519.KeyType(), + p256.KeyType(), + p384.KeyType(), + p521.KeyType(), + secp256k1.KeyType(), + x25519.KeyType(), + rsa.KeyType(), // any size within RSA's supported range + ) +} diff --git a/crypto/ed25519/key.go b/crypto/ed25519/key.go index fa2bd7a..4c5e474 100644 --- a/crypto/ed25519/key.go +++ b/crypto/ed25519/key.go @@ -3,6 +3,8 @@ package ed25519 import ( "crypto/ed25519" "crypto/rand" + + "github.com/MetaMask/go-did-it/crypto" ) const ( @@ -28,3 +30,14 @@ const ( pemPubBlockType = "PUBLIC KEY" pemPrivBlockType = "PRIVATE KEY" ) + +// KeyType returns the crypto.KeyType describing Ed25519, to be added to a crypto.KeySet. +func KeyType() crypto.KeyType { + return crypto.KeyType{ + Name: "Ed25519", + Code: MultibaseCode, + DecodePublic: func(b []byte) (crypto.PublicKey, error) { return crypto.ToPub(PublicKeyFromBytes(b)) }, + Matches: func(key crypto.PublicKey) bool { _, ok := key.(PublicKey); return ok }, + Wrap: func(key any) (crypto.PublicKey, bool, error) { return crypto.ToWrap(WrapPublicKey(key)) }, + } +} diff --git a/crypto/ed25519/public.go b/crypto/ed25519/public.go index 6b19da6..4853294 100644 --- a/crypto/ed25519/public.go +++ b/crypto/ed25519/public.go @@ -34,6 +34,18 @@ func PublicKeyFromBytes(b []byte) (PublicKey, error) { return PublicKey{k: append([]byte{}, b...)}, nil } +// WrapPublicKey converts an already-parsed public key object — for Ed25519, a standard library +// ed25519.PublicKey — into a PublicKey. It is the inverse of Unwrap. The boolean reports whether +// the given key belongs to this algorithm at all. +func WrapPublicKey(key any) (PublicKey, bool, error) { + k, ok := key.(ed25519.PublicKey) + if !ok { + return PublicKey{}, false, nil + } + pub, err := PublicKeyFromBytes(k) + return pub, true, err +} + // PublicKeyFromPublicKeyMultibase decodes the public key from its PublicKeyMultibase form func PublicKeyFromPublicKeyMultibase(multibase string) (PublicKey, error) { code, bytes, err := helpers.PublicKeyMultibaseDecode(multibase) diff --git a/crypto/keyset.go b/crypto/keyset.go new file mode 100644 index 0000000..4b7907d --- /dev/null +++ b/crypto/keyset.go @@ -0,0 +1,206 @@ +package crypto + +import ( + "cmp" + "errors" + "fmt" + "slices" + "sync" + + helpers "github.com/MetaMask/go-did-it/crypto/internal" +) + +// ErrKeyNotAccepted reports a policy rejection: the key is well-formed, but its algorithm (or its +// parameters, like the RSA modulus size) is not accepted by the KeySet. +var ErrKeyNotAccepted = errors.New("key type not accepted by the key set") + +// ErrInvalidKey reports malformed key data that failed to decode. +var ErrInvalidKey = errors.New("invalid key data") + +// KeyType describes how to decode keys of a single algorithm (and, for RSA, which key sizes to accept). +// +// It is the unit a KeySet is built from. Each crypto/ package provides one through its +// KeyType() constructor (e.g. ed25519.KeyType(), rsa.KeyType(2048)), which is the only place that knows +// the algorithm's encoding details. Because the crypto package itself doesn't import any algorithm +// package, your binary only links the algorithms you actually name. +// +// The decode functions return a nil PublicKey/PrivateKey together with an error on failure; a nil +// function means that form is not supported for this algorithm (for example RSA has no raw private +// bytes form). +type KeyType struct { + // Name is a human-readable identifier, used in error messages (e.g. "Ed25519", "RSA-2048"). + Name string + // Code is the algorithm's multicodec code (the prefix in a publicKeyMultibase form). It is unique + // within a KeySet; registering a KeyType whose code is already present replaces the previous one. + Code uint64 + + // DecodePublic decodes a public key from the body of its publicKeyMultibase form (the bytes + // after the multicodec prefix). For most algorithms that body is the raw key material; for RSA + // it is the PKCS#1 (RSAPublicKey) DER. It is also used by PublicKeyFromBytes. + DecodePublic func(body []byte) (PublicKey, error) + + // Matches reports whether an already-decoded key belongs to this KeyType. + // It is the inverse of DecodePublic: a type assertion plus any additional constraints + // (e.g. RSA key size). Nil means a code match alone is sufficient. + Matches func(key PublicKey) bool + + // Wrap converts an already-parsed public key object into this KeyType's PublicKey. It is the + // inverse of the concrete types' Unwrap: it accepts a standard library key (ed25519.PublicKey, + // *ecdsa.PublicKey, *rsa.PublicKey, *ecdh.PublicKey) or, for algorithms without a standard + // library type, the underlying library's type (e.g. dcrd's *secp256k1.PublicKey). The boolean + // reports whether the key belongs to this KeyType at all; an error means the key belongs here + // but is rejected (for example by the RSA size policy). Nil means wrapping is not supported + // for this algorithm. + Wrap func(key any) (PublicKey, bool, error) +} + +// KeySet is a configured-once set of the key algorithms (and sizes) that decoding is allowed to +// accept. Build one with NewKeySet and pass it where you need explicit, isolated control; or use +// the package-level DefaultKeySet singleton (and the matching package-level functions) for the global, +// blank-import style. +// +// A KeySet is safe for concurrent use. +type KeySet struct { + mu sync.RWMutex + byCode map[uint64]KeyType +} + +// NewKeySet builds a KeySet accepting exactly the given key types. +func NewKeySet(keyTypes ...KeyType) *KeySet { + ks := &KeySet{byCode: make(map[uint64]KeyType)} + ks.Register(keyTypes...) + return ks +} + +// Register adds key types to the KeySet, replacing any already registered under the same code. It is +// safe to call concurrently; this is how the package-level DefaultKeySet is populated (directly, via +// Register, or via a blank import of a registering package such as crypto/all). +func (ks *KeySet) Register(keyTypes ...KeyType) { + ks.mu.Lock() + defer ks.mu.Unlock() + for _, kt := range keyTypes { + ks.byCode[kt.Code] = kt + } +} + +// PublicKeyFromMultibase decodes a public key from its publicKeyMultibase form, accepting it only if +// its algorithm is in the KeySet. +func (ks *KeySet) PublicKeyFromMultibase(multibase string) (PublicKey, error) { + code, body, err := helpers.PublicKeyMultibaseDecode(multibase) + if err != nil { + return nil, fmt.Errorf("%w: invalid publicKeyMultibase: %w", ErrInvalidKey, err) + } + return ks.PublicKeyFromBytes(code, body) +} + +// PublicKeyFromBytes decodes a public key from the body bytes of the given multicodec code, accepting +// it only if its algorithm is in the KeySet. For RSA the body is the PKCS#1 (RSAPublicKey) DER. +func (ks *KeySet) PublicKeyFromBytes(code uint64, body []byte) (PublicKey, error) { + ks.mu.RLock() + kt, ok := ks.byCode[code] + ks.mu.RUnlock() + + if !ok { + return nil, fmt.Errorf("%w: multicodec code %#x not in key set", ErrKeyNotAccepted, code) + } + if kt.DecodePublic == nil { + return nil, fmt.Errorf("%w: public key decoding not supported for %s", ErrKeyNotAccepted, kt.Name) + } + pub, err := kt.DecodePublic(body) + if err != nil { + if errors.Is(err, ErrKeyNotAccepted) { + return nil, err + } + return nil, fmt.Errorf("%w: %w", ErrInvalidKey, err) + } + return pub, nil +} + +// WrapPublicKey converts an already-parsed public key object (see KeyType.Wrap) into the +// corresponding PublicKey, accepting it only if its algorithm is in the KeySet. +func (ks *KeySet) WrapPublicKey(key any) (PublicKey, error) { + ks.mu.RLock() + defer ks.mu.RUnlock() + for _, kt := range ks.byCode { + if kt.Wrap == nil { + continue + } + pub, ok, err := kt.Wrap(key) + if !ok { + continue + } + if err != nil { + return nil, err + } + return pub, nil + } + return nil, fmt.Errorf("%w: no key type in the key set matches %T", ErrKeyNotAccepted, key) +} + +// KeyTypes returns the key types registered in the KeySet, sorted by multicodec code. +func (ks *KeySet) KeyTypes() []KeyType { + ks.mu.RLock() + defer ks.mu.RUnlock() + res := make([]KeyType, 0, len(ks.byCode)) + for _, kt := range ks.byCode { + res = append(res, kt) + } + slices.SortFunc(res, func(a, b KeyType) int { return cmp.Compare(a.Code, b.Code) }) + return res +} + +// Accepts reports whether key's type (and, for constrained types like RSA, its parameters) +// are accepted by this KeySet. It uses the Matches predicate from the registered KeyType. +func (ks *KeySet) Accepts(key PublicKey) bool { + ks.mu.RLock() + defer ks.mu.RUnlock() + for _, kt := range ks.byCode { + if kt.Matches != nil && kt.Matches(key) { + return true + } + } + return false +} + +// DefaultKeySet is the package-level KeySet used by the package-level decoding functions and, by default, +// by the verifier packages (did:key, the Multikey verification method, ...). It starts empty: +// register the algorithms you want with Register, or pull them all in for tests/tools with a blank +// import of crypto/all. +var DefaultKeySet = NewKeySet() + +// Register adds key types to the DefaultKeySet KeySet. +func Register(keyTypes ...KeyType) { DefaultKeySet.Register(keyTypes...) } + +// PublicKeyFromMultibase decodes a public key from its publicKeyMultibase form using the DefaultKeySet KeySet. +func PublicKeyFromMultibase(multibase string) (PublicKey, error) { + return DefaultKeySet.PublicKeyFromMultibase(multibase) +} + +// PublicKeyFromBytes decodes a public key from its body bytes using the DefaultKeySet KeySet. +func PublicKeyFromBytes(code uint64, body []byte) (PublicKey, error) { + return DefaultKeySet.PublicKeyFromBytes(code, body) +} + +// WrapPublicKey converts an already-parsed public key object using the DefaultKeySet KeySet. +func WrapPublicKey(key any) (PublicKey, error) { + return DefaultKeySet.WrapPublicKey(key) +} + +// KeyTypes returns the key types registered in the DefaultKeySet KeySet, sorted by multicodec code. +func KeyTypes() []KeyType { + return DefaultKeySet.KeyTypes() +} + +// ToPub converts the result of a concrete public-key constructor (one returning a specific key type, +// as the crypto/ packages do) to the PublicKey interface. It is a convenience for writing the +// decode functions of a KeyType: +// +// DecodePublic: func(b []byte) (crypto.PublicKey, error) { return crypto.ToPub(PublicKeyFromBytes(b)) }, +func ToPub[T PublicKey](k T, err error) (PublicKey, error) { return k, err } + +// ToWrap converts the result of a concrete WrapPublicKey constructor (one returning a specific key +// type, as the crypto/ packages do) to the PublicKey interface. It is a convenience for +// writing the Wrap function of a KeyType: +// +// Wrap: func(key any) (crypto.PublicKey, bool, error) { return crypto.ToWrap(WrapPublicKey(key)) }, +func ToWrap[T PublicKey](k T, ok bool, err error) (PublicKey, bool, error) { return k, ok, err } diff --git a/crypto/keyset_test.go b/crypto/keyset_test.go new file mode 100644 index 0000000..bbf02d5 --- /dev/null +++ b/crypto/keyset_test.go @@ -0,0 +1,157 @@ +package crypto_test + +import ( + stdecdsa "crypto/ecdsa" + stded25519 "crypto/ed25519" + "crypto/elliptic" + "crypto/rand" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/MetaMask/go-did-it/crypto" + "github.com/MetaMask/go-did-it/crypto/ed25519" + "github.com/MetaMask/go-did-it/crypto/p256" + "github.com/MetaMask/go-did-it/crypto/p384" + "github.com/MetaMask/go-did-it/crypto/rsa" +) + +func TestKeySet_RestrictsByType(t *testing.T) { + edPub, _, err := ed25519.GenerateKeyPair() + require.NoError(t, err) + p256Pub, _, err := p256.GenerateKeyPair() + require.NoError(t, err) + + // A KeySet that only allows Ed25519. + ks := crypto.NewKeySet(ed25519.KeyType()) + + got, err := ks.PublicKeyFromMultibase(edPub.ToPublicKeyMultibase()) + require.NoError(t, err) + require.True(t, got.Equal(edPub)) + + // P-256 is a valid key, but not in this KeySet: it must be rejected. + _, err = ks.PublicKeyFromMultibase(p256Pub.ToPublicKeyMultibase()) + require.Error(t, err) + + // Widen the policy and it now decodes. + ks.Register(p256.KeyType()) + got, err = ks.PublicKeyFromMultibase(p256Pub.ToPublicKeyMultibase()) + require.NoError(t, err) + require.True(t, got.Equal(p256Pub)) +} + +func TestKeySet_RSASizePolicy(t *testing.T) { + pub, _, err := rsa.GenerateKeyPair(2048) + require.NoError(t, err) + mb := pub.ToPublicKeyMultibase() + + // Sizes 3072 and 4096 allowed: a 2048 key is rejected purely on size. + ks := crypto.NewKeySet(rsa.KeyType(3072, 4096)) + _, err = ks.PublicKeyFromMultibase(mb) + require.ErrorIs(t, err, crypto.ErrKeyNotAccepted) + + // Replace the RSA policy with one that includes 2048 and it decodes. + ks.Register(rsa.KeyType(2048, 3072, 4096)) + got, err := ks.PublicKeyFromMultibase(mb) + require.NoError(t, err) + require.True(t, got.Equal(pub)) +} + +func TestKeySet_TypedErrors(t *testing.T) { + edPub, _, err := ed25519.GenerateKeyPair() + require.NoError(t, err) + p256Pub, _, err := p256.GenerateKeyPair() + require.NoError(t, err) + + // A KeySet that only allows Ed25519. + ks := crypto.NewKeySet(ed25519.KeyType()) + + tests := []struct { + name string + multibase string + wantErr error + notErr error + }{ + { + name: "valid key of an algorithm outside the policy", + multibase: p256Pub.ToPublicKeyMultibase(), + wantErr: crypto.ErrKeyNotAccepted, + notErr: crypto.ErrInvalidKey, + }, + { + name: "garbage multibase", + multibase: "not-multibase", + wantErr: crypto.ErrInvalidKey, + notErr: crypto.ErrKeyNotAccepted, + }, + { + name: "right algorithm, corrupted body", + multibase: edPub.ToPublicKeyMultibase()[:10], + wantErr: crypto.ErrInvalidKey, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + _, err := ks.PublicKeyFromMultibase(tc.multibase) + require.ErrorIs(t, err, tc.wantErr) + if tc.notErr != nil { + require.NotErrorIs(t, err, tc.notErr) + } + }) + } +} + +func TestKeySet_WrapPublicKey(t *testing.T) { + ks := crypto.NewKeySet(ed25519.KeyType(), p256.KeyType(), p384.KeyType(), rsa.KeyType(3072, 4096)) + + stdEdPub, _, err := stded25519.GenerateKey(rand.Reader) + require.NoError(t, err) + p256Priv, err := stdecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + p384Priv, err := stdecdsa.GenerateKey(elliptic.P384(), rand.Reader) + require.NoError(t, err) + p521Priv, err := stdecdsa.GenerateKey(elliptic.P521(), rand.Reader) + require.NoError(t, err) + rsaPub, _, err := rsa.GenerateKeyPair(2048) + require.NoError(t, err) + + tests := []struct { + name string + key any + wantType crypto.PublicKey + wantErr error + }{ + {name: "std ed25519", key: stdEdPub, wantType: ed25519.PublicKey{}}, + {name: "std ecdsa P-256", key: &p256Priv.PublicKey, wantType: &p256.PublicKey{}}, + {name: "std ecdsa P-384", key: &p384Priv.PublicKey, wantType: &p384.PublicKey{}}, + {name: "valid curve not in the key set", key: &p521Priv.PublicKey, wantErr: crypto.ErrKeyNotAccepted}, + {name: "RSA outside the key set size policy", key: rsaPub.Unwrap(), wantErr: crypto.ErrKeyNotAccepted}, + {name: "not a key at all", key: "not a key", wantErr: crypto.ErrKeyNotAccepted}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got, err := ks.WrapPublicKey(tc.key) + if tc.wantErr != nil { + require.ErrorIs(t, err, tc.wantErr) + return + } + require.NoError(t, err) + require.IsType(t, tc.wantType, got) + + // the wrapped key round-trips through its multibase form + back, err := ks.PublicKeyFromMultibase(got.ToPublicKeyMultibase()) + require.NoError(t, err) + require.True(t, back.Equal(got)) + }) + } +} + +func TestKeySet_KeyTypes(t *testing.T) { + ks := crypto.NewKeySet(p256.KeyType(), ed25519.KeyType()) + + kts := ks.KeyTypes() + require.Len(t, kts, 2) + // sorted by multicodec code: ed25519 (0xed) < p256 (0x1200) + require.Equal(t, "Ed25519", kts[0].Name) + require.Equal(t, "P-256", kts[1].Name) +} diff --git a/crypto/p256/key.go b/crypto/p256/key.go index e14433d..5f8676d 100644 --- a/crypto/p256/key.go +++ b/crypto/p256/key.go @@ -4,6 +4,8 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" + + "github.com/MetaMask/go-did-it/crypto" ) const ( @@ -33,3 +35,14 @@ const ( pemPubBlockType = "PUBLIC KEY" pemPrivBlockType = "PRIVATE KEY" ) + +// KeyType returns the crypto.KeyType describing P-256, to be added to a crypto.KeySet. +func KeyType() crypto.KeyType { + return crypto.KeyType{ + Name: "P-256", + Code: MultibaseCode, + DecodePublic: func(b []byte) (crypto.PublicKey, error) { return crypto.ToPub(PublicKeyFromBytes(b)) }, + Matches: func(key crypto.PublicKey) bool { _, ok := key.(*PublicKey); return ok }, + Wrap: func(key any) (crypto.PublicKey, bool, error) { return crypto.ToWrap(WrapPublicKey(key)) }, + } +} diff --git a/crypto/p256/public.go b/crypto/p256/public.go index ffd33c9..0fc4f6d 100644 --- a/crypto/p256/public.go +++ b/crypto/p256/public.go @@ -51,6 +51,18 @@ func PublicKeyFromXY(x, y []byte) (*PublicKey, error) { return pub, nil } +// WrapPublicKey converts an already-parsed public key object — for P-256, a standard library +// *ecdsa.PublicKey on the P-256 curve — into a PublicKey. It is the inverse of Unwrap. The boolean +// reports whether the given key belongs to this algorithm at all. +func WrapPublicKey(key any) (*PublicKey, bool, error) { + k, ok := key.(*ecdsa.PublicKey) + if !ok || k.Curve != elliptic.P256() { + return nil, false, nil + } + pub, err := PublicKeyFromXY(k.X.Bytes(), k.Y.Bytes()) + return pub, true, err +} + // PublicKeyFromPublicKeyMultibase decodes the public key from its Multibase form func PublicKeyFromPublicKeyMultibase(multibase string) (*PublicKey, error) { code, bytes, err := helpers.PublicKeyMultibaseDecode(multibase) diff --git a/crypto/p384/key.go b/crypto/p384/key.go index 88deef1..eb78b65 100644 --- a/crypto/p384/key.go +++ b/crypto/p384/key.go @@ -4,6 +4,8 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" + + "github.com/MetaMask/go-did-it/crypto" ) const ( @@ -33,3 +35,14 @@ const ( pemPubBlockType = "PUBLIC KEY" pemPrivBlockType = "PRIVATE KEY" ) + +// KeyType returns the crypto.KeyType describing P-384, to be added to a crypto.KeySet. +func KeyType() crypto.KeyType { + return crypto.KeyType{ + Name: "P-384", + Code: MultibaseCode, + DecodePublic: func(b []byte) (crypto.PublicKey, error) { return crypto.ToPub(PublicKeyFromBytes(b)) }, + Matches: func(key crypto.PublicKey) bool { _, ok := key.(*PublicKey); return ok }, + Wrap: func(key any) (crypto.PublicKey, bool, error) { return crypto.ToWrap(WrapPublicKey(key)) }, + } +} diff --git a/crypto/p384/public.go b/crypto/p384/public.go index 82fa19b..db6b378 100644 --- a/crypto/p384/public.go +++ b/crypto/p384/public.go @@ -51,6 +51,18 @@ func PublicKeyFromXY(x, y []byte) (*PublicKey, error) { return pub, nil } +// WrapPublicKey converts an already-parsed public key object — for P-384, a standard library +// *ecdsa.PublicKey on the P-384 curve — into a PublicKey. It is the inverse of Unwrap. The boolean +// reports whether the given key belongs to this algorithm at all. +func WrapPublicKey(key any) (*PublicKey, bool, error) { + k, ok := key.(*ecdsa.PublicKey) + if !ok || k.Curve != elliptic.P384() { + return nil, false, nil + } + pub, err := PublicKeyFromXY(k.X.Bytes(), k.Y.Bytes()) + return pub, true, err +} + // PublicKeyFromPublicKeyMultibase decodes the public key from its Multibase form func PublicKeyFromPublicKeyMultibase(multibase string) (*PublicKey, error) { code, bytes, err := helpers.PublicKeyMultibaseDecode(multibase) diff --git a/crypto/p521/key.go b/crypto/p521/key.go index 7569396..a4ec6b8 100644 --- a/crypto/p521/key.go +++ b/crypto/p521/key.go @@ -4,6 +4,8 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" + + "github.com/MetaMask/go-did-it/crypto" ) const ( @@ -33,3 +35,14 @@ const ( pemPubBlockType = "PUBLIC KEY" pemPrivBlockType = "PRIVATE KEY" ) + +// KeyType returns the crypto.KeyType describing P-521, to be added to a crypto.KeySet. +func KeyType() crypto.KeyType { + return crypto.KeyType{ + Name: "P-521", + Code: MultibaseCode, + DecodePublic: func(b []byte) (crypto.PublicKey, error) { return crypto.ToPub(PublicKeyFromBytes(b)) }, + Matches: func(key crypto.PublicKey) bool { _, ok := key.(*PublicKey); return ok }, + Wrap: func(key any) (crypto.PublicKey, bool, error) { return crypto.ToWrap(WrapPublicKey(key)) }, + } +} diff --git a/crypto/p521/public.go b/crypto/p521/public.go index 52b1fd0..9f5270c 100644 --- a/crypto/p521/public.go +++ b/crypto/p521/public.go @@ -51,6 +51,18 @@ func PublicKeyFromXY(x, y []byte) (*PublicKey, error) { return pub, nil } +// WrapPublicKey converts an already-parsed public key object — for P-521, a standard library +// *ecdsa.PublicKey on the P-521 curve — into a PublicKey. It is the inverse of Unwrap. The boolean +// reports whether the given key belongs to this algorithm at all. +func WrapPublicKey(key any) (*PublicKey, bool, error) { + k, ok := key.(*ecdsa.PublicKey) + if !ok || k.Curve != elliptic.P521() { + return nil, false, nil + } + pub, err := PublicKeyFromXY(k.X.Bytes(), k.Y.Bytes()) + return pub, true, err +} + // PublicKeyFromPublicKeyMultibase decodes the public key from its Multibase form func PublicKeyFromPublicKeyMultibase(multibase string) (*PublicKey, error) { code, bytes, err := helpers.PublicKeyMultibaseDecode(multibase) diff --git a/crypto/rsa/key.go b/crypto/rsa/key.go index ba673d8..b37916c 100644 --- a/crypto/rsa/key.go +++ b/crypto/rsa/key.go @@ -41,3 +41,67 @@ func defaultSigHash(keyLen int) crypto.Hash { return crypto.SHA512 } } + +// KeyType returns the crypto.KeyType describing RSA, to be added to a crypto.KeySet. +// +// For RSA the accepted modulus sizes are part of the policy. Pass the exact sizes (in bits) to allow, +// e.g. rsa.KeyType(2048, 4096); pass none to accept any size within [MinRsaKeyBits, MaxRsaKeyBits]. +func KeyType(sizes ...int) crypto.KeyType { + checkSize := func(bits int) error { + if len(sizes) == 0 { + if bits < MinRsaKeyBits || bits > MaxRsaKeyBits { + return fmt.Errorf("%w: rsa key size %d not allowed", crypto.ErrKeyNotAccepted, bits) + } + return nil + } + for _, s := range sizes { + if s == bits { + return nil + } + } + return fmt.Errorf("%w: rsa key size %d not allowed", crypto.ErrKeyNotAccepted, bits) + } + return crypto.KeyType{ + Name: rsaName(sizes), + Code: MultibaseCode, + // The did:key spec encodes the RSA publicKeyMultibase body as PKCS#1 (RSAPublicKey) DER. + DecodePublic: func(body []byte) (crypto.PublicKey, error) { + k, err := PublicKeyFromPKCS1DER(body) + if err != nil { + return nil, err + } + if err := checkSize(k.k.N.BitLen()); err != nil { + return nil, err + } + return k, nil + }, + Matches: func(key crypto.PublicKey) bool { + rk, ok := key.(*PublicKey) + return ok && checkSize(rk.k.N.BitLen()) == nil + }, + Wrap: func(key any) (crypto.PublicKey, bool, error) { + pub, ok, err := WrapPublicKey(key) + if !ok || err != nil { + return nil, ok, err + } + if err := checkSize(pub.k.N.BitLen()); err != nil { + return nil, true, err + } + return pub, true, nil + }, + } +} + +func rsaName(sizes []int) string { + if len(sizes) == 0 { + return "RSA" + } + name := "RSA-" + for i, s := range sizes { + if i > 0 { + name += "/" + } + name += fmt.Sprintf("%d", s) + } + return name +} diff --git a/crypto/rsa/public.go b/crypto/rsa/public.go index bbc2007..2b0a840 100644 --- a/crypto/rsa/public.go +++ b/crypto/rsa/public.go @@ -45,6 +45,21 @@ func PublicKeyFromNE(n, e []byte) (*PublicKey, error) { return &PublicKey{k: pub}, nil } +// WrapPublicKey converts an already-parsed public key object — for RSA, a standard library +// *rsa.PublicKey — into a PublicKey. It is the inverse of Unwrap. The boolean reports whether +// the given key belongs to this algorithm at all. Note that a KeySet applies its own size policy +// on top of the library-wide [MinRsaKeyBits, MaxRsaKeyBits] bounds enforced here. +func WrapPublicKey(key any) (*PublicKey, bool, error) { + k, ok := key.(*rsa.PublicKey) + if !ok { + return nil, false, nil + } + if err := validatePublicKey(k); err != nil { + return nil, true, err + } + return &PublicKey{k: k}, true, nil +} + // PublicKeyFromPublicKeyMultibase decodes the public key from its Multibase form func PublicKeyFromPublicKeyMultibase(multibase string) (*PublicKey, error) { code, bytes, err := helpers.PublicKeyMultibaseDecode(multibase) @@ -54,7 +69,8 @@ func PublicKeyFromPublicKeyMultibase(multibase string) (*PublicKey, error) { if code != MultibaseCode { return nil, fmt.Errorf("invalid code") } - return PublicKeyFromX509DER(bytes) + // The did:key spec encodes the RSA public key as PKCS#1 (RSAPublicKey) DER. + return PublicKeyFromPKCS1DER(bytes) } // PublicKeyFromX509DER decodes an X.509 DER (binary) encoded public key. @@ -93,10 +109,10 @@ func validatePublicKey(pub *rsa.PublicKey) error { return fmt.Errorf("invalid modulus") } if pub.N.BitLen() < MinRsaKeyBits { - return fmt.Errorf("key length too small") + return fmt.Errorf("%w: key length too small", crypto.ErrKeyNotAccepted) } if pub.N.BitLen() > MaxRsaKeyBits { - return fmt.Errorf("key length too large") + return fmt.Errorf("%w: key length too large", crypto.ErrKeyNotAccepted) } if pub.N.Bit(0) == 0 { return fmt.Errorf("modulus must be odd") @@ -133,7 +149,8 @@ func (p *PublicKey) Equal(other crypto.PublicKey) bool { } func (p *PublicKey) ToPublicKeyMultibase() string { - bytes := p.ToX509DER() + // The did:key spec encodes the RSA public key as PKCS#1 (RSAPublicKey) DER. + bytes := x509.MarshalPKCS1PublicKey(p.k) return helpers.PublicKeyMultibaseEncode(MultibaseCode, bytes) } diff --git a/crypto/secp256k1/key.go b/crypto/secp256k1/key.go index 52c8efe..fa73038 100644 --- a/crypto/secp256k1/key.go +++ b/crypto/secp256k1/key.go @@ -4,6 +4,8 @@ import ( "encoding/asn1" "github.com/decred/dcrd/dcrec/secp256k1/v4" + + "github.com/MetaMask/go-did-it/crypto" ) const ( @@ -46,3 +48,14 @@ var ( // Curve is secp256k1 (OID: 1.3.132.0.10) oidSecp256k1 = asn1.ObjectIdentifier{1, 3, 132, 0, 10} ) + +// KeyType returns the crypto.KeyType describing secp256k1, to be added to a crypto.KeySet. +func KeyType() crypto.KeyType { + return crypto.KeyType{ + Name: "secp256k1", + Code: MultibaseCode, + DecodePublic: func(b []byte) (crypto.PublicKey, error) { return crypto.ToPub(PublicKeyFromBytes(b)) }, + Matches: func(key crypto.PublicKey) bool { _, ok := key.(*PublicKey); return ok }, + Wrap: func(key any) (crypto.PublicKey, bool, error) { return crypto.ToWrap(WrapPublicKey(key)) }, + } +} diff --git a/crypto/secp256k1/public.go b/crypto/secp256k1/public.go index 295f571..0307fcd 100644 --- a/crypto/secp256k1/public.go +++ b/crypto/secp256k1/public.go @@ -51,6 +51,19 @@ func PublicKeyFromXY(x, y []byte) (*PublicKey, error) { return &PublicKey{k: pub}, nil } +// WrapPublicKey converts an already-parsed public key object into a PublicKey. It is the inverse +// of Unwrap. The standard library has no secp256k1 curve, so this accepts the underlying library's +// type: dcrd's *secp256k1.PublicKey. For keys held as an *ecdsa.PublicKey with a third-party curve +// (dcrd's ToECDSA, go-ethereum's S256), convert explicitly with PublicKeyFromXY instead. The +// boolean reports whether the given key belongs to this algorithm at all. +func WrapPublicKey(key any) (*PublicKey, bool, error) { + k, ok := key.(*secp256k1.PublicKey) + if !ok { + return nil, false, nil + } + return &PublicKey{k: k}, true, nil +} + // PublicKeyFromRecovery recovers the secp256k1 public key from a compact signature // and the hash of the signed message. // The signature must be 65 bytes: [recovery_flag (1 byte) | R (32 bytes) | S (32 bytes)]. diff --git a/crypto/x25519/key.go b/crypto/x25519/key.go index 61d8146..9650485 100644 --- a/crypto/x25519/key.go +++ b/crypto/x25519/key.go @@ -4,6 +4,8 @@ import ( "crypto/ecdh" "crypto/rand" "math/big" + + "github.com/MetaMask/go-did-it/crypto" ) const ( @@ -47,3 +49,14 @@ var curve25519P = new(big.Int).SetBytes([]byte{ var curve25519PMinusOne = new(big.Int).Sub(curve25519P, big.NewInt(1)) var one = big.NewInt(1) + +// KeyType returns the crypto.KeyType describing X25519, to be added to a crypto.KeySet. +func KeyType() crypto.KeyType { + return crypto.KeyType{ + Name: "X25519", + Code: MultibaseCode, + DecodePublic: func(b []byte) (crypto.PublicKey, error) { return crypto.ToPub(PublicKeyFromBytes(b)) }, + Matches: func(key crypto.PublicKey) bool { _, ok := key.(*PublicKey); return ok }, + Wrap: func(key any) (crypto.PublicKey, bool, error) { return crypto.ToWrap(WrapPublicKey(key)) }, + } +} diff --git a/crypto/x25519/public.go b/crypto/x25519/public.go index e8957be..3f0611f 100644 --- a/crypto/x25519/public.go +++ b/crypto/x25519/public.go @@ -30,6 +30,17 @@ func PublicKeyFromBytes(b []byte) (*PublicKey, error) { return &PublicKey{k: pub}, nil } +// WrapPublicKey converts an already-parsed public key object — for X25519, a standard library +// *ecdh.PublicKey on the X25519 curve — into a PublicKey. It is the inverse of Unwrap. The boolean +// reports whether the given key belongs to this algorithm at all. +func WrapPublicKey(key any) (*PublicKey, bool, error) { + k, ok := key.(*ecdh.PublicKey) + if !ok || k.Curve() != ecdh.X25519() { + return nil, false, nil + } + return &PublicKey{k: k}, true, nil +} + // PublicKeyFromEd25519 converts an ed25519 public key to a x25519 public key. // It errors if the slice is not the right size. // diff --git a/did_test.go b/did_test.go index e4fa1c1..9d1bf25 100644 --- a/did_test.go +++ b/did_test.go @@ -8,6 +8,10 @@ import ( "github.com/stretchr/testify/require" "github.com/MetaMask/go-did-it" + "github.com/MetaMask/go-did-it/crypto" + _ "github.com/MetaMask/go-did-it/crypto/all" + "github.com/MetaMask/go-did-it/crypto/ed25519" + "github.com/MetaMask/go-did-it/crypto/p256" "github.com/MetaMask/go-did-it/crypto/x25519" _ "github.com/MetaMask/go-did-it/verifiers/did-key" ) @@ -53,6 +57,21 @@ func Example_keyAgreement() { // Verification method used: X25519KeyAgreementKey2020 did:key:z6MkgRNXpJRbEE6FoXhT8KWHwJo4KyzFo1FdSEFpRLh5vuXZ#z6LSjeQx2VkXz8yirhrYJv8uicu9BBaeYU3Q1D9sFBovhmPF } +func TestWithKeySet(t *testing.T) { + // did:key holding an Ed25519 key + d, err := did.Parse("did:key:z6MknwcywUtTy2ADJQ8FH1GcSySKPyKDmyzT4rPEE84XREse") + require.NoError(t, err) + + // A KeySet allowing Ed25519: resolution succeeds. + doc, err := d.Document(did.WithKeySet(crypto.NewKeySet(ed25519.KeyType()))) + require.NoError(t, err) + require.NotNil(t, doc) + + // A KeySet allowing only P-256: the Ed25519 key is not in the set, so resolution fails. + _, err = d.Document(did.WithKeySet(crypto.NewKeySet(p256.KeyType()))) + require.Error(t, err) +} + func TestHasValidDIDSyntax(t *testing.T) { tests := []struct { name string diff --git a/document/document_test.go b/document/document_test.go index 288d1f8..f5d6d81 100644 --- a/document/document_test.go +++ b/document/document_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/require" + _ "github.com/MetaMask/go-did-it/crypto/all" "github.com/MetaMask/go-did-it/verifiers/_methods/ed25519" "github.com/MetaMask/go-did-it/verifiers/_methods/jsonwebkey" "github.com/MetaMask/go-did-it/verifiers/_methods/x25519" diff --git a/options.go b/options.go index ab91650..a3cd4bc 100644 --- a/options.go +++ b/options.go @@ -3,10 +3,13 @@ package did import ( "context" "net/http" + + "github.com/MetaMask/go-did-it/crypto" ) type ResolutionOpts struct { ctx context.Context + keySet *crypto.KeySet hintVerificationMethod []string client HttpClient } @@ -18,6 +21,13 @@ func (opts *ResolutionOpts) Context() context.Context { return context.Background() } +func (opts *ResolutionOpts) KeySet() *crypto.KeySet { + if opts.keySet == nil { + return crypto.DefaultKeySet + } + return opts.keySet +} + func (opts *ResolutionOpts) HasVerificationMethodHint(hint string) bool { for _, h := range opts.hintVerificationMethod { if h == hint { @@ -52,6 +62,16 @@ func WithResolutionContext(ctx context.Context) ResolutionOption { } } +// WithKeySet provides a KeySet, a set of cryptographic key algorithm as +// allowed during resolution. If the DID to resolve has an algorithm not included +// in this set, resolution will fail. +// This is a way for the caller to control what algorithm are deemed safe or expected. +func WithKeySet(keyset *crypto.KeySet) ResolutionOption { + return func(opts *ResolutionOpts) { + opts.keySet = keyset + } +} + // WithResolutionHintVerificationMethod adds a hint for the type of verification method to be used // when resolving and constructing the DID Document, if possible. // Hints are expected to be VerificationMethod string types, like ed25519vm.Type. diff --git a/verifiers/_methods/multikey/multikey.go b/verifiers/_methods/multikey/multikey.go index 0234131..87cc6eb 100644 --- a/verifiers/_methods/multikey/multikey.go +++ b/verifiers/_methods/multikey/multikey.go @@ -7,7 +7,6 @@ import ( "github.com/MetaMask/go-did-it" "github.com/MetaMask/go-did-it/crypto" - allkeys "github.com/MetaMask/go-did-it/crypto/_allkeys" ) // Specification: https://www.w3.org/TR/cid-1.0/#Multikey @@ -72,7 +71,7 @@ func (m *MultiKey) UnmarshalJSON(bytes []byte) error { return errors.New("invalid controller") } - m.pubkey, err = allkeys.PublicKeyFromPublicKeyMultibase(aux.PublicKeyMultibase) + m.pubkey, err = crypto.PublicKeyFromMultibase(aux.PublicKeyMultibase) if err != nil { return fmt.Errorf("invalid publicKeyMultibase: %w", err) } diff --git a/verifiers/did-key/key.go b/verifiers/did-key/key.go index 496b662..40ee027 100644 --- a/verifiers/did-key/key.go +++ b/verifiers/did-key/key.go @@ -6,7 +6,6 @@ import ( "github.com/MetaMask/go-did-it" "github.com/MetaMask/go-did-it/crypto" - allkeys "github.com/MetaMask/go-did-it/crypto/_allkeys" "github.com/MetaMask/go-did-it/crypto/ed25519" "github.com/MetaMask/go-did-it/crypto/p256" "github.com/MetaMask/go-did-it/crypto/p384" @@ -31,8 +30,7 @@ func init() { var _ did.DID = DidKey{} type DidKey struct { - msi string // method-specific identifier, i.e. "12345" in "did:key:12345" - pubkey crypto.PublicKey + msi string // method-specific identifier, i.e. "12345" in "did:key:12345" } func Decode(identifier string) (did.DID, error) { @@ -44,15 +42,11 @@ func Decode(identifier string) (did.DID, error) { msi := identifier[len(keyPrefix):] - pub, err := allkeys.PublicKeyFromPublicKeyMultibase(msi) - if err != nil { - return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err) - } - return DidKey{msi: msi, pubkey: pub}, nil + return DidKey{msi: msi}, nil } func FromPublicKey(pub crypto.PublicKey) did.DID { - return DidKey{msi: pub.ToPublicKeyMultibase(), pubkey: pub} + return DidKey{msi: pub.ToPublicKeyMultibase()} } func FromPrivateKey(priv crypto.PrivateKey) did.DID { @@ -66,10 +60,15 @@ func (d DidKey) Method() string { func (d DidKey) Document(opts ...did.ResolutionOption) (did.Document, error) { params := did.CollectResolutionOpts(opts) + pub, err := params.KeySet().PublicKeyFromMultibase(d.msi) + if err != nil { + return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err) + } + doc := document{id: d.String()} mainVmId := fmt.Sprintf("did:key:%s#%s", d.msi, d.msi) - switch pub := d.pubkey.(type) { + switch pub := pub.(type) { case ed25519.PublicKey: xpub, err := x25519.PublicKeyFromEd25519(pub) if err != nil { diff --git a/verifiers/did-key/key_test.go b/verifiers/did-key/key_test.go index bc9fab5..518a60d 100644 --- a/verifiers/did-key/key_test.go +++ b/verifiers/did-key/key_test.go @@ -8,7 +8,10 @@ import ( "github.com/stretchr/testify/require" "github.com/MetaMask/go-did-it" + "github.com/MetaMask/go-did-it/crypto" + _ "github.com/MetaMask/go-did-it/crypto/all" "github.com/MetaMask/go-did-it/crypto/ed25519" + "github.com/MetaMask/go-did-it/crypto/p256" didkey "github.com/MetaMask/go-did-it/verifiers/did-key" ) @@ -49,7 +52,7 @@ func TestMustParseDIDKey(t *testing.T) { d := did.MustParse(str) require.Equal(t, str, d.String()) }) - str = "did:key:z7Mkod5Jr3yd5SC7UDueqK4dAAw5xYJYjksy722tA9Boxc4z" + str = "did:unknown:z6Mkod5Jr3yd5SC7UDueqK4dAAw5xYJYjksy722tA9Boxc4z" require.Panics(t, func() { did.MustParse(str) }) @@ -65,6 +68,21 @@ func TestFromPublicKey(t *testing.T) { require.NotEmpty(t, doc) } +func TestWithKeySet(t *testing.T) { + pub, _, err := ed25519.GenerateKeyPair() + require.NoError(t, err) + dk := didkey.FromPublicKey(pub) + + // A KeySet allowing Ed25519: resolution succeeds. + doc, err := dk.Document(did.WithKeySet(crypto.NewKeySet(ed25519.KeyType()))) + require.NoError(t, err) + require.NotEmpty(t, doc) + + // A KeySet allowing only P-256: the Ed25519 key is not in the set, so resolution fails. + _, err = dk.Document(did.WithKeySet(crypto.NewKeySet(p256.KeyType()))) + require.Error(t, err) +} + func TestEquivalence(t *testing.T) { did0A, err := did.Parse("did:key:z6Mkod5Jr3yd5SC7UDueqK4dAAw5xYJYjksy722tA9Boxc4z") require.NoError(t, err) diff --git a/verifiers/did-plc/document_test.go b/verifiers/did-plc/document_test.go index 26d9058..8f6c6a7 100644 --- a/verifiers/did-plc/document_test.go +++ b/verifiers/did-plc/document_test.go @@ -10,8 +10,35 @@ import ( "github.com/stretchr/testify/require" "github.com/MetaMask/go-did-it" + "github.com/MetaMask/go-did-it/crypto" + "github.com/MetaMask/go-did-it/crypto/ed25519" + "github.com/MetaMask/go-did-it/crypto/secp256k1" ) +func TestWithKeySet(t *testing.T) { + // current resolved /data for did:plc:ewvi7nxzyoun6zhxrhs64oiz, + // whose "atproto" verification method holds a secp256k1 key. + resolvedData := `{"did":"did:plc:ewvi7nxzyoun6zhxrhs64oiz","verificationMethods":{"atproto":"did:key:zQ3shunBKsXixLxKtC5qeSG9E4J5RkGN57im31pcTzbNQnm5w"},"rotationKeys":["did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg","did:key:zQ3shpKnbdPx3g3CmPf5cRVTPe1HtSwVn5ish3wSnDPQCbLJK"],"alsoKnownAs":["at://atproto.com"],"services":{"atproto_pds":{"type":"AtprotoPersonalDataServer","endpoint":"https://enoki.us-east.host.bsky.network"}}}` + + d, err := did.Parse("did:plc:ewvi7nxzyoun6zhxrhs64oiz") + require.NoError(t, err) + + // A KeySet allowing secp256k1: resolution succeeds. + doc, err := d.Document( + did.WithHttpClient(&MockHTTPClient{resp: resolvedData}), + did.WithKeySet(crypto.NewKeySet(secp256k1.KeyType())), + ) + require.NoError(t, err) + require.NotNil(t, doc) + + // A KeySet allowing only Ed25519: the secp256k1 key is not in the set, so resolution fails. + _, err = d.Document( + did.WithHttpClient(&MockHTTPClient{resp: resolvedData}), + did.WithKeySet(crypto.NewKeySet(ed25519.KeyType())), + ) + require.Error(t, err) +} + func TestDocument(t *testing.T) { // current resolved /data for did:plc:ewvi7nxzyoun6zhxrhs64oiz resolvedData := `{"did":"did:plc:ewvi7nxzyoun6zhxrhs64oiz","verificationMethods":{"atproto":"did:key:zQ3shunBKsXixLxKtC5qeSG9E4J5RkGN57im31pcTzbNQnm5w"},"rotationKeys":["did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg","did:key:zQ3shpKnbdPx3g3CmPf5cRVTPe1HtSwVn5ish3wSnDPQCbLJK"],"alsoKnownAs":["at://atproto.com"],"services":{"atproto_pds":{"type":"AtprotoPersonalDataServer","endpoint":"https://enoki.us-east.host.bsky.network"}}}` diff --git a/verifiers/did-plc/plc.go b/verifiers/did-plc/plc.go index c596911..10a2e45 100644 --- a/verifiers/did-plc/plc.go +++ b/verifiers/did-plc/plc.go @@ -9,7 +9,6 @@ import ( "strings" "github.com/MetaMask/go-did-it" - allkeys "github.com/MetaMask/go-did-it/crypto/_allkeys" "github.com/MetaMask/go-did-it/crypto/ed25519" "github.com/MetaMask/go-did-it/crypto/p256" "github.com/MetaMask/go-did-it/crypto/p384" @@ -138,7 +137,7 @@ func (d DidPlc) Document(opts ...did.ResolutionOption) (did.Document, error) { } msi := data[len(keyPrefix):] - pub, err := allkeys.PublicKeyFromPublicKeyMultibase(msi) + pub, err := params.KeySet().PublicKeyFromMultibase(msi) if err != nil { return nil, fmt.Errorf("%w: %w", did.ErrInvalidDid, err) } diff --git a/verifiers/did-plc/plc_test.go b/verifiers/did-plc/plc_test.go index 490beaa..36045fd 100644 --- a/verifiers/did-plc/plc_test.go +++ b/verifiers/did-plc/plc_test.go @@ -6,6 +6,7 @@ import ( "github.com/stretchr/testify/require" "github.com/MetaMask/go-did-it" + _ "github.com/MetaMask/go-did-it/crypto/all" ) func TestParseDIDPlc(t *testing.T) { diff --git a/verifiers/did-web/web.go b/verifiers/did-web/web.go index 6410756..4b85523 100644 --- a/verifiers/did-web/web.go +++ b/verifiers/did-web/web.go @@ -64,6 +64,7 @@ func (d DidWeb) Method() string { return "web" } +// Does not support WithKeySet. func (d DidWeb) Document(opts ...did.ResolutionOption) (did.Document, error) { params := did.CollectResolutionOpts(opts)