Skip to content
This repository has been archived by the owner on Mar 5, 2022. It is now read-only.

feat: Sign config files in cli #149

Merged
merged 1 commit into from
May 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 37 additions & 21 deletions cmd/did-method-cli/createconfigcmd/createconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ package createconfigcmd
import (
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
Expand All @@ -18,8 +17,8 @@ import (
"strings"

docdid "github.com/hyperledger/aries-framework-go/pkg/doc/did"
"github.com/hyperledger/aries-framework-go/pkg/doc/jose"
"github.com/spf13/cobra"
gojose "github.com/square/go-jose/v3"
cmdutils "github.com/trustbloc/edge-core/pkg/utils/cmd"
tlsutils "github.com/trustbloc/edge-core/pkg/utils/tls"

Expand Down Expand Up @@ -79,10 +78,11 @@ type memberData struct {
Policy models.StakeholderSettings `json:"policy"`
// Endpoints is a list of sidetree endpoints owned by this stakeholder organization
Endpoints []string `json:"endpoints"`
// PrivateKeyJwk is privatekey jwk
PrivateKeyJwk json.RawMessage `json:"privateKeyJwk,omitempty"`
// PrivateKeyJwk is privatekey jwk file
PrivateKeyJwkPath string `json:"privateKeyJwkPath,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will need some additional followup.

a) the private key might not be in a file.
b) the private key can be protected.
c) might make sense to refactor how keys are associated to stakeholders.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do that as followup


jsonWebKey jose.JWK
jsonWebKey gojose.JSONWebKey
sigKey gojose.SigningKey
}

type didClient interface {
Expand Down Expand Up @@ -156,8 +156,10 @@ func createCreateConfigCmd() *cobra.Command {
}

func writeConfig(outputDirectory string, filesData map[string][]byte) error {
if err := os.MkdirAll(outputDirectory, 0700); err != nil {
return err
if outputDirectory != "" {
if err := os.MkdirAll(outputDirectory, 0700); err != nil {
return err
}
}

for k, v := range filesData {
Expand Down Expand Up @@ -189,9 +191,16 @@ func getConfig(cmd *cobra.Command) (*config, error) {
}

for _, member := range config.MembersData {
if err := member.jsonWebKey.UnmarshalJSON(member.PrivateKeyJwk); err != nil {
return nil, err
jwkData, err := ioutil.ReadFile(member.PrivateKeyJwkPath) //nolint: gosec
Copy link
Contributor

@troyronda troyronda May 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i hope we are not assuming all keys are available on a single system.

some stakeholders will sign their own copy on their own system.

Copy link
Contributor

@troyronda troyronda May 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i.e. this is somewhat like fabric endorsement - generally ask the stakeholder to sign it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if err != nil {
return nil, fmt.Errorf("failed to read jwk file '%s' : %w", member.PrivateKeyJwkPath, err)
}

if err := member.jsonWebKey.UnmarshalJSON(jwkData); err != nil {
return nil, fmt.Errorf("failed to unmarshal to jwk: %w", err)
}
// TODO add support for ECDSA using P-256 and SHA-256
member.sigKey = gojose.SigningKey{Key: member.jsonWebKey.Key, Algorithm: gojose.EdDSA}
}

return &config, nil
Expand Down Expand Up @@ -233,6 +242,7 @@ func createFlags(startCmd *cobra.Command) {

func createConfig(parameters *parameters) (map[string][]byte, error) {
filesData := make(map[string][]byte)
sigKeys := make([]gojose.SigningKey, 0)

consortium := models.Consortium{Domain: parameters.config.ConsortiumData.Domain,
Policy: parameters.config.ConsortiumData.Policy}
Expand Down Expand Up @@ -260,40 +270,46 @@ func createConfig(parameters *parameters) (map[string][]byte, error) {
return nil, err
}

jwsBytes, err := signConfig(stakeholderBytes)
jws, err := signConfig(stakeholderBytes, []gojose.SigningKey{member.sigKey})
if err != nil {
return nil, err
}

filesData[member.Domain] = jwsBytes
sigKeys = append(sigKeys, member.sigKey)

filesData[member.Domain] = []byte(jws)
}

consortiumBytes, err := json.Marshal(consortium)
if err != nil {
return nil, err
}

jwsBytes, err := signConfig(consortiumBytes)
jws, err := signConfig(consortiumBytes, sigKeys)
if err != nil {
return nil, err
}

filesData[consortium.Domain] = jwsBytes
filesData[consortium.Domain] = []byte(jws)

return filesData, nil
}

func signConfig(configBytes []byte) ([]byte, error) {
// TODO add logic for jws
// for now return dummy jws
// remove this code after adding logic for jws
m := make(map[string]interface{})
m["payload"] = base64.RawURLEncoding.EncodeToString(configBytes)
func signConfig(configBytes []byte, keys []gojose.SigningKey) (string, error) {
signer, err := gojose.NewMultiSigner(keys, nil)
if err != nil {
return "", err
}

jws, err := signer.Sign(configBytes)
if err != nil {
return "", err
}

return json.Marshal(m)
return jws.FullSerialize(), nil
}

func createDID(didClient didClient, sidetreeURL string, jwk *jose.JWK) (*docdid.Doc, error) {
func createDID(didClient didClient, sidetreeURL string, jwk *gojose.JSONWebKey) (*docdid.Doc, error) {
pubKey, err := jwk.Public().MarshalJSON()
if err != nil {
return nil, err
Expand Down
97 changes: 55 additions & 42 deletions cmd/did-method-cli/createconfigcmd/createconfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ SPDX-License-Identifier: Apache-2.0
package createconfigcmd

import (
"fmt"
"io/ioutil"
"os"
"testing"
Expand All @@ -21,7 +22,7 @@ import (
const flag = "--"

// nolint: gochecknoglobals
var configDataWithWrongJWK = `{
var configData = `{
"consortium_data": {
"domain": "consortium.net",
"policy": {
Expand All @@ -46,53 +47,19 @@ var configDataWithWrongJWK = `{
"http://endpoints.stakeholder.one/peer1/",
"http://endpoints.stakeholder.one/peer2/"
],
"privateKeyJwk": {
"kty": "OKP",
"kid": "key1",
"d": 1,
"crv": "Ed25519",
"x": "bWRCy8DtNhRO3HdKTFB2eEG5Ac1J00D0DQPffOwtAD0"
}
"privateKeyJwkPath": "%s"
}
]
}`

// nolint: gochecknoglobals
var configData = `{
"consortium_data": {
"domain": "consortium.net",
"policy": {
"cache": {
"max_age": 2419200
},
"num_queries": 2,
"history_hash": "SHA256",
"sidetree": {
"hash_algorithm": "SHA256",
"key_algorithm": "NotARealAlg2018",
"max_encoded_hash_length": 100,
"max_operation_size": 8192
}
}
},
"members_data": [
{
"domain": "stakeholder.one",
"policy": {"cache": {"max_age": 604800}},
"endpoints": [
"http://endpoints.stakeholder.one/peer1/",
"http://endpoints.stakeholder.one/peer2/"
],
"privateKeyJwk": {
var jwkData = `{
"kty": "OKP",
"kid": "key1",
"d": "-YawjZSeB9Rkdol9SHeOcT9hIvo_VuH6zM-pgtk3b10",
"crv": "Ed25519",
"x": "bWRCy8DtNhRO3HdKTFB2eEG5Ac1J00D0DQPffOwtAD0"
}
}
]
}`
}`

func TestCreateConfigCmdWithMissingArg(t *testing.T) {
t.Run("test missing arg sidetree url", func(t *testing.T) {
Expand Down Expand Up @@ -133,13 +100,43 @@ func TestCreateConfigCmd(t *testing.T) {
require.Contains(t, err.Error(), "failed to read config file")
})

t.Run("test wrong path for private key jwk", func(t *testing.T) {
cmd := GetCreateConfigCmd()

file, err := ioutil.TempFile("", "*.json")
require.NoError(t, err)

_, err = file.WriteString(fmt.Sprintf(configData, "notexist.json"))
require.NoError(t, err)

defer func() { require.NoError(t, os.Remove(file.Name())) }()

var args []string
args = append(args, sidetreeURLArg()...)
args = append(args, configFileArg(file.Name())...)

cmd.SetArgs(args)

err = cmd.Execute()
require.Error(t, err)
require.Contains(t, err.Error(), "failed to read jwk file")
})

t.Run("test wrong private key jwk", func(t *testing.T) {
cmd := GetCreateConfigCmd()

jwkFile, err := ioutil.TempFile("", "*.json")
require.NoError(t, err)

defer func() { require.NoError(t, os.Remove(jwkFile.Name())) }()

_, err = jwkFile.WriteString("wrongjwk")
require.NoError(t, err)

file, err := ioutil.TempFile("", "*.json")
require.NoError(t, err)

_, err = file.WriteString(configDataWithWrongJWK)
_, err = file.WriteString(fmt.Sprintf(configData, jwkFile.Name()))
require.NoError(t, err)

defer func() { require.NoError(t, os.Remove(file.Name())) }()
Expand All @@ -152,16 +149,24 @@ func TestCreateConfigCmd(t *testing.T) {

err = cmd.Execute()
require.Error(t, err)
require.Contains(t, err.Error(), "unable to read JWK")
require.Contains(t, err.Error(), "failed to unmarshal to jwk")
})

t.Run("test error from create did", func(t *testing.T) {
cmd := GetCreateConfigCmd()

jwkFile, err := ioutil.TempFile("", "*.json")
require.NoError(t, err)

defer func() { require.NoError(t, os.Remove(jwkFile.Name())) }()

_, err = jwkFile.WriteString(jwkData)
require.NoError(t, err)

file, err := ioutil.TempFile("", "*.json")
require.NoError(t, err)

_, err = file.WriteString(configData)
_, err = file.WriteString(fmt.Sprintf(configData, jwkFile.Name()))
require.NoError(t, err)

defer func() { require.NoError(t, os.Remove(file.Name())) }()
Expand All @@ -180,10 +185,18 @@ func TestCreateConfigCmd(t *testing.T) {
t.Run("test create config and write them to file", func(t *testing.T) {
os.Clearenv()

jwkFile, err := ioutil.TempFile("", "*.json")
require.NoError(t, err)

defer func() { require.NoError(t, os.Remove(jwkFile.Name())) }()

_, err = jwkFile.WriteString(jwkData)
require.NoError(t, err)

file, err := ioutil.TempFile("", "*.json")
require.NoError(t, err)

_, err = file.WriteString(configData)
_, err = file.WriteString(fmt.Sprintf(configData, jwkFile.Name()))
require.NoError(t, err)

defer func() { require.NoError(t, os.Remove(file.Name())) }()
Expand Down
2 changes: 2 additions & 0 deletions cmd/did-method-cli/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ replace github.com/trustbloc/trustbloc-did-method => ../..
require (
github.com/hyperledger/aries-framework-go v0.1.4-0.20200521101441-dcc599e23d09
github.com/spf13/cobra v1.0.0
github.com/square/go-jose/v3 v3.0.0-20191119004800-96c717272387
github.com/stretchr/testify v1.5.1
github.com/trustbloc/edge-core v0.1.3
github.com/trustbloc/trustbloc-did-method v0.0.0-00010101000000-000000000000
)
Expand Down
11 changes: 7 additions & 4 deletions cmd/did-method-cli/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5
github.com/aws/aws-sdk-go v1.25.39/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/bluele/gcache v0.0.0-20190518031135-bc40bd653833/go.mod h1:8c4/i2VlovMO2gBnHGQPN5EJw+H0lx1u/5p+cgsXtCk=
github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
Expand Down Expand Up @@ -73,6 +74,7 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
Expand Down Expand Up @@ -181,8 +183,8 @@ github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb6
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/square/go-jose v2.4.1+incompatible h1:KFYc54wTtgnd3x4B/Y7Zr1s/QaEx2BNzRsB3Hae5LHo=
github.com/square/go-jose v2.4.1+incompatible/go.mod h1:7MxpAF/1WTVUu8Am+T5kNy+t0902CaLWM4Z745MkOa8=
github.com/square/go-jose v2.5.1+incompatible h1:FC+BwI9FzJZWpKaE0yUhFNbp/CyFHndARzuGVME/LGk=
github.com/square/go-jose v2.5.1+incompatible/go.mod h1:7MxpAF/1WTVUu8Am+T5kNy+t0902CaLWM4Z745MkOa8=
github.com/square/go-jose/v3 v3.0.0-20191119004800-96c717272387 h1:PjfQbTWDEoNh4v+4NNirclXoCIxjjLXsqSAP1iYxuOM=
github.com/square/go-jose/v3 v3.0.0-20191119004800-96c717272387/go.mod h1:iYbsnddeHsxZC0AxvsQsVV1gPR8VPiSYT5FsUTeaEuY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand Down Expand Up @@ -278,6 +280,7 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
Expand All @@ -297,8 +300,8 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/square/go-jose.v2 v2.4.1 h1:H0TmLt7/KmzlrDOpa1F+zr0Tk90PbJYBfsVUmRLrf9Y=
gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w=
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
Expand Down
16 changes: 2 additions & 14 deletions cmd/did-method-cli/sample/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,7 @@
"http://endpoints.stakeholder.one/peer1/",
"http://endpoints.stakeholder.one/peer2/"
],
"privateKeyJwk": {
"kty": "OKP",
"kid": "key1",
"d": "CSLczqR1ly2lpyBcWne9gFKnsjaKJw0dKfoSQu7lNvg",
"crv": "Ed25519",
"x": "bWRCy8DtNhRO3HdKTFB2eEG5Ac1J00D0DQPffOwtAD0"
}
"privateKeyJwkPath": "./sample/stakeholderone_jwk.json"
},
{
"domain": "stakeholder.two",
Expand All @@ -38,13 +32,7 @@
"http://endpoints.stakeholder.two/peer1/",
"http://endpoints.stakeholder.two/peer2/"
],
"privateKeyJwk": {
"kty": "OKP",
"kid": "key1",
"d": "-YawjZSeB9Rkdol9SHeOcT9hIvo_VuH6zM-pgtk3b10",
"crv": "Ed25519",
"x": "8rfXFZNHZs9GYzGbQLYDasGUAm1brAgTLI0jrD4KheU"
}
"privateKeyJwkPath": "./sample/stakeholdertwo_jwk.json"
}
]
}
7 changes: 7 additions & 0 deletions cmd/did-method-cli/sample/stakeholderone_jwk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"kty": "OKP",
"kid": "key1",
"d": "CSLczqR1ly2lpyBcWne9gFKnsjaKJw0dKfoSQu7lNvg",
"crv": "Ed25519",
"x": "bWRCy8DtNhRO3HdKTFB2eEG5Ac1J00D0DQPffOwtAD0"
}
7 changes: 7 additions & 0 deletions cmd/did-method-cli/sample/stakeholdertwo_jwk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"kty": "OKP",
"kid": "key1",
"d": "-YawjZSeB9Rkdol9SHeOcT9hIvo_VuH6zM-pgtk3b10",
"crv": "Ed25519",
"x": "8rfXFZNHZs9GYzGbQLYDasGUAm1brAgTLI0jrD4KheU"
}
Loading