Skip to content

Commit

Permalink
feat(sdk): Improved did:key create function (#598)
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
Derek Trider authored Sep 12, 2023
1 parent 3abc2fe commit 2ffa912
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 11 deletions.
38 changes: 38 additions & 0 deletions cmd/wallet-sdk-gomobile/didkey/didkey.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
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/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"
)

// Create creates a new did:key document using the given JWK.
func Create(jwk *api.JSONWebKey) (*api.DIDDocResolution, error) {
if jwk == nil {
return nil, wrapper.ToMobileError(walleterror.NewInvalidSDKUsageError(
key.ErrorModule, errors.New("jwk object cannot be null/nil")))
}

didDocResolution, err := key.Create(jwk.JWK)
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
}
63 changes: 63 additions & 0 deletions cmd/wallet-sdk-gomobile/didkey/didkey_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
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")
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
}
43 changes: 42 additions & 1 deletion pkg/did/creator/key/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,64 @@ SPDX-License-Identifier: Apache-2.0
package key

import (
"errors"

"github.com/trustbloc/did-go/doc/did"
"github.com/trustbloc/did-go/method/key"
"github.com/trustbloc/kms-go/doc/jose/jwk"
"github.com/trustbloc/wallet-sdk/pkg/walleterror"
)

// ErrorModule is the error module name used for errors relating to did:key creation.
const ErrorModule = "DIDKEY"

// Creator is used for creating did:key DID Documents.
type Creator struct {
vdr *key.VDR
}

// NewCreator returns a new did:key document Creator.
// Deprecated: The standalone Create function should be used instead.
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.
// Deprecated: The standalone Create function should be used instead.
func (d *Creator) Create(vm *did.VerificationMethod) (*did.DocResolution, error) {
didDocArgument := &did.Doc{VerificationMethod: []did.VerificationMethod{*vm}}

return d.vdr.Create(didDocArgument)
}

// Create creates a new did:key document using the given verification method.
func Create(jsonWebKey *jwk.JWK) (*did.DocResolution, error) {
if jsonWebKey == nil {
return nil, walleterror.NewInvalidSDKUsageError(
ErrorModule, errors.New("jwk object cannot be nil"))
}

var vm *did.VerificationMethod

if jsonWebKey.Crv == "Ed25519" {
// Workaround: when the did:key VDR creates a DID for ed25519, Ed25519VerificationKey2018 is the expected
// verification method.
publicKeyBytes, err := jsonWebKey.PublicKeyBytes()
if err != nil {
return nil, err
}

vm = &did.VerificationMethod{Value: publicKeyBytes, Type: "Ed25519VerificationKey2018"}
} else {
var err error

vm, err = did.NewVerificationMethodFromJWK("", "JsonWebKey2020", "", jsonWebKey)
if err != nil {
return nil, err
}
}

didDocArgument := &did.Doc{VerificationMethod: []did.VerificationMethod{*vm}}

return key.New().Create(didDocArgument)
}
70 changes: 70 additions & 0 deletions pkg/did/creator/key/key_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
Copyright Gen Digital Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package key_test

import (
"testing"

"github.com/trustbloc/kms-go/doc/jose/jwk"

kmsspi "github.com/trustbloc/kms-go/spi/kms"
"github.com/trustbloc/wallet-sdk/pkg/did/creator/key"

"github.com/stretchr/testify/require"

"github.com/trustbloc/wallet-sdk/pkg/localkms"
)

func TestCreate(t *testing.T) {
t.Run("Using an ED25519 key", func(t *testing.T) {
localKMS := createTestKMS(t)

_, jsonWebKey, err := localKMS.Create(kmsspi.ED25519)
require.NoError(t, err)

didDoc, err := key.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(kmsspi.ECDSAP384IEEEP1363)
require.NoError(t, err)

didDoc, err := key.Create(jsonWebKey)
require.NoError(t, err)
require.NotNil(t, didDoc)
})
t.Run("Nil JWK", func(t *testing.T) {
didDoc, err := key.Create(nil)
require.Contains(t, err.Error(), "jwk object cannot be nil")
require.Nil(t, didDoc)
})
t.Run("Fail to get public key bytes", func(t *testing.T) {
didDoc, err := key.Create(&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 := key.Create(&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.LocalKMS {
t.Helper()

kmsStore := localkms.NewMemKMSStore()

localKMS, err := localkms.NewLocalKMS(localkms.Config{Storage: kmsStore})
require.NoError(t, err)

return localKMS
}
36 changes: 26 additions & 10 deletions test/integration/pkg/helpers/openid4ci.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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,
Expand Down

0 comments on commit 2ffa912

Please sign in to comment.