Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Tooling] SLIP-0010 HD Child Key Generation #510

Merged
merged 39 commits into from
Feb 28, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
2f14790
Implement SLIP-0010 key derivation
h5law Feb 11, 2023
d27b260
Add SLIPS test cases
h5law Feb 14, 2023
ff4f93a
Add endpoints to interact with slips keys
h5law Feb 14, 2023
5736220
Reduce scope
h5law Feb 14, 2023
459edf7
Add GetSeed() function
h5law Feb 14, 2023
4b7fe83
Update docs
h5law Feb 14, 2023
db1a2dc
Change SLIPS to SLIP
h5law Feb 14, 2023
4f4dc10
Address linter errors
h5law Feb 14, 2023
2ae5eda
Merge branch 'main' into slip-0010-keys
h5law Feb 14, 2023
f99a56e
Remove BIP-44 paths
h5law Feb 21, 2023
aaa86a1
Link to SLIP spec
h5law Feb 21, 2023
6c9601d
Address comments
h5law Feb 21, 2023
24dd94c
Add SLIP-0010 test vectors
h5law Feb 21, 2023
962ba1d
Add BIP-44 paths back in
h5law Feb 21, 2023
c82cd0e
Update go.mod
h5law Feb 21, 2023
879eb17
Merge branch 'main' into slip-0010-keys
h5law Feb 21, 2023
10312e2
Add comments to clarify test vectors
h5law Feb 22, 2023
6675cd7
Merge branch 'main' into slip-0010-keys
h5law Feb 23, 2023
644b614
Fix merge error
h5law Feb 23, 2023
7024b64
Add DeriveChild method to KeyPair interface
h5law Feb 23, 2023
8bf2eb6
Combine test vectors
h5law Feb 23, 2023
35fbacb
Update graphs
h5law Feb 23, 2023
851c9c1
Address comments
h5law Feb 23, 2023
645be44
Add pokt specific test vectors
h5law Feb 23, 2023
e339e23
Add deriveChild CLI endpoint
h5law Feb 23, 2023
5e34cd5
Return (KeyPair, error) when storing derived child
h5law Feb 23, 2023
9a23603
Fix tests
h5law Feb 23, 2023
8a51fcd
make generate_cli_commands_docs
h5law Feb 23, 2023
749c3cf
Address comments
h5law Feb 24, 2023
fb6f3b1
make generate_cli_commands_docs
h5law Feb 24, 2023
9b9da19
Address linter error
h5law Feb 24, 2023
2df7edf
Update comments to match godoc format
h5law Feb 24, 2023
a667312
Merge branch 'main' into slip-0010-keys
h5law Feb 24, 2023
d93431d
Remove viper binding and pass store as argument to function
h5law Feb 26, 2023
b9fa9dd
Merge branch 'main' into slip-0010-keys
h5law Feb 26, 2023
3057fda
Address comments
h5law Feb 27, 2023
4282c91
Update comments
h5law Feb 28, 2023
69b310a
Update CHANGELOG.md files
h5law Feb 28, 2023
61aece6
Fix linter error
h5law Feb 28, 2023
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
70 changes: 70 additions & 0 deletions app/client/cli/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/pokt-network/pocket/shared/crypto"
utilTypes "github.com/pokt-network/pocket/utility/types"
"path/filepath"
"strconv"
"strings"

"github.com/pokt-network/pocket/app/client/keybase"
Expand All @@ -23,6 +24,9 @@ var (
importAs string
hint string
newPwd string
storeChild bool
childPwd string
childHint string
)

