From 47f93837571805aaa015850e62a2c83e19d08c9e Mon Sep 17 00:00:00 2001 From: Derek Trider Date: Mon, 11 Sep 2023 18:23:44 -0400 Subject: [PATCH] feat(sdk): Improved did:key create function Added a new function for creating did:key DIDs. This new standalone function directly takes in a key instead of a keyWriter or keyReader, giving the caller more flexibility in terms of key management. It also minimizes input parameters to only what's required, helping reduce confusion caused by unused parameters. Signed-off-by: Derek Trider --- cmd/wallet-sdk-gomobile/didkey/didkey.go | 62 +++++++++++++++++ cmd/wallet-sdk-gomobile/didkey/didkey_test.go | 68 +++++++++++++++++++ pkg/did/creator/key/key.go | 9 ++- test/integration/pkg/helpers/openid4ci.go | 36 +++++++--- 4 files changed, 163 insertions(+), 12 deletions(-) create mode 100644 cmd/wallet-sdk-gomobile/didkey/didkey.go create mode 100644 cmd/wallet-sdk-gomobile/didkey/didkey_test.go diff --git a/cmd/wallet-sdk-gomobile/didkey/didkey.go b/cmd/wallet-sdk-gomobile/didkey/didkey.go new file mode 100644 index 000000000..3d9027659 --- /dev/null +++ b/cmd/wallet-sdk-gomobile/didkey/didkey.go @@ -0,0 +1,62 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +// Package didkey contains a function that can be used to create did:key documents. +package didkey + +import ( + "errors" + + "github.com/trustbloc/did-go/doc/did" + "github.com/trustbloc/wallet-sdk/pkg/walleterror" + + "github.com/trustbloc/wallet-sdk/cmd/wallet-sdk-gomobile/api" + "github.com/trustbloc/wallet-sdk/cmd/wallet-sdk-gomobile/wrapper" + "github.com/trustbloc/wallet-sdk/pkg/did/creator/key" +) + +// ErrorModule is the error module name for this package. +const ErrorModule = "DIDKEY" + +// Create creates a new did:key document using the given JWK. +func Create(jwk *api.JSONWebKey) (*api.DIDDocResolution, error) { + if jwk == nil || jwk.JWK == nil { + return nil, wrapper.ToMobileError(walleterror.NewInvalidSDKUsageError( + ErrorModule, errors.New("jwk object cannot be null/nil/empty"))) + } + + var vm *did.VerificationMethod + + if jwk.JWK.Crv == "Ed25519" { + // Workaround: when the did:key VDR creates a DID for ed25519, Ed25519VerificationKey2018 is the expected + // verification method. + publicKeyBytes, err := jwk.JWK.PublicKeyBytes() + if err != nil { + return nil, wrapper.ToMobileError(err) + } + + vm = &did.VerificationMethod{Value: publicKeyBytes, Type: "Ed25519VerificationKey2018"} + } else { + var err error + + vm, err = did.NewVerificationMethodFromJWK("", "JsonWebKey2020", "", jwk.JWK) + if err != nil { + return nil, wrapper.ToMobileError(err) + } + } + + didDocResolution, err := key.Create(vm) + if err != nil { + return nil, wrapper.ToMobileError(err) + } + + didDocResolutionBytes, err := didDocResolution.JSONBytes() + if err != nil { + return nil, wrapper.ToMobileError(err) + } + + return &api.DIDDocResolution{Content: string(didDocResolutionBytes)}, nil +} diff --git a/cmd/wallet-sdk-gomobile/didkey/didkey_test.go b/cmd/wallet-sdk-gomobile/didkey/didkey_test.go new file mode 100644 index 000000000..9a5af1921 --- /dev/null +++ b/cmd/wallet-sdk-gomobile/didkey/didkey_test.go @@ -0,0 +1,68 @@ +/* +Copyright Gen Digital Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package didkey_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/trustbloc/kms-go/doc/jose/jwk" + + "github.com/trustbloc/wallet-sdk/cmd/wallet-sdk-gomobile/api" + "github.com/trustbloc/wallet-sdk/cmd/wallet-sdk-gomobile/didkey" + "github.com/trustbloc/wallet-sdk/cmd/wallet-sdk-gomobile/localkms" +) + +func TestCreate(t *testing.T) { + t.Run("Using an ED25519 key", func(t *testing.T) { + localKMS := createTestKMS(t) + + jsonWebKey, err := localKMS.Create(localkms.KeyTypeED25519) + require.NoError(t, err) + + didDoc, err := didkey.Create(jsonWebKey) + require.NoError(t, err) + require.NotNil(t, didDoc) + }) + t.Run("Using a P-384 key", func(t *testing.T) { + localKMS := createTestKMS(t) + + jsonWebKey, err := localKMS.Create(localkms.KeyTypeP384) + require.NoError(t, err) + + didDoc, err := didkey.Create(jsonWebKey) + require.NoError(t, err) + require.NotNil(t, didDoc) + }) + t.Run("Nil JWK", func(t *testing.T) { + didDoc, err := didkey.Create(nil) + require.Contains(t, err.Error(), "jwk object cannot be null/nil/empty") + require.Nil(t, didDoc) + }) + t.Run("Fail to get public key bytes", func(t *testing.T) { + didDoc, err := didkey.Create(&api.JSONWebKey{JWK: &jwk.JWK{Crv: "Ed25519"}}) + require.Contains(t, err.Error(), "unsupported public key type in kid ''") + require.Nil(t, didDoc) + }) + t.Run("Fail to create verification method from JWK", func(t *testing.T) { + didDoc, err := didkey.Create(&api.JSONWebKey{JWK: &jwk.JWK{}}) + require.Contains(t, err.Error(), + "convert JWK to public key bytes: unsupported public key type in kid ''") + require.Nil(t, didDoc) + }) +} + +func createTestKMS(t *testing.T) *localkms.KMS { + t.Helper() + + kmsStore := localkms.NewMemKMSStore() + + localKMS, err := localkms.NewKMS(kmsStore) + require.NoError(t, err) + + return localKMS +} diff --git a/pkg/did/creator/key/key.go b/pkg/did/creator/key/key.go index 298cd5bc8..1cb72591b 100644 --- a/pkg/did/creator/key/key.go +++ b/pkg/did/creator/key/key.go @@ -22,9 +22,14 @@ func NewCreator() *Creator { return &Creator{vdr: key.New()} } -// Create creates a new did:key document using the given rawKey and verificationMethodType. +// Create creates a new did:key document using the given verification method. func (d *Creator) Create(vm *did.VerificationMethod) (*did.DocResolution, error) { + return Create(vm) +} + +// Create creates a new did:key document using the given verification method. +func Create(vm *did.VerificationMethod) (*did.DocResolution, error) { didDocArgument := &did.Doc{VerificationMethod: []did.VerificationMethod{*vm}} - return d.vdr.Create(didDocArgument) + return key.New().Create(didDocArgument) } diff --git a/test/integration/pkg/helpers/openid4ci.go b/test/integration/pkg/helpers/openid4ci.go index ec67a9117..07861de39 100644 --- a/test/integration/pkg/helpers/openid4ci.go +++ b/test/integration/pkg/helpers/openid4ci.go @@ -17,6 +17,7 @@ import ( "github.com/trustbloc/wallet-sdk/cmd/wallet-sdk-gomobile/activitylogger/mem" "github.com/trustbloc/wallet-sdk/cmd/wallet-sdk-gomobile/api" "github.com/trustbloc/wallet-sdk/cmd/wallet-sdk-gomobile/did" + "github.com/trustbloc/wallet-sdk/cmd/wallet-sdk-gomobile/didkey" "github.com/trustbloc/wallet-sdk/cmd/wallet-sdk-gomobile/display" "github.com/trustbloc/wallet-sdk/cmd/wallet-sdk-gomobile/localkms" goapi "github.com/trustbloc/wallet-sdk/pkg/api" @@ -36,20 +37,35 @@ func NewCITestHelper(t *testing.T, didMethod string, keyType string) *CITestHelp kms, err := localkms.NewKMS(localkms.NewMemKMSStore()) require.NoError(t, err) - // create DID - c, err := did.NewCreator(kms) - require.NoError(t, err) + var didDoc *api.DIDDocResolution - metricsLogger := metricslogger.NewMetricsLogger() + if didMethod == "key" { + // Create the DID using our new DID creation pattern. - opts := did.NewCreateOpts() - opts.SetKeyType(keyType) - opts.SetMetricsLogger(metricsLogger) + if keyType == "" { + keyType = localkms.KeyTypeED25519 + } - didDoc, err := c.Create(didMethod, opts) - require.NoError(t, err) + jwk, err := kms.Create(keyType) + require.NoError(t, err) + + didDoc, err = didkey.Create(jwk) + require.NoError(t, err) + } else { + c, err := did.NewCreator(kms) + require.NoError(t, err) + + metricsLogger := metricslogger.NewMetricsLogger() + + opts := did.NewCreateOpts() + opts.SetKeyType(keyType) + opts.SetMetricsLogger(metricsLogger) - checkDIDCreationMetricsLoggerEvents(t, metricsLogger) + didDoc, err = c.Create(didMethod, opts) + require.NoError(t, err) + + checkDIDCreationMetricsLoggerEvents(t, metricsLogger) + } return &CITestHelper{ KMS: kms,