Skip to content

Commit

Permalink
feat: Sign DID Doc during creation in the wallet
Browse files Browse the repository at this point in the history
Did document will be signed during creation (in the wallet)

Closes hyperledger-archives#421

Signed-off-by: Sandra Vrtikapa <[email protected]>
  • Loading branch information
sandrask committed Oct 7, 2019
1 parent 6f2ca09 commit d01c345
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 17 deletions.
90 changes: 74 additions & 16 deletions pkg/wallet/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@ import (
"github.com/hyperledger/aries-framework-go/pkg/didcomm/crypto"
"github.com/hyperledger/aries-framework-go/pkg/didcomm/crypto/jwe/authcrypt"
"github.com/hyperledger/aries-framework-go/pkg/doc/did"
"github.com/hyperledger/aries-framework-go/pkg/doc/signature/ed25519signature2018"
"github.com/hyperledger/aries-framework-go/pkg/doc/signature/signer"
"github.com/hyperledger/aries-framework-go/pkg/storage"
)

const (
didFormat = "did:%s:%s"
didPKID = "%s#keys-%d"
didServiceID = "%s#endpoint-%d"
didFormat = "did:%s:%s"
didPKID = "%s#keys-%d"
didServiceID = "%s#endpoint-%d"
ed25519VerificationKeyType = "Ed25519VerificationKey2018"
)

// provider contains dependencies for the base wallet and is typically created by using aries.Context()
Expand Down Expand Up @@ -87,7 +90,11 @@ func (w *BaseWallet) CreateSigningKey() (string, error) {

// SignMessage sign a message using the private key associated with a given verification key.
func (w *BaseWallet) SignMessage(message []byte, fromVerKey string) ([]byte, error) {
return nil, fmt.Errorf("not implemented")
keyPair, err := w.getKey(fromVerKey)
if err != nil {
return nil, fmt.Errorf("failed to get key: %w", err)
}
return ed25519signature2018.New().Sign(keyPair.Priv, message)
}

// DecryptMessage decrypt message
Expand Down Expand Up @@ -157,30 +164,45 @@ func (w *BaseWallet) Close() error {

// CreateDID returns new DID Document
// TODO write the DID Doc to the chosen DID method.
func (w *BaseWallet) CreateDID(method string, opts ...DocOpts) (*did.Doc, error) {
// TODO remove lint when encryption key gets removed from function
func (w *BaseWallet) CreateDID(method string, opts ...DocOpts) (*did.Doc, error) { //nolint:funlen
docOpts := &createDIDOpts{}
// Apply options
for _, opt := range opts {
opt(docOpts)
}

// Generate key pair
pub, err := w.CreateEncryptionKey()
// TODO: remove generate key pair for encryption (there is no key type in DID Spec for this one)
// It seems that we only need one signing key in DID Doc and that we can
// generate encryption key from that signing key when we need it
pubEncryption, err := w.CreateEncryptionKey()
if err != nil {
return nil, fmt.Errorf("failed to create DID: %w", err)
}

// DID identifier
id := fmt.Sprintf(didFormat, method, pub[:16])
id := fmt.Sprintf(didFormat, method, pubEncryption[:16])

pubKeyEncryption := did.PublicKey{
ID: fmt.Sprintf(didPKID, id, 1),
Type: "Curve25519",
Controller: id,
Value: []byte(pubEncryption),
}

// Supporting only one public key now
pubKey := did.PublicKey{
ID: fmt.Sprintf(didPKID, id, 1),
// Generate key pair for signing
pubSigning, err := w.CreateSigningKey()
if err != nil {
return nil, fmt.Errorf("failed to create DID: %w", err)
}

pubKeySigning := did.PublicKey{
ID: fmt.Sprintf(didPKID, id, 2),
// TODO hardcoding public key type for now
// Should be dynamic for multi-key support
Type: "Ed25519VerificationKey2018",
Type: ed25519VerificationKeyType,
Controller: id,
Value: []byte(pub),
Value: []byte(pubSigning),
}

// Service model to be included only if service type is provided through opts
Expand All @@ -199,14 +221,50 @@ func (w *BaseWallet) CreateDID(method string, opts ...DocOpts) (*did.Doc, error)
// Created time
createdTime := time.Now()

return &did.Doc{
doc := &did.Doc{
Context: []string{did.Context},
ID: id,
PublicKey: []did.PublicKey{pubKey},
PublicKey: []did.PublicKey{pubKeyEncryption, pubKeySigning},
Service: service,
Created: &createdTime,
Updated: &createdTime,
}, nil
}

// TODO: Resolve signature type based on key type
signingContext := &signer.Context{
SignatureType: "Ed25519Signature2018",
Creator: pubKeySigning.ID,
Signer: newSigner(pubSigning, w),
}

return signDocument(signingContext, doc)
}

func newSigner(keyID string, wallet Crypto) *didSigner {
return &didSigner{keyID: keyID, wallet: wallet}
}

type didSigner struct {
keyID string
wallet Crypto
}

func (s *didSigner) Sign(doc []byte) ([]byte, error) {
return s.wallet.SignMessage(doc, s.keyID)
}

func signDocument(context *signer.Context, doc *did.Doc) (*did.Doc, error) {
docBytes, err := doc.JSONBytes()
if err != nil {
return nil, err
}

signedDocBytes, err := signer.New().Sign(context, docBytes)
if err != nil {
return nil, err
}

return did.ParseDocument(signedDocBytes)
}

// persistKey save key in storage
Expand Down
20 changes: 19 additions & 1 deletion pkg/wallet/wallet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ import (
"github.com/hyperledger/aries-framework-go/pkg/didcomm/crypto"
"github.com/hyperledger/aries-framework-go/pkg/didcomm/crypto/jwe/authcrypt"
"github.com/hyperledger/aries-framework-go/pkg/doc/did"
"github.com/hyperledger/aries-framework-go/pkg/doc/signature/signer"
"github.com/hyperledger/aries-framework-go/pkg/internal/mock/didcomm"
mockstorage "github.com/hyperledger/aries-framework-go/pkg/internal/mock/storage"
"github.com/hyperledger/aries-framework-go/pkg/storage"
)

const (
serviceEndpoint = "sample-endpoint.com"
serviceEndpoint = "https://abc.example.com/83hfh37dj"
serviceTypeDIDComm = "did-communication"
)

Expand Down Expand Up @@ -319,6 +320,12 @@ func TestBaseWallet_NewDID(t *testing.T) {

// verify DID identifier
require.Equal(t, didDoc.ID, fmt.Sprintf(didFormat, method, pub[:16]))

// check that document has been signed
require.True(t, len(didDoc.Proof) > 0)
require.Equal(t, didDoc.Proof[0].Type, "Ed25519Signature2018")
require.Equal(t, didDoc.ID+"#keys-2", didDoc.Proof[0].Creator)
require.NotEmpty(t, didDoc.Proof[0].ProofValue)
}

t.Run("create new DID with service type", func(t *testing.T) {
Expand Down Expand Up @@ -356,6 +363,17 @@ func TestBaseWallet_NewDID(t *testing.T) {
})
}

func TestSignDocumentError(t *testing.T) {
context := &signer.Context{
SignatureType: "Ed25519Signature2018",
}

signedDoc, err := signDocument(context, &did.Doc{})
require.NotNil(t, err)
require.Nil(t, signedDoc)
require.Contains(t, err.Error(), "creator is missing")
}

func newMockWalletProvider(storagePvdr *mockstorage.MockStoreProvider) *mockProvider {
return &mockProvider{storagePvdr}
}
Expand Down

0 comments on commit d01c345

Please sign in to comment.