func init() {
Expand All @@ -45,6 +49,7 @@ func NewKeysCommand() *cobra.Command {
importCmds := keysImportCommands()
signMsgCmds := keysSignMsgCommands()
signTxCmds := keysSignTxCommands()
slipCmds := keysSlipCommands()

// Add --pwd and --hint flags
applySubcommandOptions(createCmds, attachPwdFlagToSubcommands())
Expand Down Expand Up @@ -77,6 +82,12 @@ func NewKeysCommand() *cobra.Command {
applySubcommandOptions(signTxCmds, attachInputFlagToSubcommands())
applySubcommandOptions(signTxCmds, attachOutputFlagToSubcommands())

// Add --pwd, --store_child, --child_pwd, --child_hint flags
applySubcommandOptions(slipCmds, attachPwdFlagToSubcommands())
applySubcommandOptions(slipCmds, attachStoreChildFlagToSubcommands())
applySubcommandOptions(slipCmds, attachChildPwdFlagToSubcommands())
applySubcommandOptions(slipCmds, attachChildHintFlagToSubcommands())

cmd.AddCommand(createCmds...)
cmd.AddCommand(updateCmds...)
cmd.AddCommand(deleteCmds...)
Expand All @@ -85,6 +96,7 @@ func NewKeysCommand() *cobra.Command {
cmd.AddCommand(importCmds...)
cmd.AddCommand(signMsgCmds...)
cmd.AddCommand(signTxCmds...)
cmd.AddCommand(slipCmds...)

return cmd
}
Expand Down Expand Up @@ -680,3 +692,61 @@ func keysSignTxCommands() []*cobra.Command {
}
return cmds
}

func keysSlipCommands() []*cobra.Command {
cmds := []*cobra.Command{
{
Use: "DeriveChild <parentAddrHex> <index>",
Short: "Derive the child key at the given index from a parent key",
Long: "Derive the child key at <index> from the parent key provided optionally store it in the keybase with [--store_child]",
Aliases: []string{"derivechild"},
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
// Unpack CLI args
parentAddr := args[0]
idx64, err := strconv.ParseUint(args[1], 10, 32)
if err != nil {
return err
}
index := uint32(idx64)

// Open the debug keybase at the specified path
pocketDir := strings.TrimSuffix(dataDir, "/")
keybasePath, err := filepath.Abs(pocketDir + keybaseSuffix)
if err != nil {
return err
}
kb, err := keybase.NewKeybase(keybasePath)
if err != nil {
return err
}

if !nonInteractive {
pwd = readPassphrase(pwd)
}

var kp crypto.KeyPair
if storeChild {
kp, err = kb.StoreChildFromKey(parentAddr, pwd, index, childPwd, childHint)
if err != nil {
return err
}
logger.Global.Info().Str("address", kp.GetAddressString()).Str("parent", parentAddr).Uint32("index", index).Msg("Child key stored in keybase")
} else {
kp, err = kb.DeriveChildFromKey(parentAddr, pwd, index)
if err != nil {
return err
}
logger.Global.Info().Str("address", kp.GetAddressString()).Str("parent", parentAddr).Uint32("index", index).Msg("Child key derived")
}

if err := kb.Stop(); err != nil {
return err
}

return nil
},
},
}
return cmds
}
18 changes: 18 additions & 0 deletions app/client/cli/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,24 @@ func attachHintFlagToSubcommands() []cmdOption {
}}
}

func attachStoreChildFlagToSubcommands() []cmdOption {
return []cmdOption{func(c *cobra.Command) {
c.Flags().BoolVar(&storeChild, "store_child", false, "store the derived child key in the keybase")
}}
}

func attachChildHintFlagToSubcommands() []cmdOption {
return []cmdOption{func(c *cobra.Command) {
c.Flags().StringVar(&childHint, "child_hint", "", "hint for the passphrase of the derived child's private key")
}}
}

func attachChildPwdFlagToSubcommands() []cmdOption {
return []cmdOption{func(c *cobra.Command) {
c.Flags().StringVar(&childPwd, "child_pwd", "", "passphrase for the derived child's private key")
}}
}

