Skip to content

Commit

Permalink
GCloud backend and refactoring (#66)
Browse files Browse the repository at this point in the history
* Cloud KMS WIP #51

* Cloud KMS signing is working #51

* Cloud KMS key import #51

* Cloud KMS key import WIP #51

* Cloud KMS #51

* Tezos key encodings WIP #51

* Key decryption #51

* Please the linter

* Foolproofing

* All prefixes has been added for testing purposes #51

* Refactoring is almost done

* Missed files

* It builds

* Missed files

* KMS works fine

* Correct HTTP status codes

* Error handling fix

* GCP README with permission list

* Azure authentication library

* Azure refactoring

* Azure refactoring

* Azure signing

* Azure importing

* Azure health is not finished

* Un comment azure health check

* Azure health done

* Add file based backend

* Update go modules

* Change test for unlock message

* YubiHSM backend is ported

* Some gloss

* YubiHSM key import

* Move server to separate binary

* Documentation updated

* Documentation paths fixed

* Import options

* Import options doc

* file backend simplified

Co-authored-by: Simon B.Robert <simrob7000@gmail.com>
  • Loading branch information
e-asphyx and carte7000 committed Jan 21, 2020
1 parent aa3c62d commit 36df804
Show file tree
Hide file tree
Showing 68 changed files with 6,533 additions and 2,874 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,4 @@ sig

vendor/
.vscode
signatory
.devcontainer
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Security and convenience are typically at odds with each other. Signatory makes

## Quick Start

Coming soon
[See docs](/docs/README.md)

---

Expand Down Expand Up @@ -64,7 +64,7 @@ By supporting multiple Cloud KMS/HSM systems, we hope to help the network from c
| ---------------- | ----------- |
| Azure KMS | In Testing |
| YubiHSM2 | In Testing |
| Google Cloud KMS | Planned |
| Google Cloud KMS | In Testing |
| AWS KMS | Planned |

### Tezos Address Types
Expand All @@ -79,10 +79,10 @@ In Tezos, the signing algorithm you can infer from the first three characters of

| | tz1 | tz2 | tz3 |
| ---------------- | --- | --- | --- |
| Google Cloud KMS | | | |
| AWS KMS | | | |
| Azure KMS | | | |
| YubiHSM2 | | | |
| Google Cloud KMS | | | |
| AWS KMS | | | |
| Azure KMS | | | |
| YubiHSM2 | | | |

---

Expand Down
61 changes: 61 additions & 0 deletions cmd/commands/import.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package commands

import (
"fmt"
"syscall"

"github.com/ecadlabs/signatory/pkg/utils"
"github.com/spf13/cobra"
"golang.org/x/crypto/ssh/terminal"
)

func NewImportCommand(c *Context) *cobra.Command {
var (
vaultName string
password string
opt string
)

importCmd := &cobra.Command{
Use: "import",
Short: "Import Tezos private keys (edsk..., spsk..., p2sk...)",
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
o, err := utils.ParseMap(opt, ':', ',')
if err != nil {
return err
}

var passCB func() ([]byte, error)
if password != "" {
passCB = func() ([]byte, error) { return []byte(password), nil }
} else {
passCB = func() ([]byte, error) {
fmt.Print("Enter Password: ")
return terminal.ReadPassword(int(syscall.Stdin))
}
}

options := make(utils.Options)
for k, v := range o {
options[k] = v
}

for _, key := range args {
_, err := c.signatory.Import(c.Context, vaultName, key, passCB, options)
if err != nil {
return err
}
}

return nil
},
}

importCmd.Flags().StringVar(&vaultName, "vault", "", "Vault name for importing")
importCmd.Flags().StringVar(&password, "password", "", "Password for private key(s)")
importCmd.Flags().StringVarP(&opt, "opt", "o", "", "Options to be passed to the backend. Syntax: key:val[,...]")
cobra.MarkFlagRequired(importCmd.Flags(), "vault")

return importCmd
}
42 changes: 42 additions & 0 deletions cmd/commands/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package commands

import (
"os"
"text/template"

"github.com/spf13/cobra"
)

const listTemplateSrc = `{{range . -}}
Public Key Hash: {{.PublicKeyHash}}
Vault: {{.VaultName}}
ID: {{.ID}}
{{with .Policy -}}
Allowed Operations: {{.AllowedOperations}}
Allowed Kinds: {{.AllowedKinds}}
{{else -}}
*DISABLED*
{{end}}
{{end -}}
`

var (
listTpl = template.Must(template.New("list").Parse(listTemplateSrc))
)

func NewListCommand(c *Context) *cobra.Command {
listCmd := &cobra.Command{
Use: "list",
Short: "List public keys",
RunE: func(cmd *cobra.Command, args []string) error {
keys, err := c.signatory.ListPublicKeys(c.Context)
if err != nil {
return err
}

return listTpl.Execute(os.Stdout, keys)
},
}

return listCmd
}
85 changes: 85 additions & 0 deletions cmd/commands/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package commands

