From 2f14790bc720d70290f6b0aa2a57c3f93c4d1f00 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sat, 11 Feb 2023 15:02:57 +0000 Subject: [PATCH 01/34] Implement SLIP-0010 key derivation --- app/client/keybase/keybase_test.go | 15 +++ shared/crypto/slip.go | 157 +++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 shared/crypto/slip.go diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go index aafbf0865..0634d79d5 100644 --- a/app/client/keybase/keybase_test.go +++ b/app/client/keybase/keybase_test.go @@ -27,6 +27,10 @@ const ( testJSONPubString = "408bec6320b540aa0cc86b3e633e214f2fd4dce4caa08f164fa3a9d3e577b46c" testJSONPrivString = "3554119cec1c0c8c5b3845a5d3fc6346eb44ed21aab5c063ae9b6b1d38bec275408bec6320b540aa0cc86b3e633e214f2fd4dce4caa08f164fa3a9d3e577b46c" testJSONString = `{"kdf":"scrypt","salt":"197d2754445a7e5ce3e6c8d7b1d0ff6f","secparam":"12","hint":"pocket wallet","ciphertext":"B/AORJrSeQrR5ewQGel4FeCCXscoCsMUzq9gXAAxDqjXMmMxa7TedBTuemtO82JyTCoQWFHbGxRx8A7IoETNh5T5yBAjNNrr7DDkVrcfSAM3ez9lQem17DsfowCvRtmbesDlvbSZMRy8mQgClLqWRN+c6W/fPQ/lxLUy1G1A965U/uImcMXzSwbfqYrBPEux"}` + + // SLIP-0010 Key + testSeedHex = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542" + testPrimarySlipAddr = "dbeed1c166fb8d1647559e4155eadeda2eca8c10" ) func TestKeybase_CreateNewKey(t *testing.T) { @@ -443,6 +447,17 @@ func TestKeybase_ExportJSON(t *testing.T) { require.Equal(t, privKey.String(), testPrivString) } +func TestKeybase_DerivePrimarySlipKey(t *testing.T) { + db := initDB(t) + defer stopDB(t, db) + + seed, err := hex.DecodeString(testSeedHex) + require.NoError(t, err) + kp, err := crypto.DeriveKeyFromPath(crypto.PoktPrimaryAccountPath, seed, testPassphrase, testHint) + require.NoError(t, err) + require.Equal(t, kp.GetAddressString(), testPrimarySlipAddr) +} + func initDB(t *testing.T) Keybase { db, err := NewKeybaseInMemory() require.NoError(t, err) diff --git a/shared/crypto/slip.go b/shared/crypto/slip.go new file mode 100644 index 000000000..5b4603af2 --- /dev/null +++ b/shared/crypto/slip.go @@ -0,0 +1,157 @@ +package crypto + +import ( + "bytes" + "crypto/hmac" + "crypto/sha512" + "encoding/binary" + "fmt" + "regexp" + "strconv" + "strings" +) + +const ( + // PoktAccountPrefix is the prefix used for HD key derivation where 635 is the SLIP-0044 coin type + PoktAccountPrefix = "m/44'/635'" + // PoktPrimaryAccountPath is the derivation path for the primary account + PoktPrimaryAccountPath = "m/44'/635'/0'" + // PoktAccountPathFormat is to be used with fmt.Sprintf to generate child keys + PoktAccountPathFormat = "m/44'/635'/%d" + // FirstHardenedKey is the index for the first hardened key for ed25519 keys + FirstHardenedKey = uint32(0x80000000) + MaxHardenedKey = ^uint32(0) + // As defined in: https://github.com/satoshilabs/slips/blob/master/slip-0010.md#master-key-generation + seedModifier = "ed25519 seed" +) + +var ( + ErrInvalidPath = fmt.Errorf("invalid BIP-44 derivation path") + ErrNoDerivation = fmt.Errorf("no derivation for an ed25519 key is possible") + pathRegex = regexp.MustCompile(`m(\/[0-9]+')+$`) +) + +type slipKey struct { + SecretKey []byte + ChainCode []byte +} + +// Derives a key from a BIP-44 path and a seed only operating on hardened ed25519 keys +func DeriveKeyFromPath(path string, seed []byte, passphrase, hint string) (KeyPair, error) { + segments, err := pathToSegments(path) + if err != nil { + return nil, err + } + + key, err := NewMasterKey(seed) + if err != nil { + return nil, err + } + + for _, i32 := range segments { + // Force hardened keys + if i32 >= MaxHardenedKey-FirstHardenedKey { + return nil, fmt.Errorf("hardened key index too large, max: %d, got: %d", MaxHardenedKey-FirstHardenedKey, i32) + } + i := i32 + FirstHardenedKey + + // Derive the correct child until final segment + key, err = key.DeriveChild(i) + if err != nil { + return nil, err + } + } + + return key.ConvertToKeypair(passphrase, hint) +} + +// Reference: https://github.com/satoshilabs/slips/blob/master/slip-0010.md#master-key-generation +func NewMasterKey(seed []byte) (*slipKey, error) { + // Create HMAC hash from curve and seed + hmacHash := hmac.New(sha512.New, []byte(seedModifier)) + if _, err := hmacHash.Write(seed); err != nil { + return nil, err + } + + // Convert hash to []byte + sum := hmacHash.Sum(nil) + + // Create SLIP-0010 Master key + key := &slipKey{ + SecretKey: sum[:32], + ChainCode: sum[32:], + } + + return key, nil +} + +// Reference: https://github.com/satoshilabs/slips/blob/master/slip-0010.md#private-parent-key--private-child-key +func (k *slipKey) DeriveChild(i uint32) (*slipKey, error) { + // Check i>=2^31 or else no public key can be derived for the ed25519 key + if i < FirstHardenedKey { + return nil, ErrNoDerivation + } + + // i is a hardened child compute the HMAC hash of the key + iBytes := make([]byte, 4) + binary.BigEndian.PutUint32(iBytes, i) + key := append([]byte{0x0}, k.SecretKey...) + data := append(key, iBytes...) + + hmacHash := hmac.New(sha512.New, k.ChainCode) + if _, err := hmacHash.Write(data); err != nil { + return nil, err + } + + // Convert hash to []byte + sum := hmacHash.Sum(nil) + + // Create SLIP-0010 Child key + child := &slipKey{ + SecretKey: sum[:32], + ChainCode: sum[32:], + } + return child, nil +} + +func (k *slipKey) ConvertToKeypair(passphrase, hint string) (KeyPair, error) { + // Generate PrivateKey interface form secret key + reader := bytes.NewReader(k.SecretKey) + privKey, err := GeneratePrivateKeyWithReader(reader) + if err != nil { + return nil, err + } + + // Armour and encrypt private key into JSON string + armouredStr, err := encryptArmourPrivKey(privKey, passphrase, hint) + if err != nil { + return nil, err + } + + // Return KeyPair interface + return &encKeyPair{ + PublicKey: privKey.PublicKey(), + PrivKeyArmour: armouredStr, + }, nil +} + +// Check the BIP-44 path provided is valid and return the []uint32 segments it contains +func pathToSegments(path string) ([]uint32, error) { + // Check whether the path is valid + if !pathRegex.MatchString(path) { + return nil, ErrInvalidPath + } + + // Split into segments and check for valid uint32 types + segments := make([]uint32, 0) + segs := strings.Split(path, "/") + for _, seg := range segs[1:] { + ui64, err := strconv.ParseUint(strings.TrimRight(seg, "'"), 10, 32) + if err != nil { + return nil, err + } + segments = append(segments, uint32(ui64)) + } + + return segments, nil +} From d27b260cd02868b614cac974c9d14af4f88c0c2f Mon Sep 17 00:00:00 2001 From: Harry Law Date: Tue, 14 Feb 2023 18:38:38 +0000 Subject: [PATCH 02/34] Add SLIPS test cases --- app/client/keybase/keybase_test.go | 66 ++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 7 deletions(-) diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go index 0634d79d5..f4fa03e3c 100644 --- a/app/client/keybase/keybase_test.go +++ b/app/client/keybase/keybase_test.go @@ -28,9 +28,8 @@ const ( testJSONPrivString = "3554119cec1c0c8c5b3845a5d3fc6346eb44ed21aab5c063ae9b6b1d38bec275408bec6320b540aa0cc86b3e633e214f2fd4dce4caa08f164fa3a9d3e577b46c" testJSONString = `{"kdf":"scrypt","salt":"197d2754445a7e5ce3e6c8d7b1d0ff6f","secparam":"12","hint":"pocket wallet","ciphertext":"B/AORJrSeQrR5ewQGel4FeCCXscoCsMUzq9gXAAxDqjXMmMxa7TedBTuemtO82JyTCoQWFHbGxRx8A7IoETNh5T5yBAjNNrr7DDkVrcfSAM3ez9lQem17DsfowCvRtmbesDlvbSZMRy8mQgClLqWRN+c6W/fPQ/lxLUy1G1A965U/uImcMXzSwbfqYrBPEux"}` - // SLIP-0010 Key - testSeedHex = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542" - testPrimarySlipAddr = "dbeed1c166fb8d1647559e4155eadeda2eca8c10" + // SLIPS-0010 + testChildAddr1 = "8b83d7057df7ac1d20a2f0aa0edadf206eb6764d" ) func TestKeybase_CreateNewKey(t *testing.T) { @@ -447,15 +446,68 @@ func TestKeybase_ExportJSON(t *testing.T) { require.Equal(t, privKey.String(), testPrivString) } -func TestKeybase_DerivePrimarySlipKey(t *testing.T) { +func TestKeybase_DeriveChildFromKey(t *testing.T) { db := initDB(t) defer stopDB(t, db) - seed, err := hex.DecodeString(testSeedHex) + err := db.ImportFromString(testPrivString, testPassphrase, testHint) + require.NoError(t, err) + + childKey, err := db.DeriveChildFromKey(testAddr, testPassphrase, 1) + require.NoError(t, err) + require.Equal(t, childKey.GetAddressString(), testChildAddr1) +} + +func TestKeybase_DeriveChildFromSeed(t *testing.T) { + db := initDB(t) + defer stopDB(t, db) + + err := db.ImportFromString(testPrivString, testPassphrase, testHint) require.NoError(t, err) - kp, err := crypto.DeriveKeyFromPath(crypto.PoktPrimaryAccountPath, seed, testPassphrase, testHint) + + kp, err := db.Get(testAddr) + require.NoError(t, err) + + seed, err := kp.GetSeed(testPassphrase) + require.NoError(t, err) + + childKey, err := db.DeriveChildFromSeed(seed, 1) require.NoError(t, err) - require.Equal(t, kp.GetAddressString(), testPrimarySlipAddr) + require.Equal(t, childKey.GetAddressString(), testChildAddr1) +} + +func TestKeybase_StoreChildFromKey(t *testing.T) { + db := initDB(t) + defer stopDB(t, db) + + err := db.ImportFromString(testPrivString, testPassphrase, testHint) + require.NoError(t, err) + + err = db.StoreChildFromKey(testAddr, testPassphrase, 1, testPassphrase, testHint) + require.NoError(t, err) + + childKey, err := db.GetPrivKey(testChildAddr1, testPassphrase) + require.Equal(t, childKey.Address().String(), testChildAddr1) +} + +func TestKeybase_StoreChildFromSeed(t *testing.T) { + db := initDB(t) + defer stopDB(t, db) + + err := db.ImportFromString(testPrivString, testPassphrase, testHint) + require.NoError(t, err) + + kp, err := db.Get(testAddr) + require.NoError(t, err) + + seed, err := kp.GetSeed(testPassphrase) + require.NoError(t, err) + + err = db.StoreChildFromSeed(seed, 1, testPassphrase, testHint) + require.NoError(t, err) + + childKey, err := db.GetPrivKey(testChildAddr1, testPassphrase) + require.Equal(t, childKey.Address().String(), testChildAddr1) } func initDB(t *testing.T) Keybase { From ff4f93a4f8ae5058b6b25e9b8b079fe25eadefa7 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Tue, 14 Feb 2023 18:39:07 +0000 Subject: [PATCH 03/34] Add endpoints to interact with slips keys --- app/client/keybase/keybase.go | 9 +++ app/client/keybase/keystore.go | 130 +++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) diff --git a/app/client/keybase/keybase.go b/app/client/keybase/keybase.go index 567fd2e7f..c2dc8fd86 100644 --- a/app/client/keybase/keybase.go +++ b/app/client/keybase/keybase.go @@ -14,6 +14,15 @@ type Keybase interface { // Insert a new keypair from the JSON string of the encrypted private key into the DB ImportFromJSON(jsonStr, passphrase string) error + // DISCUSSION: Is there a more appropriate way to handle the HD interactions? + // SLIPS-0010 Key Derivation + // Deterministically generate and return the child key + DeriveChildFromKey(masterAddrHex, passphrase string, childIndex int32) (crypto.KeyPair, error) + DeriveChildFromSeed(seed []byte, childIndex int32) (crypto.KeyPair, error) + // Store the derived child key in the keybase + StoreChildFromKey(masterAddrHex, masterPassphrase string, childIndex int32, childPassphrase, childHint string) error + StoreChildFromSeed(seed []byte, childIndex int32, childPassphrase, childHint string) error + // Accessors Get(address string) (crypto.KeyPair, error) GetPubKey(address string) (crypto.PublicKey, error) diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index 418816979..95a63aa43 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -223,6 +223,136 @@ func (keybase *badgerKeybase) GetAll() (addresses []string, keyPairs []crypto.Ke return addresses, keyPairs, nil } +// Deterministically generate and return the ith child key from the masterAddrHex key stored in the keybase +func (keybase *badgerKeybase) DeriveChildFromKey(masterAddrHex, passphrase string, childIndex int32) (crypto.KeyPair, error) { + privKey, err := keybase.GetPrivKey(masterAddrHex, passphrase) + if err != nil { + return nil, err + } + seed := privKey.Seed() + path := fmt.Sprintf(crypto.PoktAccountPathFormat, childIndex) + childKey, err := crypto.DeriveKeyFromPath(path, seed) + if err != nil { + return nil, err + } + return childKey, nil +} + +// Deterministically generate and return the ith child from the seed provided +func (keybase *badgerKeybase) DeriveChildFromSeed(seed []byte, childIndex int32) (crypto.KeyPair, error) { + path := fmt.Sprintf(crypto.PoktAccountPathFormat, childIndex) + childKey, err := crypto.DeriveKeyFromPath(path, seed) + 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 int32, childPassphrase, childHint string) error { + masterPrivKey, err := keybase.GetPrivKey(masterAddrHex, masterPassphrase) + if err != nil { + return err + } + seed := masterPrivKey.Seed() + path := fmt.Sprintf(crypto.PoktAccountPathFormat, childIndex) + childKey, err := crypto.DeriveKeyFromPath(path, seed) + if err != nil { + return err + } + // No need to re-encrypt with provided passphrase + if childPassphrase == "" && childHint == "" { + err = keybase.db.Update(func(tx *badger.Txn) error { + // Use key address as key in DB + addrKey := childKey.GetAddressBytes() + + // Encode KeyPair into []byte for value + keypairBz, err := childKey.Marshal() + if err != nil { + return err + } + + return tx.Set(addrKey, keypairBz) + }) + } else { + // 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 + } + + // 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 + } + + return tx.Set(addrKey, keypairBz) + }) + } + return nil +} + +// Deterministically generate and store the ith child from the masterAddrHex key stored in the keybase +func (keybase *badgerKeybase) StoreChildFromSeed(seed []byte, childIndex int32, childPassphrase, childHint string) error { + path := fmt.Sprintf(crypto.PoktAccountPathFormat, childIndex) + childKey, err := crypto.DeriveKeyFromPath(path, seed) + if err != nil { + return err + } + // No need to re-encrypt with provided passphrase + if childPassphrase == "" && childHint == "" { + err = keybase.db.Update(func(tx *badger.Txn) error { + // Use key address as key in DB + addrKey := childKey.GetAddressBytes() + + // Encode KeyPair into []byte for value + keypairBz, err := childKey.Marshal() + if err != nil { + return err + } + + return tx.Set(addrKey, keypairBz) + }) + } else { + // 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 + } + + // 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 + } + + return tx.Set(addrKey, keypairBz) + }) + } + return nil +} + // Check whether an address is currently stored in the DB func (keybase *badgerKeybase) Exists(address string) (bool, error) { val, err := keybase.Get(address) From 57362206ba2d80ad9e36b979be0e18f0874686ea Mon Sep 17 00:00:00 2001 From: Harry Law Date: Tue, 14 Feb 2023 18:39:42 +0000 Subject: [PATCH 04/34] Reduce scope --- shared/crypto/slip.go | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/shared/crypto/slip.go b/shared/crypto/slip.go index 5b4603af2..7060feeaa 100644 --- a/shared/crypto/slip.go +++ b/shared/crypto/slip.go @@ -12,15 +12,13 @@ import ( ) const ( - // PoktAccountPrefix is the prefix used for HD key derivation where 635 is the SLIP-0044 coin type - PoktAccountPrefix = "m/44'/635'" - // PoktPrimaryAccountPath is the derivation path for the primary account - PoktPrimaryAccountPath = "m/44'/635'/0'" - // PoktAccountPathFormat is to be used with fmt.Sprintf to generate child keys - PoktAccountPathFormat = "m/44'/635'/%d" - // FirstHardenedKey is the index for the first hardened key for ed25519 keys - FirstHardenedKey = uint32(0x80000000) - MaxHardenedKey = ^uint32(0) + // PoktAccountPathFormat used for HD key derivation where 635 is the SLIP-0044 coin type + // To be used with fmt.Sprintf to generate child keys + PoktAccountPathFormat = "m/44'/635'/%d'" + // firstHardenedKey is the index for the first hardened key for ed25519 keys + firstHardenedKey = uint32(0x80000000) + maxHardenedKey = ^uint32(0) + MaxChildKeyIndex = maxHardenedKey - firstHardenedKey // As defined in: https://github.com/satoshilabs/slips/blob/master/slip-0010.md#master-key-generation seedModifier = "ed25519 seed" ) @@ -37,36 +35,36 @@ type slipKey struct { } // Derives a key from a BIP-44 path and a seed only operating on hardened ed25519 keys -func DeriveKeyFromPath(path string, seed []byte, passphrase, hint string) (KeyPair, error) { +func DeriveKeyFromPath(path string, seed []byte) (KeyPair, error) { segments, err := pathToSegments(path) if err != nil { return nil, err } - key, err := NewMasterKey(seed) + key, err := newMasterKey(seed) if err != nil { return nil, err } for _, i32 := range segments { // Force hardened keys - if i32 >= MaxHardenedKey-FirstHardenedKey { - return nil, fmt.Errorf("hardened key index too large, max: %d, got: %d", MaxHardenedKey-FirstHardenedKey, i32) + if i32 > MaxChildKeyIndex { + return nil, fmt.Errorf("hardened key index too large, max: %d, got: %d", MaxChildKeyIndex, i32) } - i := i32 + FirstHardenedKey + i := i32 + firstHardenedKey // Derive the correct child until final segment - key, err = key.DeriveChild(i) + key, err = key.deriveChild(i) if err != nil { return nil, err } } - return key.ConvertToKeypair(passphrase, hint) + return key.convertToKeypair() } // Reference: https://github.com/satoshilabs/slips/blob/master/slip-0010.md#master-key-generation -func NewMasterKey(seed []byte) (*slipKey, error) { +func newMasterKey(seed []byte) (*slipKey, error) { // Create HMAC hash from curve and seed hmacHash := hmac.New(sha512.New, []byte(seedModifier)) if _, err := hmacHash.Write(seed); err != nil { @@ -86,9 +84,9 @@ func NewMasterKey(seed []byte) (*slipKey, error) { } // Reference: https://github.com/satoshilabs/slips/blob/master/slip-0010.md#private-parent-key--private-child-key -func (k *slipKey) DeriveChild(i uint32) (*slipKey, error) { +func (k *slipKey) deriveChild(i uint32) (*slipKey, error) { // Check i>=2^31 or else no public key can be derived for the ed25519 key - if i < FirstHardenedKey { + if i < firstHardenedKey { return nil, ErrNoDerivation } @@ -114,7 +112,7 @@ func (k *slipKey) DeriveChild(i uint32) (*slipKey, error) { return child, nil } -func (k *slipKey) ConvertToKeypair(passphrase, hint string) (KeyPair, error) { +func (k *slipKey) convertToKeypair() (KeyPair, error) { // Generate PrivateKey interface form secret key reader := bytes.NewReader(k.SecretKey) privKey, err := GeneratePrivateKeyWithReader(reader) @@ -123,7 +121,7 @@ func (k *slipKey) ConvertToKeypair(passphrase, hint string) (KeyPair, error) { } // Armour and encrypt private key into JSON string - armouredStr, err := encryptArmourPrivKey(privKey, passphrase, hint) + armouredStr, err := encryptArmourPrivKey(privKey, "", "") // No passphrase or hint as they depend on the master key if err != nil { return nil, err } From 459edf707382f6ed55d16cc1201e6ca7c3d40387 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Tue, 14 Feb 2023 18:39:55 +0000 Subject: [PATCH 05/34] Add GetSeed() function --- shared/crypto/keypair.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/shared/crypto/keypair.go b/shared/crypto/keypair.go index ee6008e7b..59975b616 100644 --- a/shared/crypto/keypair.go +++ b/shared/crypto/keypair.go @@ -30,6 +30,9 @@ type KeyPair interface { ExportString(passphrase string) (string, error) ExportJSON(passphrase string) (string, error) + // Seed + GetSeed(passphrase string) ([]byte, error) + // Marshalling Marshal() ([]byte, error) Unmarshal([]byte) error @@ -99,6 +102,15 @@ func (kp encKeyPair) ExportJSON(passphrase string) (string, error) { return kp.PrivKeyArmour, nil } +// Return the seed of the key +func (kp encKeyPair) GetSeed(passphrase string) ([]byte, error) { + privKey, err := unarmourDecryptPrivKey(kp.PrivKeyArmour, passphrase) + if err != nil { + return []byte{}, err + } + return privKey.Seed(), nil +} + // Marshal KeyPair into a []byte func (kp encKeyPair) Marshal() ([]byte, error) { buf := new(bytes.Buffer) From 4b7fe8368911db2399f9282eb5f34c920238c4ca Mon Sep 17 00:00:00 2001 From: Harry Law Date: Tue, 14 Feb 2023 18:40:07 +0000 Subject: [PATCH 06/34] Update docs --- app/client/keybase/README.md | 5 ++++ shared/crypto/README.md | 49 ++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/app/client/keybase/README.md b/app/client/keybase/README.md index 0d03ba860..ae124a16f 100644 --- a/app/client/keybase/README.md +++ b/app/client/keybase/README.md @@ -8,6 +8,7 @@ This document is intended to outline the current Keybase implementation used by - [Keybase Code Structure](#keybase-code-structure) - [Makefile Testing Helper](#makefile-testing-helper) - [KeyPair Encryption \& Armouring](#keypair-encryption--armouring) +- [SLIPS-0010 Child Key Generation](#slips-child-key-generation) - [TODO: Future Work](#todo-future-work) _TODO(#150): The current keybase has not been integrated with any CLI endpoints, and as such is only accessible through the [keybase interface](#keybase-interface)_ @@ -73,6 +74,10 @@ The [documentation in the crypto library](../../../shared/crypto/README.md) cove The primitives and functions defined there are heavily used throughout this package. +## Slips Child Key Generation + +The [documentation in the crypto library](../../../shared/crypto/README.md) covers the specifics of the SLIPS-0010 implementation related to child key generation from a single master key + ## TODO: Future Work - [ ] Improve error handling and error messages for importing keys with invalid strings/invalid JSON diff --git a/shared/crypto/README.md b/shared/crypto/README.md index f98e88a8a..94c79e940 100644 --- a/shared/crypto/README.md +++ b/shared/crypto/README.md @@ -3,6 +3,7 @@ - [KeyPair Interface](#keypair-interface) - [KeyPair Code Structure](#keypair-code-structure) - [Encryption and Armouring](#encryption-and-armouring) +- [Child Key Generation](#slips-0010-hd-child-key-generation) _DOCUMENT: Note that this README is a WIP and does not exhaustively document all the current types in this package_ @@ -104,4 +105,52 @@ flowchart LR AES-GCM-->PrivateKey ``` +## SLIPS-0010 HD Child Key Generation + +[SLIPS-0010](https://github.com/satoshilabs/slips/blob/master/slip-0010.md) key generation from a master key or seed is supported through the file [slip.go](./slip.go) + +The keys are generated using the BIP-44 path `m/44'/635'/%d'` where `%d` is the index of the child key - this allows for the deterministic generation of up to `2147483647` hardened ed25519 child keys per master key. + +Master key derivation is done as follows: +```mermaid +flowchart LR + subgraph HMAC + direction TB + A["hmacNew(sha512, seedModifier)"] + B["hmacWrite(seed)"] + C["convertToBytes(hmac)"] + A-->B-->C + end + subgraph MASTER-KEY + direction TB + D["SecretKey: hmacBytes[:32]"] + E["ChainCode: hmacBytes[32:]"] + D --> E --Secret+Chaincode--> KEY + end + HMAC--hmacBytes-->MASTER-KEY +``` + +Child keys are derived from their parents as follows: +```mermaid +flowchart LR + subgraph HMAC-CHILD + direction TB + C["append(0x0, parent.SecretKey, bigEndian(index))"] + A["hmacNew(sha512, parent.Chaincode)"] + B["hmacWrite(data)"] + D["convertToBytes(hmac)"] + A--hmac-->B + C--data-->B + B-->D + end + subgraph CHILD-KEY + direction TB + F["secret: hmacBytes[:32]"] + G["chaincode: hmacBytes[32:]"] + F --> G --Secret+Chaincode--> KEY + end + Parent --> HMAC-CHILD + HMAC-CHILD --hmacBytes--> CHILD-KEY +``` + From db1a2dc9cf07c9b84020618b58b22451ceb76e78 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Tue, 14 Feb 2023 18:43:52 +0000 Subject: [PATCH 07/34] Change SLIPS to SLIP --- app/client/keybase/README.md | 4 ++-- shared/crypto/README.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/client/keybase/README.md b/app/client/keybase/README.md index ae124a16f..fb16be00b 100644 --- a/app/client/keybase/README.md +++ b/app/client/keybase/README.md @@ -8,7 +8,7 @@ This document is intended to outline the current Keybase implementation used by - [Keybase Code Structure](#keybase-code-structure) - [Makefile Testing Helper](#makefile-testing-helper) - [KeyPair Encryption \& Armouring](#keypair-encryption--armouring) -- [SLIPS-0010 Child Key Generation](#slips-child-key-generation) +- [SLIP-0010 Child Key Generation](#child-key-generation) - [TODO: Future Work](#todo-future-work) _TODO(#150): The current keybase has not been integrated with any CLI endpoints, and as such is only accessible through the [keybase interface](#keybase-interface)_ @@ -74,7 +74,7 @@ The [documentation in the crypto library](../../../shared/crypto/README.md) cove The primitives and functions defined there are heavily used throughout this package. -## Slips Child Key Generation +## Child Key Generation The [documentation in the crypto library](../../../shared/crypto/README.md) covers the specifics of the SLIPS-0010 implementation related to child key generation from a single master key diff --git a/shared/crypto/README.md b/shared/crypto/README.md index 94c79e940..257daac0d 100644 --- a/shared/crypto/README.md +++ b/shared/crypto/README.md @@ -3,7 +3,7 @@ - [KeyPair Interface](#keypair-interface) - [KeyPair Code Structure](#keypair-code-structure) - [Encryption and Armouring](#encryption-and-armouring) -- [Child Key Generation](#slips-0010-hd-child-key-generation) +- [Child Key Generation](#slip-0010-hd-child-key-generation) _DOCUMENT: Note that this README is a WIP and does not exhaustively document all the current types in this package_ @@ -105,9 +105,9 @@ flowchart LR AES-GCM-->PrivateKey ``` -## SLIPS-0010 HD Child Key Generation +## SLIP-0010 HD Child Key Generation -[SLIPS-0010](https://github.com/satoshilabs/slips/blob/master/slip-0010.md) key generation from a master key or seed is supported through the file [slip.go](./slip.go) +[SLIP-0010](https://github.com/satoshilabs/slips/blob/master/slip-0010.md) key generation from a master key or seed is supported through the file [slip.go](./slip.go) The keys are generated using the BIP-44 path `m/44'/635'/%d'` where `%d` is the index of the child key - this allows for the deterministic generation of up to `2147483647` hardened ed25519 child keys per master key. From 4f4dc1023355cf6e4c42bcfe60c9689fab57ff49 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Tue, 14 Feb 2023 20:13:10 +0000 Subject: [PATCH 08/34] Address linter errors --- app/client/keybase/keybase_test.go | 2 + app/client/keybase/keystore.go | 95 +++++++++++++++--------------- shared/crypto/slip.go | 7 ++- 3 files changed, 55 insertions(+), 49 deletions(-) diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go index f4fa03e3c..74618c9fa 100644 --- a/app/client/keybase/keybase_test.go +++ b/app/client/keybase/keybase_test.go @@ -487,6 +487,7 @@ func TestKeybase_StoreChildFromKey(t *testing.T) { require.NoError(t, err) childKey, err := db.GetPrivKey(testChildAddr1, testPassphrase) + require.NoError(t, err) require.Equal(t, childKey.Address().String(), testChildAddr1) } @@ -507,6 +508,7 @@ func TestKeybase_StoreChildFromSeed(t *testing.T) { require.NoError(t, err) childKey, err := db.GetPrivKey(testChildAddr1, testPassphrase) + require.NoError(t, err) require.Equal(t, childKey.Address().String(), testChildAddr1) } diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index 95a63aa43..884c89e92 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -274,33 +274,34 @@ func (keybase *badgerKeybase) StoreChildFromKey(masterAddrHex, masterPassphrase return tx.Set(addrKey, keypairBz) }) - } else { - // 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 - } + return err + } + // 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 nil + return tx.Set(addrKey, keypairBz) + }) + + return err } // Deterministically generate and store the ith child from the masterAddrHex key stored in the keybase @@ -324,33 +325,35 @@ func (keybase *badgerKeybase) StoreChildFromSeed(seed []byte, childIndex int32, return tx.Set(addrKey, keypairBz) }) - } else { - // 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 - } + return err + } - keyPair, err := crypto.CreateNewKeyFromString(privKeyHex, childPassphrase, childHint) - if err != nil { - return err - } + // 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 + } - // Use key address as key in DB - addrKey := keyPair.GetAddressBytes() + keyPair, err := crypto.CreateNewKeyFromString(privKeyHex, childPassphrase, childHint) + if err != nil { + return err + } - // Encode KeyPair into []byte for value - keypairBz, err := keyPair.Marshal() - if err != nil { - return err - } + // Use key address as key in DB + addrKey := keyPair.GetAddressBytes() - return tx.Set(addrKey, keypairBz) - }) - } - return nil + // Encode KeyPair into []byte for value + keypairBz, err := keyPair.Marshal() + if err != nil { + return err + } + + return tx.Set(addrKey, keypairBz) + }) + + return err } // Check whether an address is currently stored in the DB diff --git a/shared/crypto/slip.go b/shared/crypto/slip.go index 7060feeaa..81cd35493 100644 --- a/shared/crypto/slip.go +++ b/shared/crypto/slip.go @@ -26,7 +26,7 @@ const ( var ( ErrInvalidPath = fmt.Errorf("invalid BIP-44 derivation path") ErrNoDerivation = fmt.Errorf("no derivation for an ed25519 key is possible") - pathRegex = regexp.MustCompile(`m(\/[0-9]+')+$`) + pathRegex = regexp.MustCompile(`m(/\d+')+$`) ) type slipKey struct { @@ -91,10 +91,11 @@ func (k *slipKey) deriveChild(i uint32) (*slipKey, error) { } // i is a hardened child compute the HMAC hash of the key + data := []byte{0x0} iBytes := make([]byte, 4) binary.BigEndian.PutUint32(iBytes, i) - key := append([]byte{0x0}, k.SecretKey...) - data := append(key, iBytes...) + data = append(data, k.SecretKey...) + data = append(data, iBytes...) hmacHash := hmac.New(sha512.New, k.ChainCode) if _, err := hmacHash.Write(data); err != nil { From f99a56e528a997d8db7b10e8e4409f0186981cad Mon Sep 17 00:00:00 2001 From: Harry Law Date: Tue, 21 Feb 2023 14:16:59 +0000 Subject: [PATCH 09/34] Remove BIP-44 paths --- shared/crypto/README.md | 2 +- shared/crypto/slip.go | 72 +++++++++++------------------------------ 2 files changed, 19 insertions(+), 55 deletions(-) diff --git a/shared/crypto/README.md b/shared/crypto/README.md index 257daac0d..4c83798ba 100644 --- a/shared/crypto/README.md +++ b/shared/crypto/README.md @@ -109,7 +109,7 @@ flowchart LR [SLIP-0010](https://github.com/satoshilabs/slips/blob/master/slip-0010.md) key generation from a master key or seed is supported through the file [slip.go](./slip.go) -The keys are generated using the BIP-44 path `m/44'/635'/%d'` where `%d` is the index of the child key - this allows for the deterministic generation of up to `2147483647` hardened ed25519 child keys per master key. +The child keys are generated from an index, modified to force using hardened keys - this allows for the deterministic generation of up to `2147483647` hardened ed25519 child keys per master key. Master key derivation is done as follows: ```mermaid diff --git a/shared/crypto/slip.go b/shared/crypto/slip.go index 81cd35493..15964ea04 100644 --- a/shared/crypto/slip.go +++ b/shared/crypto/slip.go @@ -6,27 +6,19 @@ import ( "crypto/sha512" "encoding/binary" "fmt" - "regexp" - "strconv" - "strings" ) const ( - // PoktAccountPathFormat used for HD key derivation where 635 is the SLIP-0044 coin type - // To be used with fmt.Sprintf to generate child keys - PoktAccountPathFormat = "m/44'/635'/%d'" - // firstHardenedKey is the index for the first hardened key for ed25519 keys - firstHardenedKey = uint32(0x80000000) - maxHardenedKey = ^uint32(0) - MaxChildKeyIndex = maxHardenedKey - firstHardenedKey // As defined in: https://github.com/satoshilabs/slips/blob/master/slip-0010.md#master-key-generation seedModifier = "ed25519 seed" + // Hardened key values, as defined in: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#extended-keys + firstHardenedKeyIndex = uint32(2147483648) // 2^31 + maxHardenedKeyIndex = ^uint32(0) // 2^32-1 + maxChildKeyIndex = maxHardenedKeyIndex - firstHardenedKeyIndex ) var ( - ErrInvalidPath = fmt.Errorf("invalid BIP-44 derivation path") ErrNoDerivation = fmt.Errorf("no derivation for an ed25519 key is possible") - pathRegex = regexp.MustCompile(`m(/\d+')+$`) ) type slipKey struct { @@ -34,33 +26,27 @@ type slipKey struct { ChainCode []byte } -// Derives a key from a BIP-44 path and a seed only operating on hardened ed25519 keys -func DeriveKeyFromPath(path string, seed []byte) (KeyPair, error) { - segments, err := pathToSegments(path) +// Derives a master key from the seed provided and returns the child at the correct index +func DeriveChild(index uint32, seed []byte) (KeyPair, error) { + masterKey, err := newMasterKey(seed) if err != nil { return nil, err } - key, err := newMasterKey(seed) - if err != nil { - return nil, err + // Allow index usage from 0 while using hardened keys + if index > maxChildKeyIndex { + return nil, fmt.Errorf("child index is greater than max hardened ed25519 key index: got %d, max %d", index, maxChildKeyIndex) } - for _, i32 := range segments { - // Force hardened keys - if i32 > MaxChildKeyIndex { - return nil, fmt.Errorf("hardened key index too large, max: %d, got: %d", MaxChildKeyIndex, i32) - } - i := i32 + firstHardenedKey - - // Derive the correct child until final segment - key, err = key.deriveChild(i) - if err != nil { - return nil, err - } + // Force hardened keys + index += firstHardenedKeyIndex + + childKey, err := masterKey.deriveChild(index) + if err != nil { + return nil, err } - return key.convertToKeypair() + return childKey.convertToKeypair() } // Reference: https://github.com/satoshilabs/slips/blob/master/slip-0010.md#master-key-generation @@ -85,8 +71,7 @@ func newMasterKey(seed []byte) (*slipKey, error) { // Reference: https://github.com/satoshilabs/slips/blob/master/slip-0010.md#private-parent-key--private-child-key func (k *slipKey) deriveChild(i uint32) (*slipKey, error) { - // Check i>=2^31 or else no public key can be derived for the ed25519 key - if i < firstHardenedKey { + if i < firstHardenedKeyIndex { return nil, ErrNoDerivation } @@ -133,24 +118,3 @@ func (k *slipKey) convertToKeypair() (KeyPair, error) { PrivKeyArmour: armouredStr, }, nil } - -// Check the BIP-44 path provided is valid and return the []uint32 segments it contains -func pathToSegments(path string) ([]uint32, error) { - // Check whether the path is valid - if !pathRegex.MatchString(path) { - return nil, ErrInvalidPath - } - - // Split into segments and check for valid uint32 types - segments := make([]uint32, 0) - segs := strings.Split(path, "/") - for _, seg := range segs[1:] { - ui64, err := strconv.ParseUint(strings.TrimRight(seg, "'"), 10, 32) - if err != nil { - return nil, err - } - segments = append(segments, uint32(ui64)) - } - - return segments, nil -} From aaa86a12efaf09bb780c83be5f050a124fa02fd1 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Tue, 21 Feb 2023 14:17:20 +0000 Subject: [PATCH 10/34] Link to SLIP spec --- app/client/keybase/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/keybase/README.md b/app/client/keybase/README.md index fb16be00b..be6acef85 100644 --- a/app/client/keybase/README.md +++ b/app/client/keybase/README.md @@ -76,7 +76,7 @@ The primitives and functions defined there are heavily used throughout this pack ## Child Key Generation -The [documentation in the crypto library](../../../shared/crypto/README.md) covers the specifics of the SLIPS-0010 implementation related to child key generation from a single master key +The [documentation in the crypto library](../../../shared/crypto/README.md) covers the specifics of the [SLIPS-0010](https://github.com/satoshilabs/slips/blob/master/slip-0010.md) implementation related to child key generation from a single master key ## TODO: Future Work From 6c9601da9e2c61df1d7b58ed86d58b2e1701528e Mon Sep 17 00:00:00 2001 From: Harry Law Date: Tue, 21 Feb 2023 14:17:44 +0000 Subject: [PATCH 11/34] Address comments --- app/client/keybase/keybase.go | 11 ++-- app/client/keybase/keybase_test.go | 14 ++--- app/client/keybase/keystore.go | 87 ++++++------------------------ go.mod | 3 ++ go.sum | 10 ++++ 5 files changed, 41 insertions(+), 84 deletions(-) diff --git a/app/client/keybase/keybase.go b/app/client/keybase/keybase.go index 5aeedccd9..9737524eb 100644 --- a/app/client/keybase/keybase.go +++ b/app/client/keybase/keybase.go @@ -20,14 +20,13 @@ type Keybase interface { // Insert a new keypair from the JSON string of the encrypted private key into the DB ImportFromJSON(jsonStr, passphrase string) error - // DISCUSSION: Is there a more appropriate way to handle the HD interactions? // SLIPS-0010 Key Derivation - // Deterministically generate and return the child key - DeriveChildFromKey(masterAddrHex, passphrase string, childIndex int32) (crypto.KeyPair, error) - DeriveChildFromSeed(seed []byte, childIndex int32) (crypto.KeyPair, error) + // Deterministically generate and return the derived child key + DeriveChildFromKey(masterAddrHex, passphrase string, childIndex uint32) (crypto.KeyPair, error) + DeriveChildFromSeed(seed []byte, childIndex uint32) (crypto.KeyPair, error) // Store the derived child key in the keybase - StoreChildFromKey(masterAddrHex, masterPassphrase string, childIndex int32, childPassphrase, childHint string) error - StoreChildFromSeed(seed []byte, childIndex int32, childPassphrase, childHint string) error + StoreChildFromKey(masterAddrHex, masterPassphrase string, childIndex uint32, childPassphrase, childHint string) error + StoreChildFromSeed(seed []byte, childIndex uint32, childPassphrase, childHint string) error // Accessors Get(address string) (crypto.KeyPair, error) diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go index 74618c9fa..c2d64ca8c 100644 --- a/app/client/keybase/keybase_test.go +++ b/app/client/keybase/keybase_test.go @@ -29,7 +29,7 @@ const ( testJSONString = `{"kdf":"scrypt","salt":"197d2754445a7e5ce3e6c8d7b1d0ff6f","secparam":"12","hint":"pocket wallet","ciphertext":"B/AORJrSeQrR5ewQGel4FeCCXscoCsMUzq9gXAAxDqjXMmMxa7TedBTuemtO82JyTCoQWFHbGxRx8A7IoETNh5T5yBAjNNrr7DDkVrcfSAM3ez9lQem17DsfowCvRtmbesDlvbSZMRy8mQgClLqWRN+c6W/fPQ/lxLUy1G1A965U/uImcMXzSwbfqYrBPEux"}` // SLIPS-0010 - testChildAddr1 = "8b83d7057df7ac1d20a2f0aa0edadf206eb6764d" + testChildAddrIdx1 = "11c4bf38bf5de98dd39fdf935da0165c2f0fd408" ) func TestKeybase_CreateNewKey(t *testing.T) { @@ -455,7 +455,7 @@ func TestKeybase_DeriveChildFromKey(t *testing.T) { childKey, err := db.DeriveChildFromKey(testAddr, testPassphrase, 1) require.NoError(t, err) - require.Equal(t, childKey.GetAddressString(), testChildAddr1) + require.Equal(t, childKey.GetAddressString(), testChildAddrIdx1) } func TestKeybase_DeriveChildFromSeed(t *testing.T) { @@ -473,7 +473,7 @@ func TestKeybase_DeriveChildFromSeed(t *testing.T) { childKey, err := db.DeriveChildFromSeed(seed, 1) require.NoError(t, err) - require.Equal(t, childKey.GetAddressString(), testChildAddr1) + require.Equal(t, childKey.GetAddressString(), testChildAddrIdx1) } func TestKeybase_StoreChildFromKey(t *testing.T) { @@ -486,9 +486,9 @@ func TestKeybase_StoreChildFromKey(t *testing.T) { err = db.StoreChildFromKey(testAddr, testPassphrase, 1, testPassphrase, testHint) require.NoError(t, err) - childKey, err := db.GetPrivKey(testChildAddr1, testPassphrase) + childKey, err := db.GetPrivKey(testChildAddrIdx1, testPassphrase) require.NoError(t, err) - require.Equal(t, childKey.Address().String(), testChildAddr1) + require.Equal(t, childKey.Address().String(), testChildAddrIdx1) } func TestKeybase_StoreChildFromSeed(t *testing.T) { @@ -507,9 +507,9 @@ func TestKeybase_StoreChildFromSeed(t *testing.T) { err = db.StoreChildFromSeed(seed, 1, testPassphrase, testHint) require.NoError(t, err) - childKey, err := db.GetPrivKey(testChildAddr1, testPassphrase) + childKey, err := db.GetPrivKey(testChildAddrIdx1, testPassphrase) require.NoError(t, err) - require.Equal(t, childKey.Address().String(), testChildAddr1) + require.Equal(t, childKey.Address().String(), testChildAddrIdx1) } func initDB(t *testing.T) Keybase { diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index e482c9a64..6a41c9dd0 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -235,40 +235,28 @@ func (keybase *badgerKeybase) GetAll() (addresses []string, keyPairs []crypto.Ke return addresses, keyPairs, nil } -// Deterministically generate and return the ith child key from the masterAddrHex key stored in the keybase -func (keybase *badgerKeybase) DeriveChildFromKey(masterAddrHex, passphrase string, childIndex int32) (crypto.KeyPair, error) { - privKey, err := keybase.GetPrivKey(masterAddrHex, passphrase) - if err != nil { - return nil, err - } - seed := privKey.Seed() - path := fmt.Sprintf(crypto.PoktAccountPathFormat, childIndex) - childKey, err := crypto.DeriveKeyFromPath(path, seed) +// Deterministically generate and return the ith child from the seed provided +func (keybase *badgerKeybase) DeriveChildFromSeed(seed []byte, childIndex uint32) (crypto.KeyPair, error) { + childKey, err := crypto.DeriveChild(childIndex, seed) if err != nil { return nil, err } return childKey, nil } -// Deterministically generate and return the ith child from the seed provided -func (keybase *badgerKeybase) DeriveChildFromSeed(seed []byte, childIndex int32) (crypto.KeyPair, error) { - path := fmt.Sprintf(crypto.PoktAccountPathFormat, childIndex) - childKey, err := crypto.DeriveKeyFromPath(path, seed) +// Deterministically generate and return the ith child key from the masterAddrHex key stored in the keybase +func (keybase *badgerKeybase) DeriveChildFromKey(masterAddrHex, passphrase string, childIndex uint32) (crypto.KeyPair, error) { + privKey, err := keybase.GetPrivKey(masterAddrHex, passphrase) if err != nil { return nil, err } - return childKey, nil + seed := privKey.Seed() + return keybase.DeriveChildFromSeed(seed, childIndex) } // Deterministically generate and store the ith child from the masterAddrHex key stored in the keybase -func (keybase *badgerKeybase) StoreChildFromKey(masterAddrHex, masterPassphrase string, childIndex int32, childPassphrase, childHint string) error { - masterPrivKey, err := keybase.GetPrivKey(masterAddrHex, masterPassphrase) - if err != nil { - return err - } - seed := masterPrivKey.Seed() - path := fmt.Sprintf(crypto.PoktAccountPathFormat, childIndex) - childKey, err := crypto.DeriveKeyFromPath(path, seed) +func (keybase *badgerKeybase) StoreChildFromSeed(seed []byte, childIndex uint32, childPassphrase, childHint string) error { + childKey, err := crypto.DeriveChild(childIndex, seed) if err != nil { return err } @@ -288,8 +276,9 @@ func (keybase *badgerKeybase) StoreChildFromKey(masterAddrHex, masterPassphrase }) return err } + // Re-encrypt child key with passphrase and hint - err = keybase.db.Update(func(tx *badger.Txn) error { + 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 { @@ -312,60 +301,16 @@ func (keybase *badgerKeybase) StoreChildFromKey(masterAddrHex, masterPassphrase return tx.Set(addrKey, keypairBz) }) - - return err } // Deterministically generate and store the ith child from the masterAddrHex key stored in the keybase -func (keybase *badgerKeybase) StoreChildFromSeed(seed []byte, childIndex int32, childPassphrase, childHint string) error { - path := fmt.Sprintf(crypto.PoktAccountPathFormat, childIndex) - childKey, err := crypto.DeriveKeyFromPath(path, seed) +func (keybase *badgerKeybase) StoreChildFromKey(masterAddrHex, masterPassphrase string, childIndex uint32, childPassphrase, childHint string) error { + masterPrivKey, err := keybase.GetPrivKey(masterAddrHex, masterPassphrase) if err != nil { return err } - // No need to re-encrypt with provided passphrase - if childPassphrase == "" && childHint == "" { - err = keybase.db.Update(func(tx *badger.Txn) error { - // Use key address as key in DB - addrKey := childKey.GetAddressBytes() - - // Encode KeyPair into []byte for value - keypairBz, err := childKey.Marshal() - if err != nil { - return err - } - - return tx.Set(addrKey, keypairBz) - }) - return err - } - - // 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 - } - - // 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 - } - - return tx.Set(addrKey, keypairBz) - }) - - return err + seed := masterPrivKey.Seed() + return keybase.StoreChildFromSeed(seed, childIndex, childPassphrase, childHint) } // Check whether an address is currently stored in the DB diff --git a/go.mod b/go.mod index f79d0a6c3..6ffe683ae 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( require ( github.com/benbjohnson/clock v1.3.0 github.com/celestiaorg/smt v0.2.1-0.20220414134126-dba215ccb884 + github.com/deepmap/oapi-codegen v1.12.4 github.com/dgraph-io/badger/v3 v3.2103.2 github.com/getkin/kin-openapi v0.107.0 github.com/jackc/pgconn v1.13.0 @@ -64,7 +65,9 @@ require ( ) require ( + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/jackc/puddle/v2 v2.1.2 // indirect diff --git a/go.sum b/go.sum index cd45047dc..cc412c8f3 100644 --- a/go.sum +++ b/go.sum @@ -51,11 +51,14 @@ github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-ecvrf v0.0.1 h1:wv45+kZ0mG4G9oSTMjAlbgKqa4tPbNr4WLoCWqz5/bo= github.com/ProtonMail/go-ecvrf v0.0.1/go.mod h1:fhZbiRYn62/JGnBG2NGwCx0oT+gr/+I5R/hwiyAFpAU= +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -63,6 +66,7 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/celestiaorg/smt v0.2.1-0.20220414134126-dba215ccb884 h1:iRNKw2WmAbVgGMNYzDH5Y2yY3+jyxwEK9Hc5pwIjZAE= github.com/celestiaorg/smt v0.2.1-0.20220414134126-dba215ccb884/go.mod h1:/sdYDakowo/XaxS2Fl7CBqtuf/O2uTqF2zmAUFAtAiw= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= @@ -106,6 +110,8 @@ github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxG github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deepmap/oapi-codegen v1.12.4 h1:pPmn6qI9MuOtCz82WY2Xaw46EQjgvxednXXrP7g5Q2s= +github.com/deepmap/oapi-codegen v1.12.4/go.mod h1:3lgHGMu6myQ2vqbbTXH2H1o4eXFTGnFiDaOaKKl5yas= github.com/dgraph-io/badger/v3 v3.2103.2 h1:dpyM5eCJAtQCBcMCZcT4UBZchuTJgCywerHHgmxfxM8= github.com/dgraph-io/badger/v3 v3.2103.2/go.mod h1:RHo4/GmYcKKh5Lxu63wLEMHJ70Pac2JqZRYGhlyAo2M= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= @@ -226,6 +232,8 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= @@ -296,6 +304,7 @@ github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -459,6 +468,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU= github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= From 24dd94c43aa1c07771beab7b3c712cfc3f5878e4 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Tue, 21 Feb 2023 15:36:51 +0000 Subject: [PATCH 12/34] Add SLIP-0010 test vectors --- app/client/keybase/keybase_test.go | 69 ------- app/client/keybase/slip_test.go | 306 +++++++++++++++++++++++++++++ 2 files changed, 306 insertions(+), 69 deletions(-) create mode 100644 app/client/keybase/slip_test.go diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go index c2d64ca8c..aafbf0865 100644 --- a/app/client/keybase/keybase_test.go +++ b/app/client/keybase/keybase_test.go @@ -27,9 +27,6 @@ const ( testJSONPubString = "408bec6320b540aa0cc86b3e633e214f2fd4dce4caa08f164fa3a9d3e577b46c" testJSONPrivString = "3554119cec1c0c8c5b3845a5d3fc6346eb44ed21aab5c063ae9b6b1d38bec275408bec6320b540aa0cc86b3e633e214f2fd4dce4caa08f164fa3a9d3e577b46c" testJSONString = `{"kdf":"scrypt","salt":"197d2754445a7e5ce3e6c8d7b1d0ff6f","secparam":"12","hint":"pocket wallet","ciphertext":"B/AORJrSeQrR5ewQGel4FeCCXscoCsMUzq9gXAAxDqjXMmMxa7TedBTuemtO82JyTCoQWFHbGxRx8A7IoETNh5T5yBAjNNrr7DDkVrcfSAM3ez9lQem17DsfowCvRtmbesDlvbSZMRy8mQgClLqWRN+c6W/fPQ/lxLUy1G1A965U/uImcMXzSwbfqYrBPEux"}` - - // SLIPS-0010 - testChildAddrIdx1 = "11c4bf38bf5de98dd39fdf935da0165c2f0fd408" ) func TestKeybase_CreateNewKey(t *testing.T) { @@ -446,72 +443,6 @@ func TestKeybase_ExportJSON(t *testing.T) { require.Equal(t, privKey.String(), testPrivString) } -func TestKeybase_DeriveChildFromKey(t *testing.T) { - db := initDB(t) - defer stopDB(t, db) - - err := db.ImportFromString(testPrivString, testPassphrase, testHint) - require.NoError(t, err) - - childKey, err := db.DeriveChildFromKey(testAddr, testPassphrase, 1) - require.NoError(t, err) - require.Equal(t, childKey.GetAddressString(), testChildAddrIdx1) -} - -func TestKeybase_DeriveChildFromSeed(t *testing.T) { - db := initDB(t) - defer stopDB(t, db) - - err := db.ImportFromString(testPrivString, testPassphrase, testHint) - require.NoError(t, err) - - kp, err := db.Get(testAddr) - require.NoError(t, err) - - seed, err := kp.GetSeed(testPassphrase) - require.NoError(t, err) - - childKey, err := db.DeriveChildFromSeed(seed, 1) - require.NoError(t, err) - require.Equal(t, childKey.GetAddressString(), testChildAddrIdx1) -} - -func TestKeybase_StoreChildFromKey(t *testing.T) { - db := initDB(t) - defer stopDB(t, db) - - err := db.ImportFromString(testPrivString, testPassphrase, testHint) - require.NoError(t, err) - - err = db.StoreChildFromKey(testAddr, testPassphrase, 1, testPassphrase, testHint) - require.NoError(t, err) - - childKey, err := db.GetPrivKey(testChildAddrIdx1, testPassphrase) - require.NoError(t, err) - require.Equal(t, childKey.Address().String(), testChildAddrIdx1) -} - -func TestKeybase_StoreChildFromSeed(t *testing.T) { - db := initDB(t) - defer stopDB(t, db) - - err := db.ImportFromString(testPrivString, testPassphrase, testHint) - require.NoError(t, err) - - kp, err := db.Get(testAddr) - require.NoError(t, err) - - seed, err := kp.GetSeed(testPassphrase) - require.NoError(t, err) - - err = db.StoreChildFromSeed(seed, 1, testPassphrase, testHint) - require.NoError(t, err) - - childKey, err := db.GetPrivKey(testChildAddrIdx1, testPassphrase) - require.NoError(t, err) - require.Equal(t, childKey.Address().String(), testChildAddrIdx1) -} - func initDB(t *testing.T) Keybase { db, err := NewKeybaseInMemory() require.NoError(t, err) diff --git a/app/client/keybase/slip_test.go b/app/client/keybase/slip_test.go new file mode 100644 index 000000000..74677287f --- /dev/null +++ b/app/client/keybase/slip_test.go @@ -0,0 +1,306 @@ +package keybase + +import ( + "encoding/hex" + "github.com/pokt-network/pocket/shared/crypto" + "github.com/stretchr/testify/require" + "testing" +) + +const ( + // SLIPS-0010 + testChildAddrIdx1 = "8b83d7057df7ac1d20a2f0aa0edadf206eb6764d" +) + +func TestSlip_DeriveChild_Vector1(t *testing.T) { + // https://github.com/satoshilabs/slips/blob/master/slip-0010.md#test-vector-1-for-ed25519 + seed, err := hex.DecodeString("000102030405060708090a0b0c0d0e0f") + require.NoError(t, err) + + type args struct { + path string + seed []byte + } + tests := []struct { + name string + args args + wantPrivHex string + wantPubHex string + + wantErr bool + }{ + { + name: "Key(m) – master key", + args: args{ + path: "m", + seed: seed, + }, + wantPrivHex: "2b4be7f19ee27bbf30c667b642d5f4aa69fd169872f8fc3059c08ebae2eb19e7", + wantPubHex: "00a4b2856bfec510abab89753fac1ac0e1112364e7d250545963f135f2a33188ed", + + wantErr: false, + }, + { + name: "Key(m/0')", + args: args{ + path: "m/0'", + seed: seed, + }, + wantPrivHex: "68e0fe46dfb67e368c75379acec591dad19df3cde26e63b93a8e704f1dade7a3", + wantPubHex: "008c8a13df77a28f3445213a0f432fde644acaa215fc72dcdf300d5efaa85d350c", + + wantErr: false, + }, + { + name: "Key(m/0'/1')", + args: args{ + path: "m/0'/1'", + seed: seed, + }, + wantPrivHex: "b1d0bad404bf35da785a64ca1ac54b2617211d2777696fbffaf208f746ae84f2", + wantPubHex: "001932a5270f335bed617d5b935c80aedb1a35bd9fc1e31acafd5372c30f5c1187", + + wantErr: false, + }, + { + name: "Key(m/0'/1'/2')", + args: args{ + path: "m/0'/1'/2'", + seed: seed, + }, + wantPrivHex: "92a5b23c0b8a99e37d07df3fb9966917f5d06e02ddbd909c7e184371463e9fc9", + wantPubHex: "00ae98736566d30ed0e9d2f4486a64bc95740d89c7db33f52121f8ea8f76ff0fc1", + + wantErr: false, + }, + { + name: "Key(m/0'/1'/2'/2')", + args: args{ + path: "m/0'/1'/2'/2'", + seed: seed, + }, + wantPrivHex: "30d1dc7e5fc04c31219ab25a27ae00b50f6fd66622f6e9c913253d6511d1e662", + wantPubHex: "008abae2d66361c879b900d204ad2cc4984fa2aa344dd7ddc46007329ac76c429c", + + wantErr: false, + }, + { + name: "Key(m/0'/1'/2'/2'/1000000000')", + args: args{ + path: "m/0'/1'/2'/2'/1000000000'", + seed: seed, + }, + wantPrivHex: "8f94d394a8e8fd6b1bc2f3f49f5c47e385281d5c17e65324b0f62483e37e8793", + wantPubHex: "003c24da049451555d51a7014a37337aa4e12d41e485abccfa46b47dfb2af54b7a", + + wantErr: false, + }, + { + name: "Key(invalid)", + args: args{ + path: "m/0", + seed: seed, + }, + wantPrivHex: "", + wantErr: true, + }, + } + for _, tv := range tests { + t.Run(tv.name, func(t *testing.T) { + childKey, err := crypto.DeriveChild(tv.args.path, tv.args.seed) + if (err != nil) != tv.wantErr { + t.Errorf("DeriveChild() error = %v, wantErr %v", err, tv.wantErr) + return + } + + if err != nil { + return + } + + // Slip-0010 private keys in test vector are only the seed of the full private key + privSeed, err := childKey.GetSeed("") + require.NoError(t, err) + privHex := hex.EncodeToString(privSeed) + require.Equal(t, privHex, tv.wantPrivHex) + + // Slip-0010 keys are prefixed with "00" in the test vectors + pubHex := childKey.GetPublicKey().String() + require.Equal(t, "00"+pubHex, tv.wantPubHex) + }) + } +} + +func TestSlip_DeriveChild_Vector2(t *testing.T) { + // https://github.com/satoshilabs/slips/blob/master/slip-0010.md#test-vector-2-for-ed25519 + seed, err := hex.DecodeString("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542") + require.NoError(t, err) + + type args struct { + path string + seed []byte + } + tests := []struct { + name string + args args + wantPrivHex string + wantPubHex string + + wantErr bool + }{ + { + name: "Key(m) – master key", + args: args{ + path: "m", + seed: seed, + }, + wantPrivHex: "171cb88b1b3c1db25add599712e36245d75bc65a1a5c9e18d76f9f2b1eab4012", + wantPubHex: "008fe9693f8fa62a4305a140b9764c5ee01e455963744fe18204b4fb948249308a", + + wantErr: false, + }, + { + name: "Key(m/0')", + args: args{ + path: "m/0'", + seed: seed, + }, + wantPrivHex: "1559eb2bbec5790b0c65d8693e4d0875b1747f4970ae8b650486ed7470845635", + wantPubHex: "0086fab68dcb57aa196c77c5f264f215a112c22a912c10d123b0d03c3c28ef1037", + + wantErr: false, + }, + { + name: "Key(m/0'/2147483647')", + args: args{ + path: "m/0'/2147483647'", + seed: seed, + }, + wantPrivHex: "ea4f5bfe8694d8bb74b7b59404632fd5968b774ed545e810de9c32a4fb4192f4", + wantPubHex: "005ba3b9ac6e90e83effcd25ac4e58a1365a9e35a3d3ae5eb07b9e4d90bcf7506d", + + wantErr: false, + }, + { + name: "Key(m/0'/2147483647'/1')", + args: args{ + path: "m/0'/2147483647'/1'", + seed: seed, + }, + wantPrivHex: "3757c7577170179c7868353ada796c839135b3d30554bbb74a4b1e4a5a58505c", + wantPubHex: "002e66aa57069c86cc18249aecf5cb5a9cebbfd6fadeab056254763874a9352b45", + + wantErr: false, + }, + { + name: "Key(m/0'/2147483647'/1'/2147483646')", + args: args{ + path: "m/0'/2147483647'/1'/2147483646'", + seed: seed, + }, + wantPrivHex: "5837736c89570de861ebc173b1086da4f505d4adb387c6a1b1342d5e4ac9ec72", + wantPubHex: "00e33c0f7d81d843c572275f287498e8d408654fdf0d1e065b84e2e6f157aab09b", + + wantErr: false, + }, + { + name: "Key(m/0'/2147483647'/1'/2147483646'/2')", + args: args{ + path: "m/0'/2147483647'/1'/2147483646'/2'", + seed: seed, + }, + wantPrivHex: "551d333177df541ad876a60ea71f00447931c0a9da16f227c11ea080d7391b8d", + wantPubHex: "0047150c75db263559a70d5778bf36abbab30fb061ad69f69ece61a72b0cfa4fc0", + + wantErr: false, + }, + } + for _, tv := range tests { + t.Run(tv.name, func(t *testing.T) { + childKey, err := crypto.DeriveChild(tv.args.path, tv.args.seed) + if (err != nil) != tv.wantErr { + t.Errorf("DeriveChild() error = %v, wantErr %v", err, tv.wantErr) + return + } + + if err != nil { + return + } + + // Slip-0010 private keys in test vector are only the seed of the full private key + privSeed, err := childKey.GetSeed("") + require.NoError(t, err) + privHex := hex.EncodeToString(privSeed) + require.Equal(t, privHex, tv.wantPrivHex) + + // Slip-0010 keys are prefixed with "00" in the test vectors + pubHex := childKey.GetPublicKey().String() + require.Equal(t, "00"+pubHex, tv.wantPubHex) + }) + } +} + +func TestKeybase_DeriveChildFromKey(t *testing.T) { + db := initDB(t) + defer stopDB(t, db) + + err := db.ImportFromString(testPrivString, testPassphrase, testHint) + require.NoError(t, err) + + childKey, err := db.DeriveChildFromKey(testAddr, testPassphrase, 1) + require.NoError(t, err) + require.Equal(t, childKey.GetAddressString(), testChildAddrIdx1) +} + +func TestKeybase_DeriveChildFromSeed(t *testing.T) { + db := initDB(t) + defer stopDB(t, db) + + err := db.ImportFromString(testPrivString, testPassphrase, testHint) + require.NoError(t, err) + + kp, err := db.Get(testAddr) + require.NoError(t, err) + + seed, err := kp.GetSeed(testPassphrase) + require.NoError(t, err) + + childKey, err := db.DeriveChildFromSeed(seed, 1) + require.NoError(t, err) + require.Equal(t, childKey.GetAddressString(), testChildAddrIdx1) +} + +func TestKeybase_StoreChildFromKey(t *testing.T) { + db := initDB(t) + defer stopDB(t, db) + + err := db.ImportFromString(testPrivString, testPassphrase, testHint) + require.NoError(t, err) + + err = db.StoreChildFromKey(testAddr, testPassphrase, 1, testPassphrase, testHint) + require.NoError(t, err) + + childKey, err := db.GetPrivKey(testChildAddrIdx1, testPassphrase) + require.NoError(t, err) + require.Equal(t, childKey.Address().String(), testChildAddrIdx1) +} + +func TestKeybase_StoreChildFromSeed(t *testing.T) { + db := initDB(t) + defer stopDB(t, db) + + err := db.ImportFromString(testPrivString, testPassphrase, testHint) + require.NoError(t, err) + + kp, err := db.Get(testAddr) + require.NoError(t, err) + + seed, err := kp.GetSeed(testPassphrase) + require.NoError(t, err) + + err = db.StoreChildFromSeed(seed, 1, testPassphrase, testHint) + require.NoError(t, err) + + childKey, err := db.GetPrivKey(testChildAddrIdx1, testPassphrase) + require.NoError(t, err) + require.Equal(t, childKey.Address().String(), testChildAddrIdx1) +} From 962ba1d145c3ead66693e2c4499280ba5768fb2e Mon Sep 17 00:00:00 2001 From: Harry Law Date: Tue, 21 Feb 2023 15:37:46 +0000 Subject: [PATCH 13/34] Add BIP-44 paths back in --- app/client/keybase/keybase.go | 2 +- app/client/keybase/keystore.go | 6 ++- shared/crypto/README.md | 3 +- shared/crypto/slip.go | 74 +++++++++++++++++++++++++++------- 4 files changed, 65 insertions(+), 20 deletions(-) diff --git a/app/client/keybase/keybase.go b/app/client/keybase/keybase.go index 9737524eb..8bd03a317 100644 --- a/app/client/keybase/keybase.go +++ b/app/client/keybase/keybase.go @@ -24,7 +24,7 @@ type Keybase interface { // Deterministically generate and return the derived child key DeriveChildFromKey(masterAddrHex, passphrase string, childIndex uint32) (crypto.KeyPair, error) DeriveChildFromSeed(seed []byte, childIndex uint32) (crypto.KeyPair, error) - // Store the derived child key in the keybase + // 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 diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index 6a41c9dd0..13c5c612d 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -237,7 +237,8 @@ func (keybase *badgerKeybase) GetAll() (addresses []string, keyPairs []crypto.Ke // Deterministically generate and return the ith child from the seed provided func (keybase *badgerKeybase) DeriveChildFromSeed(seed []byte, childIndex uint32) (crypto.KeyPair, error) { - childKey, err := crypto.DeriveChild(childIndex, seed) + path := fmt.Sprintf(crypto.PoktAccountPathFormat, childIndex) + childKey, err := crypto.DeriveChild(path, seed) if err != nil { return nil, err } @@ -256,7 +257,8 @@ 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 { - childKey, err := crypto.DeriveChild(childIndex, seed) + path := fmt.Sprintf(crypto.PoktAccountPathFormat, childIndex) + childKey, err := crypto.DeriveChild(path, seed) if err != nil { return err } diff --git a/shared/crypto/README.md b/shared/crypto/README.md index 4c83798ba..f20bdb08e 100644 --- a/shared/crypto/README.md +++ b/shared/crypto/README.md @@ -109,8 +109,7 @@ flowchart LR [SLIP-0010](https://github.com/satoshilabs/slips/blob/master/slip-0010.md) key generation from a master key or seed is supported through the file [slip.go](./slip.go) -The child keys are generated from an index, modified to force using hardened keys - this allows for the deterministic generation of up to `2147483647` hardened ed25519 child keys per master key. - +The keys are generated using the BIP-44 path `m/44'/635'/%d'` where `%d` is the index of the child key - this allows for the deterministic generation of up to `2147483647` hardened ed25519 child keys per master key. Master key derivation is done as follows: ```mermaid flowchart LR diff --git a/shared/crypto/slip.go b/shared/crypto/slip.go index 15964ea04..e1b964fb7 100644 --- a/shared/crypto/slip.go +++ b/shared/crypto/slip.go @@ -6,19 +6,28 @@ import ( "crypto/sha512" "encoding/binary" "fmt" + "regexp" + "strconv" + "strings" ) const ( + // PoktAccountPathFormat used for HD key derivation where 635 is the SLIP-0044 coin type + // To be used with fmt.Sprintf to generate child keys + // Ref: https://github.com/satoshilabs/slips/blob/master/slip-0044.md + PoktAccountPathFormat = "m/44'/635'/%d'" // m/purpose'/coin_type'/account_idx' // As defined in: https://github.com/satoshilabs/slips/blob/master/slip-0010.md#master-key-generation seedModifier = "ed25519 seed" // Hardened key values, as defined in: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#extended-keys - firstHardenedKeyIndex = uint32(2147483648) // 2^31 - maxHardenedKeyIndex = ^uint32(0) // 2^32-1 + firstHardenedKeyIndex = uint32(1 << 31) // 2^31 + maxHardenedKeyIndex = ^uint32(0) // 2^32-1 maxChildKeyIndex = maxHardenedKeyIndex - firstHardenedKeyIndex ) var ( - ErrNoDerivation = fmt.Errorf("no derivation for an ed25519 key is possible") + ErrNoDerivation = fmt.Errorf("no derivation for a hardened ed25519 key is possible") + ErrInvalidPath = fmt.Errorf("invalid BIP-44 derivation path") + pathRegex = regexp.MustCompile(`m(/\d+')+$`) ) type slipKey struct { @@ -27,26 +36,35 @@ type slipKey struct { } // Derives a master key from the seed provided and returns the child at the correct index -func DeriveChild(index uint32, seed []byte) (KeyPair, error) { - masterKey, err := newMasterKey(seed) +func DeriveChild(path string, seed []byte) (KeyPair, error) { + // Break down path into uint32 segments + segments, err := pathToSegments(path) if err != nil { return nil, err } - // Allow index usage from 0 while using hardened keys - if index > maxChildKeyIndex { - return nil, fmt.Errorf("child index is greater than max hardened ed25519 key index: got %d, max %d", index, maxChildKeyIndex) - } - - // Force hardened keys - index += firstHardenedKeyIndex - - childKey, err := masterKey.deriveChild(index) + // Initialise a master key from seed to start child generation + key, err := newMasterKey(seed) if err != nil { return nil, err } - return childKey.convertToKeypair() + // Iterate over segments in path regenerating the child key until correct + for _, i32 := range segments { + // Force hardened keys + if i32 > maxChildKeyIndex { + return nil, fmt.Errorf("hardened key index too large, max: %d, got: %d", maxChildKeyIndex, i32) + } + i := i32 + firstHardenedKeyIndex + + // Derive the correct child until final segment + key, err = key.deriveChild(i) + if err != nil { + return nil, err + } + } + + return key.convertToKeypair() } // Reference: https://github.com/satoshilabs/slips/blob/master/slip-0010.md#master-key-generation @@ -118,3 +136,29 @@ func (k *slipKey) convertToKeypair() (KeyPair, error) { PrivKeyArmour: armouredStr, }, nil } + +// Check the BIP-44 path provided is valid and return the []uint32 segments it contains +func pathToSegments(path string) ([]uint32, error) { + // Master path exception + if path == "m" { + return []uint32{}, nil + } + + // Check whether the path is valid + if !pathRegex.MatchString(path) { + return nil, ErrInvalidPath + } + + // Split into segments and check for valid uint32 types + segments := make([]uint32, 0) + segs := strings.Split(path, "/") + for _, seg := range segs[1:] { + ui64, err := strconv.ParseUint(strings.TrimRight(seg, "'"), 10, 32) + if err != nil { + return nil, err + } + segments = append(segments, uint32(ui64)) + } + + return segments, nil +} From c82cd0e58bd85fb632b5fa25e0efb3c13ffe7562 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Tue, 21 Feb 2023 15:46:39 +0000 Subject: [PATCH 14/34] Update go.mod --- go.mod | 3 --- go.sum | 10 ---------- 2 files changed, 13 deletions(-) diff --git a/go.mod b/go.mod index 6ffe683ae..f79d0a6c3 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,6 @@ require ( require ( github.com/benbjohnson/clock v1.3.0 github.com/celestiaorg/smt v0.2.1-0.20220414134126-dba215ccb884 - github.com/deepmap/oapi-codegen v1.12.4 github.com/dgraph-io/badger/v3 v3.2103.2 github.com/getkin/kin-openapi v0.107.0 github.com/jackc/pgconn v1.13.0 @@ -65,9 +64,7 @@ require ( ) require ( - github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/google/uuid v1.3.0 // indirect github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/jackc/puddle/v2 v2.1.2 // indirect diff --git a/go.sum b/go.sum index cc412c8f3..cd45047dc 100644 --- a/go.sum +++ b/go.sum @@ -51,14 +51,11 @@ github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-ecvrf v0.0.1 h1:wv45+kZ0mG4G9oSTMjAlbgKqa4tPbNr4WLoCWqz5/bo= github.com/ProtonMail/go-ecvrf v0.0.1/go.mod h1:fhZbiRYn62/JGnBG2NGwCx0oT+gr/+I5R/hwiyAFpAU= -github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= -github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -66,7 +63,6 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= github.com/celestiaorg/smt v0.2.1-0.20220414134126-dba215ccb884 h1:iRNKw2WmAbVgGMNYzDH5Y2yY3+jyxwEK9Hc5pwIjZAE= github.com/celestiaorg/smt v0.2.1-0.20220414134126-dba215ccb884/go.mod h1:/sdYDakowo/XaxS2Fl7CBqtuf/O2uTqF2zmAUFAtAiw= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= @@ -110,8 +106,6 @@ github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxG github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/deepmap/oapi-codegen v1.12.4 h1:pPmn6qI9MuOtCz82WY2Xaw46EQjgvxednXXrP7g5Q2s= -github.com/deepmap/oapi-codegen v1.12.4/go.mod h1:3lgHGMu6myQ2vqbbTXH2H1o4eXFTGnFiDaOaKKl5yas= github.com/dgraph-io/badger/v3 v3.2103.2 h1:dpyM5eCJAtQCBcMCZcT4UBZchuTJgCywerHHgmxfxM8= github.com/dgraph-io/badger/v3 v3.2103.2/go.mod h1:RHo4/GmYcKKh5Lxu63wLEMHJ70Pac2JqZRYGhlyAo2M= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= @@ -232,8 +226,6 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= @@ -304,7 +296,6 @@ github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -468,7 +459,6 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU= github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw= -github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= From 10312e2d6b2c4b11708e9125a40f408ba703a22f Mon Sep 17 00:00:00 2001 From: Harry Law Date: Wed, 22 Feb 2023 10:13:49 +0000 Subject: [PATCH 15/34] Add comments to clarify test vectors --- app/client/keybase/slip_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/client/keybase/slip_test.go b/app/client/keybase/slip_test.go index 74677287f..aca6b2179 100644 --- a/app/client/keybase/slip_test.go +++ b/app/client/keybase/slip_test.go @@ -12,8 +12,8 @@ const ( testChildAddrIdx1 = "8b83d7057df7ac1d20a2f0aa0edadf206eb6764d" ) +// https://github.com/satoshilabs/slips/blob/master/slip-0010.md#test-vector-1-for-ed25519 func TestSlip_DeriveChild_Vector1(t *testing.T) { - // https://github.com/satoshilabs/slips/blob/master/slip-0010.md#test-vector-1-for-ed25519 seed, err := hex.DecodeString("000102030405060708090a0b0c0d0e0f") require.NoError(t, err) @@ -118,6 +118,7 @@ func TestSlip_DeriveChild_Vector1(t *testing.T) { } // Slip-0010 private keys in test vector are only the seed of the full private key + // This is equivalent to the SecretKey of the HMAC key used to generate the ed25519 key privSeed, err := childKey.GetSeed("") require.NoError(t, err) privHex := hex.EncodeToString(privSeed) @@ -130,8 +131,8 @@ func TestSlip_DeriveChild_Vector1(t *testing.T) { } } +// https://github.com/satoshilabs/slips/blob/master/slip-0010.md#test-vector-2-for-ed25519 func TestSlip_DeriveChild_Vector2(t *testing.T) { - // https://github.com/satoshilabs/slips/blob/master/slip-0010.md#test-vector-2-for-ed25519 seed, err := hex.DecodeString("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542") require.NoError(t, err) @@ -227,6 +228,7 @@ func TestSlip_DeriveChild_Vector2(t *testing.T) { } // Slip-0010 private keys in test vector are only the seed of the full private key + // This is equivalent to the SecretKey of the HMAC key used to generate the ed25519 key privSeed, err := childKey.GetSeed("") require.NoError(t, err) privHex := hex.EncodeToString(privSeed) From 644b61481b7b2ecb6ce845c414e7c0b06a9efd3c Mon Sep 17 00:00:00 2001 From: Harry Law Date: Thu, 23 Feb 2023 10:14:16 +0000 Subject: [PATCH 16/34] Fix merge error --- app/client/keybase/slip_test.go | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/app/client/keybase/slip_test.go b/app/client/keybase/slip_test.go index aca6b2179..d6f393f82 100644 --- a/app/client/keybase/slip_test.go +++ b/app/client/keybase/slip_test.go @@ -2,9 +2,10 @@ package keybase import ( "encoding/hex" + "testing" + "github.com/pokt-network/pocket/shared/crypto" "github.com/stretchr/testify/require" - "testing" ) const ( @@ -245,7 +246,7 @@ func TestKeybase_DeriveChildFromKey(t *testing.T) { db := initDB(t) defer stopDB(t, db) - err := db.ImportFromString(testPrivString, testPassphrase, testHint) + _, err := db.ImportFromString(testPrivString, testPassphrase, testHint) require.NoError(t, err) childKey, err := db.DeriveChildFromKey(testAddr, testPassphrase, 1) @@ -257,10 +258,7 @@ func TestKeybase_DeriveChildFromSeed(t *testing.T) { db := initDB(t) defer stopDB(t, db) - err := db.ImportFromString(testPrivString, testPassphrase, testHint) - require.NoError(t, err) - - kp, err := db.Get(testAddr) + kp, err := db.ImportFromString(testPrivString, testPassphrase, testHint) require.NoError(t, err) seed, err := kp.GetSeed(testPassphrase) @@ -275,7 +273,7 @@ func TestKeybase_StoreChildFromKey(t *testing.T) { db := initDB(t) defer stopDB(t, db) - err := db.ImportFromString(testPrivString, testPassphrase, testHint) + _, err := db.ImportFromString(testPrivString, testPassphrase, testHint) require.NoError(t, err) err = db.StoreChildFromKey(testAddr, testPassphrase, 1, testPassphrase, testHint) @@ -290,10 +288,7 @@ func TestKeybase_StoreChildFromSeed(t *testing.T) { db := initDB(t) defer stopDB(t, db) - err := db.ImportFromString(testPrivString, testPassphrase, testHint) - require.NoError(t, err) - - kp, err := db.Get(testAddr) + kp, err := db.ImportFromString(testPrivString, testPassphrase, testHint) require.NoError(t, err) seed, err := kp.GetSeed(testPassphrase) From 7024b64d4127e5afa520a5e528f0a80d0d410fa5 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Thu, 23 Feb 2023 13:50:39 +0000 Subject: [PATCH 17/34] Add DeriveChild method to KeyPair interface --- shared/crypto/keypair.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/shared/crypto/keypair.go b/shared/crypto/keypair.go index 59975b616..3407592e1 100644 --- a/shared/crypto/keypair.go +++ b/shared/crypto/keypair.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/ed25519" "encoding/gob" + "fmt" ) // Encoding is used to serialise the data to store the KeyPairs in the database @@ -33,6 +34,9 @@ type KeyPair interface { // Seed GetSeed(passphrase string) ([]byte, error) + // SLIP-0010 Child derivation + DeriveChild(passphrase string, index uint32) (KeyPair, error) + // Marshalling Marshal() ([]byte, error) Unmarshal([]byte) error @@ -111,6 +115,16 @@ func (kp encKeyPair) GetSeed(passphrase string) ([]byte, error) { return privKey.Seed(), nil } +// Derives a new child key using the key as the master +func (kp encKeyPair) DeriveChild(passphrase string, index uint32) (KeyPair, error) { + seed, err := kp.GetSeed(passphrase) + if err != nil { + return nil, err + } + path := fmt.Sprintf(PoktAccountPathFormat, index) + return DeriveChild(path, seed) +} + // Marshal KeyPair into a []byte func (kp encKeyPair) Marshal() ([]byte, error) { buf := new(bytes.Buffer) From 8bf2eb6e4bbcbb190689a94dab151356a13099bb Mon Sep 17 00:00:00 2001 From: Harry Law Date: Thu, 23 Feb 2023 13:51:03 +0000 Subject: [PATCH 18/34] Combine test vectors --- app/client/keybase/slip_test.go | 121 +++++++++++--------------------- 1 file changed, 41 insertions(+), 80 deletions(-) diff --git a/app/client/keybase/slip_test.go b/app/client/keybase/slip_test.go index d6f393f82..ddc897784 100644 --- a/app/client/keybase/slip_test.go +++ b/app/client/keybase/slip_test.go @@ -9,18 +9,18 @@ import ( ) const ( + // Test Vectors + testVector1SeedHex = "000102030405060708090a0b0c0d0e0f" + testVector2SeedHex = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542" + // SLIPS-0010 testChildAddrIdx1 = "8b83d7057df7ac1d20a2f0aa0edadf206eb6764d" ) -// https://github.com/satoshilabs/slips/blob/master/slip-0010.md#test-vector-1-for-ed25519 -func TestSlip_DeriveChild_Vector1(t *testing.T) { - seed, err := hex.DecodeString("000102030405060708090a0b0c0d0e0f") - require.NoError(t, err) - +func TestSlip_DeriveChild_TestVectors(t *testing.T) { type args struct { path string - seed []byte + seed string } tests := []struct { name string @@ -30,11 +30,12 @@ func TestSlip_DeriveChild_Vector1(t *testing.T) { wantErr bool }{ + // https://github.com/satoshilabs/slips/blob/master/slip-0010.md#test-vector-1-for-ed25519 { - name: "Key(m) – master key", + name: "Test key derivation is deterministic for path `m` (master key)", args: args{ path: "m", - seed: seed, + seed: testVector1SeedHex, }, wantPrivHex: "2b4be7f19ee27bbf30c667b642d5f4aa69fd169872f8fc3059c08ebae2eb19e7", wantPubHex: "00a4b2856bfec510abab89753fac1ac0e1112364e7d250545963f135f2a33188ed", @@ -42,10 +43,10 @@ func TestSlip_DeriveChild_Vector1(t *testing.T) { wantErr: false, }, { - name: "Key(m/0')", + name: "Test key derivation is deterministic for path `m/0'`", args: args{ path: "m/0'", - seed: seed, + seed: testVector1SeedHex, }, wantPrivHex: "68e0fe46dfb67e368c75379acec591dad19df3cde26e63b93a8e704f1dade7a3", wantPubHex: "008c8a13df77a28f3445213a0f432fde644acaa215fc72dcdf300d5efaa85d350c", @@ -53,10 +54,10 @@ func TestSlip_DeriveChild_Vector1(t *testing.T) { wantErr: false, }, { - name: "Key(m/0'/1')", + name: "Test key derivation is deterministic for path `m/0'/1'`", args: args{ path: "m/0'/1'", - seed: seed, + seed: testVector1SeedHex, }, wantPrivHex: "b1d0bad404bf35da785a64ca1ac54b2617211d2777696fbffaf208f746ae84f2", wantPubHex: "001932a5270f335bed617d5b935c80aedb1a35bd9fc1e31acafd5372c30f5c1187", @@ -64,10 +65,10 @@ func TestSlip_DeriveChild_Vector1(t *testing.T) { wantErr: false, }, { - name: "Key(m/0'/1'/2')", + name: "Test key derivation is deterministic for path `m/0'/1'/2'`", args: args{ path: "m/0'/1'/2'", - seed: seed, + seed: testVector1SeedHex, }, wantPrivHex: "92a5b23c0b8a99e37d07df3fb9966917f5d06e02ddbd909c7e184371463e9fc9", wantPubHex: "00ae98736566d30ed0e9d2f4486a64bc95740d89c7db33f52121f8ea8f76ff0fc1", @@ -75,10 +76,10 @@ func TestSlip_DeriveChild_Vector1(t *testing.T) { wantErr: false, }, { - name: "Key(m/0'/1'/2'/2')", + name: "Test key derivation is deterministic for path `m/0'/1'/2'/2'`", args: args{ path: "m/0'/1'/2'/2'", - seed: seed, + seed: testVector1SeedHex, }, wantPrivHex: "30d1dc7e5fc04c31219ab25a27ae00b50f6fd66622f6e9c913253d6511d1e662", wantPubHex: "008abae2d66361c879b900d204ad2cc4984fa2aa344dd7ddc46007329ac76c429c", @@ -86,10 +87,10 @@ func TestSlip_DeriveChild_Vector1(t *testing.T) { wantErr: false, }, { - name: "Key(m/0'/1'/2'/2'/1000000000')", + name: "Test key derivation is deterministic for path `m/0'/1'/2'/2'/1000000000'`", args: args{ path: "m/0'/1'/2'/2'/1000000000'", - seed: seed, + seed: testVector1SeedHex, }, wantPrivHex: "8f94d394a8e8fd6b1bc2f3f49f5c47e385281d5c17e65324b0f62483e37e8793", wantPubHex: "003c24da049451555d51a7014a37337aa4e12d41e485abccfa46b47dfb2af54b7a", @@ -97,63 +98,20 @@ func TestSlip_DeriveChild_Vector1(t *testing.T) { wantErr: false, }, { - name: "Key(invalid)", + name: "Test key derivation fails with invalid path `m/0`", args: args{ path: "m/0", - seed: seed, + seed: testVector1SeedHex, }, wantPrivHex: "", wantErr: true, }, - } - for _, tv := range tests { - t.Run(tv.name, func(t *testing.T) { - childKey, err := crypto.DeriveChild(tv.args.path, tv.args.seed) - if (err != nil) != tv.wantErr { - t.Errorf("DeriveChild() error = %v, wantErr %v", err, tv.wantErr) - return - } - - if err != nil { - return - } - - // Slip-0010 private keys in test vector are only the seed of the full private key - // This is equivalent to the SecretKey of the HMAC key used to generate the ed25519 key - privSeed, err := childKey.GetSeed("") - require.NoError(t, err) - privHex := hex.EncodeToString(privSeed) - require.Equal(t, privHex, tv.wantPrivHex) - - // Slip-0010 keys are prefixed with "00" in the test vectors - pubHex := childKey.GetPublicKey().String() - require.Equal(t, "00"+pubHex, tv.wantPubHex) - }) - } -} - -// https://github.com/satoshilabs/slips/blob/master/slip-0010.md#test-vector-2-for-ed25519 -func TestSlip_DeriveChild_Vector2(t *testing.T) { - seed, err := hex.DecodeString("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542") - require.NoError(t, err) - - type args struct { - path string - seed []byte - } - tests := []struct { - name string - args args - wantPrivHex string - wantPubHex string - - wantErr bool - }{ + // https://github.com/satoshilabs/slips/blob/master/slip-0010.md#test-vector-2-for-ed25519 { - name: "Key(m) – master key", + name: "Test key derivation is deterministic for path `m` (master key)", args: args{ path: "m", - seed: seed, + seed: testVector2SeedHex, }, wantPrivHex: "171cb88b1b3c1db25add599712e36245d75bc65a1a5c9e18d76f9f2b1eab4012", wantPubHex: "008fe9693f8fa62a4305a140b9764c5ee01e455963744fe18204b4fb948249308a", @@ -161,10 +119,10 @@ func TestSlip_DeriveChild_Vector2(t *testing.T) { wantErr: false, }, { - name: "Key(m/0')", + name: "Test key derivation is deterministic for path `m/0'`", args: args{ path: "m/0'", - seed: seed, + seed: testVector2SeedHex, }, wantPrivHex: "1559eb2bbec5790b0c65d8693e4d0875b1747f4970ae8b650486ed7470845635", wantPubHex: "0086fab68dcb57aa196c77c5f264f215a112c22a912c10d123b0d03c3c28ef1037", @@ -172,10 +130,10 @@ func TestSlip_DeriveChild_Vector2(t *testing.T) { wantErr: false, }, { - name: "Key(m/0'/2147483647')", + name: "Test key derivation is deterministic for path `m/0'/2147483647'`", args: args{ path: "m/0'/2147483647'", - seed: seed, + seed: testVector2SeedHex, }, wantPrivHex: "ea4f5bfe8694d8bb74b7b59404632fd5968b774ed545e810de9c32a4fb4192f4", wantPubHex: "005ba3b9ac6e90e83effcd25ac4e58a1365a9e35a3d3ae5eb07b9e4d90bcf7506d", @@ -183,10 +141,10 @@ func TestSlip_DeriveChild_Vector2(t *testing.T) { wantErr: false, }, { - name: "Key(m/0'/2147483647'/1')", + name: "Test key derivation is deterministic for path `m/0'/2147483647'/1'`", args: args{ path: "m/0'/2147483647'/1'", - seed: seed, + seed: testVector2SeedHex, }, wantPrivHex: "3757c7577170179c7868353ada796c839135b3d30554bbb74a4b1e4a5a58505c", wantPubHex: "002e66aa57069c86cc18249aecf5cb5a9cebbfd6fadeab056254763874a9352b45", @@ -194,10 +152,10 @@ func TestSlip_DeriveChild_Vector2(t *testing.T) { wantErr: false, }, { - name: "Key(m/0'/2147483647'/1'/2147483646')", + name: "Test key derivation is deterministic for path `m/0'/2147483647'/1'/2147483646'`", args: args{ path: "m/0'/2147483647'/1'/2147483646'", - seed: seed, + seed: testVector2SeedHex, }, wantPrivHex: "5837736c89570de861ebc173b1086da4f505d4adb387c6a1b1342d5e4ac9ec72", wantPubHex: "00e33c0f7d81d843c572275f287498e8d408654fdf0d1e065b84e2e6f157aab09b", @@ -205,10 +163,10 @@ func TestSlip_DeriveChild_Vector2(t *testing.T) { wantErr: false, }, { - name: "Key(m/0'/2147483647'/1'/2147483646'/2')", + name: "Test key derivation is deterministic for path `m/0'/2147483647'/1'/2147483646'/2'`", args: args{ path: "m/0'/2147483647'/1'/2147483646'/2'", - seed: seed, + seed: testVector2SeedHex, }, wantPrivHex: "551d333177df541ad876a60ea71f00447931c0a9da16f227c11ea080d7391b8d", wantPubHex: "0047150c75db263559a70d5778bf36abbab30fb061ad69f69ece61a72b0cfa4fc0", @@ -218,10 +176,13 @@ func TestSlip_DeriveChild_Vector2(t *testing.T) { } for _, tv := range tests { t.Run(tv.name, func(t *testing.T) { - childKey, err := crypto.DeriveChild(tv.args.path, tv.args.seed) - if (err != nil) != tv.wantErr { - t.Errorf("DeriveChild() error = %v, wantErr %v", err, tv.wantErr) - return + seed, err := hex.DecodeString(tv.args.seed) + require.NoError(t, err) + childKey, err := crypto.DeriveChild(tv.args.path, seed) + if tv.wantErr { + require.Error(t, err) + } else { + require.NoError(t, err) } if err != nil { From 35fbacb0dc61a9e8c2fd6cbb344945f60c5e4980 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Thu, 23 Feb 2023 13:51:15 +0000 Subject: [PATCH 19/34] Update graphs --- shared/crypto/README.md | 57 ++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/shared/crypto/README.md b/shared/crypto/README.md index f20bdb08e..5e8bfeddd 100644 --- a/shared/crypto/README.md +++ b/shared/crypto/README.md @@ -113,43 +113,48 @@ The keys are generated using the BIP-44 path `m/44'/635'/%d'` where `%d` is the Master key derivation is done as follows: ```mermaid flowchart LR - subgraph HMAC + subgraph HCHILD["HMAC-CHILD"] direction TB - A["hmacNew(sha512, seedModifier)"] - B["hmacWrite(seed)"] - C["convertToBytes(hmac)"] - A-->B-->C + C["append(0x0, parent.SecretKey, bigEndian(index))"] + A["hmacNew(sha512, parent.Chaincode)"] + B["hmac.Write(data)"] + D["convertToBytes(hmac)"] + A--hmac-->B + C--data-->B + B--hmac-->D end - subgraph MASTER-KEY - direction TB - D["SecretKey: hmacBytes[:32]"] - E["ChainCode: hmacBytes[32:]"] - D --> E --Secret+Chaincode--> KEY + subgraph CKEY[CHILD-KEY] + direction LR + F["SecretKey: hmacBytes[:32]"] + G["ChainCode: hmacBytes[32:]"] + F --> KEY + G --> KEY end - HMAC--hmacBytes-->MASTER-KEY + Index-->HCHILD + Parent-->HCHILD + HCHILD--hmacBytes-->CKEY ``` Child keys are derived from their parents as follows: ```mermaid flowchart LR - subgraph HMAC-CHILD + subgraph HMAC direction TB - C["append(0x0, parent.SecretKey, bigEndian(index))"] - A["hmacNew(sha512, parent.Chaincode)"] - B["hmacWrite(data)"] - D["convertToBytes(hmac)"] - A--hmac-->B - C--data-->B - B-->D + A["hmac = hmacNew(sha512, seedModifier)"] + B["hmac.Write(seed)"] + C["convertToBytes(hmac)"] + A-->B + B--hmac-->C end - subgraph CHILD-KEY - direction TB - F["secret: hmacBytes[:32]"] - G["chaincode: hmacBytes[32:]"] - F --> G --Secret+Chaincode--> KEY + subgraph MASTER-KEY + direction LR + D["SecretKey: hmacBytes[:32]"] + E["ChainCode: hmacBytes[32:]"] + D --> KEY + E --> KEY end - Parent --> HMAC-CHILD - HMAC-CHILD --hmacBytes--> CHILD-KEY + seed-->HMAC + HMAC--hmacBytes-->MASTER-KEY ``` From 851c9c1daf7b9ec6c714ec5d4d1d5e4b4a402616 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Thu, 23 Feb 2023 13:51:29 +0000 Subject: [PATCH 20/34] Address comments --- shared/crypto/slip.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shared/crypto/slip.go b/shared/crypto/slip.go index e1b964fb7..fbf8ddbcf 100644 --- a/shared/crypto/slip.go +++ b/shared/crypto/slip.go @@ -19,7 +19,7 @@ const ( // As defined in: https://github.com/satoshilabs/slips/blob/master/slip-0010.md#master-key-generation seedModifier = "ed25519 seed" // Hardened key values, as defined in: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#extended-keys - firstHardenedKeyIndex = uint32(1 << 31) // 2^31 + firstHardenedKeyIndex = uint32(1 << 31) // 2^31; [0, 2^31) are non-hardened keys which are not supported for ed25519 maxHardenedKeyIndex = ^uint32(0) // 2^32-1 maxChildKeyIndex = maxHardenedKeyIndex - firstHardenedKeyIndex ) @@ -55,10 +55,10 @@ func DeriveChild(path string, seed []byte) (KeyPair, error) { if i32 > maxChildKeyIndex { return nil, fmt.Errorf("hardened key index too large, max: %d, got: %d", maxChildKeyIndex, i32) } - i := i32 + firstHardenedKeyIndex + keyIdx := i32 + firstHardenedKeyIndex // Derive the correct child until final segment - key, err = key.deriveChild(i) + key, err = key.deriveChild(keyIdx) if err != nil { return nil, err } From 645be443589a5d28bf451a5040e6f815eb7491a0 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Thu, 23 Feb 2023 14:14:35 +0000 Subject: [PATCH 21/34] Add pokt specific test vectors --- app/client/keybase/slip_test.go | 218 ++++++++++++++++++-------------- 1 file changed, 122 insertions(+), 96 deletions(-) diff --git a/app/client/keybase/slip_test.go b/app/client/keybase/slip_test.go index ddc897784..931ed2a56 100644 --- a/app/client/keybase/slip_test.go +++ b/app/client/keybase/slip_test.go @@ -2,6 +2,7 @@ package keybase import ( "encoding/hex" + "fmt" "testing" "github.com/pokt-network/pocket/shared/crypto" @@ -14,17 +15,15 @@ const ( testVector2SeedHex = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542" // SLIPS-0010 + testSeedHex = "045e8380086abc6f6e941d6fe47ca93b86723bc246ec8c4beee411b410028675" testChildAddrIdx1 = "8b83d7057df7ac1d20a2f0aa0edadf206eb6764d" ) func TestSlip_DeriveChild_TestVectors(t *testing.T) { - type args struct { - path string - seed string - } tests := []struct { name string - args args + path string + seed string wantPrivHex string wantPubHex string @@ -32,153 +31,180 @@ func TestSlip_DeriveChild_TestVectors(t *testing.T) { }{ // https://github.com/satoshilabs/slips/blob/master/slip-0010.md#test-vector-1-for-ed25519 { - name: "Test key derivation is deterministic for path `m` (master key)", - args: args{ - path: "m", - seed: testVector1SeedHex, - }, + name: "TestVector1 Key derivation is deterministic for path `m` (master key)", + path: "m", + seed: testVector1SeedHex, wantPrivHex: "2b4be7f19ee27bbf30c667b642d5f4aa69fd169872f8fc3059c08ebae2eb19e7", wantPubHex: "00a4b2856bfec510abab89753fac1ac0e1112364e7d250545963f135f2a33188ed", - - wantErr: false, + wantErr: false, }, { - name: "Test key derivation is deterministic for path `m/0'`", - args: args{ - path: "m/0'", - seed: testVector1SeedHex, - }, + name: "TestVector1 Key derivation is deterministic for path `m/0'`", + path: "m/0'", + seed: testVector1SeedHex, wantPrivHex: "68e0fe46dfb67e368c75379acec591dad19df3cde26e63b93a8e704f1dade7a3", wantPubHex: "008c8a13df77a28f3445213a0f432fde644acaa215fc72dcdf300d5efaa85d350c", - - wantErr: false, + wantErr: false, }, { - name: "Test key derivation is deterministic for path `m/0'/1'`", - args: args{ - path: "m/0'/1'", - seed: testVector1SeedHex, - }, + name: "TestVector1 Key derivation is deterministic for path `m/0'/1'`", + path: "m/0'/1'", + seed: testVector1SeedHex, wantPrivHex: "b1d0bad404bf35da785a64ca1ac54b2617211d2777696fbffaf208f746ae84f2", wantPubHex: "001932a5270f335bed617d5b935c80aedb1a35bd9fc1e31acafd5372c30f5c1187", - - wantErr: false, + wantErr: false, }, { - name: "Test key derivation is deterministic for path `m/0'/1'/2'`", - args: args{ - path: "m/0'/1'/2'", - seed: testVector1SeedHex, - }, + name: "TestVector1 Key derivation is deterministic for path `m/0'/1'/2'`", + path: "m/0'/1'/2'", + seed: testVector1SeedHex, wantPrivHex: "92a5b23c0b8a99e37d07df3fb9966917f5d06e02ddbd909c7e184371463e9fc9", wantPubHex: "00ae98736566d30ed0e9d2f4486a64bc95740d89c7db33f52121f8ea8f76ff0fc1", - - wantErr: false, + wantErr: false, }, { - name: "Test key derivation is deterministic for path `m/0'/1'/2'/2'`", - args: args{ - path: "m/0'/1'/2'/2'", - seed: testVector1SeedHex, - }, + name: "TestVector1 Key derivation is deterministic for path `m/0'/1'/2'/2'`", + path: "m/0'/1'/2'/2'", + seed: testVector1SeedHex, wantPrivHex: "30d1dc7e5fc04c31219ab25a27ae00b50f6fd66622f6e9c913253d6511d1e662", wantPubHex: "008abae2d66361c879b900d204ad2cc4984fa2aa344dd7ddc46007329ac76c429c", - - wantErr: false, + wantErr: false, }, { - name: "Test key derivation is deterministic for path `m/0'/1'/2'/2'/1000000000'`", - args: args{ - path: "m/0'/1'/2'/2'/1000000000'", - seed: testVector1SeedHex, - }, + name: "TestVector1 Key derivation is deterministic for path `m/0'/1'/2'/2'/1000000000'`", + path: "m/0'/1'/2'/2'/1000000000'", + seed: testVector1SeedHex, wantPrivHex: "8f94d394a8e8fd6b1bc2f3f49f5c47e385281d5c17e65324b0f62483e37e8793", wantPubHex: "003c24da049451555d51a7014a37337aa4e12d41e485abccfa46b47dfb2af54b7a", - - wantErr: false, + wantErr: false, }, { - name: "Test key derivation fails with invalid path `m/0`", - args: args{ - path: "m/0", - seed: testVector1SeedHex, - }, + name: "TestVector1 Key derivation fails with invalid path `m/0`", + path: "m/0", + seed: testVector1SeedHex, wantPrivHex: "", wantErr: true, }, // https://github.com/satoshilabs/slips/blob/master/slip-0010.md#test-vector-2-for-ed25519 { - name: "Test key derivation is deterministic for path `m` (master key)", - args: args{ - path: "m", - seed: testVector2SeedHex, - }, + name: "TestVector2 Key derivation is deterministic for path `m` (master key)", + path: "m", + seed: testVector2SeedHex, wantPrivHex: "171cb88b1b3c1db25add599712e36245d75bc65a1a5c9e18d76f9f2b1eab4012", wantPubHex: "008fe9693f8fa62a4305a140b9764c5ee01e455963744fe18204b4fb948249308a", - - wantErr: false, + wantErr: false, }, { - name: "Test key derivation is deterministic for path `m/0'`", - args: args{ - path: "m/0'", - seed: testVector2SeedHex, - }, + name: "TestVector2 Key derivation is deterministic for path `m/0'`", + path: "m/0'", + seed: testVector2SeedHex, wantPrivHex: "1559eb2bbec5790b0c65d8693e4d0875b1747f4970ae8b650486ed7470845635", wantPubHex: "0086fab68dcb57aa196c77c5f264f215a112c22a912c10d123b0d03c3c28ef1037", - - wantErr: false, + wantErr: false, }, { - name: "Test key derivation is deterministic for path `m/0'/2147483647'`", - args: args{ - path: "m/0'/2147483647'", - seed: testVector2SeedHex, - }, + name: "TestVector2 Key derivation is deterministic for path `m/0'/2147483647'`", + path: "m/0'/2147483647'", + seed: testVector2SeedHex, wantPrivHex: "ea4f5bfe8694d8bb74b7b59404632fd5968b774ed545e810de9c32a4fb4192f4", wantPubHex: "005ba3b9ac6e90e83effcd25ac4e58a1365a9e35a3d3ae5eb07b9e4d90bcf7506d", - - wantErr: false, + wantErr: false, }, { - name: "Test key derivation is deterministic for path `m/0'/2147483647'/1'`", - args: args{ - path: "m/0'/2147483647'/1'", - seed: testVector2SeedHex, - }, + name: "TestVector2 Key derivation is deterministic for path `m/0'/2147483647'/1'`", + path: "m/0'/2147483647'/1'", + seed: testVector2SeedHex, wantPrivHex: "3757c7577170179c7868353ada796c839135b3d30554bbb74a4b1e4a5a58505c", wantPubHex: "002e66aa57069c86cc18249aecf5cb5a9cebbfd6fadeab056254763874a9352b45", - - wantErr: false, + wantErr: false, }, { - name: "Test key derivation is deterministic for path `m/0'/2147483647'/1'/2147483646'`", - args: args{ - path: "m/0'/2147483647'/1'/2147483646'", - seed: testVector2SeedHex, - }, + name: "TestVector2 Key derivation is deterministic for path `m/0'/2147483647'/1'/2147483646'`", + path: "m/0'/2147483647'/1'/2147483646'", + seed: testVector2SeedHex, wantPrivHex: "5837736c89570de861ebc173b1086da4f505d4adb387c6a1b1342d5e4ac9ec72", wantPubHex: "00e33c0f7d81d843c572275f287498e8d408654fdf0d1e065b84e2e6f157aab09b", - - wantErr: false, + wantErr: false, }, { - name: "Test key derivation is deterministic for path `m/0'/2147483647'/1'/2147483646'/2'`", - args: args{ - path: "m/0'/2147483647'/1'/2147483646'/2'", - seed: testVector2SeedHex, - }, + name: "TestVector2 Key derivation is deterministic for path `m/0'/2147483647'/1'/2147483646'/2'`", + path: "m/0'/2147483647'/1'/2147483646'/2'", + seed: testVector2SeedHex, wantPrivHex: "551d333177df541ad876a60ea71f00447931c0a9da16f227c11ea080d7391b8d", wantPubHex: "0047150c75db263559a70d5778bf36abbab30fb061ad69f69ece61a72b0cfa4fc0", - - wantErr: false, + wantErr: false, + }, + // Pocket specific test vectors + { + name: "PoktTestVector Key derivation is deterministic for path `m` (master key)", + path: "m", + seed: testSeedHex, + wantPrivHex: "849e1cf075e4ba4a310c2f89036a2a7eb2bb18ccfcfdd793cdc48a28978e588e", + wantPubHex: "009ab1c51476edbb1f71f91b33d3bb342503cba9adc8cd4a4c6e62a8cc7d27859b", + wantErr: false, + }, + { + name: "PoktTestVector Key derivation is deterministic for path `m/44'`", + path: "m/44'", + seed: testSeedHex, + wantPrivHex: "35852227a2289bcba3f0a428e08cc864f2ed35f605552c378232856a2abf5564", + wantPubHex: "00d0b243e9de9f2f12410275025395bd088cf57b0272794cc66d66093a51579797", + wantErr: false, + }, + { + name: "PoktTestVector Key derivation is deterministic for path `m/44'/635'`", + path: "m/44'/635'", + seed: testSeedHex, + wantPrivHex: "9af532407b4dba729962f9d57496394e7b82ea7a995575d0bfcc8fd4845debe6", + wantPubHex: "00a428992b7afb7716cf339d2b54bede7f2932cf4cf3c542f5662dcebd7e3abaee", + wantErr: false, + }, + { + name: "PoktTestVector Child key derivation is deterministic for index `0` (first child)", + path: fmt.Sprintf(crypto.PoktAccountPathFormat, 0), + seed: testSeedHex, + wantPrivHex: "e7e0f734311bdf2f446821a464b490ae8c005f6af92a26f42a39067103726346", + wantPubHex: "000848875b5836bc71fecb10f563b84b9b8a63b6d7a1b16b1e88eeaca6a7ad5852", + wantErr: false, + }, + { + name: "PoktTestVector Child key derivation is deterministic for index `1000000`", + path: fmt.Sprintf(crypto.PoktAccountPathFormat, 1000000), + seed: testSeedHex, + wantPrivHex: "f208bf7d7afa1a12a0b74e8fbbf1bb15bdfcaa3d03507ec338d7ecd77331e964", + wantPubHex: "00e7d8f01dfbc0eb1638d9853b95cdec650ad47d72fac1d2d1c97c8f935d2cbf90", + wantErr: false, + }, + { + name: "PoktTestVector Child key derivation is deterministic for index `2147483647` (last child)", + path: fmt.Sprintf(crypto.PoktAccountPathFormat, 2147483647), + seed: testSeedHex, + wantPrivHex: "20e061dcfae5cc90cba8ee374afc2d67d518b5f542f3217b8c830293c3dbb7e6", + wantPubHex: "0008a399a3cdc1ee9a50c922d018daa98b5069bfea37944fde14a73d83ca3ec08c", + wantErr: false, + }, + { + name: "PoktTestVector Child index is too large to derive ed25519 key for index `2147483648` ", + path: fmt.Sprintf(crypto.PoktAccountPathFormat, 2147483648), + seed: testSeedHex, + wantPrivHex: "", + wantPubHex: "", + wantErr: true, + }, + { + name: "PoktTestVector Child index is too large to derive ed25519 key for index `4294967295` ", + path: fmt.Sprintf(crypto.PoktAccountPathFormat, ^uint32(0)), + seed: testSeedHex, + wantPrivHex: "", + wantPubHex: "", + wantErr: true, }, } for _, tv := range tests { t.Run(tv.name, func(t *testing.T) { - seed, err := hex.DecodeString(tv.args.seed) + seed, err := hex.DecodeString(tv.seed) require.NoError(t, err) - childKey, err := crypto.DeriveChild(tv.args.path, seed) + childKey, err := crypto.DeriveChild(tv.path, seed) if tv.wantErr { require.Error(t, err) } else { From e339e2347992bb9c55247a5c7bf6d42f69e6c7ed Mon Sep 17 00:00:00 2001 From: Harry Law Date: Thu, 23 Feb 2023 14:39:59 +0000 Subject: [PATCH 22/34] Add deriveChild CLI endpoint --- app/client/cli/keys.go | 70 +++++++++++++++++++++++++++++++++++++++++ app/client/cli/utils.go | 18 +++++++++++ 2 files changed, 88 insertions(+) diff --git a/app/client/cli/keys.go b/app/client/cli/keys.go index dc1ff75fe..4d80566a1 100644 --- a/app/client/cli/keys.go +++ b/app/client/cli/keys.go @@ -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" @@ -23,6 +24,9 @@ var ( importAs string hint string newPwd string + storeChild bool + childPwd string + childHint string ) func init() { @@ -45,6 +49,7 @@ func NewKeysCommand() *cobra.Command { importCmds := keysImportCommands() signMsgCmds := keysSignMsgCommands() signTxCmds := keysSignTxCommands() + slipCmds := keysSlipCommands() // Add --pwd and --hint flags applySubcommandOptions(createCmds, attachPwdFlagToSubcommands()) @@ -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...) @@ -85,6 +96,7 @@ func NewKeysCommand() *cobra.Command { cmd.AddCommand(importCmds...) cmd.AddCommand(signMsgCmds...) cmd.AddCommand(signTxCmds...) + cmd.AddCommand(slipCmds...) return cmd } @@ -680,3 +692,61 @@ func keysSignTxCommands() []*cobra.Command { } return cmds } + +func keysSlipCommands() []*cobra.Command { + cmds := []*cobra.Command{ + { + Use: "DeriveChild ", + Short: "Derive the child key at the given index from a parent key", + Long: "Derive the child key at 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 +} diff --git a/app/client/cli/utils.go b/app/client/cli/utils.go index 8242e4fc3..c44d75047 100644 --- a/app/client/cli/utils.go +++ b/app/client/cli/utils.go @@ -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 From 5e34cd51faa0b9076837ae2e2247b6f428915562 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Thu, 23 Feb 2023 14:40:21 +0000 Subject: [PATCH 23/34] Return (KeyPair, error) when storing derived child --- app/client/keybase/keybase.go | 4 +-- app/client/keybase/keystore.go | 61 +++++++++++++++++++--------------- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/app/client/keybase/keybase.go b/app/client/keybase/keybase.go index 6ddeeefdd..7e2c19512 100644 --- a/app/client/keybase/keybase.go +++ b/app/client/keybase/keybase.go @@ -25,8 +25,8 @@ type Keybase interface { DeriveChildFromKey(masterAddrHex, passphrase string, childIndex uint32) (crypto.KeyPair, error) 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) diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index 0e276f046..4bccf3c79 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -264,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) - 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 == "" { @@ -284,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 { + // 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) From 9a23603db09942972eff4985fc7b73001f841542 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Thu, 23 Feb 2023 14:41:41 +0000 Subject: [PATCH 24/34] Fix tests --- app/client/keybase/slip_test.go | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/app/client/keybase/slip_test.go b/app/client/keybase/slip_test.go index 931ed2a56..1bbb1b561 100644 --- a/app/client/keybase/slip_test.go +++ b/app/client/keybase/slip_test.go @@ -263,12 +263,10 @@ func TestKeybase_StoreChildFromKey(t *testing.T) { _, err := db.ImportFromString(testPrivString, testPassphrase, testHint) require.NoError(t, err) - err = db.StoreChildFromKey(testAddr, testPassphrase, 1, testPassphrase, testHint) + childKey, err := db.StoreChildFromKey(testAddr, testPassphrase, 1, testPassphrase, testHint) require.NoError(t, err) - - childKey, err := db.GetPrivKey(testChildAddrIdx1, testPassphrase) - require.NoError(t, err) - require.Equal(t, childKey.Address().String(), testChildAddrIdx1) + require.NotNil(t, childKey) + require.Equal(t, childKey.GetAddressString(), testChildAddrIdx1) } func TestKeybase_StoreChildFromSeed(t *testing.T) { @@ -281,10 +279,8 @@ func TestKeybase_StoreChildFromSeed(t *testing.T) { seed, err := kp.GetSeed(testPassphrase) require.NoError(t, err) - err = db.StoreChildFromSeed(seed, 1, testPassphrase, testHint) + childKey, err := db.StoreChildFromSeed(seed, 1, testPassphrase, testHint) require.NoError(t, err) - - childKey, err := db.GetPrivKey(testChildAddrIdx1, testPassphrase) - require.NoError(t, err) - require.Equal(t, childKey.Address().String(), testChildAddrIdx1) + require.NotNil(t, childKey) + require.Equal(t, childKey.GetAddressString(), testChildAddrIdx1) } From 8a51fcdb0d20c1d496436708dcc6b8c7a926489e Mon Sep 17 00:00:00 2001 From: Harry Law Date: Thu, 23 Feb 2023 14:44:25 +0000 Subject: [PATCH 25/34] make generate_cli_commands_docs --- app/client/doc/commands/client_Keys.md | 1 + .../doc/commands/client_Keys_DeriveChild.md | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 app/client/doc/commands/client_Keys_DeriveChild.md diff --git a/app/client/doc/commands/client_Keys.md b/app/client/doc/commands/client_Keys.md index 7929f2c37..8be8b4aaa 100644 --- a/app/client/doc/commands/client_Keys.md +++ b/app/client/doc/commands/client_Keys.md @@ -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 diff --git a/app/client/doc/commands/client_Keys_DeriveChild.md b/app/client/doc/commands/client_Keys_DeriveChild.md new file mode 100644 index 000000000..2ea96b50b --- /dev/null +++ b/app/client/doc/commands/client_Keys_DeriveChild.md @@ -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 from the parent key provided optionally store it in the keybase with [--store_child] + +``` +client Keys DeriveChild [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 :// (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 From 749c3cf3235cb97e1d207345a151b2b67450b8b2 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Fri, 24 Feb 2023 09:41:10 +0000 Subject: [PATCH 26/34] Address comments --- app/client/cli/keys.go | 23 +++--- app/client/cli/utils.go | 2 +- app/client/keybase/keybase.go | 9 +-- app/client/keybase/keybase_test.go | 66 ++++++++++++++++- app/client/keybase/keystore.go | 74 ++++++++----------- shared/crypto/keypair.go | 36 +++++---- shared/crypto/{ => slip}/slip.go | 27 ++----- .../crypto/slip}/slip_test.go | 74 ++----------------- 8 files changed, 141 insertions(+), 170 deletions(-) rename shared/crypto/{ => slip}/slip.go (85%) rename {app/client/keybase => shared/crypto/slip}/slip_test.go (79%) diff --git a/app/client/cli/keys.go b/app/client/cli/keys.go index 4d80566a1..6118028ed 100644 --- a/app/client/cli/keys.go +++ b/app/client/cli/keys.go @@ -9,6 +9,7 @@ import ( "github.com/pokt-network/pocket/shared/converters" "github.com/pokt-network/pocket/shared/crypto" utilTypes "github.com/pokt-network/pocket/utility/types" + "github.com/spf13/viper" "path/filepath" "strconv" "strings" @@ -98,6 +99,10 @@ func NewKeysCommand() *cobra.Command { cmd.AddCommand(signTxCmds...) cmd.AddCommand(slipCmds...) + // Bind the store_child flag + viper.BindPFlag("storeChild", cmd.Flags().Lookup("store_child")) + viper.SetDefault("storeChild", true) + return cmd } @@ -725,25 +730,17 @@ func keysSlipCommands() []*cobra.Command { 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") + kp, err := kb.DeriveChildFromKey(parentAddr, pwd, index, childPwd, childHint) + if err != nil { + return err } if err := kb.Stop(); err != nil { return err } + logger.Global.Info().Str("address", kp.GetAddressString()).Str("parent", parentAddr).Uint32("index", index).Bool("stored", storeChild).Msg("Child key derived") + return nil }, }, diff --git a/app/client/cli/utils.go b/app/client/cli/utils.go index c44d75047..1cebb1a7b 100644 --- a/app/client/cli/utils.go +++ b/app/client/cli/utils.go @@ -222,7 +222,7 @@ 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") + c.Flags().BoolVar(&storeChild, "store_child", true, "store the derived child key in the keybase") }} } diff --git a/app/client/keybase/keybase.go b/app/client/keybase/keybase.go index 7e2c19512..1cb2ed11f 100644 --- a/app/client/keybase/keybase.go +++ b/app/client/keybase/keybase.go @@ -21,12 +21,9 @@ type Keybase interface { ImportFromJSON(jsonStr, passphrase string) (crypto.KeyPair, error) // SLIPS-0010 Key Derivation - // Deterministically generate and return the derived child key - DeriveChildFromKey(masterAddrHex, passphrase string, childIndex uint32) (crypto.KeyPair, error) - 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) (crypto.KeyPair, error) - StoreChildFromSeed(seed []byte, childIndex uint32, childPassphrase, childHint string) (crypto.KeyPair, error) + // Deterministically generate, store and return the derived child key + DeriveChildFromKey(masterAddrHex, passphrase string, childIndex uint32, childPassphrase, childHint string) (crypto.KeyPair, error) + DeriveChildFromSeed(seed []byte, childIndex uint32, childPassphrase, childHint string) (crypto.KeyPair, error) // Accessors Get(address string) (crypto.KeyPair, error) diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go index 01abe1826..ec773da7e 100644 --- a/app/client/keybase/keybase_test.go +++ b/app/client/keybase/keybase_test.go @@ -2,6 +2,7 @@ package keybase import ( "encoding/hex" + "github.com/spf13/viper" "testing" "github.com/pokt-network/pocket/runtime/test_artifacts/keygenerator" @@ -12,9 +13,10 @@ import ( //nolint:gosec // G101 Credentials are for tests const ( // Example account - testPrivString = "045e8380086abc6f6e941d6fe47ca93b86723bc246ec8c4beee411b410028675ed78c49592f836f7a4d47d4fb6a0e6b19f07aebc201d005f6b2c6afe389086e9" - testPubString = "ed78c49592f836f7a4d47d4fb6a0e6b19f07aebc201d005f6b2c6afe389086e9" - testAddr = "26e16ccab7a898400022476332e2972b8199f2f9" + testPrivString = "045e8380086abc6f6e941d6fe47ca93b86723bc246ec8c4beee411b410028675ed78c49592f836f7a4d47d4fb6a0e6b19f07aebc201d005f6b2c6afe389086e9" + testPubString = "ed78c49592f836f7a4d47d4fb6a0e6b19f07aebc201d005f6b2c6afe389086e9" + testAddr = "26e16ccab7a898400022476332e2972b8199f2f9" + testChildAddrIdx1 = "8b83d7057df7ac1d20a2f0aa0edadf206eb6764d" // Other testPassphrase = "Testing@Testing123" @@ -375,6 +377,64 @@ func TestKeybase_ExportJSON(t *testing.T) { require.Equal(t, privKey.String(), testPrivString) } +func TestKeybase_DeriveChildFromKey(t *testing.T) { + db := initDB(t) + defer stopDB(t, db) + + _, err := db.ImportFromString(testPrivString, testPassphrase, testHint) + require.NoError(t, err) + + viper.Set("storeChild", false) + childKey, err := db.DeriveChildFromKey(testAddr, testPassphrase, 1, "", "") + require.NoError(t, err) + require.Equal(t, childKey.GetAddressString(), testChildAddrIdx1) +} + +func TestKeybase_DeriveChildFromSeed(t *testing.T) { + db := initDB(t) + defer stopDB(t, db) + + kp, err := db.ImportFromString(testPrivString, testPassphrase, testHint) + require.NoError(t, err) + + seed, err := kp.GetSeed(testPassphrase) + require.NoError(t, err) + + viper.Set("storeChild", false) + childKey, err := db.DeriveChildFromSeed(seed, 1, "", "") + require.NoError(t, err) + require.Equal(t, childKey.GetAddressString(), testChildAddrIdx1) +} + +func TestKeybase_StoreChildFromKey(t *testing.T) { + db := initDB(t) + defer stopDB(t, db) + + _, err := db.ImportFromString(testPrivString, testPassphrase, testHint) + require.NoError(t, err) + + childKey, err := db.DeriveChildFromKey(testAddr, testPassphrase, 1, testPassphrase, testHint) + require.NoError(t, err) + require.NotNil(t, childKey) + require.Equal(t, childKey.GetAddressString(), testChildAddrIdx1) +} + +func TestKeybase_StoreChildFromSeed(t *testing.T) { + db := initDB(t) + defer stopDB(t, db) + + kp, err := db.ImportFromString(testPrivString, testPassphrase, testHint) + require.NoError(t, err) + + seed, err := kp.GetSeed(testPassphrase) + require.NoError(t, err) + + childKey, err := db.DeriveChildFromSeed(seed, 1, testPassphrase, testHint) + require.NoError(t, err) + require.NotNil(t, childKey) + require.Equal(t, childKey.GetAddressString(), testChildAddrIdx1) +} + func initDB(t *testing.T) Keybase { db, err := NewKeybaseInMemory() require.NoError(t, err) diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index 4bccf3c79..bf0bf00ed 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -4,6 +4,8 @@ import ( "bytes" "encoding/hex" "fmt" + "github.com/pokt-network/pocket/shared/crypto/slip" + "github.com/spf13/viper" "strings" "github.com/pokt-network/pocket/shared/converters" @@ -243,50 +245,32 @@ func (keybase *badgerKeybase) GetAll() (addresses []string, keyPairs []crypto.Ke return addresses, keyPairs, nil } -// Deterministically generate and return the ith child from the seed provided -func (keybase *badgerKeybase) DeriveChildFromSeed(seed []byte, childIndex uint32) (crypto.KeyPair, error) { - path := fmt.Sprintf(crypto.PoktAccountPathFormat, childIndex) - childKey, err := crypto.DeriveChild(path, seed) +// Deterministically generate and return the ith child from the seed provided - storing it in the keybase by default +func (keybase *badgerKeybase) DeriveChildFromSeed(seed []byte, childIndex uint32, childPassphrase, childHint string) (crypto.KeyPair, error) { + path := fmt.Sprintf(slip.PoktAccountPathFormat, childIndex) + childKey, err := slip.DeriveChild(path, seed) if err != nil { return nil, err } - return childKey, nil -} -// Deterministically generate and return the ith child key from the masterAddrHex key stored in the keybase -func (keybase *badgerKeybase) DeriveChildFromKey(masterAddrHex, passphrase string, childIndex uint32) (crypto.KeyPair, error) { - privKey, err := keybase.GetPrivKey(masterAddrHex, passphrase) - if err != nil { - return nil, err - } - seed := privKey.Seed() - return keybase.DeriveChildFromSeed(seed, childIndex) -} + if viper.GetBool("storeChild") { + // No need to re-encrypt with provided passphrase + if childPassphrase == "" && childHint == "" { + err = keybase.db.Update(func(tx *badger.Txn) error { + // Use key address as key in DB + addrKey := childKey.GetAddressBytes() -// 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) (childKey crypto.KeyPair, err error) { - path := fmt.Sprintf(crypto.PoktAccountPathFormat, childIndex) - childKey, err = crypto.DeriveChild(path, seed) - if err != nil { - return nil, err - } - // No need to re-encrypt with provided passphrase - if childPassphrase == "" && childHint == "" { - err = keybase.db.Update(func(tx *badger.Txn) error { - // Use key address as key in DB - addrKey := childKey.GetAddressBytes() - - // Encode KeyPair into []byte for value - keypairBz, err := childKey.Marshal() - if err != nil { - return err - } + // Encode KeyPair into []byte for value + keypairBz, err := childKey.Marshal() + if err != nil { + return err + } - return tx.Set(addrKey, keypairBz) - }) + return tx.Set(addrKey, keypairBz) + }) - return childKey, err - } else { + return childKey, err + } // 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 @@ -311,23 +295,23 @@ func (keybase *badgerKeybase) StoreChildFromSeed(seed []byte, childIndex uint32, return tx.Set(addrKey, keypairBz) }) - } - if err != nil { - return nil, err + 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) (crypto.KeyPair, error) { - masterPrivKey, err := keybase.GetPrivKey(masterAddrHex, masterPassphrase) +// Deterministically generate and return the ith child key from the masterAddrHex key stored in the keybase +func (keybase *badgerKeybase) DeriveChildFromKey(masterAddrHex, passphrase string, childIndex uint32, childPassphrase, childHint string) (crypto.KeyPair, error) { + privKey, err := keybase.GetPrivKey(masterAddrHex, passphrase) if err != nil { return nil, err } - seed := masterPrivKey.Seed() - return keybase.StoreChildFromSeed(seed, childIndex, childPassphrase, childHint) + seed := privKey.Seed() + return keybase.DeriveChildFromSeed(seed, childIndex, childPassphrase, childHint) } // Check whether an address is currently stored in the DB diff --git a/shared/crypto/keypair.go b/shared/crypto/keypair.go index 3407592e1..b5af33f4a 100644 --- a/shared/crypto/keypair.go +++ b/shared/crypto/keypair.go @@ -4,7 +4,6 @@ import ( "bytes" "crypto/ed25519" "encoding/gob" - "fmt" ) // Encoding is used to serialise the data to store the KeyPairs in the database @@ -34,9 +33,6 @@ type KeyPair interface { // Seed GetSeed(passphrase string) ([]byte, error) - // SLIP-0010 Child derivation - DeriveChild(passphrase string, index uint32) (KeyPair, error) - // Marshalling Marshal() ([]byte, error) Unmarshal([]byte) error @@ -115,16 +111,6 @@ func (kp encKeyPair) GetSeed(passphrase string) ([]byte, error) { return privKey.Seed(), nil } -// Derives a new child key using the key as the master -func (kp encKeyPair) DeriveChild(passphrase string, index uint32) (KeyPair, error) { - seed, err := kp.GetSeed(passphrase) - if err != nil { - return nil, err - } - path := fmt.Sprintf(PoktAccountPathFormat, index) - return DeriveChild(path, seed) -} - // Marshal KeyPair into a []byte func (kp encKeyPair) Marshal() ([]byte, error) { buf := new(bytes.Buffer) @@ -185,6 +171,28 @@ func CreateNewKeyFromString(privStrHex, passphrase, hint string) (KeyPair, error return kp, nil } +// Generate a new KeyPair from the seed provided +func CreateNewKeyFromSeed(seed []byte, passphrase, hint string) (KeyPair, error) { + // Generate PrivateKey interface form secret key + reader := bytes.NewReader(seed) + privKey, err := GeneratePrivateKeyWithReader(reader) + if err != nil { + return nil, err + } + + // Armour and encrypt private key into JSON string + armouredStr, err := encryptArmourPrivKey(privKey, passphrase, hint) // No passphrase or hint as they depend on the master key + if err != nil { + return nil, err + } + + // Return KeyPair interface + return &encKeyPair{ + PublicKey: privKey.PublicKey(), + PrivKeyArmour: armouredStr, + }, nil +} + // Create new KeyPair from the JSON encoded privStr func ImportKeyFromJSON(jsonStr, passphrase string) (KeyPair, error) { // Get Private Key from armouredStr diff --git a/shared/crypto/slip.go b/shared/crypto/slip/slip.go similarity index 85% rename from shared/crypto/slip.go rename to shared/crypto/slip/slip.go index fbf8ddbcf..ff263a0b6 100644 --- a/shared/crypto/slip.go +++ b/shared/crypto/slip/slip.go @@ -1,11 +1,11 @@ -package crypto +package slip import ( - "bytes" "crypto/hmac" "crypto/sha512" "encoding/binary" "fmt" + "github.com/pokt-network/pocket/shared/crypto" "regexp" "strconv" "strings" @@ -36,7 +36,7 @@ type slipKey struct { } // Derives a master key from the seed provided and returns the child at the correct index -func DeriveChild(path string, seed []byte) (KeyPair, error) { +func DeriveChild(path string, seed []byte) (crypto.KeyPair, error) { // Break down path into uint32 segments segments, err := pathToSegments(path) if err != nil { @@ -116,25 +116,8 @@ func (k *slipKey) deriveChild(i uint32) (*slipKey, error) { return child, nil } -func (k *slipKey) convertToKeypair() (KeyPair, error) { - // Generate PrivateKey interface form secret key - reader := bytes.NewReader(k.SecretKey) - privKey, err := GeneratePrivateKeyWithReader(reader) - if err != nil { - return nil, err - } - - // Armour and encrypt private key into JSON string - armouredStr, err := encryptArmourPrivKey(privKey, "", "") // No passphrase or hint as they depend on the master key - if err != nil { - return nil, err - } - - // Return KeyPair interface - return &encKeyPair{ - PublicKey: privKey.PublicKey(), - PrivKeyArmour: armouredStr, - }, nil +func (k *slipKey) convertToKeypair() (crypto.KeyPair, error) { + return crypto.CreateNewKeyFromSeed(k.SecretKey, "", "") } // Check the BIP-44 path provided is valid and return the []uint32 segments it contains diff --git a/app/client/keybase/slip_test.go b/shared/crypto/slip/slip_test.go similarity index 79% rename from app/client/keybase/slip_test.go rename to shared/crypto/slip/slip_test.go index 1bbb1b561..c9f108aec 100644 --- a/app/client/keybase/slip_test.go +++ b/shared/crypto/slip/slip_test.go @@ -1,11 +1,10 @@ -package keybase +package slip import ( "encoding/hex" "fmt" "testing" - "github.com/pokt-network/pocket/shared/crypto" "github.com/stretchr/testify/require" ) @@ -15,8 +14,7 @@ const ( testVector2SeedHex = "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542" // SLIPS-0010 - testSeedHex = "045e8380086abc6f6e941d6fe47ca93b86723bc246ec8c4beee411b410028675" - testChildAddrIdx1 = "8b83d7057df7ac1d20a2f0aa0edadf206eb6764d" + testSeedHex = "045e8380086abc6f6e941d6fe47ca93b86723bc246ec8c4beee411b410028675" ) func TestSlip_DeriveChild_TestVectors(t *testing.T) { @@ -161,7 +159,7 @@ func TestSlip_DeriveChild_TestVectors(t *testing.T) { }, { name: "PoktTestVector Child key derivation is deterministic for index `0` (first child)", - path: fmt.Sprintf(crypto.PoktAccountPathFormat, 0), + path: fmt.Sprintf(PoktAccountPathFormat, 0), seed: testSeedHex, wantPrivHex: "e7e0f734311bdf2f446821a464b490ae8c005f6af92a26f42a39067103726346", wantPubHex: "000848875b5836bc71fecb10f563b84b9b8a63b6d7a1b16b1e88eeaca6a7ad5852", @@ -169,7 +167,7 @@ func TestSlip_DeriveChild_TestVectors(t *testing.T) { }, { name: "PoktTestVector Child key derivation is deterministic for index `1000000`", - path: fmt.Sprintf(crypto.PoktAccountPathFormat, 1000000), + path: fmt.Sprintf(PoktAccountPathFormat, 1000000), seed: testSeedHex, wantPrivHex: "f208bf7d7afa1a12a0b74e8fbbf1bb15bdfcaa3d03507ec338d7ecd77331e964", wantPubHex: "00e7d8f01dfbc0eb1638d9853b95cdec650ad47d72fac1d2d1c97c8f935d2cbf90", @@ -177,7 +175,7 @@ func TestSlip_DeriveChild_TestVectors(t *testing.T) { }, { name: "PoktTestVector Child key derivation is deterministic for index `2147483647` (last child)", - path: fmt.Sprintf(crypto.PoktAccountPathFormat, 2147483647), + path: fmt.Sprintf(PoktAccountPathFormat, 2147483647), seed: testSeedHex, wantPrivHex: "20e061dcfae5cc90cba8ee374afc2d67d518b5f542f3217b8c830293c3dbb7e6", wantPubHex: "0008a399a3cdc1ee9a50c922d018daa98b5069bfea37944fde14a73d83ca3ec08c", @@ -185,7 +183,7 @@ func TestSlip_DeriveChild_TestVectors(t *testing.T) { }, { name: "PoktTestVector Child index is too large to derive ed25519 key for index `2147483648` ", - path: fmt.Sprintf(crypto.PoktAccountPathFormat, 2147483648), + path: fmt.Sprintf(PoktAccountPathFormat, 2147483648), seed: testSeedHex, wantPrivHex: "", wantPubHex: "", @@ -193,7 +191,7 @@ func TestSlip_DeriveChild_TestVectors(t *testing.T) { }, { name: "PoktTestVector Child index is too large to derive ed25519 key for index `4294967295` ", - path: fmt.Sprintf(crypto.PoktAccountPathFormat, ^uint32(0)), + path: fmt.Sprintf(PoktAccountPathFormat, ^uint32(0)), seed: testSeedHex, wantPrivHex: "", wantPubHex: "", @@ -204,7 +202,7 @@ func TestSlip_DeriveChild_TestVectors(t *testing.T) { t.Run(tv.name, func(t *testing.T) { seed, err := hex.DecodeString(tv.seed) require.NoError(t, err) - childKey, err := crypto.DeriveChild(tv.path, seed) + childKey, err := DeriveChild(tv.path, seed) if tv.wantErr { require.Error(t, err) } else { @@ -228,59 +226,3 @@ func TestSlip_DeriveChild_TestVectors(t *testing.T) { }) } } - -func TestKeybase_DeriveChildFromKey(t *testing.T) { - db := initDB(t) - defer stopDB(t, db) - - _, err := db.ImportFromString(testPrivString, testPassphrase, testHint) - require.NoError(t, err) - - childKey, err := db.DeriveChildFromKey(testAddr, testPassphrase, 1) - require.NoError(t, err) - require.Equal(t, childKey.GetAddressString(), testChildAddrIdx1) -} - -func TestKeybase_DeriveChildFromSeed(t *testing.T) { - db := initDB(t) - defer stopDB(t, db) - - kp, err := db.ImportFromString(testPrivString, testPassphrase, testHint) - require.NoError(t, err) - - seed, err := kp.GetSeed(testPassphrase) - require.NoError(t, err) - - childKey, err := db.DeriveChildFromSeed(seed, 1) - require.NoError(t, err) - require.Equal(t, childKey.GetAddressString(), testChildAddrIdx1) -} - -func TestKeybase_StoreChildFromKey(t *testing.T) { - db := initDB(t) - defer stopDB(t, db) - - _, err := db.ImportFromString(testPrivString, testPassphrase, testHint) - require.NoError(t, err) - - childKey, err := db.StoreChildFromKey(testAddr, testPassphrase, 1, testPassphrase, testHint) - require.NoError(t, err) - require.NotNil(t, childKey) - require.Equal(t, childKey.GetAddressString(), testChildAddrIdx1) -} - -func TestKeybase_StoreChildFromSeed(t *testing.T) { - db := initDB(t) - defer stopDB(t, db) - - kp, err := db.ImportFromString(testPrivString, testPassphrase, testHint) - require.NoError(t, err) - - seed, err := kp.GetSeed(testPassphrase) - require.NoError(t, err) - - childKey, err := db.StoreChildFromSeed(seed, 1, testPassphrase, testHint) - require.NoError(t, err) - require.NotNil(t, childKey) - require.Equal(t, childKey.GetAddressString(), testChildAddrIdx1) -} From fb6f3b125bb4181c8ee9c8f00ebf08e6e8636043 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Fri, 24 Feb 2023 09:41:35 +0000 Subject: [PATCH 27/34] make generate_cli_commands_docs --- app/client/doc/commands/client.md | 2 +- app/client/doc/commands/client_Account.md | 2 +- app/client/doc/commands/client_Account_Send.md | 2 +- app/client/doc/commands/client_Application.md | 2 +- app/client/doc/commands/client_Application_EditStake.md | 2 +- app/client/doc/commands/client_Application_Stake.md | 2 +- app/client/doc/commands/client_Application_Unpause.md | 2 +- app/client/doc/commands/client_Application_Unstake.md | 2 +- app/client/doc/commands/client_Consensus.md | 2 +- app/client/doc/commands/client_Consensus_Height.md | 2 +- app/client/doc/commands/client_Consensus_Round.md | 2 +- app/client/doc/commands/client_Consensus_State.md | 2 +- app/client/doc/commands/client_Consensus_Step.md | 2 +- app/client/doc/commands/client_Fisherman.md | 2 +- app/client/doc/commands/client_Fisherman_EditStake.md | 2 +- app/client/doc/commands/client_Fisherman_Stake.md | 2 +- app/client/doc/commands/client_Fisherman_Unpause.md | 2 +- app/client/doc/commands/client_Fisherman_Unstake.md | 2 +- app/client/doc/commands/client_Governance.md | 2 +- app/client/doc/commands/client_Governance_ChangeParameter.md | 2 +- app/client/doc/commands/client_Keys.md | 2 +- app/client/doc/commands/client_Keys_Create.md | 2 +- app/client/doc/commands/client_Keys_Delete.md | 2 +- app/client/doc/commands/client_Keys_DeriveChild.md | 4 ++-- app/client/doc/commands/client_Keys_Export.md | 2 +- app/client/doc/commands/client_Keys_Get.md | 2 +- app/client/doc/commands/client_Keys_Import.md | 2 +- app/client/doc/commands/client_Keys_List.md | 2 +- app/client/doc/commands/client_Keys_Sign.md | 2 +- app/client/doc/commands/client_Keys_SignTx.md | 2 +- app/client/doc/commands/client_Keys_Update.md | 2 +- app/client/doc/commands/client_Keys_Verify.md | 2 +- app/client/doc/commands/client_Keys_VerifyTx.md | 2 +- app/client/doc/commands/client_Servicer.md | 2 +- app/client/doc/commands/client_Servicer_EditStake.md | 2 +- app/client/doc/commands/client_Servicer_Stake.md | 2 +- app/client/doc/commands/client_Servicer_Unpause.md | 2 +- app/client/doc/commands/client_Servicer_Unstake.md | 2 +- app/client/doc/commands/client_System.md | 2 +- app/client/doc/commands/client_System_Health.md | 2 +- app/client/doc/commands/client_System_Version.md | 2 +- app/client/doc/commands/client_Validator.md | 2 +- app/client/doc/commands/client_Validator_EditStake.md | 2 +- app/client/doc/commands/client_Validator_Stake.md | 2 +- app/client/doc/commands/client_Validator_Unpause.md | 2 +- app/client/doc/commands/client_Validator_Unstake.md | 2 +- app/client/doc/commands/client_debug.md | 2 +- 47 files changed, 48 insertions(+), 48 deletions(-) diff --git a/app/client/doc/commands/client.md b/app/client/doc/commands/client.md index 33c801a9e..8df316c81 100644 --- a/app/client/doc/commands/client.md +++ b/app/client/doc/commands/client.md @@ -28,4 +28,4 @@ The CLI is meant to be an user but also a machine friendly way for interacting w * [client Validator](client_Validator.md) - Validator actor specific commands * [client debug](client_debug.md) - Debug utility for rapid development -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Account.md b/app/client/doc/commands/client_Account.md index 8214598be..a8ccf8f77 100644 --- a/app/client/doc/commands/client_Account.md +++ b/app/client/doc/commands/client_Account.md @@ -21,4 +21,4 @@ Account specific commands * [client](client.md) - Pocket Network Command Line Interface (CLI) * [client Account Send](client_Account_Send.md) - Send -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Account_Send.md b/app/client/doc/commands/client_Account_Send.md index 38f8a80af..e75a1d580 100644 --- a/app/client/doc/commands/client_Account_Send.md +++ b/app/client/doc/commands/client_Account_Send.md @@ -29,4 +29,4 @@ client Account Send [flags] * [client Account](client_Account.md) - Account specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Application.md b/app/client/doc/commands/client_Application.md index 1f0b0392c..78be819eb 100644 --- a/app/client/doc/commands/client_Application.md +++ b/app/client/doc/commands/client_Application.md @@ -24,4 +24,4 @@ Application actor specific commands * [client Application Unpause](client_Application_Unpause.md) - Unpause * [client Application Unstake](client_Application_Unstake.md) - Unstake -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Application_EditStake.md b/app/client/doc/commands/client_Application_EditStake.md index f0bdf437d..cb8bed105 100644 --- a/app/client/doc/commands/client_Application_EditStake.md +++ b/app/client/doc/commands/client_Application_EditStake.md @@ -29,4 +29,4 @@ client Application EditStake [f * [client Application](client_Application.md) - Application actor specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Application_Stake.md b/app/client/doc/commands/client_Application_Stake.md index 33c74ab5c..71542f920 100644 --- a/app/client/doc/commands/client_Application_Stake.md +++ b/app/client/doc/commands/client_Application_Stake.md @@ -37,4 +37,4 @@ client Application Stake [flags * [client Application](client_Application.md) - Application actor specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Application_Unpause.md b/app/client/doc/commands/client_Application_Unpause.md index 7cc042b72..4e6c9bb3f 100644 --- a/app/client/doc/commands/client_Application_Unpause.md +++ b/app/client/doc/commands/client_Application_Unpause.md @@ -29,4 +29,4 @@ client Application Unpause [flags] * [client Application](client_Application.md) - Application actor specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Application_Unstake.md b/app/client/doc/commands/client_Application_Unstake.md index ec20f4f59..0db4f5521 100644 --- a/app/client/doc/commands/client_Application_Unstake.md +++ b/app/client/doc/commands/client_Application_Unstake.md @@ -29,4 +29,4 @@ client Application Unstake [flags] * [client Application](client_Application.md) - Application actor specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Consensus.md b/app/client/doc/commands/client_Consensus.md index 0aec0ab64..58036f225 100644 --- a/app/client/doc/commands/client_Consensus.md +++ b/app/client/doc/commands/client_Consensus.md @@ -24,4 +24,4 @@ Consensus specific commands * [client Consensus State](client_Consensus_State.md) - Returns "Height/Round/Step" * [client Consensus Step](client_Consensus_Step.md) - Returns the Step -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Consensus_Height.md b/app/client/doc/commands/client_Consensus_Height.md index baec2bc2f..d0d513f04 100644 --- a/app/client/doc/commands/client_Consensus_Height.md +++ b/app/client/doc/commands/client_Consensus_Height.md @@ -28,4 +28,4 @@ client Consensus Height [flags] * [client Consensus](client_Consensus.md) - Consensus specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Consensus_Round.md b/app/client/doc/commands/client_Consensus_Round.md index 0a2536c0a..09b73bdbd 100644 --- a/app/client/doc/commands/client_Consensus_Round.md +++ b/app/client/doc/commands/client_Consensus_Round.md @@ -28,4 +28,4 @@ client Consensus Round [flags] * [client Consensus](client_Consensus.md) - Consensus specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Consensus_State.md b/app/client/doc/commands/client_Consensus_State.md index fccfe174b..aacbbfeb4 100644 --- a/app/client/doc/commands/client_Consensus_State.md +++ b/app/client/doc/commands/client_Consensus_State.md @@ -28,4 +28,4 @@ client Consensus State [flags] * [client Consensus](client_Consensus.md) - Consensus specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Consensus_Step.md b/app/client/doc/commands/client_Consensus_Step.md index dc29be260..d63a2f1f7 100644 --- a/app/client/doc/commands/client_Consensus_Step.md +++ b/app/client/doc/commands/client_Consensus_Step.md @@ -28,4 +28,4 @@ client Consensus Step [flags] * [client Consensus](client_Consensus.md) - Consensus specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Fisherman.md b/app/client/doc/commands/client_Fisherman.md index fb62b8e95..ded912697 100644 --- a/app/client/doc/commands/client_Fisherman.md +++ b/app/client/doc/commands/client_Fisherman.md @@ -24,4 +24,4 @@ Fisherman actor specific commands * [client Fisherman Unpause](client_Fisherman_Unpause.md) - Unpause * [client Fisherman Unstake](client_Fisherman_Unstake.md) - Unstake -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Fisherman_EditStake.md b/app/client/doc/commands/client_Fisherman_EditStake.md index cfe7855fe..0490e1018 100644 --- a/app/client/doc/commands/client_Fisherman_EditStake.md +++ b/app/client/doc/commands/client_Fisherman_EditStake.md @@ -29,4 +29,4 @@ client Fisherman EditStake [fla * [client Fisherman](client_Fisherman.md) - Fisherman actor specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Fisherman_Stake.md b/app/client/doc/commands/client_Fisherman_Stake.md index 7f8c2f9b0..c9a109995 100644 --- a/app/client/doc/commands/client_Fisherman_Stake.md +++ b/app/client/doc/commands/client_Fisherman_Stake.md @@ -37,4 +37,4 @@ client Fisherman Stake [flags] * [client Fisherman](client_Fisherman.md) - Fisherman actor specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Fisherman_Unpause.md b/app/client/doc/commands/client_Fisherman_Unpause.md index 30930c4e7..f5b015242 100644 --- a/app/client/doc/commands/client_Fisherman_Unpause.md +++ b/app/client/doc/commands/client_Fisherman_Unpause.md @@ -29,4 +29,4 @@ client Fisherman Unpause [flags] * [client Fisherman](client_Fisherman.md) - Fisherman actor specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Fisherman_Unstake.md b/app/client/doc/commands/client_Fisherman_Unstake.md index 7e4f71d3b..e7b1144f1 100644 --- a/app/client/doc/commands/client_Fisherman_Unstake.md +++ b/app/client/doc/commands/client_Fisherman_Unstake.md @@ -29,4 +29,4 @@ client Fisherman Unstake [flags] * [client Fisherman](client_Fisherman.md) - Fisherman actor specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Governance.md b/app/client/doc/commands/client_Governance.md index d87612e41..61b2d6169 100644 --- a/app/client/doc/commands/client_Governance.md +++ b/app/client/doc/commands/client_Governance.md @@ -21,4 +21,4 @@ Governance specific commands * [client](client.md) - Pocket Network Command Line Interface (CLI) * [client Governance ChangeParameter](client_Governance_ChangeParameter.md) - ChangeParameter -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Governance_ChangeParameter.md b/app/client/doc/commands/client_Governance_ChangeParameter.md index dedb2543e..34f5e443c 100644 --- a/app/client/doc/commands/client_Governance_ChangeParameter.md +++ b/app/client/doc/commands/client_Governance_ChangeParameter.md @@ -29,4 +29,4 @@ client Governance ChangeParameter [flags] * [client Governance](client_Governance.md) - Governance specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Keys.md b/app/client/doc/commands/client_Keys.md index 8be8b4aaa..ca1e1cef3 100644 --- a/app/client/doc/commands/client_Keys.md +++ b/app/client/doc/commands/client_Keys.md @@ -32,4 +32,4 @@ Key specific commands * [client Keys Verify](client_Keys_Verify.md) - Verifies the signature is valid from the signer * [client Keys VerifyTx](client_Keys_VerifyTx.md) - Verifies the transaction's signature is valid from the signer -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Keys_Create.md b/app/client/doc/commands/client_Keys_Create.md index 0afa30144..860488579 100644 --- a/app/client/doc/commands/client_Keys_Create.md +++ b/app/client/doc/commands/client_Keys_Create.md @@ -30,4 +30,4 @@ client Keys Create [flags] * [client Keys](client_Keys.md) - Key specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Keys_Delete.md b/app/client/doc/commands/client_Keys_Delete.md index 4d02a4370..93f07b48c 100644 --- a/app/client/doc/commands/client_Keys_Delete.md +++ b/app/client/doc/commands/client_Keys_Delete.md @@ -29,4 +29,4 @@ client Keys Delete [flags] * [client Keys](client_Keys.md) - Key specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Keys_DeriveChild.md b/app/client/doc/commands/client_Keys_DeriveChild.md index 2ea96b50b..d1aea4f4a 100644 --- a/app/client/doc/commands/client_Keys_DeriveChild.md +++ b/app/client/doc/commands/client_Keys_DeriveChild.md @@ -17,7 +17,7 @@ client Keys DeriveChild [flags] --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 + --store_child store the derived child key in the keybase (default true) ``` ### Options inherited from parent commands @@ -32,4 +32,4 @@ client Keys DeriveChild [flags] * [client Keys](client_Keys.md) - Key specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Keys_Export.md b/app/client/doc/commands/client_Keys_Export.md index 51c9c7d7a..072794fb4 100644 --- a/app/client/doc/commands/client_Keys_Export.md +++ b/app/client/doc/commands/client_Keys_Export.md @@ -31,4 +31,4 @@ client Keys Export [--export_format] [--output_file] [flags] * [client Keys](client_Keys.md) - Key specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Keys_Get.md b/app/client/doc/commands/client_Keys_Get.md index 5485a6f86..77b294eae 100644 --- a/app/client/doc/commands/client_Keys_Get.md +++ b/app/client/doc/commands/client_Keys_Get.md @@ -28,4 +28,4 @@ client Keys Get [flags] * [client Keys](client_Keys.md) - Key specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Keys_Import.md b/app/client/doc/commands/client_Keys_Import.md index a2f145f77..cfac977e9 100644 --- a/app/client/doc/commands/client_Keys_Import.md +++ b/app/client/doc/commands/client_Keys_Import.md @@ -32,4 +32,4 @@ client Keys Import [privateKeyString] [--input_file] [--import_format] [flags] * [client Keys](client_Keys.md) - Key specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Keys_List.md b/app/client/doc/commands/client_Keys_List.md index 804d17ede..1bdfc86bc 100644 --- a/app/client/doc/commands/client_Keys_List.md +++ b/app/client/doc/commands/client_Keys_List.md @@ -28,4 +28,4 @@ client Keys List [flags] * [client Keys](client_Keys.md) - Key specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Keys_Sign.md b/app/client/doc/commands/client_Keys_Sign.md index e19d35c8a..ea8648316 100644 --- a/app/client/doc/commands/client_Keys_Sign.md +++ b/app/client/doc/commands/client_Keys_Sign.md @@ -29,4 +29,4 @@ client Keys Sign [flags] * [client Keys](client_Keys.md) - Key specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Keys_SignTx.md b/app/client/doc/commands/client_Keys_SignTx.md index 52c873305..907832d4a 100644 --- a/app/client/doc/commands/client_Keys_SignTx.md +++ b/app/client/doc/commands/client_Keys_SignTx.md @@ -31,4 +31,4 @@ client Keys SignTx [--input_file] [--output_file] [flags] * [client Keys](client_Keys.md) - Key specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Keys_Update.md b/app/client/doc/commands/client_Keys_Update.md index 80d3015ee..58c9ad1eb 100644 --- a/app/client/doc/commands/client_Keys_Update.md +++ b/app/client/doc/commands/client_Keys_Update.md @@ -31,4 +31,4 @@ client Keys Update [--pwd] [--new_pwd] [--hint] [flags] * [client Keys](client_Keys.md) - Key specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Keys_Verify.md b/app/client/doc/commands/client_Keys_Verify.md index 032af7da1..bbda8f18c 100644 --- a/app/client/doc/commands/client_Keys_Verify.md +++ b/app/client/doc/commands/client_Keys_Verify.md @@ -29,4 +29,4 @@ client Keys Verify [flags] * [client Keys](client_Keys.md) - Key specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Keys_VerifyTx.md b/app/client/doc/commands/client_Keys_VerifyTx.md index 94f121d67..0e2e7590f 100644 --- a/app/client/doc/commands/client_Keys_VerifyTx.md +++ b/app/client/doc/commands/client_Keys_VerifyTx.md @@ -31,4 +31,4 @@ client Keys VerifyTx [--input_file] [flags] * [client Keys](client_Keys.md) - Key specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Servicer.md b/app/client/doc/commands/client_Servicer.md index f1c7489f6..32fe63b5f 100644 --- a/app/client/doc/commands/client_Servicer.md +++ b/app/client/doc/commands/client_Servicer.md @@ -24,4 +24,4 @@ Servicer actor specific commands * [client Servicer Unpause](client_Servicer_Unpause.md) - Unpause * [client Servicer Unstake](client_Servicer_Unstake.md) - Unstake -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Servicer_EditStake.md b/app/client/doc/commands/client_Servicer_EditStake.md index 15b625366..5b0a6c0d0 100644 --- a/app/client/doc/commands/client_Servicer_EditStake.md +++ b/app/client/doc/commands/client_Servicer_EditStake.md @@ -29,4 +29,4 @@ client Servicer EditStake [flag * [client Servicer](client_Servicer.md) - Servicer actor specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Servicer_Stake.md b/app/client/doc/commands/client_Servicer_Stake.md index f49527be3..d00eb8c40 100644 --- a/app/client/doc/commands/client_Servicer_Stake.md +++ b/app/client/doc/commands/client_Servicer_Stake.md @@ -37,4 +37,4 @@ client Servicer Stake [flags] * [client Servicer](client_Servicer.md) - Servicer actor specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Servicer_Unpause.md b/app/client/doc/commands/client_Servicer_Unpause.md index c09fd83e0..1333d06b2 100644 --- a/app/client/doc/commands/client_Servicer_Unpause.md +++ b/app/client/doc/commands/client_Servicer_Unpause.md @@ -29,4 +29,4 @@ client Servicer Unpause [flags] * [client Servicer](client_Servicer.md) - Servicer actor specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Servicer_Unstake.md b/app/client/doc/commands/client_Servicer_Unstake.md index d08ec8108..76adfa4e5 100644 --- a/app/client/doc/commands/client_Servicer_Unstake.md +++ b/app/client/doc/commands/client_Servicer_Unstake.md @@ -29,4 +29,4 @@ client Servicer Unstake [flags] * [client Servicer](client_Servicer.md) - Servicer actor specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_System.md b/app/client/doc/commands/client_System.md index 77ba67c77..94087130b 100644 --- a/app/client/doc/commands/client_System.md +++ b/app/client/doc/commands/client_System.md @@ -22,4 +22,4 @@ Commands related to health and troubleshooting of the node instance * [client System Health](client_System_Health.md) - RPC endpoint liveness * [client System Version](client_System_Version.md) - Advertised node software version -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_System_Health.md b/app/client/doc/commands/client_System_Health.md index 37cdc9ad5..d1b37211c 100644 --- a/app/client/doc/commands/client_System_Health.md +++ b/app/client/doc/commands/client_System_Health.md @@ -28,4 +28,4 @@ client System Health [flags] * [client System](client_System.md) - Commands related to health and troubleshooting of the node instance -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_System_Version.md b/app/client/doc/commands/client_System_Version.md index 65610b606..cf3c479b1 100644 --- a/app/client/doc/commands/client_System_Version.md +++ b/app/client/doc/commands/client_System_Version.md @@ -28,4 +28,4 @@ client System Version [flags] * [client System](client_System.md) - Commands related to health and troubleshooting of the node instance -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Validator.md b/app/client/doc/commands/client_Validator.md index b33b9271a..7b0454586 100644 --- a/app/client/doc/commands/client_Validator.md +++ b/app/client/doc/commands/client_Validator.md @@ -24,4 +24,4 @@ Validator actor specific commands * [client Validator Unpause](client_Validator_Unpause.md) - Unpause * [client Validator Unstake](client_Validator_Unstake.md) - Unstake -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Validator_EditStake.md b/app/client/doc/commands/client_Validator_EditStake.md index 80dd3dbea..c71bd9618 100644 --- a/app/client/doc/commands/client_Validator_EditStake.md +++ b/app/client/doc/commands/client_Validator_EditStake.md @@ -29,4 +29,4 @@ client Validator EditStake [fla * [client Validator](client_Validator.md) - Validator actor specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Validator_Stake.md b/app/client/doc/commands/client_Validator_Stake.md index 836ee23c1..72842d11c 100644 --- a/app/client/doc/commands/client_Validator_Stake.md +++ b/app/client/doc/commands/client_Validator_Stake.md @@ -37,4 +37,4 @@ client Validator Stake [flags] * [client Validator](client_Validator.md) - Validator actor specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Validator_Unpause.md b/app/client/doc/commands/client_Validator_Unpause.md index 84494dbe2..9905dfd2f 100644 --- a/app/client/doc/commands/client_Validator_Unpause.md +++ b/app/client/doc/commands/client_Validator_Unpause.md @@ -29,4 +29,4 @@ client Validator Unpause [flags] * [client Validator](client_Validator.md) - Validator actor specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_Validator_Unstake.md b/app/client/doc/commands/client_Validator_Unstake.md index 486d2df25..688562803 100644 --- a/app/client/doc/commands/client_Validator_Unstake.md +++ b/app/client/doc/commands/client_Validator_Unstake.md @@ -29,4 +29,4 @@ client Validator Unstake [flags] * [client Validator](client_Validator.md) - Validator actor specific commands -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 diff --git a/app/client/doc/commands/client_debug.md b/app/client/doc/commands/client_debug.md index 6d69844bd..f0c171801 100644 --- a/app/client/doc/commands/client_debug.md +++ b/app/client/doc/commands/client_debug.md @@ -24,4 +24,4 @@ client debug [flags] * [client](client.md) - Pocket Network Command Line Interface (CLI) -###### Auto generated by spf13/cobra on 23-Feb-2023 +###### Auto generated by spf13/cobra on 24-Feb-2023 From 9b9da19034158a590b05cafbb629364ab5be001b Mon Sep 17 00:00:00 2001 From: Harry Law Date: Fri, 24 Feb 2023 09:43:49 +0000 Subject: [PATCH 28/34] Address linter error --- app/client/cli/keys.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/client/cli/keys.go b/app/client/cli/keys.go index 6118028ed..6792b3171 100644 --- a/app/client/cli/keys.go +++ b/app/client/cli/keys.go @@ -100,7 +100,9 @@ func NewKeysCommand() *cobra.Command { cmd.AddCommand(slipCmds...) // Bind the store_child flag - viper.BindPFlag("storeChild", cmd.Flags().Lookup("store_child")) + if err := viper.BindPFlag("storeChild", cmd.Flags().Lookup("store_child")); err != nil { + logger.Global.Fatal().Err(err).Msg("Unable to bind store_child flag to viper") + } viper.SetDefault("storeChild", true) return cmd From 2df7edfe59dacbce3f13d579b4fa7fd7a4dd75d9 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Fri, 24 Feb 2023 12:50:21 +0000 Subject: [PATCH 29/34] Update comments to match godoc format --- app/client/keybase/keystore.go | 55 ++++++++++++++++------------------ shared/crypto/keypair.go | 40 ++++++++++++++----------- 2 files changed, 48 insertions(+), 47 deletions(-) diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index bf0bf00ed..0a32cc5e1 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -31,7 +31,7 @@ type badgerKeybase struct { db *badger.DB } -// Creates/Opens the DB at the specified path +// NewKeybase Creates/Opens the DB at the specified path creating the path if it doesn't exist func NewKeybase(path string) (Keybase, error) { pathExists, err := converters.DirExists(path) // Creates path if it doesn't exist if err != nil || !pathExists { @@ -44,7 +44,7 @@ func NewKeybase(path string) (Keybase, error) { return &badgerKeybase{db: db}, nil } -// Creates/Opens the DB in Memory +// NewKeybaseInMemory Creates/Opens the DB in Memory // FOR TESTING PURPOSES ONLY func NewKeybaseInMemory() (Keybase, error) { db, err := badger.Open(badgerOptions("").WithInMemory(true)) @@ -54,19 +54,20 @@ func NewKeybaseInMemory() (Keybase, error) { return &badgerKeybase{db: db}, nil } -// Return DB instance +// GetBadgerDB Returns the DB instance // FOR DEBUG PURPOSES ONLY func (keybase *badgerKeybase) GetBadgerDB() *badger.DB { return keybase.db } -// Close the DB +// Stop Closes the DB connection func (keybase *badgerKeybase) Stop() error { return keybase.db.Close() } -// Create a new key and store the serialised KeyPair encoding in the DB +// Create Creates a new key and store the serialised KeyPair encoding in the DB // Using the PublicKey.Address() return value as the key for storage +// Returns the KeyPair created and any error func (keybase *badgerKeybase) Create(passphrase, hint string) (keyPair crypto.KeyPair, err error) { err = keybase.db.Update(func(tx *badger.Txn) (err error) { keyPair, err = crypto.CreateNewKey(passphrase, hint) @@ -92,8 +93,9 @@ func (keybase *badgerKeybase) Create(passphrase, hint string) (keyPair crypto.Ke return keyPair, nil } -// Create a new KeyPair from the private key hex string and store the serialised KeyPair encoding in the DB +// ImportFromString Creates a new KeyPair from the private key hex string and store the serialised KeyPair encoding in the DB // Using the PublicKey.Address() return value as the key for storage +// Returns the KeyPair created and any error func (keybase *badgerKeybase) ImportFromString(privKeyHex, passphrase, hint string) (keyPair crypto.KeyPair, err error) { err = keybase.db.Update(func(tx *badger.Txn) (err error) { keyPair, err = crypto.CreateNewKeyFromString(privKeyHex, passphrase, hint) @@ -119,8 +121,9 @@ func (keybase *badgerKeybase) ImportFromString(privKeyHex, passphrase, hint stri return keyPair, nil } -// Create a new KeyPair from the private key JSON string and store the serialised KeyPair encoding in the DB +// ImportFromJSON Creates a new KeyPair from the private key JSON string and store the serialised KeyPair encoding in the DB // Using the PublicKey.Address() return value as the key for storage +// Returns the KeyPair created and any error func (keybase *badgerKeybase) ImportFromJSON(jsonStr, passphrase string) (keyPair crypto.KeyPair, err error) { err = keybase.db.Update(func(tx *badger.Txn) (err error) { keyPair, err = crypto.ImportKeyFromJSON(jsonStr, passphrase) @@ -146,7 +149,7 @@ func (keybase *badgerKeybase) ImportFromJSON(jsonStr, passphrase string) (keyPai return keyPair, nil } -// Returns a KeyPair struct provided the address was found in the DB +// Get Returns a KeyPair struct provided the address was found in the DB func (keybase *badgerKeybase) Get(address string) (crypto.KeyPair, error) { kp := crypto.GetKeypair() addrBz, err := hex.DecodeString(address) @@ -181,7 +184,7 @@ func (keybase *badgerKeybase) Get(address string) (crypto.KeyPair, error) { return kp, nil } -// Returns a PublicKey interface provided the address was found in the DB +// GetPubKey Returns a PublicKey interface provided the address was found in the DB func (keybase *badgerKeybase) GetPubKey(address string) (crypto.PublicKey, error) { kp, err := keybase.Get(address) if err != nil { @@ -191,7 +194,7 @@ func (keybase *badgerKeybase) GetPubKey(address string) (crypto.PublicKey, error return kp.GetPublicKey(), nil } -// Returns a PrivateKey interface provided the address was found in the DB and the passphrase was correct +// GetPrivKey Returns a PrivateKey interface provided the address was found in the DB and the passphrase was correct func (keybase *badgerKeybase) GetPrivKey(address, passphrase string) (crypto.PrivateKey, error) { kp, err := keybase.Get(address) if err != nil { @@ -206,8 +209,8 @@ func (keybase *badgerKeybase) GetPrivKey(address, passphrase string) (crypto.Pri return privKey, nil } -// Get all the addresses and key pairs stored in the keybase -// Returns addresses stored and all the KeyPair structs stored in the DB +// GetAll Returns the addresses and key pairs stored in the keybase +// Returns the hex addresses stored and all the KeyPair structs stored in the DB func (keybase *badgerKeybase) GetAll() (addresses []string, keyPairs []crypto.KeyPair, err error) { // View executes the function provided managing a read only transaction err = keybase.db.View(func(tx *badger.Txn) error { @@ -245,7 +248,8 @@ func (keybase *badgerKeybase) GetAll() (addresses []string, keyPairs []crypto.Ke return addresses, keyPairs, nil } -// Deterministically generate and return the ith child from the seed provided - storing it in the keybase by default +// DeriveChildFromSeed Deterministically generates and return the child at the given index from the seed provided +// By default this stores the key in the keybase and returns the KeyPair interface and any error func (keybase *badgerKeybase) DeriveChildFromSeed(seed []byte, childIndex uint32, childPassphrase, childHint string) (crypto.KeyPair, error) { path := fmt.Sprintf(slip.PoktAccountPathFormat, childIndex) childKey, err := slip.DeriveChild(path, seed) @@ -304,7 +308,8 @@ func (keybase *badgerKeybase) DeriveChildFromSeed(seed []byte, childIndex uint32 return childKey, nil } -// Deterministically generate and return the ith child key from the masterAddrHex key stored in the keybase +// DeriveChildFromKey Deterministically generates and return the child at the given index from the parent key provided +// By default this stores the key in the keybase and returns the KeyPair interface and any error func (keybase *badgerKeybase) DeriveChildFromKey(masterAddrHex, passphrase string, childIndex uint32, childPassphrase, childHint string) (crypto.KeyPair, error) { privKey, err := keybase.GetPrivKey(masterAddrHex, passphrase) if err != nil { @@ -314,16 +319,7 @@ func (keybase *badgerKeybase) DeriveChildFromKey(masterAddrHex, passphrase strin return keybase.DeriveChildFromSeed(seed, childIndex, childPassphrase, childHint) } -// Check whether an address is currently stored in the DB -func (keybase *badgerKeybase) Exists(address string) (bool, error) { - val, err := keybase.Get(address) - if err != nil { - return false, err - } - return val != nil, nil -} - -// Export the Private Key string of the given address +// ExportPrivString Exports the raw private key string of the given address func (keybase *badgerKeybase) ExportPrivString(address, passphrase string) (string, error) { kp, err := keybase.Get(address) if err != nil { @@ -332,7 +328,7 @@ func (keybase *badgerKeybase) ExportPrivString(address, passphrase string) (stri return kp.ExportString(passphrase) } -// Export the Private Key of the given address as a JSON object +// ExportPrivJSON Exports the private key of the given address as a JSON string func (keybase *badgerKeybase) ExportPrivJSON(address, passphrase string) (string, error) { kp, err := keybase.Get(address) if err != nil { @@ -341,6 +337,7 @@ func (keybase *badgerKeybase) ExportPrivJSON(address, passphrase string) (string return kp.ExportJSON(passphrase) } +// UpdatePassphrase Updates the passphrase of the key with the matching address to use the new one provided and updates the hint in the JSON encrypted private key func (keybase *badgerKeybase) UpdatePassphrase(address, oldPassphrase, newPassphrase, hint string) error { // Check the oldPassphrase is correct privKey, err := keybase.GetPrivKey(address, oldPassphrase) @@ -378,7 +375,7 @@ func (keybase *badgerKeybase) UpdatePassphrase(address, oldPassphrase, newPassph return err } -// Sign a message using the key address provided +// Sign Signs a message using the key address provided func (keybase *badgerKeybase) Sign(address, passphrase string, msg []byte) ([]byte, error) { privKey, err := keybase.GetPrivKey(address, passphrase) if err != nil { @@ -387,7 +384,7 @@ func (keybase *badgerKeybase) Sign(address, passphrase string, msg []byte) ([]by return privKey.Sign(msg) } -// Verify a message has been signed correctly +// Verify Verifies a message has been signed correctly func (keybase *badgerKeybase) Verify(address string, msg, sig []byte) (bool, error) { kp, err := keybase.Get(address) if err != nil { @@ -397,7 +394,7 @@ func (keybase *badgerKeybase) Verify(address string, msg, sig []byte) (bool, err return pubKey.Verify(msg, sig), nil } -// Remove a KeyPair from the DB given the address +// Delete Removes a KeyPair from the DB given the address func (keybase *badgerKeybase) Delete(address, passphrase string) error { if _, err := keybase.GetPrivKey(address, passphrase); err != nil { return err @@ -414,7 +411,7 @@ func (keybase *badgerKeybase) Delete(address, passphrase string) error { return err } -// Return badger.Options for the given DB path - disable logging +// badgerOptions Returns a badger.Options struct for the given DB path - disable logging func badgerOptions(path string) badger.Options { opts := badger.DefaultOptions(path) opts.Logger = nil // Badger logger is very noisy diff --git a/shared/crypto/keypair.go b/shared/crypto/keypair.go index b5af33f4a..7c7ca5f95 100644 --- a/shared/crypto/keypair.go +++ b/shared/crypto/keypair.go @@ -6,14 +6,14 @@ import ( "encoding/gob" ) -// Encoding is used to serialise the data to store the KeyPairs in the database +// Gob Encoding is used to serialise the data to store the KeyPairs in the BadgerDB database func init() { gob.Register(Ed25519PublicKey{}) gob.Register(ed25519.PublicKey{}) gob.Register(encKeyPair{}) } -// The KeyPair interface exposes functions relating to public and encrypted private key pairs +// KeyPair interface exposes functions relating to public and encrypted private key pairs type KeyPair interface { // Accessors GetPublicKey() PublicKey @@ -41,12 +41,13 @@ type KeyPair interface { var _ KeyPair = &encKeyPair{} // encKeyPair struct stores the public key and the passphrase encrypted private key +// The encrypted private key is stored as a JSON string with the fields needed to decrypt the key type encKeyPair struct { PublicKey PublicKey PrivKeyArmour string } -// Generate a new KeyPair struct given the public key and armoured private key +// newKeyPair Generates a new KeyPair interface given the public key and armoured private key func newKeyPair(pub PublicKey, priv string) KeyPair { return &encKeyPair{ PublicKey: pub, @@ -54,27 +55,27 @@ func newKeyPair(pub PublicKey, priv string) KeyPair { } } -// Return an empty KeyPair interface +// GetKeypair Returns an empty KeyPair interface func GetKeypair() KeyPair { return &encKeyPair{} } -// Return the public key +// GetPublicKey Returns the public key func (kp encKeyPair) GetPublicKey() PublicKey { return kp.PublicKey } -// Return private key armoured string +// GetPrivArmour Returns private key armoured string func (kp encKeyPair) GetPrivArmour() string { return kp.PrivKeyArmour } -// Return the byte slice address of the public key +// GetAddressBytes Returns the byte slice address of the public key func (kp encKeyPair) GetAddressBytes() []byte { return kp.PublicKey.Address().Bytes() } -// Return the string address of the public key +// GetAddressString Returns the string address of the public key func (kp encKeyPair) GetAddressString() string { return kp.PublicKey.Address().String() } @@ -84,7 +85,7 @@ func (kp encKeyPair) Unarmour(passphrase string) (PrivateKey, error) { return unarmourDecryptPrivKey(kp.PrivKeyArmour, passphrase) } -// Export Private Key String +// ExportString Exports the private key as a raw string func (kp encKeyPair) ExportString(passphrase string) (string, error) { privKey, err := unarmourDecryptPrivKey(kp.PrivKeyArmour, passphrase) if err != nil { @@ -93,7 +94,7 @@ func (kp encKeyPair) ExportString(passphrase string) (string, error) { return privKey.String(), nil } -// Export Private Key as armoured JSON string with fields to decrypt +// ExportJSON Exports the private key as an armoured JSON string with fields to decrypt func (kp encKeyPair) ExportJSON(passphrase string) (string, error) { _, err := unarmourDecryptPrivKey(kp.PrivKeyArmour, passphrase) if err != nil { @@ -102,7 +103,7 @@ func (kp encKeyPair) ExportJSON(passphrase string) (string, error) { return kp.PrivKeyArmour, nil } -// Return the seed of the key +// GetSeed Returns the seed of the key func (kp encKeyPair) GetSeed(passphrase string) ([]byte, error) { privKey, err := unarmourDecryptPrivKey(kp.PrivKeyArmour, passphrase) if err != nil { @@ -111,7 +112,7 @@ func (kp encKeyPair) GetSeed(passphrase string) ([]byte, error) { return privKey.Seed(), nil } -// Marshal KeyPair into a []byte +// Marshal Marshals the KeyPair into a []byte func (kp encKeyPair) Marshal() ([]byte, error) { buf := new(bytes.Buffer) enc := gob.NewEncoder(buf) @@ -121,7 +122,7 @@ func (kp encKeyPair) Marshal() ([]byte, error) { return buf.Bytes(), nil } -// Unmarshal []byte into an encKeyPair struct +// Unmarshal Unmarshals a []byte into an encKeyPair struct func (kp *encKeyPair) Unmarshal(bz []byte) error { var keyPair encKeyPair keypairBz := new(bytes.Buffer) @@ -134,8 +135,8 @@ func (kp *encKeyPair) Unmarshal(bz []byte) error { return nil } -// Generate new private ED25519 key and encrypt and armour it as a string -// Returns a KeyPair struct of the Public Key and Armoured String +// CreateNewKey Generates a new private ED25519 key and encrypt and armour it as a string +// Returns a KeyPair interface and error if any func CreateNewKey(passphrase, hint string) (KeyPair, error) { privKey, err := GeneratePrivateKey() if err != nil { @@ -153,7 +154,8 @@ func CreateNewKey(passphrase, hint string) (KeyPair, error) { return kp, nil } -// Generate new KeyPair from the hex string provided, encrypt and armour it as a string +// CreateNewKeyFromString Generates new KeyPair from the hex string provided, encrypt and armour it as a string +// Returns a KeyPair interface and error if any func CreateNewKeyFromString(privStrHex, passphrase, hint string) (KeyPair, error) { privKey, err := NewPrivateKey(privStrHex) if err != nil { @@ -171,7 +173,8 @@ func CreateNewKeyFromString(privStrHex, passphrase, hint string) (KeyPair, error return kp, nil } -// Generate a new KeyPair from the seed provided +// CreateNewKeyFromSeed Generates a new KeyPair from the seed provided +// Returns a KeyPair interface and error if any func CreateNewKeyFromSeed(seed []byte, passphrase, hint string) (KeyPair, error) { // Generate PrivateKey interface form secret key reader := bytes.NewReader(seed) @@ -193,7 +196,8 @@ func CreateNewKeyFromSeed(seed []byte, passphrase, hint string) (KeyPair, error) }, nil } -// Create new KeyPair from the JSON encoded privStr +// ImportKeyFromJSON Creates new KeyPair from the JSON encoded privStr +// Returns a KeyPair interface and error if any func ImportKeyFromJSON(jsonStr, passphrase string) (KeyPair, error) { // Get Private Key from armouredStr privKey, err := unarmourDecryptPrivKey(jsonStr, passphrase) From d93431d0cdde535de6e38a50e34ae2b184833213 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sun, 26 Feb 2023 20:51:54 +0000 Subject: [PATCH 30/34] Remove viper binding and pass store as argument to function --- app/client/cli/keys.go | 9 +-------- app/client/keybase/keybase.go | 4 ++-- app/client/keybase/keybase_test.go | 11 ++++------- app/client/keybase/keystore.go | 9 ++++----- 4 files changed, 11 insertions(+), 22 deletions(-) diff --git a/app/client/cli/keys.go b/app/client/cli/keys.go index 6792b3171..2901a5471 100644 --- a/app/client/cli/keys.go +++ b/app/client/cli/keys.go @@ -9,7 +9,6 @@ import ( "github.com/pokt-network/pocket/shared/converters" "github.com/pokt-network/pocket/shared/crypto" utilTypes "github.com/pokt-network/pocket/utility/types" - "github.com/spf13/viper" "path/filepath" "strconv" "strings" @@ -99,12 +98,6 @@ func NewKeysCommand() *cobra.Command { cmd.AddCommand(signTxCmds...) cmd.AddCommand(slipCmds...) - // Bind the store_child flag - if err := viper.BindPFlag("storeChild", cmd.Flags().Lookup("store_child")); err != nil { - logger.Global.Fatal().Err(err).Msg("Unable to bind store_child flag to viper") - } - viper.SetDefault("storeChild", true) - return cmd } @@ -732,7 +725,7 @@ func keysSlipCommands() []*cobra.Command { pwd = readPassphrase(pwd) } - kp, err := kb.DeriveChildFromKey(parentAddr, pwd, index, childPwd, childHint) + kp, err := kb.DeriveChildFromKey(parentAddr, pwd, index, childPwd, childHint, storeChild) if err != nil { return err } diff --git a/app/client/keybase/keybase.go b/app/client/keybase/keybase.go index 1cb2ed11f..794e59be0 100644 --- a/app/client/keybase/keybase.go +++ b/app/client/keybase/keybase.go @@ -22,8 +22,8 @@ type Keybase interface { // SLIPS-0010 Key Derivation // Deterministically generate, store and return the derived child key - DeriveChildFromKey(masterAddrHex, passphrase string, childIndex uint32, childPassphrase, childHint string) (crypto.KeyPair, error) - DeriveChildFromSeed(seed []byte, childIndex uint32, childPassphrase, childHint string) (crypto.KeyPair, error) + DeriveChildFromKey(masterAddrHex, passphrase string, childIndex uint32, childPassphrase, childHint string, shouldStore bool) (crypto.KeyPair, error) + DeriveChildFromSeed(seed []byte, childIndex uint32, childPassphrase, childHint string, shouldStore bool) (crypto.KeyPair, error) // Accessors Get(address string) (crypto.KeyPair, error) diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go index ec773da7e..e967acd5d 100644 --- a/app/client/keybase/keybase_test.go +++ b/app/client/keybase/keybase_test.go @@ -2,7 +2,6 @@ package keybase import ( "encoding/hex" - "github.com/spf13/viper" "testing" "github.com/pokt-network/pocket/runtime/test_artifacts/keygenerator" @@ -384,8 +383,7 @@ func TestKeybase_DeriveChildFromKey(t *testing.T) { _, err := db.ImportFromString(testPrivString, testPassphrase, testHint) require.NoError(t, err) - viper.Set("storeChild", false) - childKey, err := db.DeriveChildFromKey(testAddr, testPassphrase, 1, "", "") + childKey, err := db.DeriveChildFromKey(testAddr, testPassphrase, 1, "", "", false) require.NoError(t, err) require.Equal(t, childKey.GetAddressString(), testChildAddrIdx1) } @@ -400,8 +398,7 @@ func TestKeybase_DeriveChildFromSeed(t *testing.T) { seed, err := kp.GetSeed(testPassphrase) require.NoError(t, err) - viper.Set("storeChild", false) - childKey, err := db.DeriveChildFromSeed(seed, 1, "", "") + childKey, err := db.DeriveChildFromSeed(seed, 1, "", "", false) require.NoError(t, err) require.Equal(t, childKey.GetAddressString(), testChildAddrIdx1) } @@ -413,7 +410,7 @@ func TestKeybase_StoreChildFromKey(t *testing.T) { _, err := db.ImportFromString(testPrivString, testPassphrase, testHint) require.NoError(t, err) - childKey, err := db.DeriveChildFromKey(testAddr, testPassphrase, 1, testPassphrase, testHint) + childKey, err := db.DeriveChildFromKey(testAddr, testPassphrase, 1, testPassphrase, testHint, true) require.NoError(t, err) require.NotNil(t, childKey) require.Equal(t, childKey.GetAddressString(), testChildAddrIdx1) @@ -429,7 +426,7 @@ func TestKeybase_StoreChildFromSeed(t *testing.T) { seed, err := kp.GetSeed(testPassphrase) require.NoError(t, err) - childKey, err := db.DeriveChildFromSeed(seed, 1, testPassphrase, testHint) + childKey, err := db.DeriveChildFromSeed(seed, 1, testPassphrase, testHint, true) require.NoError(t, err) require.NotNil(t, childKey) require.Equal(t, childKey.GetAddressString(), testChildAddrIdx1) diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index 0a32cc5e1..d5e11fb2d 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -5,7 +5,6 @@ import ( "encoding/hex" "fmt" "github.com/pokt-network/pocket/shared/crypto/slip" - "github.com/spf13/viper" "strings" "github.com/pokt-network/pocket/shared/converters" @@ -250,14 +249,14 @@ func (keybase *badgerKeybase) GetAll() (addresses []string, keyPairs []crypto.Ke // DeriveChildFromSeed Deterministically generates and return the child at the given index from the seed provided // By default this stores the key in the keybase and returns the KeyPair interface and any error -func (keybase *badgerKeybase) DeriveChildFromSeed(seed []byte, childIndex uint32, childPassphrase, childHint string) (crypto.KeyPair, error) { +func (keybase *badgerKeybase) DeriveChildFromSeed(seed []byte, childIndex uint32, childPassphrase, childHint string, shouldStore bool) (crypto.KeyPair, error) { path := fmt.Sprintf(slip.PoktAccountPathFormat, childIndex) childKey, err := slip.DeriveChild(path, seed) if err != nil { return nil, err } - if viper.GetBool("storeChild") { + if shouldStore { // No need to re-encrypt with provided passphrase if childPassphrase == "" && childHint == "" { err = keybase.db.Update(func(tx *badger.Txn) error { @@ -310,13 +309,13 @@ func (keybase *badgerKeybase) DeriveChildFromSeed(seed []byte, childIndex uint32 // DeriveChildFromKey Deterministically generates and return the child at the given index from the parent key provided // By default this stores the key in the keybase and returns the KeyPair interface and any error -func (keybase *badgerKeybase) DeriveChildFromKey(masterAddrHex, passphrase string, childIndex uint32, childPassphrase, childHint string) (crypto.KeyPair, error) { +func (keybase *badgerKeybase) DeriveChildFromKey(masterAddrHex, passphrase string, childIndex uint32, childPassphrase, childHint string, shouldStore bool) (crypto.KeyPair, error) { privKey, err := keybase.GetPrivKey(masterAddrHex, passphrase) if err != nil { return nil, err } seed := privKey.Seed() - return keybase.DeriveChildFromSeed(seed, childIndex, childPassphrase, childHint) + return keybase.DeriveChildFromSeed(seed, childIndex, childPassphrase, childHint, shouldStore) } // ExportPrivString Exports the raw private key string of the given address From 3057fdaf56261a9e1a4160d6f2e341c91bcca2a3 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Mon, 27 Feb 2023 22:44:14 +0000 Subject: [PATCH 31/34] Address comments --- app/client/keybase/keystore.go | 90 ++++++++++++++------------------- shared/crypto/slip/slip.go | 19 +++---- shared/crypto/slip/slip_test.go | 2 +- 3 files changed, 50 insertions(+), 61 deletions(-) diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index d5e11fb2d..1808d9bb7 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -30,7 +30,7 @@ type badgerKeybase struct { db *badger.DB } -// NewKeybase Creates/Opens the DB at the specified path creating the path if it doesn't exist +// NewKeybase creates/Opens the DB at the specified path creating the path if it doesn't exist func NewKeybase(path string) (Keybase, error) { pathExists, err := converters.DirExists(path) // Creates path if it doesn't exist if err != nil || !pathExists { @@ -43,7 +43,7 @@ func NewKeybase(path string) (Keybase, error) { return &badgerKeybase{db: db}, nil } -// NewKeybaseInMemory Creates/Opens the DB in Memory +// NewKeybaseInMemory creates/Opens the DB in Memory // FOR TESTING PURPOSES ONLY func NewKeybaseInMemory() (Keybase, error) { db, err := badger.Open(badgerOptions("").WithInMemory(true)) @@ -53,18 +53,18 @@ func NewKeybaseInMemory() (Keybase, error) { return &badgerKeybase{db: db}, nil } -// GetBadgerDB Returns the DB instance +// GetBadgerDB returns the DB instance // FOR DEBUG PURPOSES ONLY func (keybase *badgerKeybase) GetBadgerDB() *badger.DB { return keybase.db } -// Stop Closes the DB connection +// Stop closes the DB connection func (keybase *badgerKeybase) Stop() error { return keybase.db.Close() } -// Create Creates a new key and store the serialised KeyPair encoding in the DB +// Create creates a new key and store the serialised KeyPair encoding in the DB // Using the PublicKey.Address() return value as the key for storage // Returns the KeyPair created and any error func (keybase *badgerKeybase) Create(passphrase, hint string) (keyPair crypto.KeyPair, err error) { @@ -92,7 +92,7 @@ func (keybase *badgerKeybase) Create(passphrase, hint string) (keyPair crypto.Ke return keyPair, nil } -// ImportFromString Creates a new KeyPair from the private key hex string and store the serialised KeyPair encoding in the DB +// ImportFromString creates a new KeyPair from the private key hex string and store the serialised KeyPair encoding in the DB // Using the PublicKey.Address() return value as the key for storage // Returns the KeyPair created and any error func (keybase *badgerKeybase) ImportFromString(privKeyHex, passphrase, hint string) (keyPair crypto.KeyPair, err error) { @@ -120,7 +120,7 @@ func (keybase *badgerKeybase) ImportFromString(privKeyHex, passphrase, hint stri return keyPair, nil } -// ImportFromJSON Creates a new KeyPair from the private key JSON string and store the serialised KeyPair encoding in the DB +// ImportFromJSON creates a new KeyPair from the private key JSON string and store the serialised KeyPair encoding in the DB // Using the PublicKey.Address() return value as the key for storage // Returns the KeyPair created and any error func (keybase *badgerKeybase) ImportFromJSON(jsonStr, passphrase string) (keyPair crypto.KeyPair, err error) { @@ -148,7 +148,7 @@ func (keybase *badgerKeybase) ImportFromJSON(jsonStr, passphrase string) (keyPai return keyPair, nil } -// Get Returns a KeyPair struct provided the address was found in the DB +// Get returns a KeyPair struct provided the address was found in the DB func (keybase *badgerKeybase) Get(address string) (crypto.KeyPair, error) { kp := crypto.GetKeypair() addrBz, err := hex.DecodeString(address) @@ -183,7 +183,7 @@ func (keybase *badgerKeybase) Get(address string) (crypto.KeyPair, error) { return kp, nil } -// GetPubKey Returns a PublicKey interface provided the address was found in the DB +// GetPubKey returns a PublicKey interface provided the address was found in the DB func (keybase *badgerKeybase) GetPubKey(address string) (crypto.PublicKey, error) { kp, err := keybase.Get(address) if err != nil { @@ -193,7 +193,7 @@ func (keybase *badgerKeybase) GetPubKey(address string) (crypto.PublicKey, error return kp.GetPublicKey(), nil } -// GetPrivKey Returns a PrivateKey interface provided the address was found in the DB and the passphrase was correct +// GetPrivKey returns a PrivateKey interface provided the address was found in the DB and the passphrase was correct func (keybase *badgerKeybase) GetPrivKey(address, passphrase string) (crypto.PrivateKey, error) { kp, err := keybase.Get(address) if err != nil { @@ -208,7 +208,7 @@ func (keybase *badgerKeybase) GetPrivKey(address, passphrase string) (crypto.Pri return privKey, nil } -// GetAll Returns the addresses and key pairs stored in the keybase +// GetAll returns the addresses and key pairs stored in the keybase // Returns the hex addresses stored and all the KeyPair structs stored in the DB func (keybase *badgerKeybase) GetAll() (addresses []string, keyPairs []crypto.KeyPair, err error) { // View executes the function provided managing a read only transaction @@ -247,7 +247,7 @@ func (keybase *badgerKeybase) GetAll() (addresses []string, keyPairs []crypto.Ke return addresses, keyPairs, nil } -// DeriveChildFromSeed Deterministically generates and return the child at the given index from the seed provided +// DeriveChildFromSeed deterministically generates and return the child at the given index from the seed provided // By default this stores the key in the keybase and returns the KeyPair interface and any error func (keybase *badgerKeybase) DeriveChildFromSeed(seed []byte, childIndex uint32, childPassphrase, childHint string, shouldStore bool) (crypto.KeyPair, error) { path := fmt.Sprintf(slip.PoktAccountPathFormat, childIndex) @@ -256,58 +256,46 @@ func (keybase *badgerKeybase) DeriveChildFromSeed(seed []byte, childIndex uint32 return nil, err } - if shouldStore { - // No need to re-encrypt with provided passphrase - if childPassphrase == "" && childHint == "" { - err = keybase.db.Update(func(tx *badger.Txn) error { - // Use key address as key in DB - addrKey := childKey.GetAddressBytes() - - // Encode KeyPair into []byte for value - keypairBz, err := childKey.Marshal() - if err != nil { - return err - } - - return tx.Set(addrKey, keypairBz) - }) + if !shouldStore { + return childKey, nil + } - return childKey, err - } + err = keybase.db.Update(func(tx *badger.Txn) error { + keyPair := childKey // Re-encrypt child key with passphrase and hint - err = keybase.db.Update(func(tx *badger.Txn) error { + if childPassphrase != "" && childHint != "" { // 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 - } - - // Use key address as key in DB - addrKey := keyPair.GetAddressBytes() - - // Encode KeyPair into []byte for value - keypairBz, err := keyPair.Marshal() + keyPair, err = crypto.CreateNewKeyFromString(privKeyHex, childPassphrase, childHint) if err != nil { return err } + } - return tx.Set(addrKey, keypairBz) - }) + // Use key address as key in DB + addrKey := keyPair.GetAddressBytes() + // Encode KeyPair into []byte for value + keypairBz, err := keyPair.Marshal() if err != nil { - return nil, err + return err } + + return tx.Set(addrKey, keypairBz) + }) + + if err != nil { + return nil, err } return childKey, nil } -// DeriveChildFromKey Deterministically generates and return the child at the given index from the parent key provided +// DeriveChildFromKey deterministically generates and return the child at the given index from the parent key provided // By default this stores the key in the keybase and returns the KeyPair interface and any error func (keybase *badgerKeybase) DeriveChildFromKey(masterAddrHex, passphrase string, childIndex uint32, childPassphrase, childHint string, shouldStore bool) (crypto.KeyPair, error) { privKey, err := keybase.GetPrivKey(masterAddrHex, passphrase) @@ -318,7 +306,7 @@ func (keybase *badgerKeybase) DeriveChildFromKey(masterAddrHex, passphrase strin return keybase.DeriveChildFromSeed(seed, childIndex, childPassphrase, childHint, shouldStore) } -// ExportPrivString Exports the raw private key string of the given address +// ExportPrivString exports the raw private key string of the given address func (keybase *badgerKeybase) ExportPrivString(address, passphrase string) (string, error) { kp, err := keybase.Get(address) if err != nil { @@ -327,7 +315,7 @@ func (keybase *badgerKeybase) ExportPrivString(address, passphrase string) (stri return kp.ExportString(passphrase) } -// ExportPrivJSON Exports the private key of the given address as a JSON string +// ExportPrivJSON exports the private key of the given address as a JSON string func (keybase *badgerKeybase) ExportPrivJSON(address, passphrase string) (string, error) { kp, err := keybase.Get(address) if err != nil { @@ -336,7 +324,7 @@ func (keybase *badgerKeybase) ExportPrivJSON(address, passphrase string) (string return kp.ExportJSON(passphrase) } -// UpdatePassphrase Updates the passphrase of the key with the matching address to use the new one provided and updates the hint in the JSON encrypted private key +// UpdatePassphrase updates the passphrase of the key with the matching address to use the new one provided and updates the hint in the JSON encrypted private key func (keybase *badgerKeybase) UpdatePassphrase(address, oldPassphrase, newPassphrase, hint string) error { // Check the oldPassphrase is correct privKey, err := keybase.GetPrivKey(address, oldPassphrase) @@ -374,7 +362,7 @@ func (keybase *badgerKeybase) UpdatePassphrase(address, oldPassphrase, newPassph return err } -// Sign Signs a message using the key address provided +// Sign signs a message using the key address provided func (keybase *badgerKeybase) Sign(address, passphrase string, msg []byte) ([]byte, error) { privKey, err := keybase.GetPrivKey(address, passphrase) if err != nil { @@ -383,7 +371,7 @@ func (keybase *badgerKeybase) Sign(address, passphrase string, msg []byte) ([]by return privKey.Sign(msg) } -// Verify Verifies a message has been signed correctly +// Verify verifies a message has been signed correctly func (keybase *badgerKeybase) Verify(address string, msg, sig []byte) (bool, error) { kp, err := keybase.Get(address) if err != nil { @@ -393,7 +381,7 @@ func (keybase *badgerKeybase) Verify(address string, msg, sig []byte) (bool, err return pubKey.Verify(msg, sig), nil } -// Delete Removes a KeyPair from the DB given the address +// Delete removes a KeyPair from the DB given the address func (keybase *badgerKeybase) Delete(address, passphrase string) error { if _, err := keybase.GetPrivKey(address, passphrase); err != nil { return err @@ -410,7 +398,7 @@ func (keybase *badgerKeybase) Delete(address, passphrase string) error { return err } -// badgerOptions Returns a badger.Options struct for the given DB path - disable logging +// badgerOptions returns a badger.Options struct for the given DB path - disable logging func badgerOptions(path string) badger.Options { opts := badger.DefaultOptions(path) opts.Logger = nil // Badger logger is very noisy diff --git a/shared/crypto/slip/slip.go b/shared/crypto/slip/slip.go index ff263a0b6..421b3b387 100644 --- a/shared/crypto/slip/slip.go +++ b/shared/crypto/slip/slip.go @@ -31,8 +31,8 @@ var ( ) type slipKey struct { - SecretKey []byte - ChainCode []byte + secretKey []byte + chainCode []byte } // Derives a master key from the seed provided and returns the child at the correct index @@ -50,6 +50,7 @@ func DeriveChild(path string, seed []byte) (crypto.KeyPair, error) { } // Iterate over segments in path regenerating the child key until correct + // Enforce that both the purpose and coin type paths are using hardened keys for _, i32 := range segments { // Force hardened keys if i32 > maxChildKeyIndex { @@ -80,8 +81,8 @@ func newMasterKey(seed []byte) (*slipKey, error) { // Create SLIP-0010 Master key key := &slipKey{ - SecretKey: sum[:32], - ChainCode: sum[32:], + secretKey: sum[:32], + chainCode: sum[32:], } return key, nil @@ -97,10 +98,10 @@ func (k *slipKey) deriveChild(i uint32) (*slipKey, error) { data := []byte{0x0} iBytes := make([]byte, 4) binary.BigEndian.PutUint32(iBytes, i) - data = append(data, k.SecretKey...) + data = append(data, k.secretKey...) data = append(data, iBytes...) - hmacHash := hmac.New(sha512.New, k.ChainCode) + hmacHash := hmac.New(sha512.New, k.chainCode) if _, err := hmacHash.Write(data); err != nil { return nil, err } @@ -110,14 +111,14 @@ func (k *slipKey) deriveChild(i uint32) (*slipKey, error) { // Create SLIP-0010 Child key child := &slipKey{ - SecretKey: sum[:32], - ChainCode: sum[32:], + secretKey: sum[:32], + chainCode: sum[32:], } return child, nil } func (k *slipKey) convertToKeypair() (crypto.KeyPair, error) { - return crypto.CreateNewKeyFromSeed(k.SecretKey, "", "") + return crypto.CreateNewKeyFromSeed(k.secretKey, "", "") } // Check the BIP-44 path provided is valid and return the []uint32 segments it contains diff --git a/shared/crypto/slip/slip_test.go b/shared/crypto/slip/slip_test.go index c9f108aec..b7fc0bf63 100644 --- a/shared/crypto/slip/slip_test.go +++ b/shared/crypto/slip/slip_test.go @@ -218,7 +218,7 @@ func TestSlip_DeriveChild_TestVectors(t *testing.T) { privSeed, err := childKey.GetSeed("") require.NoError(t, err) privHex := hex.EncodeToString(privSeed) - require.Equal(t, privHex, tv.wantPrivHex) + require.Equal(t, tv.wantPrivHex, privHex) // Slip-0010 keys are prefixed with "00" in the test vectors pubHex := childKey.GetPublicKey().String() From 4282c910f1f4520494c619a46301ef0e834e489d Mon Sep 17 00:00:00 2001 From: Harry Law Date: Tue, 28 Feb 2023 10:01:53 +0000 Subject: [PATCH 32/34] Update comments --- shared/crypto/slip/slip_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shared/crypto/slip/slip_test.go b/shared/crypto/slip/slip_test.go index b7fc0bf63..9203f9847 100644 --- a/shared/crypto/slip/slip_test.go +++ b/shared/crypto/slip/slip_test.go @@ -28,6 +28,8 @@ func TestSlip_DeriveChild_TestVectors(t *testing.T) { wantErr bool }{ // https://github.com/satoshilabs/slips/blob/master/slip-0010.md#test-vector-1-for-ed25519 + // Note that ed25519 public keys normaly don't have a "00" prefix, but we are reflecting the + // test vectors from the spec which do { name: "TestVector1 Key derivation is deterministic for path `m` (master key)", path: "m", @@ -222,7 +224,7 @@ func TestSlip_DeriveChild_TestVectors(t *testing.T) { // Slip-0010 keys are prefixed with "00" in the test vectors pubHex := childKey.GetPublicKey().String() - require.Equal(t, "00"+pubHex, tv.wantPubHex) + require.Equal(t, tv.wantPubHex, "00"+pubHex) }) } } From 69b310ade1122818a255830261543465ca67e75c Mon Sep 17 00:00:00 2001 From: Harry Law Date: Tue, 28 Feb 2023 10:04:46 +0000 Subject: [PATCH 33/34] Update CHANGELOG.md files --- app/client/doc/CHANGELOG.md | 5 +++++ shared/CHANGELOG.md | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/app/client/doc/CHANGELOG.md b/app/client/doc/CHANGELOG.md index 47722c0e9..0d956e79e 100644 --- a/app/client/doc/CHANGELOG.md +++ b/app/client/doc/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.0.0.18] - 2023-02-28 + +- Implement SLIP-0010 HD child key derivation with the keybase +- Add CLI endpoints to derive child keys by index + ## [0.0.0.17] - 2023-02-23 - Add CLI endpoints to interact with the keybase diff --git a/shared/CHANGELOG.md b/shared/CHANGELOG.md index 7292db473..56839c35a 100644 --- a/shared/CHANGELOG.md +++ b/shared/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.0.0.35] - 2023-02-28 + +- Implement SLIP-0010 specification for HD child key derivation +- Cover both Test Vectors 1 and 2 from the specification + ## [0.0.0.34] - 2023-02-24 - Remove SetLeaderId() method from ConsensusDebugModule interface From 61aece6d3772e34ba7e943659bd774003aa7d380 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Tue, 28 Feb 2023 10:18:18 +0000 Subject: [PATCH 34/34] Fix linter error --- shared/crypto/slip/slip_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/crypto/slip/slip_test.go b/shared/crypto/slip/slip_test.go index 9203f9847..91b02cba1 100644 --- a/shared/crypto/slip/slip_test.go +++ b/shared/crypto/slip/slip_test.go @@ -28,7 +28,7 @@ func TestSlip_DeriveChild_TestVectors(t *testing.T) { wantErr bool }{ // https://github.com/satoshilabs/slips/blob/master/slip-0010.md#test-vector-1-for-ed25519 - // Note that ed25519 public keys normaly don't have a "00" prefix, but we are reflecting the + // Note that ed25519 public keys normally don't have a "00" prefix, but we are reflecting the // test vectors from the spec which do { name: "TestVector1 Key derivation is deterministic for path `m` (master key)",