Skip to content

Commit

Permalink
Update PortableDID (#19)
Browse files Browse the repository at this point in the history
* fix typos
* update `PortableDID` to reflect changes discussed for all web5 sdks
  • Loading branch information
mistermoe authored Feb 4, 2024
1 parent 180452e commit 947009d
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 68 deletions.
73 changes: 28 additions & 45 deletions dids/did/bearerdid.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package did

import (
"fmt"

"github.com/tbd54566975/web5-go/crypto"
"github.com/tbd54566975/web5-go/dids/didcore"
"github.com/tbd54566975/web5-go/jwk"
Expand All @@ -17,68 +15,53 @@ type BearerDID struct {
Document didcore.Document
}

// ToKeys exports a BearerDID into a portable format that contains the DID's URI in addition to
// every private key associated with a verification method.
func (d *BearerDID) ToKeys() (PortableDID, error) {
exporter, ok := d.KeyManager.(crypto.KeyExporter)
if !ok {
return PortableDID{}, fmt.Errorf("key manager does not implement KeyExporter")
// ToPortableDID exports a BearerDID to a portable format
func (d *BearerDID) ToPortableDID() (PortableDID, error) {
portableDID := PortableDID{
URI: d.URI,
Document: d.Document,
}

portableDID := PortableDID{URI: d.URI}
keys := make([]VerificationMethodKeyPair, 0)
exporter, ok := d.KeyManager.(crypto.KeyExporter)
if ok {
privateKeys := make([]jwk.JWK, 0)

for _, vm := range d.Document.VerificationMethod {
keyAlias, err := vm.PublicKeyJwk.ComputeThumbprint()
if err != nil {
continue
}
for _, vm := range d.Document.VerificationMethod {
keyAlias, err := vm.PublicKeyJwk.ComputeThumbprint()
if err != nil {
continue
}

key, err := exporter.ExportKey(keyAlias)
if err != nil {
// TODO: decide if we want to blow up or continue
continue
}

key, err := exporter.ExportKey(keyAlias)
if err != nil {
continue
privateKeys = append(privateKeys, key)
}

keys = append(keys, VerificationMethodKeyPair{
PublicKeyJWK: vm.PublicKeyJwk,
PrivateKeyJWK: key,
})
portableDID.PrivateKeys = privateKeys
}

portableDID.VerificationMethod = keys

return portableDID, nil
}

// FromKeys imports a BearerDID from a portable format that contains the DID's URI in addition to
// every private key associated with a verification method.
func BearerDIDFromKeys(portableDID PortableDID) (BearerDID, error) {
didURI, err := Parse(portableDID.URI)
// FromPortableDID inflates a BearerDID from a portable format.
func FromPortableDID(portableDID PortableDID) (BearerDID, error) {
did, err := Parse(portableDID.URI)
if err != nil {
return BearerDID{}, err
}

keyManager := crypto.NewLocalKeyManager()
for _, vm := range portableDID.VerificationMethod {
keyManager.ImportKey(vm.PrivateKeyJWK)
for _, key := range portableDID.PrivateKeys {
keyManager.ImportKey(key)
}

return BearerDID{
DID: didURI,
DID: did,
KeyManager: keyManager,
Document: portableDID.Document,
}, nil
}

// PortableDID is a serializable BearerDID. VerificationMethod contains the private key
// of each verification method that the BearerDID's key manager contains
type PortableDID struct {
URI string `json:"uri"`
VerificationMethod []VerificationMethodKeyPair `json:"verificationMethod"`
}

// VerificationMethodKeyPair is a public/private keypair associated to a
// BearerDID's verification method. Used in PortableDID
type VerificationMethodKeyPair struct {
PublicKeyJWK *jwk.JWK `json:"publicKeyJwk"`
PrivateKeyJWK jwk.JWK `json:"privateKeyJwk"`
}
17 changes: 8 additions & 9 deletions dids/did/bearerdid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,29 @@ import (
"github.com/tbd54566975/web5-go/jws"
)

func Test_ToKeys(t *testing.T) {
func TestToPortableDID(t *testing.T) {
did, err := didjwk.Create()
assert.NoError(t, err)

portableDID, err := did.ToKeys()
portableDID, err := did.ToPortableDID()
assert.NoError(t, err)

assert.Equal[string](t, did.URI, portableDID.URI)
assert.True(t, len(portableDID.VerificationMethod) == 1, "expected 1 key")
assert.True(t, len(portableDID.PrivateKeys) == 1, "expected 1 key")

vm := portableDID.VerificationMethod[0]
key := portableDID.PrivateKeys[0]

assert.NotEqual(t, *vm.PublicKeyJWK, jwk.JWK{}, "expected publicKeyJwk to not be empty")
assert.NotEqual(t, vm.PrivateKeyJWK, jwk.JWK{}, "expected privateKeyJWK to not be empty")
assert.NotEqual(t, key, jwk.JWK{}, "expected key to not be empty")
}

func TestBearerDIDFromKeys(t *testing.T) {
func TestFromPortableDID(t *testing.T) {
bearerDID, err := didjwk.Create()
assert.NoError(t, err)

portableDID, err := bearerDID.ToKeys()
portableDID, err := bearerDID.ToPortableDID()
assert.NoError(t, err)

importedDID, err := did.BearerDIDFromKeys(portableDID)
importedDID, err := did.FromPortableDID(portableDID)
assert.NoError(t, err)

compactJWS, err := jws.Sign("hi", bearerDID)
Expand Down
22 changes: 22 additions & 0 deletions dids/did/portabledid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package did

import (
"github.com/tbd54566975/web5-go/dids/didcore"
"github.com/tbd54566975/web5-go/jwk"
)

// PortableDID is a serializable BearerDID. VerificationMethod contains the private key
// of each verification method that the BearerDID's key manager contains
type PortableDID struct {
// URI is the DID string as per https://www.w3.org/TR/did-core/#did-syntax
URI string `json:"uri"`
// PrivateKeys is an array of private keys associated to the BearerDID's verification methods
// Note: PrivateKeys will be empty if the BearerDID was created using a KeyManager that does not
// support exporting private keys (e.g. HSM based KeyManagers)
PrivateKeys []jwk.JWK `json:"privateKeys"`
// Document is the DID Document associated to the BearerDID
Document didcore.Document `json:"document"`
// Metadata is a map that can be used to store additional method specific data
// that is necessary to inflate a BearerDID from a PortableDID
Metadata map[string]interface{} `json:"metadata"`
}
8 changes: 4 additions & 4 deletions jws/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
- [Features](#features)
- [Usage](#usage)
- [Signing:](#signing)
- [Detatched Content](#detatched-content)
- [Detached Content](#detached-content)
- [Verifying](#verifying)
- [Directory Structure](#directory-structure)
- [Rationale](#rationale)
Expand Down Expand Up @@ -47,9 +47,9 @@ func main() {
}
```

## Detatched Content
## Detached Content

returning a JWS with detatched content can be done like so:
returning a JWS with detached content can be done like so:

```go
package main
Expand All @@ -69,7 +69,7 @@ func main() {

payload := map[string]interface{}{"hello": "world"}

compactJWS, err := jws.Sign(payload, did, Detatched(true))
compactJWS, err := jws.Sign(payload, did, Detached(true))
if err != nil {
fmt.Printf("failed to sign: %v", err)
return
Expand Down
15 changes: 5 additions & 10 deletions jws/jws.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,11 @@ func Purpose(purpose string) SignOpts {
}
}

// DetatchedPayload is an option that can be passed to [github.com/tbd54566975/web5-go/jws.Sign].
// DetachedPayload is an option that can be passed to [github.com/tbd54566975/web5-go/jws.Sign].
// It is used to indicate whether the payload should be included in the signature.
// More details can be found in [Specification].
// [Specification]: https://datatracker.ietf.org/doc/html/rfc7515#appendix-F
func DetatchedPayload(detached bool) SignOpts {
func DetachedPayload(detached bool) SignOpts {
return func(opts *signOpts) {
opts.detached = detached
}
Expand All @@ -92,17 +92,12 @@ func Sign(payload JWSPayload, did did.BearerDID, opts ...SignOpts) (string, erro
opt(&o)
}

resolutionResult, err := dids.Resolve(did.URI)
if err != nil {
return "", fmt.Errorf("failed to resolve DID: %w", err)
}

var verificationMethodID string
switch o.purpose {
case "assertionMethod":
verificationMethodID = resolutionResult.Document.AssertionMethod[0]
verificationMethodID = did.Document.AssertionMethod[0]
case "authentication":
verificationMethodID = resolutionResult.Document.Authentication[0]
verificationMethodID = did.Document.Authentication[0]
default:
return "", fmt.Errorf("unsupported purpose: %s", o.purpose)
}
Expand All @@ -112,7 +107,7 @@ func Sign(payload JWSPayload, did did.BearerDID, opts ...SignOpts) (string, erro
}

var verificationMethod didcore.VerificationMethod
for _, vm := range resolutionResult.Document.VerificationMethod {
for _, vm := range did.Document.VerificationMethod {
if vm.ID == verificationMethodID {
verificationMethod = vm
break
Expand Down

0 comments on commit 947009d

Please sign in to comment.