func unableToConnectToRpc(err error) error {
fmt.Printf("❌ Unable to connect to the RPC @ %s\n\nError: %s", boldText(remoteCLIURL), err)
return nil
Expand Down
1 change: 1 addition & 0 deletions app/client/doc/commands/client_Keys.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Key specific commands
* [client](client.md) - Pocket Network Command Line Interface (CLI)
* [client Keys Create](client_Keys_Create.md) - Create new key
* [client Keys Delete](client_Keys_Delete.md) - Deletes the key from the keybase
* [client Keys DeriveChild](client_Keys_DeriveChild.md) - Derive the child key at the given index from a parent key
* [client Keys Export](client_Keys_Export.md) - Exports the private key as a raw string or JSON to either STDOUT or to a file
* [client Keys Get](client_Keys_Get.md) - Get the address and public key from the keybase
* [client Keys Import](client_Keys_Import.md) - Imports a key from a string or from a file
Expand Down
35 changes: 35 additions & 0 deletions app/client/doc/commands/client_Keys_DeriveChild.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
## client Keys DeriveChild

Derive the child key at the given index from a parent key

### Synopsis

Derive the child key at <index> from the parent key provided optionally store it in the keybase with [--store_child]

```
client Keys DeriveChild <parentAddrHex> <index> [flags]
```

### Options

```
--child_hint string hint for the passphrase of the derived child's private key
--child_pwd string passphrase for the derived child's private key
-h, --help help for DeriveChild
--pwd string passphrase used by the cmd, non empty usage bypass interactive prompt
--store_child store the derived child key in the keybase
```

### Options inherited from parent commands

```
--data_dir string Path to store pocket related data (keybase etc.) (default "/home/harry/.pocket")
--non_interactive if true skips the interactive prompts wherever possible (useful for scripting & automation)
--remote_cli_url string takes a remote endpoint in the form of <protocol>://<host> (uses RPC Port) (default "http://localhost:50832")
```

### SEE ALSO

* [client Keys](client_Keys.md) - Key specific commands

###### Auto generated by spf13/cobra on 23-Feb-2023
4 changes: 2 additions & 2 deletions app/client/keybase/keybase.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ type Keybase interface {
DeriveChildFromKey(masterAddrHex, passphrase string, childIndex uint32) (crypto.KeyPair, error)
h5law marked this conversation as resolved.
Show resolved Hide resolved
DeriveChildFromSeed(seed []byte, childIndex uint32) (crypto.KeyPair, error)
// Deterministically generate and store the derived child key in the keybase
StoreChildFromKey(masterAddrHex, masterPassphrase string, childIndex uint32, childPassphrase, childHint string) error
StoreChildFromSeed(seed []byte, childIndex uint32, childPassphrase, childHint string) error
StoreChildFromKey(masterAddrHex, masterPassphrase string, childIndex uint32, childPassphrase, childHint string) (crypto.KeyPair, error)
StoreChildFromSeed(seed []byte, childIndex uint32, childPassphrase, childHint string) (crypto.KeyPair, error)

// Accessors
Get(address string) (crypto.KeyPair, error)
Expand Down
64 changes: 36 additions & 28 deletions app/client/keybase/keystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import (
"bytes"
"encoding/hex"
"fmt"
"github.com/pokt-network/pocket/shared/converters"
"strings"

"github.com/pokt-network/pocket/shared/converters"

"github.com/dgraph-io/badger/v3"
"github.com/pokt-network/pocket/shared/crypto"
)
Expand Down Expand Up @@ -263,11 +264,11 @@ func (keybase *badgerKeybase) DeriveChildFromKey(masterAddrHex, passphrase strin
}

// Deterministically generate and store the ith child from the masterAddrHex key stored in the keybase
func (keybase *badgerKeybase) StoreChildFromSeed(seed []byte, childIndex uint32, childPassphrase, childHint string) error {
func (keybase *badgerKeybase) StoreChildFromSeed(seed []byte, childIndex uint32, childPassphrase, childHint string) (childKey crypto.KeyPair, err error) {
path := fmt.Sprintf(crypto.PoktAccountPathFormat, childIndex)
h5law marked this conversation as resolved.
Show resolved Hide resolved
childKey, err := crypto.DeriveChild(path, seed)
childKey, err = crypto.DeriveChild(path, seed)
if err != nil {
return err
return nil, err
}
// No need to re-encrypt with provided passphrase
if childPassphrase == "" && childHint == "" {
Expand All @@ -283,40 +284,47 @@ func (keybase *badgerKeybase) StoreChildFromSeed(seed []byte, childIndex uint32,

return tx.Set(addrKey, keypairBz)
})
return err
}

// Re-encrypt child key with passphrase and hint
return keybase.db.Update(func(tx *badger.Txn) error {
// Get the private key hex string from the child key
privKeyHex, err := childKey.ExportString("") // No passphrase by default
if err != nil {
return err
}
return childKey, err
} else {
h5law marked this conversation as resolved.
Show resolved Hide resolved
// Re-encrypt child key with passphrase and hint
err = keybase.db.Update(func(tx *badger.Txn) error {
// Get the private key hex string from the child key
privKeyHex, err := childKey.ExportString("") // No passphrase by default
if err != nil {
return err
}

keyPair, err := crypto.CreateNewKeyFromString(privKeyHex, childPassphrase, childHint)
if err != nil {
return err
}
keyPair, err := crypto.CreateNewKeyFromString(privKeyHex, childPassphrase, childHint)
if err != nil {
return err
}

// Use key address as key in DB
addrKey := keyPair.GetAddressBytes()
// Use key address as key in DB
addrKey := keyPair.GetAddressBytes()

// Encode KeyPair into []byte for value
keypairBz, err := keyPair.Marshal()
if err != nil {
return err
}
// Encode KeyPair into []byte for value
keypairBz, err := keyPair.Marshal()
if err != nil {
return err
}

return tx.Set(addrKey, keypairBz)
})
return tx.Set(addrKey, keypairBz)
})
}

if err != nil {
return nil, err
}

return childKey, nil
}

// Deterministically generate and store the ith child from the masterAddrHex key stored in the keybase
func (keybase *badgerKeybase) StoreChildFromKey(masterAddrHex, masterPassphrase string, childIndex uint32, childPassphrase, childHint string) error {
func (keybase *badgerKeybase) StoreChildFromKey(masterAddrHex, masterPassphrase string, childIndex uint32, childPassphrase, childHint string) (crypto.KeyPair, error) {
masterPrivKey, err := keybase.GetPrivKey(masterAddrHex, masterPassphrase)
if err != nil {
return err
return nil, err
}
seed := masterPrivKey.Seed()
return keybase.StoreChildFromSeed(seed, childIndex, childPassphrase, childHint)
Expand Down
Loading