import (
"context"

"github.com/ecadlabs/signatory/pkg/config"
"github.com/ecadlabs/signatory/pkg/metrics"
"github.com/ecadlabs/signatory/pkg/signatory"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

// Context represents root command context shared with its children
type Context struct {
Context context.Context

config *config.Config
signatory *signatory.Signatory
}

// NewRootCommand returns new root command
func NewRootCommand(c *Context, name string) *cobra.Command {
var (
level string
configFile string
)

rootCmd := cobra.Command{
Use: name,
Short: "A Tezos Remote Signer for signing block-chain operations with private keys",
PersistentPreRunE: func(cmd *cobra.Command, args []string) (err error) {
// cmd always points to the top level command!!!
var conf config.Config
if err := conf.Read(configFile); err != nil {
return err
}

validate := config.Validator()
if err := validate.Struct(&conf); err != nil {
return err
}

lv, err := log.ParseLevel(level)
if err != nil {
return err
}

log.SetLevel(lv)

sigConf := signatory.Config{
Policy: conf.Tezos,
Vaults: conf.Vaults,
Interceptor: metrics.Interceptor,
Watermark: signatory.NewInMemoryWatermark(),
}

sig, err := signatory.NewSignatory(c.Context, &sigConf)
if err != nil {
return err
}

if err = sig.Unlock(c.Context); err != nil {
return err
}

c.config = &conf
c.signatory = sig
return nil
},
}

f := rootCmd.PersistentFlags()

f.StringVarP(&configFile, "config", "c", "signatory.yaml", "Config file path")
f.StringVar(&level, "log", "info", "Log level: [error, warn, info, debug, trace]")

return &rootCmd
}

/*
// Execute executes root command
func Execute(ctx context.Context) error {
return newRootCommand(ctx).Execute()
}
*/
67 changes: 67 additions & 0 deletions cmd/commands/serve.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package commands

import (
"context"
"net/http"
"time"

"github.com/ecadlabs/signatory/pkg/server"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

// NewServeCommand returns new root command
func NewServeCommand(c *Context) *cobra.Command {
serveCmd := cobra.Command{
Use: "serve",
Short: "Run a server",
RunE: func(cmd *cobra.Command, args []string) error {
srvConf := server.Server{
Address: c.config.Server.Address,
Signer: c.signatory,
}
srv := srvConf.New()
srvErrCh := make(chan error)
go func() {
srvErrCh <- srv.ListenAndServe()
}()

utilityConf := server.UtilityServer{
Address: c.config.Server.UtilityAddress,
Health: c.signatory,
}
utilitySrv := utilityConf.New()
utilityErrCh := make(chan error)
go func() {
utilityErrCh <- utilitySrv.ListenAndServe()
}()

select {
case <-c.Context.Done():
case err := <-srvErrCh:
return err
case err := <-utilityErrCh:
return err
}

log.Println("Shutting down...")

ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()

utilitySrv.Shutdown(ctx)
if err := <-utilityErrCh; err != nil && err != context.Canceled && err != http.ErrServerClosed {
return err
}

srv.Shutdown(ctx)
if err := <-srvErrCh; err != nil && err != context.Canceled && err != http.ErrServerClosed {
return err
}

return nil
},
}

return &serveCmd
}
1 change: 1 addition & 0 deletions cmd/genkey/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
genkey
91 changes: 91 additions & 0 deletions cmd/genkey/genkey.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package main

import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"flag"
"fmt"
"os"
"text/template"

"github.com/ecadlabs/signatory/pkg/cryptoutils"
"github.com/ecadlabs/signatory/pkg/tezos"
)

const genkeyTemplateSrc = `{{range . -}}
Private Key: {{.PrivateKey}}
Public Key: {{.PublicKey}}
Public Key Hash: {{.PublicKeyHash}}
{{end}}
`

var (
genkeyTpl = template.Must(template.New("list").Parse(genkeyTemplateSrc))
)

type tplData struct {
PrivateKey string
PublicKey string
PublicKeyHash string
}

func main() {
var (
keyType string
num int
)

flag.IntVar(&num, "n", 1, "Keys number")
flag.StringVar(&keyType, "t", "edsk", "Key type [edsk, spsk, p2sk]")
flag.Parse()

var data []*tplData
for i := 0; i < num; i++ {
var (
pk cryptoutils.PrivateKey
err error
)

switch keyType {
case "edsk":
_, pk, err = ed25519.GenerateKey(rand.Reader)
case "spsk":
pk, err = ecdsa.GenerateKey(cryptoutils.S256(), rand.Reader)
case "p2sk":
pk, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
default:
err = fmt.Errorf("Unknown key type: %s", keyType)
}

if err != nil {
fmt.Println(err)
os.Exit(1)
}

var d tplData
if d.PrivateKey, err = tezos.EncodePrivateKey(pk); err != nil {
fmt.Println(err)
os.Exit(1)

}
if d.PublicKey, err = tezos.EncodePublicKey(pk.Public()); err != nil {
fmt.Println(err)
os.Exit(1)

}
if d.PublicKeyHash, err = tezos.EncodePublicKeyHash(pk.Public()); err != nil {
fmt.Println(err)
os.Exit(1)

}
data = append(data, &d)
}

if err := genkeyTpl.Execute(os.Stdout, data); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
1 change: 1 addition & 0 deletions cmd/signatory-cli/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
signatory-cli
Loading

0 comments on commit 36df804

Please sign in to comment.