Skip to content

Commit

Permalink
Merge pull request #124 from Moopli/ed25519-jwk
Browse files Browse the repository at this point in the history
feat: support jwk public keys
  • Loading branch information
rolsonquadras authored Jan 10, 2023
2 parents 93fdbe4 + 965f868 commit ae3270b
Show file tree
Hide file tree
Showing 13 changed files with 428 additions and 53 deletions.
24 changes: 19 additions & 5 deletions cmd/wallet-sdk-gomobile/api/diddocresolution.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,29 @@ import (
"fmt"

diddoc "github.com/hyperledger/aries-framework-go/pkg/doc/did"

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

// VerificationMethod represents a DID verification method.
type VerificationMethod struct {
ID string
Type string
Key models.VerificationKey
}

// ToSDKVerificationMethod returns this VerificationMethod as a models.VerificationMethod.
func (vm *VerificationMethod) ToSDKVerificationMethod() *models.VerificationMethod {
return &models.VerificationMethod{
ID: vm.ID,
Type: vm.Type,
Key: vm.Key,
}
}

// NewVerificationMethod creates VerificationMethod.
func NewVerificationMethod(keyID, vmType string) *VerificationMethod {
return &VerificationMethod{
ID: keyID,
Type: vmType,
}
return &VerificationMethod{ID: keyID, Type: vmType}
}

// DIDDocResolution represents a DID document resolution object.
Expand Down Expand Up @@ -62,7 +71,12 @@ func (d *DIDDocResolution) AssertionMethod() (*VerificationMethod, error) {
if len(verificationMethods[diddoc.AssertionMethod]) > 0 {
vm := verificationMethods[diddoc.AssertionMethod][0].VerificationMethod

return NewVerificationMethod(vm.ID, vm.Type), nil
vmJWK := vm.JSONWebKey()
if vmJWK != nil {
return &VerificationMethod{ID: vm.ID, Type: vm.Type, Key: models.VerificationKey{JSONWebKey: vmJWK}}, nil
}

return &VerificationMethod{ID: vm.ID, Type: vm.Type, Key: models.VerificationKey{Raw: vm.Value}}, nil
}

return nil, fmt.Errorf("DID provided has no assertion method to use as a default signing key")
Expand Down
22 changes: 21 additions & 1 deletion cmd/wallet-sdk-gomobile/api/diddocresolution_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ import (
//go:embed testdata/valid_doc_resolution.jsonld
var sampleDIDDocResolution []byte

//go:embed testdata/doc_with_jwk.jsonld
var jwkDIDDocResolution []byte

//go:embed testdata/doc_missing_assertionmethod.jsonld
var invalidDIDDocResolution []byte

func TestDIDDocResolution_ID(t *testing.T) {
t.Run("Success", func(t *testing.T) {
didDocResolution := api.NewDIDDocResolution(sampleDIDDocResolution)
Expand All @@ -35,14 +41,21 @@ func TestDIDDocResolution_ID(t *testing.T) {
})
}

func TestDIDDocResolution_AssertionMethodKeyID(t *testing.T) {
func TestDIDDocResolution_AssertionMethod(t *testing.T) {
t.Run("Success", func(t *testing.T) {
didDocResolution := api.NewDIDDocResolution(sampleDIDDocResolution)

vm, err := didDocResolution.AssertionMethod()
require.NoError(t, err)
require.NotEmpty(t, vm)
})
t.Run("Success with JWK", func(t *testing.T) {
didDocResolution := api.NewDIDDocResolution(jwkDIDDocResolution)

vm, err := didDocResolution.AssertionMethod()
require.NoError(t, err)
require.NotEmpty(t, vm)
})
t.Run("Fail to parse DID document resolution content", func(t *testing.T) {
didDocResolution := api.NewDIDDocResolution([]byte{})

Expand All @@ -51,4 +64,11 @@ func TestDIDDocResolution_AssertionMethodKeyID(t *testing.T) {
"unexpected end of JSON input")
require.Empty(t, vm)
})
t.Run("DID document missing assertion method", func(t *testing.T) {
didDocResolution := api.NewDIDDocResolution(invalidDIDDocResolution)

vm, err := didDocResolution.AssertionMethod()
require.EqualError(t, err, "DID provided has no assertion method to use as a default signing key")
require.Empty(t, vm)
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"@context": "https://w3id.org/did-resolution/v1",
"didDocument": {
"@context": [
"https://www.w3.org/ns/did/v1"
],
"id": "did:example:foo123"
}
}
24 changes: 24 additions & 0 deletions cmd/wallet-sdk-gomobile/api/testdata/doc_with_jwk.jsonld
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"@context": "https://w3id.org/did-resolution/v1",
"didDocument": {
"@context": [
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/suites/jws-2020/v1"
],
"id": "did:example:foo123",
"verificationMethod": [
{
"controller":"did:example:foo123",
"id": "#O6J3tndxXb9k73LxVtxxeH0wot0eIC5jPOrNk5EX7zg",
"publicKeyJwk": {
"kty": "OKP",
"crv": "Ed25519",
"x": "56VwrWF6RJ_CI5rkVMFvW52H6w5B-Juf5AvEyH4PxJ8"
},
"type": "JsonWebKey2020"
}
],
"authentication": [ "#O6J3tndxXb9k73LxVtxxeH0wot0eIC5jPOrNk5EX7zg" ],
"assertionMethod": [ "#O6J3tndxXb9k73LxVtxxeH0wot0eIC5jPOrNk5EX7zg" ]
}
}
72 changes: 72 additions & 0 deletions cmd/wallet-sdk-gomobile/localkms/localkms_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@ SPDX-License-Identifier: Apache-2.0
package localkms_test

import (
"fmt"
"testing"

"github.com/btcsuite/btcutil/base58"
"github.com/hyperledger/aries-framework-go/component/storageutil/mem"
"github.com/hyperledger/aries-framework-go/pkg/doc/jose/jwk/jwksupport"
arieskms "github.com/hyperledger/aries-framework-go/pkg/kms"
"github.com/stretchr/testify/require"

"github.com/trustbloc/wallet-sdk/cmd/wallet-sdk-gomobile/api"
Expand Down Expand Up @@ -58,13 +63,80 @@ func TestSignerCreator_Create(t *testing.T) {
require.EqualError(t, err, "failed to unmarshal verification method JSON into a did.VerificationMethod")
require.Nil(t, signer)
})
t.Run("fail to parse verification method JWK", func(t *testing.T) {
signerCreator := newSignerCreator(t)

signer, err := signerCreator.Create(&api.JSONObject{
Data: []byte(`{
"id": "foo",
"type": "JsonWebKey2020",
"publicKeyJwk": {}
}`),
})
require.EqualError(t, err, "failed to unmarshal verification method JSON into a did.VerificationMethod")
require.Nil(t, signer)
})
t.Run("Failed to create Aries signer", func(t *testing.T) {
signerCreator := newSignerCreator(t)

signer, err := signerCreator.Create(&api.JSONObject{Data: []byte("{}")})
require.EqualError(t, err, "failed to create Aries signer: parsing verification method: vm.Type '' not supported")
require.Nil(t, signer)
})
t.Run("success - verification method with raw key bytes", func(t *testing.T) {
kmsStore, err := arieskms.NewAriesProviderWrapper(mem.NewProvider())
require.NoError(t, err)

keyManager, err := localkms.NewKMS(kmsStore)
require.NoError(t, err)

key, err := keyManager.Create(localkms.KeyTypeED25519)
require.NoError(t, err)

signerCreator, err := localkms.NewSignerCreator(keyManager)
require.NoError(t, err)
require.NotNil(t, signerCreator)

signer, err := signerCreator.Create(&api.JSONObject{
Data: []byte(fmt.Sprintf(`{
"id": "%s",
"type": "Ed25519VerificationKey2018",
"publicKeyBase58": "%s"
}`, key.KeyID, base58.Encode(key.PubKey))),
})
require.NoError(t, err)
require.NotNil(t, signer)
})
t.Run("success - JWK verification method", func(t *testing.T) {
kmsStore, err := arieskms.NewAriesProviderWrapper(mem.NewProvider())
require.NoError(t, err)

keyManager, err := localkms.NewKMS(kmsStore)
require.NoError(t, err)

key, err := keyManager.Create(localkms.KeyTypeED25519)
require.NoError(t, err)

jwk, err := jwksupport.PubKeyBytesToJWK(key.PubKey, arieskms.ED25519Type)
require.NoError(t, err)

jwkBytes, err := jwk.MarshalJSON()
require.NoError(t, err)

signerCreator, err := localkms.NewSignerCreator(keyManager)
require.NoError(t, err)
require.NotNil(t, signerCreator)

signer, err := signerCreator.Create(&api.JSONObject{
Data: []byte(fmt.Sprintf(`{
"id": "%s",
"type": "JsonWebKey2020",
"publicKeyJwk": %s
}`, key.KeyID, string(jwkBytes))),
})
require.NoError(t, err)
require.NotNil(t, signer)
})
}

func newSignerCreator(t *testing.T) *localkms.SignerCreator {
Expand Down
55 changes: 51 additions & 4 deletions cmd/wallet-sdk-gomobile/localkms/signercreator.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import (
"encoding/json"
"fmt"

"github.com/btcsuite/btcutil/base58"
"github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto"
"github.com/hyperledger/aries-framework-go/pkg/doc/did"
"github.com/hyperledger/aries-framework-go/pkg/doc/jose/jwk"
"github.com/hyperledger/aries-framework-go/pkg/doc/util/didsignjwt"

"github.com/trustbloc/wallet-sdk/cmd/wallet-sdk-gomobile/api"
Expand Down Expand Up @@ -46,14 +48,12 @@ func CreateSignerCreator(kms *KMS) (*SignerCreator, error) {
ariesSignerCreator := didsignjwt.UseDefaultSigner(ariesKMS, tinkCrypto)

gomobileSignerCreator := func(verificationMethod *api.JSONObject) (api.Signer, error) {
var vm did.VerificationMethod

err := json.Unmarshal(verificationMethod.Data, &vm)
vm, err := workaroundUnmarshalVM(verificationMethod.Data)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal verification method JSON into a did.VerificationMethod")
}

ariesSigner, err := ariesSignerCreator(&vm)
ariesSigner, err := ariesSignerCreator(vm)
if err != nil {
return nil, fmt.Errorf("failed to create Aries signer: %w", err)
}
Expand All @@ -63,3 +63,50 @@ func CreateSignerCreator(kms *KMS) (*SignerCreator, error) {

return &SignerCreator{didJWTSignerCreate: gomobileSignerCreator}, nil
}

func workaroundUnmarshalVM(vmBytes []byte) (*did.VerificationMethod, error) {
vmMap := map[string]json.RawMessage{}

err := json.Unmarshal(vmBytes, &vmMap)
if err != nil {
return nil, err
}

var (
jsonKey jwk.JWK
id string
typ string
controller string
value []byte
)

id = stringEntry(vmMap["id"])
typ = stringEntry(vmMap["type"])
controller = stringEntry(vmMap["controller"])

rawValue := stringEntry(vmMap["publicKeyBase58"])
if rawValue != "" {
value = base58.Decode(rawValue)
}

if jwkBytes, ok := vmMap["publicKeyJwk"]; ok {
err = json.Unmarshal(jwkBytes, &jsonKey)
if err != nil {
return nil, err
}
}

if jsonKey.Valid() {
return did.NewVerificationMethodFromJWK(id, typ, controller, &jsonKey)
}

return did.NewVerificationMethodFromBytes(id, typ, controller, value), nil
}

func stringEntry(entry json.RawMessage) string {
if len(entry) > 1 {
return string(entry[1 : len(entry)-1])
}

return ""
}
25 changes: 24 additions & 1 deletion cmd/wallet-sdk-gomobile/openid4ci/interaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"encoding/json"
"fmt"

"github.com/btcsuite/btcutil/base58"
"github.com/hyperledger/aries-framework-go/pkg/doc/did"
"github.com/hyperledger/aries-framework-go/pkg/doc/util/didsignjwt"

Expand Down Expand Up @@ -149,7 +150,7 @@ func (i *Interaction) ResolveDisplay(preferredLocale string) (*api.JSONObject, e

func unwrapConfig(config *ClientConfig) *openid4cigoapi.ClientConfig {
goAPISignerGetter := func(vm *did.VerificationMethod) (didsignjwt.Signer, error) {
vmBytes, err := json.Marshal(vm)
vmBytes, err := workaroundMarshalVM(vm)
if err != nil {
return nil, err
}
Expand All @@ -169,3 +170,25 @@ func unwrapConfig(config *ClientConfig) *openid4cigoapi.ClientConfig {
DIDResolver: &gomobilewrappers.VDRResolverWrapper{DIDResolver: config.DIDResolver},
}
}

func workaroundMarshalVM(vm *did.VerificationMethod) ([]byte, error) {
rawVM := map[string]interface{}{
"id": vm.ID,
"type": vm.Type,
"controller": vm.Controller,
}

jsonKey := vm.JSONWebKey()
if jsonKey != nil {
jwkBytes, err := jsonKey.MarshalJSON()
if err != nil {
return nil, err
}

rawVM["publicKeyJwk"] = json.RawMessage(jwkBytes)
} else {
rawVM["publicKeyBase58"] = base58.Encode(vm.Value)
}

return json.Marshal(rawVM)
}
Loading

0 comments on commit ae3270b

Please sign in to comment.