From 8038970e23fc1a44192d49c9aeafa6353104bd93 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sat, 21 Jan 2023 18:31:55 +0000 Subject: [PATCH 01/56] Add beginnings of local account keybase using BadgerDB backend --- Makefile | 4 + app/client/keybase/keybase_test.go | 67 ++++++++ app/client/keybase/keys.go | 255 +++++++++++++++++++++++++++++ app/client/keybase/keystore.go | 227 +++++++++++++++++++++++++ go.mod | 11 +- go.sum | 18 +- 6 files changed, 571 insertions(+), 11 deletions(-) create mode 100644 app/client/keybase/keybase_test.go create mode 100644 app/client/keybase/keys.go create mode 100644 app/client/keybase/keystore.go diff --git a/Makefile b/Makefile index 68e04125f..31d7e5ee0 100644 --- a/Makefile +++ b/Makefile @@ -307,6 +307,10 @@ test_all_with_json_coverage: generate_rpc_openapi ## Run all go unit tests, outp test_race: ## Identify all unit tests that may result in race conditions go test ${VERBOSE_TEST} -race ./... +.PHONY: test_app +test_app: ## Run all go app module unit tests + go test ${VERBOSE_TEST} -p=1 -count=1 ./app/... + .PHONY: test_utility test_utility: ## Run all go utility module unit tests go test ${VERBOSE_TEST} -p=1 -count=1 ./utility/... diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go new file mode 100644 index 000000000..30a022a1a --- /dev/null +++ b/app/client/keybase/keybase_test.go @@ -0,0 +1,67 @@ +package keybase + +import ( + "github.com/pokt-network/pocket/shared/crypto" + "github.com/stretchr/testify/require" + "testing" +) + +const ( + testPassphrase = "testingtesting123" +) + +var ( + testPrivKeyAddr = "74fa6b8f3a4ec6959a2f86b63d0774af952cdb91" + testPrivKeyBytes = []byte{ + 188, 138, 150, 24, 38, 193, 136, 7, 4, 20, 162, 74, 51, 102, + 213, 188, 192, 27, 60, 71, 20, 14, 104, 116, 80, 84, 6, 134, + 197, 240, 54, 227, 83, 112, 165, 101, 75, 106, 249, 65, 126, + 242, 179, 71, 87, 172, 95, 232, 200, 31, 67, 124, 203, 84, + 178, 160, 14, 79, 38, 79, 6, 71, 43, 236, + } +) + +// TODO: Implement unarmouring privKey str and test this is correct +func TestKeybase_CreateNewKey(t *testing.T) { + db, err := initDB() + defer db.Stop() + require.NoError(t, err) + + err = db.Create(testPassphrase) + require.NoError(t, err) + + addresses, keypairs, err := db.GetAll() + require.NoError(t, err) + + addr := addresses[0] + kp := keypairs[0] + require.Equal(t, len(addr), crypto.AddressLen) + require.Equal(t, addr, kp.PublicKey.Address().Bytes()) +} + +// TODO: Implement unarmouring privKey str and test this is correct +func TestKeybase_CreateNewKeyFromBytes(t *testing.T) { + db, err := initDB() + defer db.Stop() + require.NoError(t, err) + + err = db.CreateFromBytes(testPrivKeyBytes, testPassphrase) + require.NoError(t, err) + + addresses, keypairs, err := db.GetAll() + require.NoError(t, err) + + addr := addresses[0] + kp := keypairs[0] + require.Equal(t, len(addr), crypto.AddressLen) + require.Equal(t, addr, kp.PublicKey.Address().Bytes()) + require.Equal(t, kp.PublicKey.Address().String(), testPrivKeyAddr) +} + +func initDB() (Keybase, error) { + db, err := NewKeybaseInMemory("") + if err != nil { + return nil, err + } + return db, nil +} diff --git a/app/client/keybase/keys.go b/app/client/keybase/keys.go new file mode 100644 index 000000000..e1c2c9687 --- /dev/null +++ b/app/client/keybase/keys.go @@ -0,0 +1,255 @@ +package keybase + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/ed25519" + crand "crypto/rand" + "encoding/base64" + "encoding/gob" + "encoding/hex" + "fmt" + poktCrypto "github.com/pokt-network/pocket/shared/crypto" + "golang.org/x/crypto/scrypt" +) + +const ( + // Scrypt params + n = 32768 + r = 8 + p = 1 + klen = 32 +) + +func init() { + gob.Register(poktCrypto.Ed25519PublicKey{}) + gob.Register(ed25519.PublicKey{}) + gob.Register(KeyPair{}) + gob.Register(ArmouredKey{}) +} + +// KeyPair struct stores the public key and the passphrase encrypted private key +type KeyPair struct { + PublicKey poktCrypto.PublicKey + PrivKeyArmour string +} + +// Generate a new KeyPair struct given the public key and armoured private key +func NewKeyPair(pub poktCrypto.PublicKey, priv string) KeyPair { + return KeyPair{ + PublicKey: pub, + PrivKeyArmour: priv, + } +} + +// Return the byte slice address of the public key +func (kp KeyPair) GetAddress() []byte { + return kp.PublicKey.Address().Bytes() +} + +// Armoured Private Key struct with fields to unarmour it later +type ArmouredKey struct { + Kdf string + Salt string + CipherText string +} + +// Generate new armoured private key struct with parameters for unarmouring +func NewArmouredKey(kdf, salt, cipher string) ArmouredKey { + return ArmouredKey{ + Kdf: kdf, + Salt: salt, + CipherText: cipher, + } +} + +// Generate new private ED25519 key and encrypt and armour it as a string +// Returns a KeyPair struct of the Public Key and Armoured String +func CreateNewKey(passphrase string) (KeyPair, error) { + privKey, err := poktCrypto.GeneratePrivateKey() + if err != nil { + return KeyPair{}, err + } + + privArmour, err := encryptArmourPrivKey(privKey, passphrase) + if err != nil || privArmour == "" { + return KeyPair{}, nil + } + + pubKey := privKey.PublicKey() + keyPair := NewKeyPair(pubKey, privArmour) + + return keyPair, nil +} + +// Generate new private ED25519 key from the bytes provided, encrypt and +// armour it as a string +// Returns a KeyPair struct of the Public Key and Armoured String +func CreateNewKeyFromBytes(privBytes []byte, passphrase string) (KeyPair, error) { + privKey, err := poktCrypto.NewPrivateKeyFromBytes(privBytes) + if err != nil { + return KeyPair{}, err + } + + privArmour, err := encryptArmourPrivKey(privKey, passphrase) + if err != nil || privArmour == "" { + return KeyPair{}, nil + } + + pubKey := privKey.PublicKey() + keyPair := NewKeyPair(pubKey, privArmour) + + return keyPair, nil +} + +// Encrypt the given privKey with the passphrase, armour it by encoding the ecnrypted +// []byte into base64, and convert into a json string with the parameters for unarmouring +func encryptArmourPrivKey(privKey poktCrypto.PrivateKey, passphrase string) (string, error) { + // Encrypt privKey usign AES-256 GCM Cipher + saltBytes, encBytes, err := encryptPrivKey(privKey, passphrase) + if err != nil { + return "", err + } + + // Armour encrypted bytes by encoding into Base64 + armourStr := base64.StdEncoding.EncodeToString(encBytes) + + // Create ArmouredKey object so can unarmour later + armoured := NewArmouredKey("scrypt", fmt.Sprintf("%X", saltBytes), armourStr) + + // Encode armoured struct into []byte + var bz bytes.Buffer + enc := gob.NewEncoder(&bz) + if err = enc.Encode(armoured); err != nil { + return "", err + } + + return bz.String(), nil +} + +// Encrypt the given privKey with the passphrase using a randomly +// generated salt and the AES-256 GCM cipher +func encryptPrivKey(privKey poktCrypto.PrivateKey, passphrase string) (saltBytes []byte, encBytes []byte, err error) { + // Get random bytes for salt + saltBytes = randBytes(16) + + // derive key for encryption, see: https://pkg.go.dev/golang.org/x/crypto/scrypt#Key + key, err := scrypt.Key([]byte(passphrase), saltBytes, n, r, p, klen) + if err != nil { + return nil, nil, err + } + + //encrypt using AES + privKeyBytes := privKey.Bytes() + encBytes, err = encryptAESGCM(key, privKeyBytes) + if err != nil { + return nil, nil, err + } + + return saltBytes, encBytes, nil +} + +// Unarmor and decrypt the private key using the passphrase provided +func unarmourDecryptPrivKey(armourStr string, passphrase string) (privKey poktCrypto.PrivateKey, err error) { + // Decode armourStr back into ArmouredKey struct + armouredKey := ArmouredKey{} + var bz bytes.Buffer + bz.Write([]byte(armourStr)) + dec := gob.NewDecoder(&bz) + if err = dec.Decode(&armouredKey); err != nil { + return nil, err + } + + // check the ArmouredKey for the correct parameters on kdf and salt + if armouredKey.Kdf != "scrypt" { + return nil, fmt.Errorf("Unrecognized KDF type: %v", armouredKey.Kdf) + } + if armouredKey.Salt == "" { + return nil, fmt.Errorf("Missing salt bytes") + } + + //decoding the salt + saltBytes, err := hex.DecodeString(armouredKey.Salt) + if err != nil { + return nil, fmt.Errorf("Error decoding salt: %v", err.Error()) + } + + //decoding the "armoured" ciphertext stored in base64 + encBytes, err := base64.StdEncoding.DecodeString(armouredKey.CipherText) + if err != nil { + return nil, fmt.Errorf("Error decoding ciphertext: %v", err.Error()) + } + + //decrypt the actual privkey with the parameters + privKey, err = decryptPrivKey(saltBytes, encBytes, passphrase) + + return privKey, err +} + +// Decrypt the AES-256 GCM encrypted bytes using the passphrase given +func decryptPrivKey(saltBytes []byte, encBytes []byte, passphrase string) (privKey poktCrypto.PrivateKey, err error) { + // derive key for decryption + key, err := scrypt.Key([]byte(passphrase), saltBytes, n, r, p, klen) + if err != nil { + return nil, err + } + + //decrypt using AES + privKeyBytes, err := decryptAESGCM(key, encBytes) + if err != nil { + return nil, err + } + + // Get private key from decrypted bytes + privKeyBytes, _ = hex.DecodeString(string(privKeyBytes)) + pk, err := poktCrypto.NewPrivateKeyFromBytes(privKeyBytes) + if err != nil { + return nil, err + } + + return pk, nil +} + +// Encrypt using AES-256 GCM Cipher +func encryptAESGCM(key []byte, src []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + nonce := key[:12] + out := gcm.Seal(nil, nonce, src, nil) + return out, nil +} + +// Decrypt using AES-256 GCM Cipher +func decryptAESGCM(key []byte, enBytes []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + nonce := key[:12] + result, err := gcm.Open(nil, nonce, enBytes, nil) + if err != nil { + return nil, fmt.Errorf("Can't Decrypt Using AES : %s \n", err) + } + return result, nil +} + +// Use OS randomness +func randBytes(numBytes int) []byte { + b := make([]byte, numBytes) + _, err := crand.Read(b) + if err != nil { + panic(err) + } + return b +} diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go new file mode 100644 index 000000000..09077e857 --- /dev/null +++ b/app/client/keybase/keystore.go @@ -0,0 +1,227 @@ +package keybase + +import ( + "bytes" + "crypto/ed25519" + "encoding/gob" + "github.com/dgraph-io/badger/v3" + "github.com/pokt-network/pocket/shared/crypto" +) + +func init() { + gob.Register(crypto.Ed25519PublicKey{}) + gob.Register(ed25519.PublicKey{}) + gob.Register(KeyPair{}) + gob.Register(ArmouredKey{}) +} + +// Keybase interface implements the CRUD operations for the keybase +type Keybase interface { + // Close the DB connection + Stop() error + + // Create new keypair entry in DB + Create(passphrase string) error + // Insert new keypair from private key []byte provided into the DB + CreateFromBytes(privBytes []byte, passphrase string) error + + // Accessors + Get(address []byte) (KeyPair, error) + GetAll() (addresses [][]byte, keyPairs []KeyPair, err error) + Exists(key []byte) (bool, error) + + // Removals + Delete(address []byte) error + ClearAll() error +} + +// badgerKeybase implements the Keybase struct using the BadgerDB backend +type badgerKeybase struct { + db *badger.DB +} + +// Creates/Opens the DB at the specified path +// WARNING: path must be a valid directory that already exists +func NewKeybase(path string) (Keybase, error) { + db, err := badger.Open(badgerOptions(path)) + if err != nil { + return nil, err + } + return &badgerKeybase{db: db}, nil +} + +// Creates/Opens the DB in Memory +// FOR TESTING PURPOSES ONLY +func NewKeybaseInMemory(path string) (Keybase, error) { + db, err := badger.Open(badgerOptions(path).WithInMemory(true)) + if err != nil { + return nil, err + } + return &badgerKeybase{db: db}, nil +} + +// Close the DB +func (keybase *badgerKeybase) Stop() error { + return keybase.db.Close() +} + +// Crate a new key and store it in the DB by encoding the KeyPair struct into a []byte +// Using the PublicKey.Address() return value as the key for storage +func (keybase *badgerKeybase) Create(passphrase string) error { + err := keybase.db.Update(func(tx *badger.Txn) error { + keyPair, err := CreateNewKey(passphrase) + if err != nil { + return err + } + + // Use key address as key in DB + key := keyPair.GetAddress() + // Encode entire KeyPair struct into []byte for value + bz := new(bytes.Buffer) + enc := gob.NewEncoder(bz) + if err = enc.Encode(keyPair); err != nil { + return err + } + + err = tx.Set(key, bz.Bytes()) + if err != nil { + return err + } + + return nil + }) + + return err +} + +// Crate a new key and store it in the DB by encoding the KeyPair struct into a []byte +// Using the PublicKey.Address() return value as the key for storage +func (keybase *badgerKeybase) CreateFromBytes(privBytes []byte, passphrase string) error { + err := keybase.db.Update(func(tx *badger.Txn) error { + keyPair, err := CreateNewKeyFromBytes(privBytes, passphrase) + if err != nil { + return err + } + + // Use key address as key in DB + key := keyPair.GetAddress() + // Encode entire KeyPair struct into []byte for value + bz := new(bytes.Buffer) + enc := gob.NewEncoder(bz) + if err = enc.Encode(keyPair); err != nil { + return err + } + + err = tx.Set(key, bz.Bytes()) + if err != nil { + return err + } + + return nil + }) + + return err +} + +// Returns a KeyPair struct provided the address was found in the DB +func (keybase *badgerKeybase) Get(address []byte) (KeyPair, error) { + var kp KeyPair + bz := new(bytes.Buffer) + + err := keybase.db.View(func(tx *badger.Txn) error { + item, err := tx.Get(address) + if err != nil { + return err + } + + value, err := item.ValueCopy(nil) + if err != nil { + return err + } + + // Decode []byte value back into KeyPair struct + bz.Write(value) + dec := gob.NewDecoder(bz) + if err = dec.Decode(&kp); err != nil { + return err + } + + return nil + }) + if err != nil { + return KeyPair{}, err + } + + return kp, nil +} + +// Get all the addresses and key pairs stored in the keybase +// Returns addresses stored and all the KeyPair structs stored in the DB +func (keybase *badgerKeybase) GetAll() (addresses [][]byte, keyPairs []KeyPair, err error) { + // View executes the function provided managing a read only transaction + err = keybase.db.View(func(tx *badger.Txn) error { + opts := badger.DefaultIteratorOptions + opts.PrefetchSize = 5 + it := tx.NewIterator(opts) + defer it.Close() + for it.Rewind(); it.Valid(); it.Next() { + item := it.Item() + err := item.Value(func(val []byte) error { + // Decode []byte value back into KeyPair struct + var kp KeyPair + bz := new(bytes.Buffer) + bz.Write(val) + dec := gob.NewDecoder(bz) + if err = dec.Decode(&kp); err != nil { + return err + } + + addresses = append(addresses, item.Key()) + keyPairs = append(keyPairs, kp) + return nil + }) + if err != nil { + return err + } + } + return nil + }) + + if err != nil { + return nil, nil, err + } + + return addresses, keyPairs, nil +} + +// Check whether an address is currently stored in the DB +func (keybase *badgerKeybase) Exists(address []byte) (bool, error) { + val, err := keybase.Get(address) + if err != nil { + return false, err + } + return val == KeyPair{}, nil +} + +// Remove a KeyPair from the DB given the address +// TODO: Add a check that the user can decrypt this KeyPair +func (keybase *badgerKeybase) Delete(address []byte) error { + err := keybase.db.Update(func(tx *badger.Txn) error { + tx.Delete(address) + return nil + }) + return err +} + +// Remove all keys in the DB +// TODO: Add a check that the use can decrypt all the keys +func (keybase *badgerKeybase) ClearAll() error { + return keybase.db.DropAll() +} + +// Return badger.Options 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 + return opts +} diff --git a/go.mod b/go.mod index c389c8cb7..7d6b4f985 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/manifoldco/promptui v0.9.0 github.com/mitchellh/mapstructure v1.5.0 github.com/quasilyte/go-ruleguard/dsl v0.3.21 - github.com/rs/zerolog v1.15.0 + github.com/rs/zerolog v1.27.0 github.com/spf13/cobra v1.6.0 github.com/spf13/viper v1.13.0 ) @@ -50,7 +50,6 @@ require ( github.com/golang/protobuf v1.5.2 github.com/golang/snappy v0.0.4 // indirect github.com/google/flatbuffers v22.9.29+incompatible // indirect - github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect github.com/klauspost/compress v1.15.11 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect @@ -60,24 +59,26 @@ require ( github.com/sirupsen/logrus v1.9.0 // indirect go.opencensus.io v0.23.0 // indirect golang.org/x/net v0.2.0 // indirect - gotest.tools v2.2.0+incompatible // indirect ) require ( github.com/beorn7/perks v1.0.1 // 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 github.com/josharian/intern v1.0.0 // indirect - github.com/lib/pq v1.10.2 // indirect + github.com/lib/pq v1.10.6 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.37.0 // indirect github.com/prometheus/procfs v0.8.0 // indirect + github.com/rogpeppe/go-internal v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect go.uber.org/atomic v1.10.0 // indirect golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7 // indirect golang.org/x/term v0.2.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + gotest.tools v2.2.0+incompatible // indirect ) require ( @@ -117,7 +118,7 @@ require ( golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/sys v0.2.0 // indirect golang.org/x/text v0.4.0 // indirect - golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect + golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect golang.org/x/tools v0.1.12 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index a0a4d4bae..9cd2f2275 100644 --- a/go.sum +++ b/go.sum @@ -95,6 +95,7 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= @@ -322,8 +323,8 @@ github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3 github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= -github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs= +github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= @@ -336,6 +337,7 @@ github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYt github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -377,6 +379,7 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -412,11 +415,14 @@ github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0ua github.com/quasilyte/go-ruleguard/dsl v0.3.21 h1:vNkC6fC6qMLzCOGbnIHOd5ixUGgTbp3Z4fGnUgULlDA= github.com/quasilyte/go-ruleguard/dsl v0.3.21/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= -github.com/rs/zerolog v1.15.0 h1:uPRuwkWF4J6fGsJ2R0Gn2jB1EQiav9k3S6CSdygQJXY= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/rs/zerolog v1.27.0 h1:1T7qCieN22GVc8S4Q2yuexzBb1EqjbgjSH9RohbMjKs= +github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -708,8 +714,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE= -golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= From eab44979c01ac186c5c6c35c59a14b7bfe817781 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sun, 22 Jan 2023 15:02:41 +0000 Subject: [PATCH 02/56] Add CreateFromString function, wrong passphrase error catch, Unarmour function for KeyPair struct and update comments --- app/client/keybase/keys.go | 64 +++++++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/app/client/keybase/keys.go b/app/client/keybase/keys.go index e1c2c9687..ffab6c031 100644 --- a/app/client/keybase/keys.go +++ b/app/client/keybase/keys.go @@ -12,6 +12,7 @@ import ( "fmt" poktCrypto "github.com/pokt-network/pocket/shared/crypto" "golang.org/x/crypto/scrypt" + "strings" ) const ( @@ -22,6 +23,11 @@ const ( klen = 32 ) +var ( + // Errors + ErrorWrongPassphrase = fmt.Errorf("Can't decrypt private key: wrong passphrase") +) + func init() { gob.Register(poktCrypto.Ed25519PublicKey{}) gob.Register(ed25519.PublicKey{}) @@ -44,10 +50,20 @@ func NewKeyPair(pub poktCrypto.PublicKey, priv string) KeyPair { } // Return the byte slice address of the public key -func (kp KeyPair) GetAddress() []byte { +func (kp KeyPair) GetAddressBytes() []byte { return kp.PublicKey.Address().Bytes() } +// Return the string address of the public key +func (kp KeyPair) GetAddressString() string { + return kp.PublicKey.Address().String() +} + +// Unarmour the private key with the passphrase provided +func (kp KeyPair) Unarmour(passphrase string) (poktCrypto.PrivateKey, error) { + return unarmourDecryptPrivKey(kp.PrivKeyArmour, passphrase) +} + // Armoured Private Key struct with fields to unarmour it later type ArmouredKey struct { Kdf string @@ -83,9 +99,9 @@ func CreateNewKey(passphrase string) (KeyPair, error) { return keyPair, nil } -// Generate new private ED25519 key from the bytes provided, encrypt and -// armour it as a string +// Generate new private ED25519 key from the bytes provided, encrypt and armour it as a string // Returns a KeyPair struct of the Public Key and Armoured String +// DISCUSSION: Is this needed? func CreateNewKeyFromBytes(privBytes []byte, passphrase string) (KeyPair, error) { privKey, err := poktCrypto.NewPrivateKeyFromBytes(privBytes) if err != nil { @@ -103,6 +119,25 @@ func CreateNewKeyFromBytes(privBytes []byte, passphrase string) (KeyPair, error) return keyPair, nil } +// Generate new private ED25519 key from the hex string provided, encrypt and armour it as a string +// Returns a KeyPair struct of the Public Key and Armoured String +func CreateNewKeyFromString(privStr, passphrase string) (KeyPair, error) { + privKey, err := poktCrypto.NewPrivateKey(privStr) + if err != nil { + return KeyPair{}, err + } + + privArmour, err := encryptArmourPrivKey(privKey, passphrase) + if err != nil || privArmour == "" { + return KeyPair{}, nil + } + + pubKey := privKey.PublicKey() + keyPair := NewKeyPair(pubKey, privArmour) + + return keyPair, nil +} + // Encrypt the given privKey with the passphrase, armour it by encoding the ecnrypted // []byte into base64, and convert into a json string with the parameters for unarmouring func encryptArmourPrivKey(privKey poktCrypto.PrivateKey, passphrase string) (string, error) { @@ -134,13 +169,13 @@ func encryptPrivKey(privKey poktCrypto.PrivateKey, passphrase string) (saltBytes // Get random bytes for salt saltBytes = randBytes(16) - // derive key for encryption, see: https://pkg.go.dev/golang.org/x/crypto/scrypt#Key + // Derive key for encryption, see: https://pkg.go.dev/golang.org/x/crypto/scrypt#Key key, err := scrypt.Key([]byte(passphrase), saltBytes, n, r, p, klen) if err != nil { return nil, nil, err } - //encrypt using AES + // Encrypt using AES privKeyBytes := privKey.Bytes() encBytes, err = encryptAESGCM(key, privKeyBytes) if err != nil { @@ -161,7 +196,7 @@ func unarmourDecryptPrivKey(armourStr string, passphrase string) (privKey poktCr return nil, err } - // check the ArmouredKey for the correct parameters on kdf and salt + // Check the ArmouredKey for the correct parameters on kdf and salt if armouredKey.Kdf != "scrypt" { return nil, fmt.Errorf("Unrecognized KDF type: %v", armouredKey.Kdf) } @@ -169,40 +204,39 @@ func unarmourDecryptPrivKey(armourStr string, passphrase string) (privKey poktCr return nil, fmt.Errorf("Missing salt bytes") } - //decoding the salt + // Decoding the salt saltBytes, err := hex.DecodeString(armouredKey.Salt) if err != nil { return nil, fmt.Errorf("Error decoding salt: %v", err.Error()) } - //decoding the "armoured" ciphertext stored in base64 + // Decoding the "armoured" ciphertext stored in base64 encBytes, err := base64.StdEncoding.DecodeString(armouredKey.CipherText) if err != nil { return nil, fmt.Errorf("Error decoding ciphertext: %v", err.Error()) } - //decrypt the actual privkey with the parameters + // Decrypt the actual privkey with the parameters privKey, err = decryptPrivKey(saltBytes, encBytes, passphrase) return privKey, err } // Decrypt the AES-256 GCM encrypted bytes using the passphrase given -func decryptPrivKey(saltBytes []byte, encBytes []byte, passphrase string) (privKey poktCrypto.PrivateKey, err error) { - // derive key for decryption +func decryptPrivKey(saltBytes []byte, encBytes []byte, passphrase string) (poktCrypto.PrivateKey, error) { + // Derive key for decryption, see: https://pkg.go.dev/golang.org/x/crypto/scrypt#Key key, err := scrypt.Key([]byte(passphrase), saltBytes, n, r, p, klen) if err != nil { return nil, err } - //decrypt using AES + // Decrypt using AES privKeyBytes, err := decryptAESGCM(key, encBytes) if err != nil { return nil, err } // Get private key from decrypted bytes - privKeyBytes, _ = hex.DecodeString(string(privKeyBytes)) pk, err := poktCrypto.NewPrivateKeyFromBytes(privKeyBytes) if err != nil { return nil, err @@ -238,7 +272,9 @@ func decryptAESGCM(key []byte, enBytes []byte) ([]byte, error) { } nonce := key[:12] result, err := gcm.Open(nil, nonce, enBytes, nil) - if err != nil { + if err != nil && strings.Contains(err.Error(), "authentication failed") { + return nil, ErrorWrongPassphrase + } else if err != nil { return nil, fmt.Errorf("Can't Decrypt Using AES : %s \n", err) } return result, nil From c6da3f89341ce84dbebd50fb1a5d475eed9b861b Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sun, 22 Jan 2023 15:03:58 +0000 Subject: [PATCH 03/56] Add passphrase validation, and GetPrivKey and UpdatePassphrase functions --- app/client/keybase/keystore.go | 137 +++++++++++++++++++++++++++++++-- 1 file changed, 129 insertions(+), 8 deletions(-) diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index 09077e857..1404687ff 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/ed25519" "encoding/gob" + "fmt" "github.com/dgraph-io/badger/v3" "github.com/pokt-network/pocket/shared/crypto" ) @@ -15,6 +16,9 @@ func init() { gob.Register(ArmouredKey{}) } +// badgerKeybase implements the KeyBase interface +var _ Keybase = &badgerKeybase{} + // Keybase interface implements the CRUD operations for the keybase type Keybase interface { // Close the DB connection @@ -24,14 +28,20 @@ type Keybase interface { Create(passphrase string) error // Insert new keypair from private key []byte provided into the DB CreateFromBytes(privBytes []byte, passphrase string) error + // Insert a new keypair from the private key hex string provided into the DB + CreateFromString(privStr, passphrase string) error // Accessors Get(address []byte) (KeyPair, error) + GetPrivKey(address []byte, passphrase string) (crypto.PrivateKey, error) GetAll() (addresses [][]byte, keyPairs []KeyPair, err error) Exists(key []byte) (bool, error) + // Updator + UpdatePassphrase(address []byte, oldPassphrase, newPassphrase string) error + // Removals - Delete(address []byte) error + Delete(address []byte, passphrase string) error ClearAll() error } @@ -75,7 +85,7 @@ func (keybase *badgerKeybase) Create(passphrase string) error { } // Use key address as key in DB - key := keyPair.GetAddress() + key := keyPair.GetAddressBytes() // Encode entire KeyPair struct into []byte for value bz := new(bytes.Buffer) enc := gob.NewEncoder(bz) @@ -94,8 +104,9 @@ func (keybase *badgerKeybase) Create(passphrase string) error { return err } -// Crate a new key and store it in the DB by encoding the KeyPair struct into a []byte +// Crate a new KeyPair from the []byte provided and store it in the DB by encoding the KeyPair struct into a []byte // Using the PublicKey.Address() return value as the key for storage +// DISCUSSION: Is this needed? func (keybase *badgerKeybase) CreateFromBytes(privBytes []byte, passphrase string) error { err := keybase.db.Update(func(tx *badger.Txn) error { keyPair, err := CreateNewKeyFromBytes(privBytes, passphrase) @@ -104,7 +115,36 @@ func (keybase *badgerKeybase) CreateFromBytes(privBytes []byte, passphrase strin } // Use key address as key in DB - key := keyPair.GetAddress() + key := keyPair.GetAddressBytes() + // Encode entire KeyPair struct into []byte for value + bz := new(bytes.Buffer) + enc := gob.NewEncoder(bz) + if err = enc.Encode(keyPair); err != nil { + return err + } + + err = tx.Set(key, bz.Bytes()) + if err != nil { + return err + } + + return nil + }) + + return err +} + +// Crate a new KeyPair from the private key hex string and store it in the DB by encoding the KeyPair struct into a []byte +// Using the PublicKey.Address() return value as the key for storage +func (keybase *badgerKeybase) CreateFromString(privStr, passphrase string) error { + err := keybase.db.Update(func(tx *badger.Txn) error { + keyPair, err := CreateNewKeyFromString(privStr, passphrase) + if err != nil { + return err + } + + // Use key address as key in DB + key := keyPair.GetAddressBytes() // Encode entire KeyPair struct into []byte for value bz := new(bytes.Buffer) enc := gob.NewEncoder(bz) @@ -155,6 +195,43 @@ func (keybase *badgerKeybase) Get(address []byte) (KeyPair, error) { return kp, nil } +// Returns a PrivateKey interface provided the address was found in the DB and the passphrase was correct +func (keybase *badgerKeybase) GetPrivKey(address []byte, passphrase string) (crypto.PrivateKey, error) { + var kp KeyPair + bz := new(bytes.Buffer) + + err := keybase.db.View(func(tx *badger.Txn) error { + item, err := tx.Get(address) + if err != nil { + return err + } + + value, err := item.ValueCopy(nil) + if err != nil { + return err + } + + // Decode []byte value back into KeyPair struct + bz.Write(value) + dec := gob.NewDecoder(bz) + if err = dec.Decode(&kp); err != nil { + return err + } + + return nil + }) + if err != nil { + return nil, err + } + + privKey, err := kp.Unarmour(passphrase) + if err != nil { + return nil, err + } + + 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 func (keybase *badgerKeybase) GetAll() (addresses [][]byte, keyPairs []KeyPair, err error) { @@ -167,10 +244,12 @@ func (keybase *badgerKeybase) GetAll() (addresses [][]byte, keyPairs []KeyPair, for it.Rewind(); it.Valid(); it.Next() { item := it.Item() err := item.Value(func(val []byte) error { + b := make([]byte, len(val)) + copy(b, val) // Decode []byte value back into KeyPair struct var kp KeyPair bz := new(bytes.Buffer) - bz.Write(val) + bz.Write(b) dec := gob.NewDecoder(bz) if err = dec.Decode(&kp); err != nil { return err @@ -203,10 +282,51 @@ func (keybase *badgerKeybase) Exists(address []byte) (bool, error) { return val == KeyPair{}, nil } +func (keybase *badgerKeybase) UpdatePassphrase(address []byte, oldPassphrase, newPassphrase string) error { + // Check the oldPassphrase is correct + privKey, err := keybase.GetPrivKey(address, oldPassphrase) + if err != nil { + return err + } + privStr := privKey.String() + + err = keybase.db.Update(func(tx *badger.Txn) error { + keyPair, err := CreateNewKeyFromString(privStr, newPassphrase) + if err != nil { + return err + } + + // Use key address as key in DB + key := keyPair.GetAddressBytes() + if bytes.Compare(key, address) != 0 { + return fmt.Errorf("Key address does not match previous address.") + } + // Encode entire KeyPair struct into []byte for value + bz := new(bytes.Buffer) + enc := gob.NewEncoder(bz) + if err = enc.Encode(keyPair); err != nil { + return err + } + + err = tx.Set(key, bz.Bytes()) + if err != nil { + return err + } + + return nil + }) + + return err +} + // Remove a KeyPair from the DB given the address -// TODO: Add a check that the user can decrypt this KeyPair -func (keybase *badgerKeybase) Delete(address []byte) error { - err := keybase.db.Update(func(tx *badger.Txn) error { +func (keybase *badgerKeybase) Delete(address []byte, passphrase string) error { + _, err := keybase.GetPrivKey(address, passphrase) + if err != nil { + return err + } + + err = keybase.db.Update(func(tx *badger.Txn) error { tx.Delete(address) return nil }) @@ -215,6 +335,7 @@ func (keybase *badgerKeybase) Delete(address []byte) error { // Remove all keys in the DB // TODO: Add a check that the use can decrypt all the keys +// DISCUSSION: Is this needed? func (keybase *badgerKeybase) ClearAll() error { return keybase.db.DropAll() } From 73ff0424fa3ac656673112c3219abe2a72b55e1d Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sun, 22 Jan 2023 15:04:14 +0000 Subject: [PATCH 04/56] Add more code coverage in tests --- app/client/keybase/keybase_test.go | 215 +++++++++++++++++++++++++++-- 1 file changed, 200 insertions(+), 15 deletions(-) diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go index 30a022a1a..66c3d82ad 100644 --- a/app/client/keybase/keybase_test.go +++ b/app/client/keybase/keybase_test.go @@ -1,27 +1,21 @@ package keybase import ( + "github.com/dgraph-io/badger/v3" "github.com/pokt-network/pocket/shared/crypto" "github.com/stretchr/testify/require" "testing" ) const ( - testPassphrase = "testingtesting123" + testPassphrase = "testingtesting123" + testNewPassphrase = "321gnitsetgnitset" ) var ( - testPrivKeyAddr = "74fa6b8f3a4ec6959a2f86b63d0774af952cdb91" - testPrivKeyBytes = []byte{ - 188, 138, 150, 24, 38, 193, 136, 7, 4, 20, 162, 74, 51, 102, - 213, 188, 192, 27, 60, 71, 20, 14, 104, 116, 80, 84, 6, 134, - 197, 240, 54, 227, 83, 112, 165, 101, 75, 106, 249, 65, 126, - 242, 179, 71, 87, 172, 95, 232, 200, 31, 67, 124, 203, 84, - 178, 160, 14, 79, 38, 79, 6, 71, 43, 236, - } + testKey, _ = createTestKey() ) -// TODO: Implement unarmouring privKey str and test this is correct func TestKeybase_CreateNewKey(t *testing.T) { db, err := initDB() defer db.Stop() @@ -32,30 +26,217 @@ func TestKeybase_CreateNewKey(t *testing.T) { addresses, keypairs, err := db.GetAll() require.NoError(t, err) + require.Equal(t, len(addresses), 1) + require.Equal(t, len(keypairs), 1) addr := addresses[0] kp := keypairs[0] require.Equal(t, len(addr), crypto.AddressLen) - require.Equal(t, addr, kp.PublicKey.Address().Bytes()) + require.Equal(t, addr, kp.GetAddressBytes()) } -// TODO: Implement unarmouring privKey str and test this is correct func TestKeybase_CreateNewKeyFromBytes(t *testing.T) { db, err := initDB() defer db.Stop() require.NoError(t, err) - err = db.CreateFromBytes(testPrivKeyBytes, testPassphrase) + err = db.CreateFromBytes(testKey.Bytes(), testPassphrase) + require.NoError(t, err) + + addresses, keypairs, err := db.GetAll() + require.NoError(t, err) + require.Equal(t, len(addresses), 1) + require.Equal(t, len(keypairs), 1) + + addr := addresses[0] + kp := keypairs[0] + require.Equal(t, len(addr), crypto.AddressLen) + require.Equal(t, addr, kp.GetAddressBytes()) + require.Equal(t, kp.GetAddressString(), testKey.Address().String()) + + privKey, err := kp.Unarmour(testPassphrase) + require.NoError(t, err) + require.Equal(t, privKey.String(), testKey.String()) +} + +func TestKeybase_CreateNewKeyFromString(t *testing.T) { + db, err := initDB() + defer db.Stop() + require.NoError(t, err) + + err = db.CreateFromString(testKey.String(), testPassphrase) require.NoError(t, err) addresses, keypairs, err := db.GetAll() require.NoError(t, err) + require.Equal(t, len(addresses), 1) + require.Equal(t, len(keypairs), 1) addr := addresses[0] kp := keypairs[0] require.Equal(t, len(addr), crypto.AddressLen) - require.Equal(t, addr, kp.PublicKey.Address().Bytes()) - require.Equal(t, kp.PublicKey.Address().String(), testPrivKeyAddr) + require.Equal(t, addr, kp.GetAddressBytes()) + require.Equal(t, kp.GetAddressString(), testKey.Address().String()) + + privKey, err := kp.Unarmour(testPassphrase) + require.NoError(t, err) + require.Equal(t, privKey.String(), testKey.String()) +} + +func TestKeybase_GetKey(t *testing.T) { + db, err := initDB() + defer db.Stop() + require.NoError(t, err) + + err = db.CreateFromString(testKey.String(), testPassphrase) + require.NoError(t, err) + + kp, err := db.Get(testKey.Address().Bytes()) + require.NoError(t, err) + require.Equal(t, testKey.Address().Bytes(), kp.GetAddressBytes()) + require.Equal(t, kp.GetAddressString(), testKey.Address().String()) + + privKey, err := kp.Unarmour(testPassphrase) + require.NoError(t, err) + + equal := privKey.Equals(testKey) + require.Equal(t, equal, true) + require.Equal(t, privKey.String(), testKey.String()) +} + +func TestKeybase_GetAllKeys(t *testing.T) { + db, err := initDB() + defer db.Stop() + require.NoError(t, err) + + pks := make(map[string]crypto.PrivateKey, 0) + for i := 0; i < 5; i++ { + pk, err := createTestKey() + require.NoError(t, err) + err = db.CreateFromString(pk.String(), testPassphrase) + require.NoError(t, err) + pks[pk.Address().String()] = pk + } + + addresses, keypairs, err := db.GetAll() + require.NoError(t, err) + require.Equal(t, len(addresses), 5) + require.Equal(t, len(keypairs), 5) + + for i := 0; i < 5; i++ { + privKey, err := keypairs[i].Unarmour(testPassphrase) + require.NoError(t, err) + + require.Equal(t, addresses[i], keypairs[i].GetAddressBytes()) + require.Equal(t, addresses[i], privKey.Address().Bytes()) + + equal := privKey.Equals(pks[privKey.Address().String()]) + require.Equal(t, equal, true) + } +} + +func TestKeybase_GetPrivKey(t *testing.T) { + db, err := initDB() + defer db.Stop() + require.NoError(t, err) + + err = db.CreateFromString(testKey.String(), testPassphrase) + require.NoError(t, err) + + privKey, err := db.GetPrivKey(testKey.Address().Bytes(), testPassphrase) + require.NoError(t, err) + require.Equal(t, testKey.Address().Bytes(), privKey.Address().Bytes()) + require.Equal(t, privKey.Address().String(), testKey.Address().String()) + + equal := privKey.Equals(testKey) + require.Equal(t, equal, true) + require.Equal(t, privKey.String(), testKey.String()) +} + +func TestKeybase_GetPrivKeyWrongPassphrase(t *testing.T) { + db, err := initDB() + defer db.Stop() + require.NoError(t, err) + + err = db.CreateFromString(testKey.String(), testPassphrase) + require.NoError(t, err) + + privKey, err := db.GetPrivKey(testKey.Address().Bytes(), testNewPassphrase) + require.ErrorIs(t, err, ErrorWrongPassphrase) + require.Nil(t, privKey) +} + +func TestBadgerKeybase_UpdatePassphrase(t *testing.T) { + db, err := initDB() + defer db.Stop() + require.NoError(t, err) + + err = db.CreateFromString(testKey.String(), testPassphrase) + require.NoError(t, err) + + _, err = db.GetPrivKey(testKey.Address().Bytes(), testPassphrase) + require.NoError(t, err) + + err = db.UpdatePassphrase(testKey.Address().Bytes(), testPassphrase, testNewPassphrase) + require.NoError(t, err) + + privKey, err := db.GetPrivKey(testKey.Address().Bytes(), testNewPassphrase) + require.NoError(t, err) + require.Equal(t, testKey.Address().Bytes(), privKey.Address().Bytes()) + require.Equal(t, privKey.Address().String(), testKey.Address().String()) + + equal := privKey.Equals(testKey) + require.Equal(t, equal, true) + require.Equal(t, privKey.String(), testKey.String()) +} + +func TestBadgerKeybase_UpdatePassphraseWrongPassphrase(t *testing.T) { + db, err := initDB() + defer db.Stop() + require.NoError(t, err) + + err = db.CreateFromString(testKey.String(), testPassphrase) + require.NoError(t, err) + + _, err = db.GetPrivKey(testKey.Address().Bytes(), testPassphrase) + require.NoError(t, err) + + err = db.UpdatePassphrase(testKey.Address().Bytes(), testNewPassphrase, testNewPassphrase) + require.ErrorIs(t, err, ErrorWrongPassphrase) +} + +func TestBadgerKeybase_DeleteKey(t *testing.T) { + db, err := initDB() + defer db.Stop() + require.NoError(t, err) + + err = db.CreateFromString(testKey.String(), testPassphrase) + require.NoError(t, err) + + _, err = db.GetPrivKey(testKey.Address().Bytes(), testPassphrase) + require.NoError(t, err) + + err = db.Delete(testKey.Address().Bytes(), testPassphrase) + require.NoError(t, err) + + kp, err := db.Get(testKey.Address().Bytes()) + require.ErrorIs(t, err, badger.ErrKeyNotFound) + require.Equal(t, kp, KeyPair{}) +} + +func TestBadgerKeybase_DeleteKeyWrongPassphrase(t *testing.T) { + db, err := initDB() + defer db.Stop() + require.NoError(t, err) + + err = db.CreateFromString(testKey.String(), testPassphrase) + require.NoError(t, err) + + _, err = db.GetPrivKey(testKey.Address().Bytes(), testPassphrase) + require.NoError(t, err) + + err = db.Delete(testKey.Address().Bytes(), testNewPassphrase) + require.ErrorIs(t, err, ErrorWrongPassphrase) } func initDB() (Keybase, error) { @@ -65,3 +246,7 @@ func initDB() (Keybase, error) { } return db, nil } + +func createTestKey() (crypto.PrivateKey, error) { + return crypto.GeneratePrivateKey() +} From 30904c030e1cf9208404a511544d471b669b1f85 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sun, 22 Jan 2023 15:59:54 +0000 Subject: [PATCH 05/56] Use address string in function calls --- app/client/keybase/keystore.go | 62 +++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index 1404687ff..990490bd4 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -4,9 +4,11 @@ import ( "bytes" "crypto/ed25519" "encoding/gob" + "encoding/hex" "fmt" "github.com/dgraph-io/badger/v3" "github.com/pokt-network/pocket/shared/crypto" + "strings" ) func init() { @@ -32,16 +34,16 @@ type Keybase interface { CreateFromString(privStr, passphrase string) error // Accessors - Get(address []byte) (KeyPair, error) - GetPrivKey(address []byte, passphrase string) (crypto.PrivateKey, error) + Get(address string) (KeyPair, error) + GetPrivKey(address, passphrase string) (crypto.PrivateKey, error) GetAll() (addresses [][]byte, keyPairs []KeyPair, err error) - Exists(key []byte) (bool, error) + Exists(address string) (bool, error) // Updator - UpdatePassphrase(address []byte, oldPassphrase, newPassphrase string) error + UpdatePassphrase(address, oldPassphrase, newPassphrase string) error // Removals - Delete(address []byte, passphrase string) error + Delete(address, passphrase string) error ClearAll() error } @@ -164,13 +166,19 @@ func (keybase *badgerKeybase) CreateFromString(privStr, passphrase string) error } // Returns a KeyPair struct provided the address was found in the DB -func (keybase *badgerKeybase) Get(address []byte) (KeyPair, error) { +func (keybase *badgerKeybase) Get(address string) (KeyPair, error) { var kp KeyPair bz := new(bytes.Buffer) + addrBz, err := hex.DecodeString(address) + if err != nil { + return KeyPair{}, err + } - err := keybase.db.View(func(tx *badger.Txn) error { - item, err := tx.Get(address) - if err != nil { + err = keybase.db.View(func(tx *badger.Txn) error { + item, err := tx.Get(addrBz) + if err != nil && strings.Contains(err.Error(), "Key not found") { + return ErrorAddrNotFound(address) + } else if err != nil { return err } @@ -196,13 +204,19 @@ func (keybase *badgerKeybase) Get(address []byte) (KeyPair, error) { } // Returns a PrivateKey interface provided the address was found in the DB and the passphrase was correct -func (keybase *badgerKeybase) GetPrivKey(address []byte, passphrase string) (crypto.PrivateKey, error) { +func (keybase *badgerKeybase) GetPrivKey(address, passphrase string) (crypto.PrivateKey, error) { var kp KeyPair bz := new(bytes.Buffer) + addrBz, err := hex.DecodeString(address) + if err != nil { + return nil, err + } - err := keybase.db.View(func(tx *badger.Txn) error { - item, err := tx.Get(address) - if err != nil { + err = keybase.db.View(func(tx *badger.Txn) error { + item, err := tx.Get(addrBz) + if err != nil && strings.Contains(err.Error(), "not found") { + return ErrorAddrNotFound(address) + } else if err != nil { return err } @@ -274,15 +288,15 @@ func (keybase *badgerKeybase) GetAll() (addresses [][]byte, keyPairs []KeyPair, } // Check whether an address is currently stored in the DB -func (keybase *badgerKeybase) Exists(address []byte) (bool, error) { +func (keybase *badgerKeybase) Exists(address string) (bool, error) { val, err := keybase.Get(address) if err != nil { return false, err } - return val == KeyPair{}, nil + return val != KeyPair{}, nil } -func (keybase *badgerKeybase) UpdatePassphrase(address []byte, oldPassphrase, newPassphrase string) error { +func (keybase *badgerKeybase) UpdatePassphrase(address, oldPassphrase, newPassphrase string) error { // Check the oldPassphrase is correct privKey, err := keybase.GetPrivKey(address, oldPassphrase) if err != nil { @@ -290,6 +304,11 @@ func (keybase *badgerKeybase) UpdatePassphrase(address []byte, oldPassphrase, ne } privStr := privKey.String() + addrBz, err := hex.DecodeString(address) + if err != nil { + return err + } + err = keybase.db.Update(func(tx *badger.Txn) error { keyPair, err := CreateNewKeyFromString(privStr, newPassphrase) if err != nil { @@ -298,7 +317,7 @@ func (keybase *badgerKeybase) UpdatePassphrase(address []byte, oldPassphrase, ne // Use key address as key in DB key := keyPair.GetAddressBytes() - if bytes.Compare(key, address) != 0 { + if bytes.Compare(key, addrBz) != 0 { return fmt.Errorf("Key address does not match previous address.") } // Encode entire KeyPair struct into []byte for value @@ -320,14 +339,19 @@ func (keybase *badgerKeybase) UpdatePassphrase(address []byte, oldPassphrase, ne } // Remove a KeyPair from the DB given the address -func (keybase *badgerKeybase) Delete(address []byte, passphrase string) error { +func (keybase *badgerKeybase) Delete(address, passphrase string) error { _, err := keybase.GetPrivKey(address, passphrase) if err != nil { return err } + addrBz, err := hex.DecodeString(address) + if err != nil { + return err + } + err = keybase.db.Update(func(tx *badger.Txn) error { - tx.Delete(address) + tx.Delete(addrBz) return nil }) return err From ae4cbc102c59733ed0ccb1dbebf166eb46cdd6f1 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sun, 22 Jan 2023 16:00:56 +0000 Subject: [PATCH 06/56] Use error function for address not found --- app/client/keybase/keys.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/client/keybase/keys.go b/app/client/keybase/keys.go index ffab6c031..b996b3f1a 100644 --- a/app/client/keybase/keys.go +++ b/app/client/keybase/keys.go @@ -9,6 +9,7 @@ import ( "encoding/base64" "encoding/gob" "encoding/hex" + "errors" "fmt" poktCrypto "github.com/pokt-network/pocket/shared/crypto" "golang.org/x/crypto/scrypt" @@ -25,9 +26,13 @@ const ( var ( // Errors - ErrorWrongPassphrase = fmt.Errorf("Can't decrypt private key: wrong passphrase") + ErrorWrongPassphrase = errors.New("Can't decrypt private key: wrong passphrase") ) +func ErrorAddrNotFound(addr string) error { + return fmt.Errorf("No key found with address: %s", addr) +} + func init() { gob.Register(poktCrypto.Ed25519PublicKey{}) gob.Register(ed25519.PublicKey{}) From 2ccb0a7987e7e567e1d21dcf1ead83620e25a839 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sun, 22 Jan 2023 16:01:24 +0000 Subject: [PATCH 07/56] Update to use address string in function calls, check errors properly --- app/client/keybase/keybase_test.go | 79 ++++++++++++++++++++++++------ 1 file changed, 63 insertions(+), 16 deletions(-) diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go index 66c3d82ad..36d9e91f5 100644 --- a/app/client/keybase/keybase_test.go +++ b/app/client/keybase/keybase_test.go @@ -1,7 +1,7 @@ package keybase import ( - "github.com/dgraph-io/badger/v3" + "encoding/hex" "github.com/pokt-network/pocket/shared/crypto" "github.com/stretchr/testify/require" "testing" @@ -83,6 +83,20 @@ func TestKeybase_CreateNewKeyFromString(t *testing.T) { require.Equal(t, privKey.String(), testKey.String()) } +// TODO: Improve this test/create functions to check string validity +func TestKeybase_CreateNewKeyFromStringInvalidString(t *testing.T) { + db, err := initDB() + defer db.Stop() + require.NoError(t, err) + + falseAddr := testKey.String() + "aa" + falseBz, err := hex.DecodeString(falseAddr) + require.NoError(t, err) + + err = db.CreateFromString(falseAddr, testPassphrase) + require.EqualError(t, err, crypto.ErrInvalidPrivateKeyLen(len(falseBz)).Error()) +} + func TestKeybase_GetKey(t *testing.T) { db, err := initDB() defer db.Stop() @@ -91,7 +105,7 @@ func TestKeybase_GetKey(t *testing.T) { err = db.CreateFromString(testKey.String(), testPassphrase) require.NoError(t, err) - kp, err := db.Get(testKey.Address().Bytes()) + kp, err := db.Get(testKey.Address().String()) require.NoError(t, err) require.Equal(t, testKey.Address().Bytes(), kp.GetAddressBytes()) require.Equal(t, kp.GetAddressString(), testKey.Address().String()) @@ -104,6 +118,39 @@ func TestKeybase_GetKey(t *testing.T) { require.Equal(t, privKey.String(), testKey.String()) } +func TestKeybase_GetKeyDoesntExist(t *testing.T) { + db, err := initDB() + defer db.Stop() + require.NoError(t, err) + + kp, err := db.Get(testKey.Address().String()) + require.EqualError(t, err, ErrorAddrNotFound(testKey.Address().String()).Error()) + require.Equal(t, kp, KeyPair{}) +} + +func TestKeybase_CheckKeyExists(t *testing.T) { + db, err := initDB() + defer db.Stop() + require.NoError(t, err) + + err = db.CreateFromString(testKey.String(), testPassphrase) + require.NoError(t, err) + + exists, err := db.Exists(testKey.Address().String()) + require.NoError(t, err) + require.Equal(t, exists, true) +} + +func TestKeybase_CheckKeyExistsDoesntExist(t *testing.T) { + db, err := initDB() + defer db.Stop() + require.NoError(t, err) + + exists, err := db.Exists(testKey.Address().String()) + require.EqualError(t, err, ErrorAddrNotFound(testKey.Address().String()).Error()) + require.Equal(t, exists, false) +} + func TestKeybase_GetAllKeys(t *testing.T) { db, err := initDB() defer db.Stop() @@ -143,7 +190,7 @@ func TestKeybase_GetPrivKey(t *testing.T) { err = db.CreateFromString(testKey.String(), testPassphrase) require.NoError(t, err) - privKey, err := db.GetPrivKey(testKey.Address().Bytes(), testPassphrase) + privKey, err := db.GetPrivKey(testKey.Address().String(), testPassphrase) require.NoError(t, err) require.Equal(t, testKey.Address().Bytes(), privKey.Address().Bytes()) require.Equal(t, privKey.Address().String(), testKey.Address().String()) @@ -161,8 +208,8 @@ func TestKeybase_GetPrivKeyWrongPassphrase(t *testing.T) { err = db.CreateFromString(testKey.String(), testPassphrase) require.NoError(t, err) - privKey, err := db.GetPrivKey(testKey.Address().Bytes(), testNewPassphrase) - require.ErrorIs(t, err, ErrorWrongPassphrase) + privKey, err := db.GetPrivKey(testKey.Address().String(), testNewPassphrase) + require.Equal(t, err, ErrorWrongPassphrase) require.Nil(t, privKey) } @@ -174,13 +221,13 @@ func TestBadgerKeybase_UpdatePassphrase(t *testing.T) { err = db.CreateFromString(testKey.String(), testPassphrase) require.NoError(t, err) - _, err = db.GetPrivKey(testKey.Address().Bytes(), testPassphrase) + _, err = db.GetPrivKey(testKey.Address().String(), testPassphrase) require.NoError(t, err) - err = db.UpdatePassphrase(testKey.Address().Bytes(), testPassphrase, testNewPassphrase) + err = db.UpdatePassphrase(testKey.Address().String(), testPassphrase, testNewPassphrase) require.NoError(t, err) - privKey, err := db.GetPrivKey(testKey.Address().Bytes(), testNewPassphrase) + privKey, err := db.GetPrivKey(testKey.Address().String(), testNewPassphrase) require.NoError(t, err) require.Equal(t, testKey.Address().Bytes(), privKey.Address().Bytes()) require.Equal(t, privKey.Address().String(), testKey.Address().String()) @@ -198,10 +245,10 @@ func TestBadgerKeybase_UpdatePassphraseWrongPassphrase(t *testing.T) { err = db.CreateFromString(testKey.String(), testPassphrase) require.NoError(t, err) - _, err = db.GetPrivKey(testKey.Address().Bytes(), testPassphrase) + _, err = db.GetPrivKey(testKey.Address().String(), testPassphrase) require.NoError(t, err) - err = db.UpdatePassphrase(testKey.Address().Bytes(), testNewPassphrase, testNewPassphrase) + err = db.UpdatePassphrase(testKey.Address().String(), testNewPassphrase, testNewPassphrase) require.ErrorIs(t, err, ErrorWrongPassphrase) } @@ -213,14 +260,14 @@ func TestBadgerKeybase_DeleteKey(t *testing.T) { err = db.CreateFromString(testKey.String(), testPassphrase) require.NoError(t, err) - _, err = db.GetPrivKey(testKey.Address().Bytes(), testPassphrase) + _, err = db.GetPrivKey(testKey.Address().String(), testPassphrase) require.NoError(t, err) - err = db.Delete(testKey.Address().Bytes(), testPassphrase) + err = db.Delete(testKey.Address().String(), testPassphrase) require.NoError(t, err) - kp, err := db.Get(testKey.Address().Bytes()) - require.ErrorIs(t, err, badger.ErrKeyNotFound) + kp, err := db.Get(testKey.Address().String()) + require.EqualError(t, err, ErrorAddrNotFound(testKey.Address().String()).Error()) require.Equal(t, kp, KeyPair{}) } @@ -232,10 +279,10 @@ func TestBadgerKeybase_DeleteKeyWrongPassphrase(t *testing.T) { err = db.CreateFromString(testKey.String(), testPassphrase) require.NoError(t, err) - _, err = db.GetPrivKey(testKey.Address().Bytes(), testPassphrase) + _, err = db.GetPrivKey(testKey.Address().String(), testPassphrase) require.NoError(t, err) - err = db.Delete(testKey.Address().Bytes(), testNewPassphrase) + err = db.Delete(testKey.Address().String(), testNewPassphrase) require.ErrorIs(t, err, ErrorWrongPassphrase) } From 0262c1c0b0e75b4d30465f21719c680cc2501c28 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sun, 22 Jan 2023 16:44:07 +0000 Subject: [PATCH 08/56] Fix test names to be consistent --- app/client/keybase/keybase_test.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go index 36d9e91f5..7126481b2 100644 --- a/app/client/keybase/keybase_test.go +++ b/app/client/keybase/keybase_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 ( @@ -213,7 +214,7 @@ func TestKeybase_GetPrivKeyWrongPassphrase(t *testing.T) { require.Nil(t, privKey) } -func TestBadgerKeybase_UpdatePassphrase(t *testing.T) { +func TestKeybase_UpdatePassphrase(t *testing.T) { db, err := initDB() defer db.Stop() require.NoError(t, err) @@ -237,7 +238,7 @@ func TestBadgerKeybase_UpdatePassphrase(t *testing.T) { require.Equal(t, privKey.String(), testKey.String()) } -func TestBadgerKeybase_UpdatePassphraseWrongPassphrase(t *testing.T) { +func TestKeybase_UpdatePassphraseWrongPassphrase(t *testing.T) { db, err := initDB() defer db.Stop() require.NoError(t, err) @@ -252,7 +253,7 @@ func TestBadgerKeybase_UpdatePassphraseWrongPassphrase(t *testing.T) { require.ErrorIs(t, err, ErrorWrongPassphrase) } -func TestBadgerKeybase_DeleteKey(t *testing.T) { +func TestKeybase_DeleteKey(t *testing.T) { db, err := initDB() defer db.Stop() require.NoError(t, err) @@ -271,7 +272,7 @@ func TestBadgerKeybase_DeleteKey(t *testing.T) { require.Equal(t, kp, KeyPair{}) } -func TestBadgerKeybase_DeleteKeyWrongPassphrase(t *testing.T) { +func TestKeybase_DeleteKeyWrongPassphrase(t *testing.T) { db, err := initDB() defer db.Stop() require.NoError(t, err) From fc52ee351496532f49833ce85ced91dc07d3983a Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sun, 22 Jan 2023 20:54:33 +0000 Subject: [PATCH 09/56] Reorder imports --- app/client/keybase/keys.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/client/keybase/keys.go b/app/client/keybase/keys.go index b996b3f1a..aec67d2e1 100644 --- a/app/client/keybase/keys.go +++ b/app/client/keybase/keys.go @@ -11,9 +11,10 @@ import ( "encoding/hex" "errors" "fmt" + "strings" + poktCrypto "github.com/pokt-network/pocket/shared/crypto" "golang.org/x/crypto/scrypt" - "strings" ) const ( From e7b1faf00649202de163da6c09c910a1c89bc2fb Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sun, 22 Jan 2023 22:44:13 +0000 Subject: [PATCH 10/56] Remove create from bytes --- app/client/keybase/keys.go | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/app/client/keybase/keys.go b/app/client/keybase/keys.go index aec67d2e1..c36069a04 100644 --- a/app/client/keybase/keys.go +++ b/app/client/keybase/keys.go @@ -11,6 +11,7 @@ import ( "encoding/hex" "errors" "fmt" + "strconv" "strings" poktCrypto "github.com/pokt-network/pocket/shared/crypto" @@ -23,6 +24,8 @@ const ( r = 8 p = 1 klen = 32 + // Sec param + secParam = 12 ) var ( @@ -74,14 +77,18 @@ func (kp KeyPair) Unarmour(passphrase string) (poktCrypto.PrivateKey, error) { type ArmouredKey struct { Kdf string Salt string + SecParam string + Hint string CipherText string } // Generate new armoured private key struct with parameters for unarmouring -func NewArmouredKey(kdf, salt, cipher string) ArmouredKey { +func NewArmouredKey(kdf, salt, hint, cipher string) ArmouredKey { return ArmouredKey{ Kdf: kdf, Salt: salt, + SecParam: strconv.Itoa(secParam), + Hint: hint, CipherText: cipher, } } @@ -105,28 +112,7 @@ func CreateNewKey(passphrase string) (KeyPair, error) { return keyPair, nil } -// Generate new private ED25519 key from the bytes provided, encrypt and armour it as a string -// Returns a KeyPair struct of the Public Key and Armoured String -// DISCUSSION: Is this needed? -func CreateNewKeyFromBytes(privBytes []byte, passphrase string) (KeyPair, error) { - privKey, err := poktCrypto.NewPrivateKeyFromBytes(privBytes) - if err != nil { - return KeyPair{}, err - } - - privArmour, err := encryptArmourPrivKey(privKey, passphrase) - if err != nil || privArmour == "" { - return KeyPair{}, nil - } - - pubKey := privKey.PublicKey() - keyPair := NewKeyPair(pubKey, privArmour) - - return keyPair, nil -} - -// Generate new private ED25519 key from the hex string provided, encrypt and armour it as a string -// Returns a KeyPair struct of the Public Key and Armoured String +// Generate new KeyPair from the hex string provided, encrypt and armour it as a string func CreateNewKeyFromString(privStr, passphrase string) (KeyPair, error) { privKey, err := poktCrypto.NewPrivateKey(privStr) if err != nil { @@ -157,7 +143,8 @@ func encryptArmourPrivKey(privKey poktCrypto.PrivateKey, passphrase string) (str armourStr := base64.StdEncoding.EncodeToString(encBytes) // Create ArmouredKey object so can unarmour later - armoured := NewArmouredKey("scrypt", fmt.Sprintf("%X", saltBytes), armourStr) + armoured := NewArmouredKey("scrypt", fmt.Sprintf("%X", saltBytes), "", armourStr) + //fmt.Println(armoured.CipherText) // Encode armoured struct into []byte var bz bytes.Buffer From e180b0dc727e81871d6831cfc390c53676b87f88 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sun, 22 Jan 2023 22:44:44 +0000 Subject: [PATCH 11/56] Remove create from bytes and clear all, add signature and verification support --- app/client/keybase/keystore.go | 66 +++++++++++++--------------------- 1 file changed, 25 insertions(+), 41 deletions(-) diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index 990490bd4..0caebb1cb 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -6,9 +6,10 @@ import ( "encoding/gob" "encoding/hex" "fmt" + "strings" + "github.com/dgraph-io/badger/v3" "github.com/pokt-network/pocket/shared/crypto" - "strings" ) func init() { @@ -28,8 +29,6 @@ type Keybase interface { // Create new keypair entry in DB Create(passphrase string) error - // Insert new keypair from private key []byte provided into the DB - CreateFromBytes(privBytes []byte, passphrase string) error // Insert a new keypair from the private key hex string provided into the DB CreateFromString(privStr, passphrase string) error @@ -42,9 +41,12 @@ type Keybase interface { // Updator UpdatePassphrase(address, oldPassphrase, newPassphrase string) error + // Sign Messages + Sign(address, passphrase string, msg []byte) ([]byte, error) + Verify(address string, msg, sig []byte) (bool, error) + // Removals Delete(address, passphrase string) error - ClearAll() error } // badgerKeybase implements the Keybase struct using the BadgerDB backend @@ -106,36 +108,6 @@ func (keybase *badgerKeybase) Create(passphrase string) error { return err } -// Crate a new KeyPair from the []byte provided and store it in the DB by encoding the KeyPair struct into a []byte -// Using the PublicKey.Address() return value as the key for storage -// DISCUSSION: Is this needed? -func (keybase *badgerKeybase) CreateFromBytes(privBytes []byte, passphrase string) error { - err := keybase.db.Update(func(tx *badger.Txn) error { - keyPair, err := CreateNewKeyFromBytes(privBytes, passphrase) - if err != nil { - return err - } - - // Use key address as key in DB - key := keyPair.GetAddressBytes() - // Encode entire KeyPair struct into []byte for value - bz := new(bytes.Buffer) - enc := gob.NewEncoder(bz) - if err = enc.Encode(keyPair); err != nil { - return err - } - - err = tx.Set(key, bz.Bytes()) - if err != nil { - return err - } - - return nil - }) - - return err -} - // Crate a new KeyPair from the private key hex string and store it in the DB by encoding the KeyPair struct into a []byte // Using the PublicKey.Address() return value as the key for storage func (keybase *badgerKeybase) CreateFromString(privStr, passphrase string) error { @@ -338,6 +310,25 @@ func (keybase *badgerKeybase) UpdatePassphrase(address, oldPassphrase, newPassph return err } +// Sign 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 { + return nil, err + } + return privKey.Sign(msg) +} + +// Verify 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 { + return false, err + } + pubKey := kp.PublicKey + return pubKey.Verify(msg, sig), nil +} + // Remove a KeyPair from the DB given the address func (keybase *badgerKeybase) Delete(address, passphrase string) error { _, err := keybase.GetPrivKey(address, passphrase) @@ -357,13 +348,6 @@ func (keybase *badgerKeybase) Delete(address, passphrase string) error { return err } -// Remove all keys in the DB -// TODO: Add a check that the use can decrypt all the keys -// DISCUSSION: Is this needed? -func (keybase *badgerKeybase) ClearAll() error { - return keybase.db.DropAll() -} - // Return badger.Options for the given DB path - disable logging func badgerOptions(path string) badger.Options { opts := badger.DefaultOptions(path) From f169b688cd48d77fdbc8d2731e69924063c9b2bf Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sun, 22 Jan 2023 22:44:57 +0000 Subject: [PATCH 12/56] Add signature tests --- app/client/keybase/keybase_test.go | 83 +++++++++++++++++++++--------- 1 file changed, 58 insertions(+), 25 deletions(-) diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go index 7126481b2..bc31ac9f7 100644 --- a/app/client/keybase/keybase_test.go +++ b/app/client/keybase/keybase_test.go @@ -9,8 +9,10 @@ import ( ) const ( - testPassphrase = "testingtesting123" + testPrivString = "5ff9e3522eecfd3ccd42e187537bca9f2b9ac7f35d7074573e789ed7ec49870d4479e7524be67bb1f435836afbef4592bfc75afbf52e51495f9ac6d141ddbc02" + testPassphrase = "Testing@Testing123" testNewPassphrase = "321gnitsetgnitset" + testTx = "79fca587bbcfd5da86d73e1d849769017b1c91cc8177dec0fc0e3e0d345f2b35" ) var ( @@ -36,30 +38,6 @@ func TestKeybase_CreateNewKey(t *testing.T) { require.Equal(t, addr, kp.GetAddressBytes()) } -func TestKeybase_CreateNewKeyFromBytes(t *testing.T) { - db, err := initDB() - defer db.Stop() - require.NoError(t, err) - - err = db.CreateFromBytes(testKey.Bytes(), testPassphrase) - require.NoError(t, err) - - addresses, keypairs, err := db.GetAll() - require.NoError(t, err) - require.Equal(t, len(addresses), 1) - require.Equal(t, len(keypairs), 1) - - addr := addresses[0] - kp := keypairs[0] - require.Equal(t, len(addr), crypto.AddressLen) - require.Equal(t, addr, kp.GetAddressBytes()) - require.Equal(t, kp.GetAddressString(), testKey.Address().String()) - - privKey, err := kp.Unarmour(testPassphrase) - require.NoError(t, err) - require.Equal(t, privKey.String(), testKey.String()) -} - func TestKeybase_CreateNewKeyFromString(t *testing.T) { db, err := initDB() defer db.Stop() @@ -287,6 +265,53 @@ func TestKeybase_DeleteKeyWrongPassphrase(t *testing.T) { require.ErrorIs(t, err, ErrorWrongPassphrase) } +func TestKeybase_SignMessage(t *testing.T) { + db, err := initDB() + defer db.Stop() + require.NoError(t, err) + + pk, err := createTestKeyFromString(testPrivString) + require.NoError(t, err) + + err = db.CreateFromString(testPrivString, testPassphrase) + require.NoError(t, err) + + privKey, err := db.GetPrivKey(pk.Address().String(), testPassphrase) + require.NoError(t, err) + + txBz, err := hex.DecodeString(testTx) + require.NoError(t, err) + + signedMsg, err := db.Sign(privKey.Address().String(), testPassphrase, txBz) + require.NoError(t, err) + + verified, err := db.Verify(privKey.Address().String(), txBz, signedMsg) + require.NoError(t, err) + require.Equal(t, verified, true) +} + +func TestKeybase_SignMessageWrongPassphrase(t *testing.T) { + db, err := initDB() + defer db.Stop() + require.NoError(t, err) + + pk, err := createTestKeyFromString(testPrivString) + require.NoError(t, err) + + err = db.CreateFromString(testPrivString, testPassphrase) + require.NoError(t, err) + + privKey, err := db.GetPrivKey(pk.Address().String(), testPassphrase) + require.NoError(t, err) + + txBz, err := hex.DecodeString(testTx) + require.NoError(t, err) + + signedMsg, err := db.Sign(privKey.Address().String(), testNewPassphrase, txBz) + require.ErrorIs(t, err, ErrorWrongPassphrase) + require.Nil(t, signedMsg) +} + func initDB() (Keybase, error) { db, err := NewKeybaseInMemory("") if err != nil { @@ -298,3 +323,11 @@ func initDB() (Keybase, error) { func createTestKey() (crypto.PrivateKey, error) { return crypto.GeneratePrivateKey() } + +func createTestKeyFromString(str string) (crypto.PrivateKey, error) { + bz, err := hex.DecodeString(str) + if err != nil { + return nil, err + } + return crypto.NewPrivateKeyFromBytes(bz) +} From 2287a2e6e89c6bf45a2e6933700f7e988a9c1db4 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Mon, 23 Jan 2023 09:31:21 +0000 Subject: [PATCH 13/56] Rename CreateFromString to ImportFromString, add GetPubKey commented out --- app/client/keybase/keybase_test.go | 30 +++++++++++++++--------------- app/client/keybase/keystore.go | 5 +++-- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go index bc31ac9f7..16c131c7a 100644 --- a/app/client/keybase/keybase_test.go +++ b/app/client/keybase/keybase_test.go @@ -38,12 +38,12 @@ func TestKeybase_CreateNewKey(t *testing.T) { require.Equal(t, addr, kp.GetAddressBytes()) } -func TestKeybase_CreateNewKeyFromString(t *testing.T) { +func TestKeybase_ImportKeyFromString(t *testing.T) { db, err := initDB() defer db.Stop() require.NoError(t, err) - err = db.CreateFromString(testKey.String(), testPassphrase) + err = db.ImportFromString(testKey.String(), testPassphrase) require.NoError(t, err) addresses, keypairs, err := db.GetAll() @@ -63,7 +63,7 @@ func TestKeybase_CreateNewKeyFromString(t *testing.T) { } // TODO: Improve this test/create functions to check string validity -func TestKeybase_CreateNewKeyFromStringInvalidString(t *testing.T) { +func TestKeybase_ImportKeyFromStringInvalidString(t *testing.T) { db, err := initDB() defer db.Stop() require.NoError(t, err) @@ -72,7 +72,7 @@ func TestKeybase_CreateNewKeyFromStringInvalidString(t *testing.T) { falseBz, err := hex.DecodeString(falseAddr) require.NoError(t, err) - err = db.CreateFromString(falseAddr, testPassphrase) + err = db.ImportFromString(falseAddr, testPassphrase) require.EqualError(t, err, crypto.ErrInvalidPrivateKeyLen(len(falseBz)).Error()) } @@ -81,7 +81,7 @@ func TestKeybase_GetKey(t *testing.T) { defer db.Stop() require.NoError(t, err) - err = db.CreateFromString(testKey.String(), testPassphrase) + err = db.ImportFromString(testKey.String(), testPassphrase) require.NoError(t, err) kp, err := db.Get(testKey.Address().String()) @@ -112,7 +112,7 @@ func TestKeybase_CheckKeyExists(t *testing.T) { defer db.Stop() require.NoError(t, err) - err = db.CreateFromString(testKey.String(), testPassphrase) + err = db.ImportFromString(testKey.String(), testPassphrase) require.NoError(t, err) exists, err := db.Exists(testKey.Address().String()) @@ -139,7 +139,7 @@ func TestKeybase_GetAllKeys(t *testing.T) { for i := 0; i < 5; i++ { pk, err := createTestKey() require.NoError(t, err) - err = db.CreateFromString(pk.String(), testPassphrase) + err = db.ImportFromString(pk.String(), testPassphrase) require.NoError(t, err) pks[pk.Address().String()] = pk } @@ -166,7 +166,7 @@ func TestKeybase_GetPrivKey(t *testing.T) { defer db.Stop() require.NoError(t, err) - err = db.CreateFromString(testKey.String(), testPassphrase) + err = db.ImportFromString(testKey.String(), testPassphrase) require.NoError(t, err) privKey, err := db.GetPrivKey(testKey.Address().String(), testPassphrase) @@ -184,7 +184,7 @@ func TestKeybase_GetPrivKeyWrongPassphrase(t *testing.T) { defer db.Stop() require.NoError(t, err) - err = db.CreateFromString(testKey.String(), testPassphrase) + err = db.ImportFromString(testKey.String(), testPassphrase) require.NoError(t, err) privKey, err := db.GetPrivKey(testKey.Address().String(), testNewPassphrase) @@ -197,7 +197,7 @@ func TestKeybase_UpdatePassphrase(t *testing.T) { defer db.Stop() require.NoError(t, err) - err = db.CreateFromString(testKey.String(), testPassphrase) + err = db.ImportFromString(testKey.String(), testPassphrase) require.NoError(t, err) _, err = db.GetPrivKey(testKey.Address().String(), testPassphrase) @@ -221,7 +221,7 @@ func TestKeybase_UpdatePassphraseWrongPassphrase(t *testing.T) { defer db.Stop() require.NoError(t, err) - err = db.CreateFromString(testKey.String(), testPassphrase) + err = db.ImportFromString(testKey.String(), testPassphrase) require.NoError(t, err) _, err = db.GetPrivKey(testKey.Address().String(), testPassphrase) @@ -236,7 +236,7 @@ func TestKeybase_DeleteKey(t *testing.T) { defer db.Stop() require.NoError(t, err) - err = db.CreateFromString(testKey.String(), testPassphrase) + err = db.ImportFromString(testKey.String(), testPassphrase) require.NoError(t, err) _, err = db.GetPrivKey(testKey.Address().String(), testPassphrase) @@ -255,7 +255,7 @@ func TestKeybase_DeleteKeyWrongPassphrase(t *testing.T) { defer db.Stop() require.NoError(t, err) - err = db.CreateFromString(testKey.String(), testPassphrase) + err = db.ImportFromString(testKey.String(), testPassphrase) require.NoError(t, err) _, err = db.GetPrivKey(testKey.Address().String(), testPassphrase) @@ -273,7 +273,7 @@ func TestKeybase_SignMessage(t *testing.T) { pk, err := createTestKeyFromString(testPrivString) require.NoError(t, err) - err = db.CreateFromString(testPrivString, testPassphrase) + err = db.ImportFromString(testPrivString, testPassphrase) require.NoError(t, err) privKey, err := db.GetPrivKey(pk.Address().String(), testPassphrase) @@ -298,7 +298,7 @@ func TestKeybase_SignMessageWrongPassphrase(t *testing.T) { pk, err := createTestKeyFromString(testPrivString) require.NoError(t, err) - err = db.CreateFromString(testPrivString, testPassphrase) + err = db.ImportFromString(testPrivString, testPassphrase) require.NoError(t, err) privKey, err := db.GetPrivKey(pk.Address().String(), testPassphrase) diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index 0caebb1cb..bcd4142c1 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -30,10 +30,11 @@ type Keybase interface { // Create new keypair entry in DB Create(passphrase string) error // Insert a new keypair from the private key hex string provided into the DB - CreateFromString(privStr, passphrase string) error + ImportFromString(privStr, passphrase string) error // Accessors Get(address string) (KeyPair, error) + //GetPubKey(address string) (crypto.PublicKey, error) GetPrivKey(address, passphrase string) (crypto.PrivateKey, error) GetAll() (addresses [][]byte, keyPairs []KeyPair, err error) Exists(address string) (bool, error) @@ -110,7 +111,7 @@ func (keybase *badgerKeybase) Create(passphrase string) error { // Crate a new KeyPair from the private key hex string and store it in the DB by encoding the KeyPair struct into a []byte // Using the PublicKey.Address() return value as the key for storage -func (keybase *badgerKeybase) CreateFromString(privStr, passphrase string) error { +func (keybase *badgerKeybase) ImportFromString(privStr, passphrase string) error { err := keybase.db.Update(func(tx *badger.Txn) error { keyPair, err := CreateNewKeyFromString(privStr, passphrase) if err != nil { From eae9f407d60cc9f4adb3233d5c623ded2c8cd2c3 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Mon, 23 Jan 2023 09:49:20 +0000 Subject: [PATCH 14/56] Add GetPubKey function and tests --- app/client/keybase/keybase_test.go | 17 +++++++++++++ app/client/keybase/keystore.go | 38 +++++++++--------------------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go index 16c131c7a..697ec4dd1 100644 --- a/app/client/keybase/keybase_test.go +++ b/app/client/keybase/keybase_test.go @@ -161,6 +161,23 @@ func TestKeybase_GetAllKeys(t *testing.T) { } } +func TestKeybase_GetPubKey(t *testing.T) { + db, err := initDB() + defer db.Stop() + require.NoError(t, err) + + err = db.ImportFromString(testKey.String(), testPassphrase) + require.NoError(t, err) + + pubKey, err := db.GetPubKey(testKey.Address().String()) + require.NoError(t, err) + require.Equal(t, testKey.Address().Bytes(), pubKey.Address().Bytes()) + require.Equal(t, pubKey.Address().String(), testKey.Address().String()) + + equal := pubKey.Equals(testKey.PublicKey()) + require.Equal(t, equal, true) +} + func TestKeybase_GetPrivKey(t *testing.T) { db, err := initDB() defer db.Stop() diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index bcd4142c1..d4299ac19 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -31,10 +31,12 @@ type Keybase interface { Create(passphrase string) error // Insert a new keypair from the private key hex string provided into the DB ImportFromString(privStr, passphrase string) error + // Insert a new keypair from the JSON string of the encrypted private key into the DB + //ImportFromJSON(jsonStr, passphrase string) error // Accessors Get(address string) (KeyPair, error) - //GetPubKey(address string) (crypto.PublicKey, error) + GetPubKey(address string) (crypto.PublicKey, error) GetPrivKey(address, passphrase string) (crypto.PrivateKey, error) GetAll() (addresses [][]byte, keyPairs []KeyPair, err error) Exists(address string) (bool, error) @@ -176,37 +178,19 @@ func (keybase *badgerKeybase) Get(address string) (KeyPair, error) { return kp, nil } -// 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) { - var kp KeyPair - bz := new(bytes.Buffer) - addrBz, err := hex.DecodeString(address) +// 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 { return nil, err } - err = keybase.db.View(func(tx *badger.Txn) error { - item, err := tx.Get(addrBz) - if err != nil && strings.Contains(err.Error(), "not found") { - return ErrorAddrNotFound(address) - } else if err != nil { - return err - } - - value, err := item.ValueCopy(nil) - if err != nil { - return err - } - - // Decode []byte value back into KeyPair struct - bz.Write(value) - dec := gob.NewDecoder(bz) - if err = dec.Decode(&kp); err != nil { - return err - } + return kp.PublicKey, nil +} - return nil - }) +// 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 { return nil, err } From 182222516ebbb90f5fe4227fdaadf904b2a672cb Mon Sep 17 00:00:00 2001 From: Harry Law Date: Mon, 23 Jan 2023 09:49:37 +0000 Subject: [PATCH 15/56] Add JSON tags to structs --- app/client/keybase/keys.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/client/keybase/keys.go b/app/client/keybase/keys.go index c36069a04..d57fc9a60 100644 --- a/app/client/keybase/keys.go +++ b/app/client/keybase/keys.go @@ -46,8 +46,8 @@ func init() { // KeyPair struct stores the public key and the passphrase encrypted private key type KeyPair struct { - PublicKey poktCrypto.PublicKey - PrivKeyArmour string + PublicKey poktCrypto.PublicKey `json:"pubkey"` + PrivKeyArmour string `json:"privkey.armor"` } // Generate a new KeyPair struct given the public key and armoured private key @@ -75,11 +75,11 @@ func (kp KeyPair) Unarmour(passphrase string) (poktCrypto.PrivateKey, error) { // Armoured Private Key struct with fields to unarmour it later type ArmouredKey struct { - Kdf string - Salt string - SecParam string - Hint string - CipherText string + Kdf string `json:"kdf"` + Salt string `json:"salt"` + SecParam string `json:"secparam"` + Hint string `json:"hint"` + CipherText string `json:"ciphertext"` } // Generate new armoured private key struct with parameters for unarmouring From 224b82d32a6b6cd487e36304264965c817aa01c9 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Mon, 23 Jan 2023 13:42:40 +0000 Subject: [PATCH 16/56] Add test account hardcoded details, add import from JSON test and export tests --- app/client/keybase/keybase_test.go | 80 ++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 5 deletions(-) diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go index 697ec4dd1..93eb2cff5 100644 --- a/app/client/keybase/keybase_test.go +++ b/app/client/keybase/keybase_test.go @@ -9,10 +9,17 @@ import ( ) const ( - testPrivString = "5ff9e3522eecfd3ccd42e187537bca9f2b9ac7f35d7074573e789ed7ec49870d4479e7524be67bb1f435836afbef4592bfc75afbf52e51495f9ac6d141ddbc02" - testPassphrase = "Testing@Testing123" + // Example account + testPrivString = "045e8380086abc6f6e941d6fe47ca93b86723bc246ec8c4beee411b410028675ed78c49592f836f7a4d47d4fb6a0e6b19f07aebc201d005f6b2c6afe389086e9" + testPubString = "ed78c49592f836f7a4d47d4fb6a0e6b19f07aebc201d005f6b2c6afe389086e9" + testAddr = "26e16ccab7a898400022476332e2972b8199f2f9" + testJSON = `{"kdf":"scrypt","salt":"AD165E014A42709A7EBF03594867C970","secparam":"12","hint":"","ciphertext":"BJ49gRklmHmnZP9cMBfExmpbQ7aGxyVgq/6ItDqbY5gFoses2+g3+aaHdgFv2PIWIAEojfqX3k1A6tBWF47Yw3OJ+vaTzje8sN/roKFJop4="}` + testPassphrase = "Testing@Testing123" + + // Other testNewPassphrase = "321gnitsetgnitset" testTx = "79fca587bbcfd5da86d73e1d849769017b1c91cc8177dec0fc0e3e0d345f2b35" + //testJSONString = `{"kdf":"scrypt","salt":"6abd9619621dc8c2a62860c05dea6df3","secparam":"12","hint":"pocket wallet","ciphertext":"SZHQyzlYHke9g/7u/x1pCMPpA7wdeJSDjUN+9Oe5W0ep+y5enf2r2qVUnEgsZiQLin+t/kd5zS5DtgIS1IQwDrU3CR0ulbc7B5qy16oKkmE6lSCYO5MoLT1ixyenqNINvlmCwN2yaGpZ0LgDumI2pVAI1acjwXdNydG+Ph2m4RsoMwbEhftXN5LNekzjgbuV"}` ) var ( @@ -43,7 +50,7 @@ func TestKeybase_ImportKeyFromString(t *testing.T) { defer db.Stop() require.NoError(t, err) - err = db.ImportFromString(testKey.String(), testPassphrase) + err = db.ImportFromString(testPrivString, testPassphrase) require.NoError(t, err) addresses, keypairs, err := db.GetAll() @@ -55,11 +62,12 @@ func TestKeybase_ImportKeyFromString(t *testing.T) { kp := keypairs[0] require.Equal(t, len(addr), crypto.AddressLen) require.Equal(t, addr, kp.GetAddressBytes()) - require.Equal(t, kp.GetAddressString(), testKey.Address().String()) + require.Equal(t, kp.GetAddressString(), testAddr) + require.Equal(t, kp.PublicKey.String(), testPubString) privKey, err := kp.Unarmour(testPassphrase) require.NoError(t, err) - require.Equal(t, privKey.String(), testKey.String()) + require.Equal(t, privKey.String(), testPrivString) } // TODO: Improve this test/create functions to check string validity @@ -76,6 +84,32 @@ func TestKeybase_ImportKeyFromStringInvalidString(t *testing.T) { require.EqualError(t, err, crypto.ErrInvalidPrivateKeyLen(len(falseBz)).Error()) } +/* +func TestKeybase_ImportKeyFromJSON(t *testing.T) { + db, err := initDB() + defer db.Stop() + require.NoError(t, err) + + err = db.ImportFromJSON(testJSONString, testPassphrase) + require.NoError(t, err) + + addresses, keypairs, err := db.GetAll() + require.NoError(t, err) + require.Equal(t, len(addresses), 1) + require.Equal(t, len(keypairs), 1) + + addr := addresses[0] + kp := keypairs[0] + require.Equal(t, len(addr), crypto.AddressLen) + require.Equal(t, addr, kp.GetAddressBytes()) + require.Equal(t, kp.GetAddressString(), testKey.Address().String()) + + privKey, err := kp.Unarmour(testPassphrase) + require.NoError(t, err) + require.Equal(t, privKey.String(), testKey.String()) +} +*/ + func TestKeybase_GetKey(t *testing.T) { db, err := initDB() defer db.Stop() @@ -329,6 +363,42 @@ func TestKeybase_SignMessageWrongPassphrase(t *testing.T) { require.Nil(t, signedMsg) } +func TestKeybase_ExportString(t *testing.T) { + db, err := initDB() + defer db.Stop() + require.NoError(t, err) + + err = db.ImportFromString(testPrivString, testPassphrase) + require.NoError(t, err) + + privStr, err := db.ExportPrivString(testAddr, testPassphrase) + require.NoError(t, err) + require.Equal(t, privStr, testPrivString) +} + +func TestKeybase_ExportJSON(t *testing.T) { + db, err := initDB() + defer db.Stop() + require.NoError(t, err) + + err = db.ImportFromString(testPrivString, testPassphrase) + require.NoError(t, err) + + jsonStr, err := db.ExportPrivJSON(testAddr, testPassphrase) + require.NoError(t, err) + + err = db.Delete(testAddr, testPassphrase) + require.NoError(t, err) + + err = db.ImportFromJSON(jsonStr, testPassphrase) + require.NoError(t, err) + + privKey, err := db.GetPrivKey(testAddr, testPassphrase) + require.NoError(t, err) + require.Equal(t, privKey.Address().String(), testAddr) + require.Equal(t, privKey.String(), testPrivString) +} + func initDB() (Keybase, error) { db, err := NewKeybaseInMemory("") if err != nil { From c65b3f2165f3f9f772ded0a1da56ecfa001779d0 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Mon, 23 Jan 2023 13:43:21 +0000 Subject: [PATCH 17/56] Add export string and json functions and import from JSON string functionality --- app/client/keybase/keys.go | 44 ++++++++++++++++++++------- app/client/keybase/keystore.go | 54 ++++++++++++++++++++++++++++++++-- 2 files changed, 85 insertions(+), 13 deletions(-) diff --git a/app/client/keybase/keys.go b/app/client/keybase/keys.go index d57fc9a60..9d37750f4 100644 --- a/app/client/keybase/keys.go +++ b/app/client/keybase/keys.go @@ -1,7 +1,6 @@ package keybase import ( - "bytes" "crypto/aes" "crypto/cipher" "crypto/ed25519" @@ -9,6 +8,7 @@ import ( "encoding/base64" "encoding/gob" "encoding/hex" + "encoding/json" "errors" "fmt" "strconv" @@ -41,7 +41,6 @@ func init() { gob.Register(poktCrypto.Ed25519PublicKey{}) gob.Register(ed25519.PublicKey{}) gob.Register(KeyPair{}) - gob.Register(ArmouredKey{}) } // KeyPair struct stores the public key and the passphrase encrypted private key @@ -73,6 +72,20 @@ func (kp KeyPair) Unarmour(passphrase string) (poktCrypto.PrivateKey, error) { return unarmourDecryptPrivKey(kp.PrivKeyArmour, passphrase) } +// Export Private Key String +func (kp KeyPair) ExportString(passphrase string) (string, error) { + privKey, err := unarmourDecryptPrivKey(kp.PrivKeyArmour, passphrase) + if err != nil { + return "", err + } + return privKey.String(), nil +} + +// Export Private Key as armoured JSON string with fields to decrypt +func (kp KeyPair) ExportJSON(passphrase string) string { + return kp.PrivKeyArmour +} + // Armoured Private Key struct with fields to unarmour it later type ArmouredKey struct { Kdf string `json:"kdf"` @@ -130,6 +143,19 @@ func CreateNewKeyFromString(privStr, passphrase string) (KeyPair, error) { return keyPair, nil } +// Create new KeyPair from the JSON encoded privStr +func ImportKeyFromJSON(jsonStr, passphrase string) (KeyPair, error) { + // Get Private Key from armouredStr + privKey, err := unarmourDecryptPrivKey(jsonStr, passphrase) + if err != nil { + return KeyPair{}, err + } + pubKey := privKey.PublicKey() + keyPair := NewKeyPair(pubKey, jsonStr) + + return keyPair, nil +} + // Encrypt the given privKey with the passphrase, armour it by encoding the ecnrypted // []byte into base64, and convert into a json string with the parameters for unarmouring func encryptArmourPrivKey(privKey poktCrypto.PrivateKey, passphrase string) (string, error) { @@ -144,16 +170,14 @@ func encryptArmourPrivKey(privKey poktCrypto.PrivateKey, passphrase string) (str // Create ArmouredKey object so can unarmour later armoured := NewArmouredKey("scrypt", fmt.Sprintf("%X", saltBytes), "", armourStr) - //fmt.Println(armoured.CipherText) // Encode armoured struct into []byte - var bz bytes.Buffer - enc := gob.NewEncoder(&bz) - if err = enc.Encode(armoured); err != nil { + js, err := json.Marshal(armoured) + if err != nil { return "", err } - return bz.String(), nil + return string(js), nil } // Encrypt the given privKey with the passphrase using a randomly @@ -182,10 +206,8 @@ func encryptPrivKey(privKey poktCrypto.PrivateKey, passphrase string) (saltBytes func unarmourDecryptPrivKey(armourStr string, passphrase string) (privKey poktCrypto.PrivateKey, err error) { // Decode armourStr back into ArmouredKey struct armouredKey := ArmouredKey{} - var bz bytes.Buffer - bz.Write([]byte(armourStr)) - dec := gob.NewDecoder(&bz) - if err = dec.Decode(&armouredKey); err != nil { + err = json.Unmarshal([]byte(armourStr), &armouredKey) + if err != nil { return nil, err } diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index d4299ac19..a16866e02 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -16,7 +16,6 @@ func init() { gob.Register(crypto.Ed25519PublicKey{}) gob.Register(ed25519.PublicKey{}) gob.Register(KeyPair{}) - gob.Register(ArmouredKey{}) } // badgerKeybase implements the KeyBase interface @@ -32,7 +31,7 @@ type Keybase interface { // Insert a new keypair from the private key hex string provided into the DB ImportFromString(privStr, passphrase string) error // Insert a new keypair from the JSON string of the encrypted private key into the DB - //ImportFromJSON(jsonStr, passphrase string) error + ImportFromJSON(jsonStr, passphrase string) error // Accessors Get(address string) (KeyPair, error) @@ -41,6 +40,10 @@ type Keybase interface { GetAll() (addresses [][]byte, keyPairs []KeyPair, err error) Exists(address string) (bool, error) + // Exporters + ExportPrivString(address, passphrase string) (string, error) + ExportPrivJSON(address, passphrase string) (string, error) + // Updator UpdatePassphrase(address, oldPassphrase, newPassphrase string) error @@ -140,6 +143,35 @@ func (keybase *badgerKeybase) ImportFromString(privStr, passphrase string) error return err } +// Crate a new KeyPair from the private key JSON string and store it in the DB by encoding the KeyPair struct into a []byte +// Using the PublicKey.Address() return value as the key for storage +func (keybase *badgerKeybase) ImportFromJSON(jsonStr, passphrase string) error { + err := keybase.db.Update(func(tx *badger.Txn) error { + keyPair, err := ImportKeyFromJSON(jsonStr, passphrase) + if err != nil { + return err + } + + // Use key address as key in DB + key := keyPair.GetAddressBytes() + // Encode entire KeyPair struct into []byte for value + bz := new(bytes.Buffer) + enc := gob.NewEncoder(bz) + if err = enc.Encode(keyPair); err != nil { + return err + } + + err = tx.Set(key, bz.Bytes()) + if err != nil { + return err + } + + return nil + }) + + return err +} + // Returns a KeyPair struct provided the address was found in the DB func (keybase *badgerKeybase) Get(address string) (KeyPair, error) { var kp KeyPair @@ -253,6 +285,24 @@ func (keybase *badgerKeybase) Exists(address string) (bool, error) { return val != KeyPair{}, nil } +// Export the Private Key string of the given address +func (keybase *badgerKeybase) ExportPrivString(address, passphrase string) (string, error) { + kp, err := keybase.Get(address) + if err != nil { + return "", err + } + return kp.ExportString(passphrase) +} + +// Export the Private Key of the given address as a JSON object +func (keybase *badgerKeybase) ExportPrivJSON(address, passphrase string) (string, error) { + kp, err := keybase.Get(address) + if err != nil { + return "", err + } + return kp.ExportJSON(passphrase), nil +} + func (keybase *badgerKeybase) UpdatePassphrase(address, oldPassphrase, newPassphrase string) error { // Check the oldPassphrase is correct privKey, err := keybase.GetPrivKey(address, oldPassphrase) From 4755415e8e1f76ddef7ddd7ea326f941cc00e7ac Mon Sep 17 00:00:00 2001 From: Harry Law Date: Mon, 23 Jan 2023 13:49:08 +0000 Subject: [PATCH 18/56] Add JSON comment - why are V0 keys JSON too long for this implementation? --- app/client/keybase/keystore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index a16866e02..2eb72d659 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -31,7 +31,7 @@ type Keybase interface { // Insert a new keypair from the private key hex string provided into the DB ImportFromString(privStr, passphrase string) error // Insert a new keypair from the JSON string of the encrypted private key into the DB - ImportFromJSON(jsonStr, passphrase string) error + ImportFromJSON(jsonStr, passphrase string) error // TODO: Figure out why V0 keys are too long to import // Accessors Get(address string) (KeyPair, error) From 18000e056bb9e4fc81d36a5cfa7c55f142a6238e Mon Sep 17 00:00:00 2001 From: Harry Law Date: Mon, 23 Jan 2023 15:38:00 +0000 Subject: [PATCH 19/56] Change GetAll() to return only []KeyPair and error --- app/client/keybase/keybase_test.go | 58 ++++++++++++++++++++++-------- app/client/keybase/keystore.go | 9 +++-- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go index 93eb2cff5..e4fbed266 100644 --- a/app/client/keybase/keybase_test.go +++ b/app/client/keybase/keybase_test.go @@ -34,15 +34,28 @@ func TestKeybase_CreateNewKey(t *testing.T) { err = db.Create(testPassphrase) require.NoError(t, err) - addresses, keypairs, err := db.GetAll() + keypairs, err := db.GetAll() require.NoError(t, err) - require.Equal(t, len(addresses), 1) require.Equal(t, len(keypairs), 1) - addr := addresses[0] kp := keypairs[0] - require.Equal(t, len(addr), crypto.AddressLen) - require.Equal(t, addr, kp.GetAddressBytes()) + require.Equal(t, len(kp.GetAddressBytes()), crypto.AddressLen) +} + +func TestKeybase_CreateNewKeyNoPassphrase(t *testing.T) { + db, err := initDB() + defer db.Stop() + require.NoError(t, err) + + err = db.Create("") + require.NoError(t, err) + + keypairs, err := db.GetAll() + require.NoError(t, err) + require.Equal(t, len(keypairs), 1) + + kp := keypairs[0] + require.Equal(t, len(kp.GetAddressBytes()), crypto.AddressLen) } func TestKeybase_ImportKeyFromString(t *testing.T) { @@ -53,15 +66,12 @@ func TestKeybase_ImportKeyFromString(t *testing.T) { err = db.ImportFromString(testPrivString, testPassphrase) require.NoError(t, err) - addresses, keypairs, err := db.GetAll() + keypairs, err := db.GetAll() require.NoError(t, err) - require.Equal(t, len(addresses), 1) require.Equal(t, len(keypairs), 1) - addr := addresses[0] kp := keypairs[0] - require.Equal(t, len(addr), crypto.AddressLen) - require.Equal(t, addr, kp.GetAddressBytes()) + require.Equal(t, len(kp.GetAddressBytes()), crypto.AddressLen) require.Equal(t, kp.GetAddressString(), testAddr) require.Equal(t, kp.PublicKey.String(), testPubString) @@ -70,6 +80,28 @@ func TestKeybase_ImportKeyFromString(t *testing.T) { require.Equal(t, privKey.String(), testPrivString) } +func TestKeybase_ImportKeyFromStringNoPassphrase(t *testing.T) { + db, err := initDB() + defer db.Stop() + require.NoError(t, err) + + err = db.ImportFromString(testPrivString, "") + require.NoError(t, err) + + keypairs, err := db.GetAll() + require.NoError(t, err) + require.Equal(t, len(keypairs), 1) + + kp := keypairs[0] + require.Equal(t, len(kp.GetAddressBytes()), crypto.AddressLen) + require.Equal(t, kp.GetAddressString(), testAddr) + require.Equal(t, kp.PublicKey.String(), testPubString) + + privKey, err := kp.Unarmour("") + require.NoError(t, err) + require.Equal(t, privKey.String(), testPrivString) +} + // TODO: Improve this test/create functions to check string validity func TestKeybase_ImportKeyFromStringInvalidString(t *testing.T) { db, err := initDB() @@ -178,18 +210,14 @@ func TestKeybase_GetAllKeys(t *testing.T) { pks[pk.Address().String()] = pk } - addresses, keypairs, err := db.GetAll() + keypairs, err := db.GetAll() require.NoError(t, err) - require.Equal(t, len(addresses), 5) require.Equal(t, len(keypairs), 5) for i := 0; i < 5; i++ { privKey, err := keypairs[i].Unarmour(testPassphrase) require.NoError(t, err) - require.Equal(t, addresses[i], keypairs[i].GetAddressBytes()) - require.Equal(t, addresses[i], privKey.Address().Bytes()) - equal := privKey.Equals(pks[privKey.Address().String()]) require.Equal(t, equal, true) } diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index 2eb72d659..62ce34f02 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -37,7 +37,7 @@ type Keybase interface { Get(address string) (KeyPair, error) GetPubKey(address string) (crypto.PublicKey, error) GetPrivKey(address, passphrase string) (crypto.PrivateKey, error) - GetAll() (addresses [][]byte, keyPairs []KeyPair, err error) + GetAll() (keyPairs []KeyPair, err error) Exists(address string) (bool, error) // Exporters @@ -237,7 +237,7 @@ func (keybase *badgerKeybase) GetPrivKey(address, passphrase string) (crypto.Pri // Get all the addresses and key pairs stored in the keybase // Returns addresses stored and all the KeyPair structs stored in the DB -func (keybase *badgerKeybase) GetAll() (addresses [][]byte, keyPairs []KeyPair, err error) { +func (keybase *badgerKeybase) GetAll() (keyPairs []KeyPair, err error) { // View executes the function provided managing a read only transaction err = keybase.db.View(func(tx *badger.Txn) error { opts := badger.DefaultIteratorOptions @@ -258,7 +258,6 @@ func (keybase *badgerKeybase) GetAll() (addresses [][]byte, keyPairs []KeyPair, return err } - addresses = append(addresses, item.Key()) keyPairs = append(keyPairs, kp) return nil }) @@ -270,10 +269,10 @@ func (keybase *badgerKeybase) GetAll() (addresses [][]byte, keyPairs []KeyPair, }) if err != nil { - return nil, nil, err + return nil, err } - return addresses, keyPairs, nil + return keyPairs, nil } // Check whether an address is currently stored in the DB From 2f7663d774208d6377292e625f0a2f519b4fa2f0 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Mon, 23 Jan 2023 17:20:37 +0000 Subject: [PATCH 20/56] Have GetAll return (addresses []string, keypairs []KeyPairs, error) --- app/client/keybase/keybase_test.go | 29 ++++++++++++++++++++++------- app/client/keybase/keystore.go | 9 +++++---- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go index e4fbed266..23fd1f164 100644 --- a/app/client/keybase/keybase_test.go +++ b/app/client/keybase/keybase_test.go @@ -34,12 +34,15 @@ func TestKeybase_CreateNewKey(t *testing.T) { err = db.Create(testPassphrase) require.NoError(t, err) - keypairs, err := db.GetAll() + addresses, keypairs, err := db.GetAll() require.NoError(t, err) + require.Equal(t, len(addresses), 1) require.Equal(t, len(keypairs), 1) + addr := addresses[0] kp := keypairs[0] require.Equal(t, len(kp.GetAddressBytes()), crypto.AddressLen) + require.Equal(t, addr, kp.GetAddressString()) } func TestKeybase_CreateNewKeyNoPassphrase(t *testing.T) { @@ -50,12 +53,15 @@ func TestKeybase_CreateNewKeyNoPassphrase(t *testing.T) { err = db.Create("") require.NoError(t, err) - keypairs, err := db.GetAll() + addresses, keypairs, err := db.GetAll() require.NoError(t, err) + require.Equal(t, len(addresses), 1) require.Equal(t, len(keypairs), 1) + addr := addresses[0] kp := keypairs[0] require.Equal(t, len(kp.GetAddressBytes()), crypto.AddressLen) + require.Equal(t, addr, kp.GetAddressString()) } func TestKeybase_ImportKeyFromString(t *testing.T) { @@ -66,12 +72,15 @@ func TestKeybase_ImportKeyFromString(t *testing.T) { err = db.ImportFromString(testPrivString, testPassphrase) require.NoError(t, err) - keypairs, err := db.GetAll() + addresses, keypairs, err := db.GetAll() require.NoError(t, err) + require.Equal(t, len(addresses), 1) require.Equal(t, len(keypairs), 1) + addr := addresses[0] kp := keypairs[0] require.Equal(t, len(kp.GetAddressBytes()), crypto.AddressLen) + require.Equal(t, addr, kp.GetAddressString()) require.Equal(t, kp.GetAddressString(), testAddr) require.Equal(t, kp.PublicKey.String(), testPubString) @@ -88,12 +97,15 @@ func TestKeybase_ImportKeyFromStringNoPassphrase(t *testing.T) { err = db.ImportFromString(testPrivString, "") require.NoError(t, err) - keypairs, err := db.GetAll() + addresses, keypairs, err := db.GetAll() require.NoError(t, err) + require.Equal(t, len(addresses), 1) require.Equal(t, len(keypairs), 1) + addr := addresses[0] kp := keypairs[0] require.Equal(t, len(kp.GetAddressBytes()), crypto.AddressLen) + require.Equal(t, addr, kp.GetAddressString()) require.Equal(t, kp.GetAddressString(), testAddr) require.Equal(t, kp.PublicKey.String(), testPubString) @@ -132,8 +144,8 @@ func TestKeybase_ImportKeyFromJSON(t *testing.T) { addr := addresses[0] kp := keypairs[0] - require.Equal(t, len(addr), crypto.AddressLen) - require.Equal(t, addr, kp.GetAddressBytes()) + require.Equal(t, len(kp.GetAddressBytes()), crypto.AddressLen) + require.Equal(t, addr, kp.GetAddressString()) require.Equal(t, kp.GetAddressString(), testKey.Address().String()) privKey, err := kp.Unarmour(testPassphrase) @@ -210,7 +222,7 @@ func TestKeybase_GetAllKeys(t *testing.T) { pks[pk.Address().String()] = pk } - keypairs, err := db.GetAll() + addresses, keypairs, err := db.GetAll() require.NoError(t, err) require.Equal(t, len(keypairs), 5) @@ -218,6 +230,9 @@ func TestKeybase_GetAllKeys(t *testing.T) { privKey, err := keypairs[i].Unarmour(testPassphrase) require.NoError(t, err) + require.Equal(t, addresses[i], keypairs[i].GetAddressString()) + require.Equal(t, addresses[i], privKey.Address().String()) + equal := privKey.Equals(pks[privKey.Address().String()]) require.Equal(t, equal, true) } diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index 62ce34f02..75a132040 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -37,7 +37,7 @@ type Keybase interface { Get(address string) (KeyPair, error) GetPubKey(address string) (crypto.PublicKey, error) GetPrivKey(address, passphrase string) (crypto.PrivateKey, error) - GetAll() (keyPairs []KeyPair, err error) + GetAll() (addresses []string, keyPairs []KeyPair, err error) Exists(address string) (bool, error) // Exporters @@ -237,7 +237,7 @@ func (keybase *badgerKeybase) GetPrivKey(address, passphrase string) (crypto.Pri // Get all the addresses and key pairs stored in the keybase // Returns addresses stored and all the KeyPair structs stored in the DB -func (keybase *badgerKeybase) GetAll() (keyPairs []KeyPair, err error) { +func (keybase *badgerKeybase) GetAll() (addresses []string, keyPairs []KeyPair, err error) { // View executes the function provided managing a read only transaction err = keybase.db.View(func(tx *badger.Txn) error { opts := badger.DefaultIteratorOptions @@ -258,6 +258,7 @@ func (keybase *badgerKeybase) GetAll() (keyPairs []KeyPair, err error) { return err } + addresses = append(addresses, kp.GetAddressString()) keyPairs = append(keyPairs, kp) return nil }) @@ -269,10 +270,10 @@ func (keybase *badgerKeybase) GetAll() (keyPairs []KeyPair, err error) { }) if err != nil { - return nil, err + return nil, nil, err } - return keyPairs, nil + return addresses, keyPairs, nil } // Check whether an address is currently stored in the DB From 5cacecfef13782a21b53ed89fb8131dff74194dd Mon Sep 17 00:00:00 2001 From: Harry Law Date: Mon, 23 Jan 2023 17:40:02 +0000 Subject: [PATCH 21/56] Add passphrase check to ExportJSON --- app/client/keybase/keys.go | 12 ++++++++---- app/client/keybase/keystore.go | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/client/keybase/keys.go b/app/client/keybase/keys.go index 9d37750f4..1acdd5f41 100644 --- a/app/client/keybase/keys.go +++ b/app/client/keybase/keys.go @@ -45,8 +45,8 @@ func init() { // KeyPair struct stores the public key and the passphrase encrypted private key type KeyPair struct { - PublicKey poktCrypto.PublicKey `json:"pubkey"` - PrivKeyArmour string `json:"privkey.armor"` + PublicKey poktCrypto.PublicKey + PrivKeyArmour string } // Generate a new KeyPair struct given the public key and armoured private key @@ -82,8 +82,12 @@ func (kp KeyPair) ExportString(passphrase string) (string, error) { } // Export Private Key as armoured JSON string with fields to decrypt -func (kp KeyPair) ExportJSON(passphrase string) string { - return kp.PrivKeyArmour +func (kp KeyPair) ExportJSON(passphrase string) (string, error) { + _, err := unarmourDecryptPrivKey(kp.PrivKeyArmour, passphrase) + if err != nil { + return "", err + } + return kp.PrivKeyArmour, nil } // Armoured Private Key struct with fields to unarmour it later diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index 75a132040..0bdc057b3 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -300,7 +300,7 @@ func (keybase *badgerKeybase) ExportPrivJSON(address, passphrase string) (string if err != nil { return "", err } - return kp.ExportJSON(passphrase), nil + return kp.ExportJSON(passphrase) } func (keybase *badgerKeybase) UpdatePassphrase(address, oldPassphrase, newPassphrase string) error { From 8a04071d45083df30ed0c6bac568cc4e0dd407f3 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Tue, 24 Jan 2023 20:07:37 +0000 Subject: [PATCH 22/56] Add default passphrase for encryption to allow decryption when user provides no passphrase --- app/client/keybase/keys.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/client/keybase/keys.go b/app/client/keybase/keys.go index 1acdd5f41..44dccdf7a 100644 --- a/app/client/keybase/keys.go +++ b/app/client/keybase/keys.go @@ -26,6 +26,8 @@ const ( klen = 32 // Sec param secParam = 12 + // No passphrase + defaultPassphrase = "passphrase" ) var ( @@ -187,6 +189,11 @@ func encryptArmourPrivKey(privKey poktCrypto.PrivateKey, passphrase string) (str // Encrypt the given privKey with the passphrase using a randomly // generated salt and the AES-256 GCM cipher func encryptPrivKey(privKey poktCrypto.PrivateKey, passphrase string) (saltBytes []byte, encBytes []byte, err error) { + // Use a default passphrase when none is given + if passphrase == "" { + passphrase = defaultPassphrase + } + // Get random bytes for salt saltBytes = randBytes(16) @@ -243,6 +250,11 @@ func unarmourDecryptPrivKey(armourStr string, passphrase string) (privKey poktCr // Decrypt the AES-256 GCM encrypted bytes using the passphrase given func decryptPrivKey(saltBytes []byte, encBytes []byte, passphrase string) (poktCrypto.PrivateKey, error) { + // Use a default passphrase when none is given + if passphrase == "" { + passphrase = defaultPassphrase + } + // Derive key for decryption, see: https://pkg.go.dev/golang.org/x/crypto/scrypt#Key key, err := scrypt.Key([]byte(passphrase), saltBytes, n, r, p, klen) if err != nil { From cc488f9f43958098f814fd1402e889edbaf1a32d Mon Sep 17 00:00:00 2001 From: Harry Law Date: Tue, 24 Jan 2023 22:23:47 +0000 Subject: [PATCH 23/56] Fix JSON import/export interoperability between V0/V1 --- app/client/keybase/keybase_test.go | 14 +++++++++----- app/client/keybase/keys.go | 10 +++++++--- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go index 23fd1f164..761b0540c 100644 --- a/app/client/keybase/keybase_test.go +++ b/app/client/keybase/keybase_test.go @@ -19,7 +19,12 @@ const ( // Other testNewPassphrase = "321gnitsetgnitset" testTx = "79fca587bbcfd5da86d73e1d849769017b1c91cc8177dec0fc0e3e0d345f2b35" - //testJSONString = `{"kdf":"scrypt","salt":"6abd9619621dc8c2a62860c05dea6df3","secparam":"12","hint":"pocket wallet","ciphertext":"SZHQyzlYHke9g/7u/x1pCMPpA7wdeJSDjUN+9Oe5W0ep+y5enf2r2qVUnEgsZiQLin+t/kd5zS5DtgIS1IQwDrU3CR0ulbc7B5qy16oKkmE6lSCYO5MoLT1ixyenqNINvlmCwN2yaGpZ0LgDumI2pVAI1acjwXdNydG+Ph2m4RsoMwbEhftXN5LNekzjgbuV"}` + + // JSON account + testJSONAddr = "572f306e2d29cb8d77c02ebed7d11a5750c815f2" + testJSONPubString = "408bec6320b540aa0cc86b3e633e214f2fd4dce4caa08f164fa3a9d3e577b46c" + testJSONPrivString = "3554119cec1c0c8c5b3845a5d3fc6346eb44ed21aab5c063ae9b6b1d38bec275408bec6320b540aa0cc86b3e633e214f2fd4dce4caa08f164fa3a9d3e577b46c" + testJSONString = `{"kdf":"scrypt","salt":"197d2754445a7e5ce3e6c8d7b1d0ff6f","secparam":"12","hint":"pocket wallet","ciphertext":"B/AORJrSeQrR5ewQGel4FeCCXscoCsMUzq9gXAAxDqjXMmMxa7TedBTuemtO82JyTCoQWFHbGxRx8A7IoETNh5T5yBAjNNrr7DDkVrcfSAM3ez9lQem17DsfowCvRtmbesDlvbSZMRy8mQgClLqWRN+c6W/fPQ/lxLUy1G1A965U/uImcMXzSwbfqYrBPEux"}` ) var ( @@ -128,7 +133,6 @@ func TestKeybase_ImportKeyFromStringInvalidString(t *testing.T) { require.EqualError(t, err, crypto.ErrInvalidPrivateKeyLen(len(falseBz)).Error()) } -/* func TestKeybase_ImportKeyFromJSON(t *testing.T) { db, err := initDB() defer db.Stop() @@ -146,13 +150,13 @@ func TestKeybase_ImportKeyFromJSON(t *testing.T) { kp := keypairs[0] require.Equal(t, len(kp.GetAddressBytes()), crypto.AddressLen) require.Equal(t, addr, kp.GetAddressString()) - require.Equal(t, kp.GetAddressString(), testKey.Address().String()) + require.Equal(t, kp.GetAddressString(), testJSONAddr) + require.Equal(t, kp.PublicKey.String(), testJSONPubString) privKey, err := kp.Unarmour(testPassphrase) require.NoError(t, err) - require.Equal(t, privKey.String(), testKey.String()) + require.Equal(t, privKey.String(), testJSONPrivString) } -*/ func TestKeybase_GetKey(t *testing.T) { db, err := initDB() diff --git a/app/client/keybase/keys.go b/app/client/keybase/keys.go index 44dccdf7a..a2c136eb5 100644 --- a/app/client/keybase/keys.go +++ b/app/client/keybase/keys.go @@ -204,8 +204,8 @@ func encryptPrivKey(privKey poktCrypto.PrivateKey, passphrase string) (saltBytes } // Encrypt using AES - privKeyBytes := privKey.Bytes() - encBytes, err = encryptAESGCM(key, privKeyBytes) + privKeyBytes := privKey.String() + encBytes, err = encryptAESGCM(key, []byte(privKeyBytes)) if err != nil { return nil, nil, err } @@ -266,9 +266,13 @@ func decryptPrivKey(saltBytes []byte, encBytes []byte, passphrase string) (poktC if err != nil { return nil, err } + bz, err := hex.DecodeString(string(privKeyBytes)) + if err != nil { + return nil, err + } // Get private key from decrypted bytes - pk, err := poktCrypto.NewPrivateKeyFromBytes(privKeyBytes) + pk, err := poktCrypto.NewPrivateKeyFromBytes(bz) if err != nil { return nil, err } From 533ced5e70a22535ed92bd77b3c0fb5aabfb9948 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Tue, 24 Jan 2023 22:38:39 +0000 Subject: [PATCH 24/56] Remove JSON comment --- app/client/keybase/keystore.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index 0bdc057b3..2bdc4aa17 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -31,7 +31,7 @@ type Keybase interface { // Insert a new keypair from the private key hex string provided into the DB ImportFromString(privStr, passphrase string) error // Insert a new keypair from the JSON string of the encrypted private key into the DB - ImportFromJSON(jsonStr, passphrase string) error // TODO: Figure out why V0 keys are too long to import + ImportFromJSON(jsonStr, passphrase string) error // Accessors Get(address string) (KeyPair, error) From 7eb890e7dce00a131b4ab0cc50a8be69ee0cebe8 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Wed, 25 Jan 2023 09:40:45 +0000 Subject: [PATCH 25/56] Add t *testing.T argument to helper functions - use deterministic key generation from keygenerator.GetInstance().Next() --- app/client/keybase/keybase_test.go | 180 ++++++++++++++--------------- 1 file changed, 90 insertions(+), 90 deletions(-) diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go index 761b0540c..05c155183 100644 --- a/app/client/keybase/keybase_test.go +++ b/app/client/keybase/keybase_test.go @@ -4,6 +4,7 @@ import ( "encoding/hex" "testing" + "github.com/pokt-network/pocket/runtime/test_artifacts/keygenerator" "github.com/pokt-network/pocket/shared/crypto" "github.com/stretchr/testify/require" ) @@ -27,16 +28,11 @@ const ( testJSONString = `{"kdf":"scrypt","salt":"197d2754445a7e5ce3e6c8d7b1d0ff6f","secparam":"12","hint":"pocket wallet","ciphertext":"B/AORJrSeQrR5ewQGel4FeCCXscoCsMUzq9gXAAxDqjXMmMxa7TedBTuemtO82JyTCoQWFHbGxRx8A7IoETNh5T5yBAjNNrr7DDkVrcfSAM3ez9lQem17DsfowCvRtmbesDlvbSZMRy8mQgClLqWRN+c6W/fPQ/lxLUy1G1A965U/uImcMXzSwbfqYrBPEux"}` ) -var ( - testKey, _ = createTestKey() -) - func TestKeybase_CreateNewKey(t *testing.T) { - db, err := initDB() + db := initDB(t) defer db.Stop() - require.NoError(t, err) - err = db.Create(testPassphrase) + err := db.Create(testPassphrase) require.NoError(t, err) addresses, keypairs, err := db.GetAll() @@ -51,11 +47,10 @@ func TestKeybase_CreateNewKey(t *testing.T) { } func TestKeybase_CreateNewKeyNoPassphrase(t *testing.T) { - db, err := initDB() + db := initDB(t) defer db.Stop() - require.NoError(t, err) - err = db.Create("") + err := db.Create("") require.NoError(t, err) addresses, keypairs, err := db.GetAll() @@ -70,11 +65,10 @@ func TestKeybase_CreateNewKeyNoPassphrase(t *testing.T) { } func TestKeybase_ImportKeyFromString(t *testing.T) { - db, err := initDB() + db := initDB(t) defer db.Stop() - require.NoError(t, err) - err = db.ImportFromString(testPrivString, testPassphrase) + err := db.ImportFromString(testPrivString, testPassphrase) require.NoError(t, err) addresses, keypairs, err := db.GetAll() @@ -95,11 +89,10 @@ func TestKeybase_ImportKeyFromString(t *testing.T) { } func TestKeybase_ImportKeyFromStringNoPassphrase(t *testing.T) { - db, err := initDB() + db := initDB(t) defer db.Stop() - require.NoError(t, err) - err = db.ImportFromString(testPrivString, "") + err := db.ImportFromString(testPrivString, "") require.NoError(t, err) addresses, keypairs, err := db.GetAll() @@ -121,9 +114,10 @@ func TestKeybase_ImportKeyFromStringNoPassphrase(t *testing.T) { // TODO: Improve this test/create functions to check string validity func TestKeybase_ImportKeyFromStringInvalidString(t *testing.T) { - db, err := initDB() + db := initDB(t) defer db.Stop() - require.NoError(t, err) + + testKey := createTestKeys(t, 1)[0] falseAddr := testKey.String() + "aa" falseBz, err := hex.DecodeString(falseAddr) @@ -134,11 +128,10 @@ func TestKeybase_ImportKeyFromStringInvalidString(t *testing.T) { } func TestKeybase_ImportKeyFromJSON(t *testing.T) { - db, err := initDB() + db := initDB(t) defer db.Stop() - require.NoError(t, err) - err = db.ImportFromJSON(testJSONString, testPassphrase) + err := db.ImportFromJSON(testJSONString, testPassphrase) require.NoError(t, err) addresses, keypairs, err := db.GetAll() @@ -159,11 +152,12 @@ func TestKeybase_ImportKeyFromJSON(t *testing.T) { } func TestKeybase_GetKey(t *testing.T) { - db, err := initDB() + db := initDB(t) defer db.Stop() - require.NoError(t, err) - err = db.ImportFromString(testKey.String(), testPassphrase) + testKey := createTestKeys(t, 1)[0] + + err := db.ImportFromString(testKey.String(), testPassphrase) require.NoError(t, err) kp, err := db.Get(testKey.Address().String()) @@ -180,9 +174,10 @@ func TestKeybase_GetKey(t *testing.T) { } func TestKeybase_GetKeyDoesntExist(t *testing.T) { - db, err := initDB() + db := initDB(t) defer db.Stop() - require.NoError(t, err) + + testKey := createTestKeys(t, 1)[0] kp, err := db.Get(testKey.Address().String()) require.EqualError(t, err, ErrorAddrNotFound(testKey.Address().String()).Error()) @@ -190,11 +185,12 @@ func TestKeybase_GetKeyDoesntExist(t *testing.T) { } func TestKeybase_CheckKeyExists(t *testing.T) { - db, err := initDB() + db := initDB(t) defer db.Stop() - require.NoError(t, err) - err = db.ImportFromString(testKey.String(), testPassphrase) + testKey := createTestKeys(t, 1)[0] + + err := db.ImportFromString(testKey.String(), testPassphrase) require.NoError(t, err) exists, err := db.Exists(testKey.Address().String()) @@ -203,9 +199,10 @@ func TestKeybase_CheckKeyExists(t *testing.T) { } func TestKeybase_CheckKeyExistsDoesntExist(t *testing.T) { - db, err := initDB() + db := initDB(t) defer db.Stop() - require.NoError(t, err) + + testKey := createTestKeys(t, 1)[0] exists, err := db.Exists(testKey.Address().String()) require.EqualError(t, err, ErrorAddrNotFound(testKey.Address().String()).Error()) @@ -213,17 +210,15 @@ func TestKeybase_CheckKeyExistsDoesntExist(t *testing.T) { } func TestKeybase_GetAllKeys(t *testing.T) { - db, err := initDB() + db := initDB(t) defer db.Stop() - require.NoError(t, err) - pks := make(map[string]crypto.PrivateKey, 0) + pkm := make(map[string]crypto.PrivateKey, 0) + pks := createTestKeys(t, 5) for i := 0; i < 5; i++ { - pk, err := createTestKey() + err := db.ImportFromString(pks[i].String(), testPassphrase) require.NoError(t, err) - err = db.ImportFromString(pk.String(), testPassphrase) - require.NoError(t, err) - pks[pk.Address().String()] = pk + pkm[pks[i].Address().String()] = pks[i] } addresses, keypairs, err := db.GetAll() @@ -237,17 +232,18 @@ func TestKeybase_GetAllKeys(t *testing.T) { require.Equal(t, addresses[i], keypairs[i].GetAddressString()) require.Equal(t, addresses[i], privKey.Address().String()) - equal := privKey.Equals(pks[privKey.Address().String()]) + equal := privKey.Equals(pkm[privKey.Address().String()]) require.Equal(t, equal, true) } } func TestKeybase_GetPubKey(t *testing.T) { - db, err := initDB() + db := initDB(t) defer db.Stop() - require.NoError(t, err) - err = db.ImportFromString(testKey.String(), testPassphrase) + testKey := createTestKeys(t, 1)[0] + + err := db.ImportFromString(testKey.String(), testPassphrase) require.NoError(t, err) pubKey, err := db.GetPubKey(testKey.Address().String()) @@ -260,11 +256,12 @@ func TestKeybase_GetPubKey(t *testing.T) { } func TestKeybase_GetPrivKey(t *testing.T) { - db, err := initDB() + db := initDB(t) defer db.Stop() - require.NoError(t, err) - err = db.ImportFromString(testKey.String(), testPassphrase) + testKey := createTestKeys(t, 1)[0] + + err := db.ImportFromString(testKey.String(), testPassphrase) require.NoError(t, err) privKey, err := db.GetPrivKey(testKey.Address().String(), testPassphrase) @@ -278,11 +275,12 @@ func TestKeybase_GetPrivKey(t *testing.T) { } func TestKeybase_GetPrivKeyWrongPassphrase(t *testing.T) { - db, err := initDB() + db := initDB(t) defer db.Stop() - require.NoError(t, err) - err = db.ImportFromString(testKey.String(), testPassphrase) + testKey := createTestKeys(t, 1)[0] + + err := db.ImportFromString(testKey.String(), testPassphrase) require.NoError(t, err) privKey, err := db.GetPrivKey(testKey.Address().String(), testNewPassphrase) @@ -291,11 +289,12 @@ func TestKeybase_GetPrivKeyWrongPassphrase(t *testing.T) { } func TestKeybase_UpdatePassphrase(t *testing.T) { - db, err := initDB() + db := initDB(t) defer db.Stop() - require.NoError(t, err) - err = db.ImportFromString(testKey.String(), testPassphrase) + testKey := createTestKeys(t, 1)[0] + + err := db.ImportFromString(testKey.String(), testPassphrase) require.NoError(t, err) _, err = db.GetPrivKey(testKey.Address().String(), testPassphrase) @@ -315,11 +314,12 @@ func TestKeybase_UpdatePassphrase(t *testing.T) { } func TestKeybase_UpdatePassphraseWrongPassphrase(t *testing.T) { - db, err := initDB() + db := initDB(t) defer db.Stop() - require.NoError(t, err) - err = db.ImportFromString(testKey.String(), testPassphrase) + testKey := createTestKeys(t, 1)[0] + + err := db.ImportFromString(testKey.String(), testPassphrase) require.NoError(t, err) _, err = db.GetPrivKey(testKey.Address().String(), testPassphrase) @@ -330,11 +330,12 @@ func TestKeybase_UpdatePassphraseWrongPassphrase(t *testing.T) { } func TestKeybase_DeleteKey(t *testing.T) { - db, err := initDB() + db := initDB(t) defer db.Stop() - require.NoError(t, err) - err = db.ImportFromString(testKey.String(), testPassphrase) + testKey := createTestKeys(t, 1)[0] + + err := db.ImportFromString(testKey.String(), testPassphrase) require.NoError(t, err) _, err = db.GetPrivKey(testKey.Address().String(), testPassphrase) @@ -349,11 +350,12 @@ func TestKeybase_DeleteKey(t *testing.T) { } func TestKeybase_DeleteKeyWrongPassphrase(t *testing.T) { - db, err := initDB() + db := initDB(t) defer db.Stop() - require.NoError(t, err) - err = db.ImportFromString(testKey.String(), testPassphrase) + testKey := createTestKeys(t, 1)[0] + + err := db.ImportFromString(testKey.String(), testPassphrase) require.NoError(t, err) _, err = db.GetPrivKey(testKey.Address().String(), testPassphrase) @@ -364,14 +366,12 @@ func TestKeybase_DeleteKeyWrongPassphrase(t *testing.T) { } func TestKeybase_SignMessage(t *testing.T) { - db, err := initDB() + db := initDB(t) defer db.Stop() - require.NoError(t, err) - pk, err := createTestKeyFromString(testPrivString) - require.NoError(t, err) + pk := createTestKeyFromString(t, testPrivString) - err = db.ImportFromString(testPrivString, testPassphrase) + err := db.ImportFromString(testPrivString, testPassphrase) require.NoError(t, err) privKey, err := db.GetPrivKey(pk.Address().String(), testPassphrase) @@ -389,14 +389,12 @@ func TestKeybase_SignMessage(t *testing.T) { } func TestKeybase_SignMessageWrongPassphrase(t *testing.T) { - db, err := initDB() + db := initDB(t) defer db.Stop() - require.NoError(t, err) - pk, err := createTestKeyFromString(testPrivString) - require.NoError(t, err) + pk := createTestKeyFromString(t, testPrivString) - err = db.ImportFromString(testPrivString, testPassphrase) + err := db.ImportFromString(testPrivString, testPassphrase) require.NoError(t, err) privKey, err := db.GetPrivKey(pk.Address().String(), testPassphrase) @@ -411,11 +409,10 @@ func TestKeybase_SignMessageWrongPassphrase(t *testing.T) { } func TestKeybase_ExportString(t *testing.T) { - db, err := initDB() + db := initDB(t) defer db.Stop() - require.NoError(t, err) - err = db.ImportFromString(testPrivString, testPassphrase) + err := db.ImportFromString(testPrivString, testPassphrase) require.NoError(t, err) privStr, err := db.ExportPrivString(testAddr, testPassphrase) @@ -424,11 +421,10 @@ func TestKeybase_ExportString(t *testing.T) { } func TestKeybase_ExportJSON(t *testing.T) { - db, err := initDB() + db := initDB(t) defer db.Stop() - require.NoError(t, err) - err = db.ImportFromString(testPrivString, testPassphrase) + err := db.ImportFromString(testPrivString, testPassphrase) require.NoError(t, err) jsonStr, err := db.ExportPrivJSON(testAddr, testPassphrase) @@ -446,22 +442,26 @@ func TestKeybase_ExportJSON(t *testing.T) { require.Equal(t, privKey.String(), testPrivString) } -func initDB() (Keybase, error) { - db, err := NewKeybaseInMemory("") - if err != nil { - return nil, err - } - return db, nil +func initDB(t *testing.T) Keybase { + db, err := NewKeybaseInMemory() + require.NoError(t, err) + return db } -func createTestKey() (crypto.PrivateKey, error) { - return crypto.GeneratePrivateKey() -} +func createTestKeys(t *testing.T, n int) []crypto.PrivateKey { + pks := make([]crypto.PrivateKey, 0) + for i := 0; i < n; i++ { + privKeyString, _, _ := keygenerator.GetInstance().Next() + privKey, err := crypto.NewPrivateKey(privKeyString) + require.NoError(t, err) + pks = append(pks, privKey) -func createTestKeyFromString(str string) (crypto.PrivateKey, error) { - bz, err := hex.DecodeString(str) - if err != nil { - return nil, err } - return crypto.NewPrivateKeyFromBytes(bz) + return pks +} + +func createTestKeyFromString(t *testing.T, str string) crypto.PrivateKey { + privKey, err := crypto.NewPrivateKey(str) + require.NoError(t, err) + return privKey } From f182b056a4019fde3a0f34b9d30313dac5677620 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Wed, 25 Jan 2023 09:42:26 +0000 Subject: [PATCH 26/56] Add comments to scrypt parameters, refactor names used for encryption and decryption to better reflect their nature --- app/client/keybase/keys.go | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/app/client/keybase/keys.go b/app/client/keybase/keys.go index a2c136eb5..59efb12cd 100644 --- a/app/client/keybase/keys.go +++ b/app/client/keybase/keys.go @@ -3,10 +3,8 @@ package keybase import ( "crypto/aes" "crypto/cipher" - "crypto/ed25519" crand "crypto/rand" "encoding/base64" - "encoding/gob" "encoding/hex" "encoding/json" "errors" @@ -20,10 +18,10 @@ import ( const ( // Scrypt params - n = 32768 - r = 8 - p = 1 - klen = 32 + n = 32768 // CPU/memory cost param; power of 2 greater than 1 + r = 8 // r * p < 2³⁰ + p = 1 // r * p < 2³⁰ + klen = 32 // bytes // Sec param secParam = 12 // No passphrase @@ -35,16 +33,6 @@ var ( ErrorWrongPassphrase = errors.New("Can't decrypt private key: wrong passphrase") ) -func ErrorAddrNotFound(addr string) error { - return fmt.Errorf("No key found with address: %s", addr) -} - -func init() { - gob.Register(poktCrypto.Ed25519PublicKey{}) - gob.Register(ed25519.PublicKey{}) - gob.Register(KeyPair{}) -} - // KeyPair struct stores the public key and the passphrase encrypted private key type KeyPair struct { PublicKey poktCrypto.PublicKey @@ -204,8 +192,8 @@ func encryptPrivKey(privKey poktCrypto.PrivateKey, passphrase string) (saltBytes } // Encrypt using AES - privKeyBytes := privKey.String() - encBytes, err = encryptAESGCM(key, []byte(privKeyBytes)) + privKeyHexString := privKey.String() + encBytes, err = encryptAESGCM(key, []byte(privKeyHexString)) if err != nil { return nil, nil, err } @@ -262,11 +250,11 @@ func decryptPrivKey(saltBytes []byte, encBytes []byte, passphrase string) (poktC } // Decrypt using AES - privKeyBytes, err := decryptAESGCM(key, encBytes) + privKeyRawHexBytes, err := decryptAESGCM(key, encBytes) if err != nil { return nil, err } - bz, err := hex.DecodeString(string(privKeyBytes)) + bz, err := hex.DecodeString(string(privKeyRawHexBytes)) if err != nil { return nil, err } From 82b2cfa7cb29d928e46a1f5bc856dd6b23de92ba Mon Sep 17 00:00:00 2001 From: Harry Law Date: Wed, 25 Jan 2023 09:42:48 +0000 Subject: [PATCH 27/56] Seperate keybase interface into its own file --- app/client/keybase/keybase.go | 37 +++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 app/client/keybase/keybase.go diff --git a/app/client/keybase/keybase.go b/app/client/keybase/keybase.go new file mode 100644 index 000000000..a432fb07d --- /dev/null +++ b/app/client/keybase/keybase.go @@ -0,0 +1,37 @@ +package keybase + +import "github.com/pokt-network/pocket/shared/crypto" + +// Keybase interface implements the CRUD operations for the keybase +type Keybase interface { + // Close the DB connection + Stop() error + + // Create new keypair entry in DB + Create(passphrase string) error + // Insert a new keypair from the private key hex string provided into the DB + ImportFromString(privStr, passphrase string) error + // Insert a new keypair from the JSON string of the encrypted private key into the DB + ImportFromJSON(jsonStr, passphrase string) error + + // Accessors + Get(address string) (KeyPair, error) + GetPubKey(address string) (crypto.PublicKey, error) + GetPrivKey(address, passphrase string) (crypto.PrivateKey, error) + GetAll() (addresses []string, keyPairs []KeyPair, err error) + Exists(address string) (bool, error) + + // Exporters + ExportPrivString(address, passphrase string) (string, error) + ExportPrivJSON(address, passphrase string) (string, error) + + // Updator + UpdatePassphrase(address, oldPassphrase, newPassphrase string) error + + // Sign Messages + Sign(address, passphrase string, msg []byte) ([]byte, error) + Verify(address string, msg, sig []byte) (bool, error) + + // Removals + Delete(address, passphrase string) error +} From 13a49f6f06d99efbc84121b0b5dd5ae4525b0c8b Mon Sep 17 00:00:00 2001 From: Harry Law Date: Wed, 25 Jan 2023 09:43:32 +0000 Subject: [PATCH 28/56] Remove path argument from NewInMemoryDB function, remove Keybase interface into its own file --- app/client/keybase/keystore.go | 42 +++++----------------------------- 1 file changed, 6 insertions(+), 36 deletions(-) diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index 2bdc4aa17..1958fcb76 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -12,6 +12,10 @@ import ( "github.com/pokt-network/pocket/shared/crypto" ) +func ErrorAddrNotFound(addr string) error { + return fmt.Errorf("No key found with address: %s", addr) +} + func init() { gob.Register(crypto.Ed25519PublicKey{}) gob.Register(ed25519.PublicKey{}) @@ -21,40 +25,6 @@ func init() { // badgerKeybase implements the KeyBase interface var _ Keybase = &badgerKeybase{} -// Keybase interface implements the CRUD operations for the keybase -type Keybase interface { - // Close the DB connection - Stop() error - - // Create new keypair entry in DB - Create(passphrase string) error - // Insert a new keypair from the private key hex string provided into the DB - ImportFromString(privStr, passphrase string) error - // Insert a new keypair from the JSON string of the encrypted private key into the DB - ImportFromJSON(jsonStr, passphrase string) error - - // Accessors - Get(address string) (KeyPair, error) - GetPubKey(address string) (crypto.PublicKey, error) - GetPrivKey(address, passphrase string) (crypto.PrivateKey, error) - GetAll() (addresses []string, keyPairs []KeyPair, err error) - Exists(address string) (bool, error) - - // Exporters - ExportPrivString(address, passphrase string) (string, error) - ExportPrivJSON(address, passphrase string) (string, error) - - // Updator - UpdatePassphrase(address, oldPassphrase, newPassphrase string) error - - // Sign Messages - Sign(address, passphrase string, msg []byte) ([]byte, error) - Verify(address string, msg, sig []byte) (bool, error) - - // Removals - Delete(address, passphrase string) error -} - // badgerKeybase implements the Keybase struct using the BadgerDB backend type badgerKeybase struct { db *badger.DB @@ -72,8 +42,8 @@ func NewKeybase(path string) (Keybase, error) { // Creates/Opens the DB in Memory // FOR TESTING PURPOSES ONLY -func NewKeybaseInMemory(path string) (Keybase, error) { - db, err := badger.Open(badgerOptions(path).WithInMemory(true)) +func NewKeybaseInMemory() (Keybase, error) { + db, err := badger.Open(badgerOptions("").WithInMemory(true)) if err != nil { return nil, err } From 026e9dab6ce221409ca8ac50cb614243826b4c4a Mon Sep 17 00:00:00 2001 From: Harry Law Date: Wed, 25 Jan 2023 11:34:02 +0000 Subject: [PATCH 29/56] Seperate keys.go into keypair and armour logic + move into crypto package --- app/client/keybase/keys.go | 314 ------------------------------------- shared/crypto/armour.go | 221 ++++++++++++++++++++++++++ shared/crypto/keypair.go | 98 ++++++++++++ 3 files changed, 319 insertions(+), 314 deletions(-) delete mode 100644 app/client/keybase/keys.go create mode 100644 shared/crypto/armour.go create mode 100644 shared/crypto/keypair.go diff --git a/app/client/keybase/keys.go b/app/client/keybase/keys.go deleted file mode 100644 index 59efb12cd..000000000 --- a/app/client/keybase/keys.go +++ /dev/null @@ -1,314 +0,0 @@ -package keybase - -import ( - "crypto/aes" - "crypto/cipher" - crand "crypto/rand" - "encoding/base64" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "strconv" - "strings" - - poktCrypto "github.com/pokt-network/pocket/shared/crypto" - "golang.org/x/crypto/scrypt" -) - -const ( - // Scrypt params - n = 32768 // CPU/memory cost param; power of 2 greater than 1 - r = 8 // r * p < 2³⁰ - p = 1 // r * p < 2³⁰ - klen = 32 // bytes - // Sec param - secParam = 12 - // No passphrase - defaultPassphrase = "passphrase" -) - -var ( - // Errors - ErrorWrongPassphrase = errors.New("Can't decrypt private key: wrong passphrase") -) - -// KeyPair struct stores the public key and the passphrase encrypted private key -type KeyPair struct { - PublicKey poktCrypto.PublicKey - PrivKeyArmour string -} - -// Generate a new KeyPair struct given the public key and armoured private key -func NewKeyPair(pub poktCrypto.PublicKey, priv string) KeyPair { - return KeyPair{ - PublicKey: pub, - PrivKeyArmour: priv, - } -} - -// Return the byte slice address of the public key -func (kp KeyPair) GetAddressBytes() []byte { - return kp.PublicKey.Address().Bytes() -} - -// Return the string address of the public key -func (kp KeyPair) GetAddressString() string { - return kp.PublicKey.Address().String() -} - -// Unarmour the private key with the passphrase provided -func (kp KeyPair) Unarmour(passphrase string) (poktCrypto.PrivateKey, error) { - return unarmourDecryptPrivKey(kp.PrivKeyArmour, passphrase) -} - -// Export Private Key String -func (kp KeyPair) ExportString(passphrase string) (string, error) { - privKey, err := unarmourDecryptPrivKey(kp.PrivKeyArmour, passphrase) - if err != nil { - return "", err - } - return privKey.String(), nil -} - -// Export Private Key as armoured JSON string with fields to decrypt -func (kp KeyPair) ExportJSON(passphrase string) (string, error) { - _, err := unarmourDecryptPrivKey(kp.PrivKeyArmour, passphrase) - if err != nil { - return "", err - } - return kp.PrivKeyArmour, nil -} - -// Armoured Private Key struct with fields to unarmour it later -type ArmouredKey struct { - Kdf string `json:"kdf"` - Salt string `json:"salt"` - SecParam string `json:"secparam"` - Hint string `json:"hint"` - CipherText string `json:"ciphertext"` -} - -// Generate new armoured private key struct with parameters for unarmouring -func NewArmouredKey(kdf, salt, hint, cipher string) ArmouredKey { - return ArmouredKey{ - Kdf: kdf, - Salt: salt, - SecParam: strconv.Itoa(secParam), - Hint: hint, - CipherText: cipher, - } -} - -// Generate new private ED25519 key and encrypt and armour it as a string -// Returns a KeyPair struct of the Public Key and Armoured String -func CreateNewKey(passphrase string) (KeyPair, error) { - privKey, err := poktCrypto.GeneratePrivateKey() - if err != nil { - return KeyPair{}, err - } - - privArmour, err := encryptArmourPrivKey(privKey, passphrase) - if err != nil || privArmour == "" { - return KeyPair{}, nil - } - - pubKey := privKey.PublicKey() - keyPair := NewKeyPair(pubKey, privArmour) - - return keyPair, nil -} - -// Generate new KeyPair from the hex string provided, encrypt and armour it as a string -func CreateNewKeyFromString(privStr, passphrase string) (KeyPair, error) { - privKey, err := poktCrypto.NewPrivateKey(privStr) - if err != nil { - return KeyPair{}, err - } - - privArmour, err := encryptArmourPrivKey(privKey, passphrase) - if err != nil || privArmour == "" { - return KeyPair{}, nil - } - - pubKey := privKey.PublicKey() - keyPair := NewKeyPair(pubKey, privArmour) - - return keyPair, nil -} - -// Create new KeyPair from the JSON encoded privStr -func ImportKeyFromJSON(jsonStr, passphrase string) (KeyPair, error) { - // Get Private Key from armouredStr - privKey, err := unarmourDecryptPrivKey(jsonStr, passphrase) - if err != nil { - return KeyPair{}, err - } - pubKey := privKey.PublicKey() - keyPair := NewKeyPair(pubKey, jsonStr) - - return keyPair, nil -} - -// Encrypt the given privKey with the passphrase, armour it by encoding the ecnrypted -// []byte into base64, and convert into a json string with the parameters for unarmouring -func encryptArmourPrivKey(privKey poktCrypto.PrivateKey, passphrase string) (string, error) { - // Encrypt privKey usign AES-256 GCM Cipher - saltBytes, encBytes, err := encryptPrivKey(privKey, passphrase) - if err != nil { - return "", err - } - - // Armour encrypted bytes by encoding into Base64 - armourStr := base64.StdEncoding.EncodeToString(encBytes) - - // Create ArmouredKey object so can unarmour later - armoured := NewArmouredKey("scrypt", fmt.Sprintf("%X", saltBytes), "", armourStr) - - // Encode armoured struct into []byte - js, err := json.Marshal(armoured) - if err != nil { - return "", err - } - - return string(js), nil -} - -// Encrypt the given privKey with the passphrase using a randomly -// generated salt and the AES-256 GCM cipher -func encryptPrivKey(privKey poktCrypto.PrivateKey, passphrase string) (saltBytes []byte, encBytes []byte, err error) { - // Use a default passphrase when none is given - if passphrase == "" { - passphrase = defaultPassphrase - } - - // Get random bytes for salt - saltBytes = randBytes(16) - - // Derive key for encryption, see: https://pkg.go.dev/golang.org/x/crypto/scrypt#Key - key, err := scrypt.Key([]byte(passphrase), saltBytes, n, r, p, klen) - if err != nil { - return nil, nil, err - } - - // Encrypt using AES - privKeyHexString := privKey.String() - encBytes, err = encryptAESGCM(key, []byte(privKeyHexString)) - if err != nil { - return nil, nil, err - } - - return saltBytes, encBytes, nil -} - -// Unarmor and decrypt the private key using the passphrase provided -func unarmourDecryptPrivKey(armourStr string, passphrase string) (privKey poktCrypto.PrivateKey, err error) { - // Decode armourStr back into ArmouredKey struct - armouredKey := ArmouredKey{} - err = json.Unmarshal([]byte(armourStr), &armouredKey) - if err != nil { - return nil, err - } - - // Check the ArmouredKey for the correct parameters on kdf and salt - if armouredKey.Kdf != "scrypt" { - return nil, fmt.Errorf("Unrecognized KDF type: %v", armouredKey.Kdf) - } - if armouredKey.Salt == "" { - return nil, fmt.Errorf("Missing salt bytes") - } - - // Decoding the salt - saltBytes, err := hex.DecodeString(armouredKey.Salt) - if err != nil { - return nil, fmt.Errorf("Error decoding salt: %v", err.Error()) - } - - // Decoding the "armoured" ciphertext stored in base64 - encBytes, err := base64.StdEncoding.DecodeString(armouredKey.CipherText) - if err != nil { - return nil, fmt.Errorf("Error decoding ciphertext: %v", err.Error()) - } - - // Decrypt the actual privkey with the parameters - privKey, err = decryptPrivKey(saltBytes, encBytes, passphrase) - - return privKey, err -} - -// Decrypt the AES-256 GCM encrypted bytes using the passphrase given -func decryptPrivKey(saltBytes []byte, encBytes []byte, passphrase string) (poktCrypto.PrivateKey, error) { - // Use a default passphrase when none is given - if passphrase == "" { - passphrase = defaultPassphrase - } - - // Derive key for decryption, see: https://pkg.go.dev/golang.org/x/crypto/scrypt#Key - key, err := scrypt.Key([]byte(passphrase), saltBytes, n, r, p, klen) - if err != nil { - return nil, err - } - - // Decrypt using AES - privKeyRawHexBytes, err := decryptAESGCM(key, encBytes) - if err != nil { - return nil, err - } - bz, err := hex.DecodeString(string(privKeyRawHexBytes)) - if err != nil { - return nil, err - } - - // Get private key from decrypted bytes - pk, err := poktCrypto.NewPrivateKeyFromBytes(bz) - if err != nil { - return nil, err - } - - return pk, nil -} - -// Encrypt using AES-256 GCM Cipher -func encryptAESGCM(key []byte, src []byte) ([]byte, error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - gcm, err := cipher.NewGCM(block) - if err != nil { - return nil, err - } - nonce := key[:12] - out := gcm.Seal(nil, nonce, src, nil) - return out, nil -} - -// Decrypt using AES-256 GCM Cipher -func decryptAESGCM(key []byte, enBytes []byte) ([]byte, error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - gcm, err := cipher.NewGCM(block) - if err != nil { - return nil, err - } - nonce := key[:12] - result, err := gcm.Open(nil, nonce, enBytes, nil) - if err != nil && strings.Contains(err.Error(), "authentication failed") { - return nil, ErrorWrongPassphrase - } else if err != nil { - return nil, fmt.Errorf("Can't Decrypt Using AES : %s \n", err) - } - return result, nil -} - -// Use OS randomness -func randBytes(numBytes int) []byte { - b := make([]byte, numBytes) - _, err := crand.Read(b) - if err != nil { - panic(err) - } - return b -} diff --git a/shared/crypto/armour.go b/shared/crypto/armour.go new file mode 100644 index 000000000..65eca1a90 --- /dev/null +++ b/shared/crypto/armour.go @@ -0,0 +1,221 @@ +package crypto + +import ( + "crypto/aes" + "crypto/cipher" + crand "crypto/rand" + "encoding/base64" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "strconv" + "strings" + + "golang.org/x/crypto/scrypt" +) + +const ( + // Encryption params + kdf = "scrypt" + randBz = 16 + AESNonceSize = 12 + // Scrypt params + n = 32768 // CPU/memory cost param; power of 2 greater than 1 + r = 8 // r * p < 2³⁰ + p = 1 // r * p < 2³⁰ + klen = 32 // bytes + // Sec param + secParam = 12 + // An empty passphrase causes encryption to be unrecoverable + // When no passphrase is given a default passphrase is used instead + defaultPassphrase = "passphrase" +) + +// Errors +var ( + ErrorWrongPassphrase = errors.New("Can't decrypt private key: wrong passphrase") +) + +// Armoured Private Key struct with fields to unarmour it later +type ArmouredKey struct { + Kdf string `json:"kdf"` + Salt string `json:"salt"` + SecParam string `json:"secparam"` + Hint string `json:"hint"` + CipherText string `json:"ciphertext"` +} + +// Generate new armoured private key struct with parameters for unarmouring +func NewArmouredKey(kdf, salt, hint, cipher string) ArmouredKey { + return ArmouredKey{ + Kdf: kdf, + Salt: salt, + SecParam: strconv.Itoa(secParam), + Hint: hint, + CipherText: cipher, + } +} + +// Encrypt the given privKey with the passphrase, armour it by encoding the ecnrypted +// []byte into base64, and convert into a json string with the parameters for unarmouring +func encryptArmourPrivKey(privKey PrivateKey, passphrase string) (string, error) { + // Encrypt privKey usign AES-256 GCM Cipher + saltBz, encBz, err := encryptPrivKey(privKey, passphrase) + if err != nil { + return "", err + } + + // Armour encrypted bytes by encoding into Base64 + armourStr := base64.StdEncoding.EncodeToString(encBz) + + // Create ArmouredKey object so can unarmour later + armoured := NewArmouredKey(kdf, hex.EncodeToString(saltBz), "", armourStr) + + // Encode armoured struct into []byte + js, err := json.Marshal(armoured) + if err != nil { + return "", err + } + + return string(js), nil +} + +// Encrypt the given privKey with the passphrase using a randomly +// generated salt and the AES-256 GCM cipher +func encryptPrivKey(privKey PrivateKey, passphrase string) (saltBz, encBz []byte, err error) { + // Use a default passphrase when none is given + if passphrase == "" { + passphrase = defaultPassphrase + } + + // Get random bytes for salt + saltBz = randBytes(randBz) + + // Derive key for encryption, see: https://pkg.go.dev/golang.org/x/crypto/scrypt#Key + encryptionKey, err := scrypt.Key([]byte(passphrase), saltBz, n, r, p, klen) + if err != nil { + return nil, nil, err + } + + // Encrypt using AES + privKeyHexString := privKey.String() + encBz, err = encryptAESGCM(encryptionKey, []byte(privKeyHexString)) + if err != nil { + return nil, nil, err + } + + return saltBz, encBz, nil +} + +// Unarmor and decrypt the private key using the passphrase provided +func unarmourDecryptPrivKey(armourStr string, passphrase string) (privKey PrivateKey, err error) { + // Decode armourStr back into ArmouredKey struct + armouredKey := ArmouredKey{} + err = json.Unmarshal([]byte(armourStr), &armouredKey) + if err != nil { + return nil, err + } + + // Check the ArmouredKey for the correct parameters on kdf and salt + if armouredKey.Kdf != kdf { + return nil, fmt.Errorf("Unrecognized KDF type: %v", armouredKey.Kdf) + } + if armouredKey.Salt == "" { + return nil, fmt.Errorf("Missing salt bytes") + } + + // Decoding the salt + saltBz, err := hex.DecodeString(armouredKey.Salt) + if err != nil { + return nil, fmt.Errorf("Error decoding salt: %v", err.Error()) + } + + // Decoding the "armoured" ciphertext stored in base64 + encBz, err := base64.StdEncoding.DecodeString(armouredKey.CipherText) + if err != nil { + return nil, fmt.Errorf("Error decoding ciphertext: %v", err.Error()) + } + + // Decrypt the actual privkey with the parameters + privKey, err = decryptPrivKey(saltBz, encBz, passphrase) + + return privKey, err +} + +// Decrypt the AES-256 GCM encrypted bytes using the passphrase given +func decryptPrivKey(saltBz, encBz []byte, passphrase string) (PrivateKey, error) { + // Use a default passphrase when none is given + if passphrase == "" { + passphrase = defaultPassphrase + } + + // Derive key for decryption, see: https://pkg.go.dev/golang.org/x/crypto/scrypt#Key + encryptionKey, err := scrypt.Key([]byte(passphrase), saltBz, n, r, p, klen) + if err != nil { + return nil, err + } + + // Decrypt using AES + privKeyRawHexBz, err := decryptAESGCM(encryptionKey, encBz) + if err != nil { + return nil, err + } + bz, err := hex.DecodeString(string(privKeyRawHexBz)) + if err != nil { + return nil, err + } + + // Get private key from decrypted bytes + pk, err := NewPrivateKeyFromBytes(bz) + if err != nil { + return nil, err + } + + return pk, nil +} + +// Encrypt using AES-256 GCM Cipher +func encryptAESGCM(key, plaintext []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + nonce := key[:12] + encBz := gcm.Seal(nil, nonce, plaintext, nil) + return encBz, nil +} + +// Decrypt using AES-256 GCM Cipher +func decryptAESGCM(key, encBz []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + nonce := key[:12] + result, err := gcm.Open(nil, nonce, encBz, nil) + if err != nil && strings.Contains(err.Error(), "authentication failed") { + return nil, ErrorWrongPassphrase + } else if err != nil { + return nil, fmt.Errorf("Can't Decrypt Using AES : %s \n", err) + } + return result, nil +} + +// Use OS randomness +func randBytes(numBytes int) []byte { + b := make([]byte, numBytes) + _, err := crand.Read(b) + if err != nil { + panic(err) + } + return b +} diff --git a/shared/crypto/keypair.go b/shared/crypto/keypair.go new file mode 100644 index 000000000..fa7169bdc --- /dev/null +++ b/shared/crypto/keypair.go @@ -0,0 +1,98 @@ +package crypto + +// KeyPair struct stores the public key and the passphrase encrypted private key +type KeyPair struct { + PublicKey PublicKey + PrivKeyArmour string +} + +// Generate a new KeyPair struct given the public key and armoured private key +func NewKeyPair(pub PublicKey, priv string) KeyPair { + return KeyPair{ + PublicKey: pub, + PrivKeyArmour: priv, + } +} + +// Return the byte slice address of the public key +func (kp KeyPair) GetAddressBytes() []byte { + return kp.PublicKey.Address().Bytes() +} + +// Return the string address of the public key +func (kp KeyPair) GetAddressString() string { + return kp.PublicKey.Address().String() +} + +// Unarmour the private key with the passphrase provided +func (kp KeyPair) Unarmour(passphrase string) (PrivateKey, error) { + return unarmourDecryptPrivKey(kp.PrivKeyArmour, passphrase) +} + +// Export Private Key String +func (kp KeyPair) ExportString(passphrase string) (string, error) { + privKey, err := unarmourDecryptPrivKey(kp.PrivKeyArmour, passphrase) + if err != nil { + return "", err + } + return privKey.String(), nil +} + +// Export Private Key as armoured JSON string with fields to decrypt +func (kp KeyPair) ExportJSON(passphrase string) (string, error) { + _, err := unarmourDecryptPrivKey(kp.PrivKeyArmour, passphrase) + if err != nil { + return "", err + } + return kp.PrivKeyArmour, 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 +func CreateNewKey(passphrase string) (KeyPair, error) { + privKey, err := GeneratePrivateKey() + if err != nil { + return KeyPair{}, err + } + + privArmour, err := encryptArmourPrivKey(privKey, passphrase) + if err != nil || privArmour == "" { + return KeyPair{}, err + } + + pubKey := privKey.PublicKey() + keyPair := NewKeyPair(pubKey, privArmour) + + return keyPair, nil +} + +// Generate new KeyPair from the hex string provided, encrypt and armour it as a string +func CreateNewKeyFromString(privStrHex, passphrase string) (KeyPair, error) { + privKey, err := NewPrivateKey(privStrHex) + if err != nil { + return KeyPair{}, err + } + + privArmour, err := encryptArmourPrivKey(privKey, passphrase) + if err != nil || privArmour == "" { + return KeyPair{}, err + } + + pubKey := privKey.PublicKey() + keyPair := NewKeyPair(pubKey, privArmour) + + return keyPair, nil +} + +// Create new KeyPair from the JSON encoded privStr +func ImportKeyFromJSON(jsonStr, passphrase string) (KeyPair, error) { + // Get Private Key from armouredStr + privKey, err := unarmourDecryptPrivKey(jsonStr, passphrase) + if err != nil { + return KeyPair{}, err + } + pubKey := privKey.PublicKey() + keyPair := NewKeyPair(pubKey, jsonStr) + + return keyPair, nil +} From 5a2d059ef7276b7a5e0acf0182c7218938dc0954 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Wed, 25 Jan 2023 11:34:49 +0000 Subject: [PATCH 30/56] Fix imports --- app/client/keybase/keybase_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go index 05c155183..1b4ca94ff 100644 --- a/app/client/keybase/keybase_test.go +++ b/app/client/keybase/keybase_test.go @@ -181,7 +181,7 @@ func TestKeybase_GetKeyDoesntExist(t *testing.T) { kp, err := db.Get(testKey.Address().String()) require.EqualError(t, err, ErrorAddrNotFound(testKey.Address().String()).Error()) - require.Equal(t, kp, KeyPair{}) + require.Equal(t, kp, crypto.KeyPair{}) } func TestKeybase_CheckKeyExists(t *testing.T) { @@ -284,7 +284,7 @@ func TestKeybase_GetPrivKeyWrongPassphrase(t *testing.T) { require.NoError(t, err) privKey, err := db.GetPrivKey(testKey.Address().String(), testNewPassphrase) - require.Equal(t, err, ErrorWrongPassphrase) + require.Equal(t, err, crypto.ErrorWrongPassphrase) require.Nil(t, privKey) } @@ -326,7 +326,7 @@ func TestKeybase_UpdatePassphraseWrongPassphrase(t *testing.T) { require.NoError(t, err) err = db.UpdatePassphrase(testKey.Address().String(), testNewPassphrase, testNewPassphrase) - require.ErrorIs(t, err, ErrorWrongPassphrase) + require.ErrorIs(t, err, crypto.ErrorWrongPassphrase) } func TestKeybase_DeleteKey(t *testing.T) { @@ -346,7 +346,7 @@ func TestKeybase_DeleteKey(t *testing.T) { kp, err := db.Get(testKey.Address().String()) require.EqualError(t, err, ErrorAddrNotFound(testKey.Address().String()).Error()) - require.Equal(t, kp, KeyPair{}) + require.Equal(t, kp, crypto.KeyPair{}) } func TestKeybase_DeleteKeyWrongPassphrase(t *testing.T) { @@ -362,7 +362,7 @@ func TestKeybase_DeleteKeyWrongPassphrase(t *testing.T) { require.NoError(t, err) err = db.Delete(testKey.Address().String(), testNewPassphrase) - require.ErrorIs(t, err, ErrorWrongPassphrase) + require.ErrorIs(t, err, crypto.ErrorWrongPassphrase) } func TestKeybase_SignMessage(t *testing.T) { @@ -404,7 +404,7 @@ func TestKeybase_SignMessageWrongPassphrase(t *testing.T) { require.NoError(t, err) signedMsg, err := db.Sign(privKey.Address().String(), testNewPassphrase, txBz) - require.ErrorIs(t, err, ErrorWrongPassphrase) + require.ErrorIs(t, err, crypto.ErrorWrongPassphrase) require.Nil(t, signedMsg) } From 9ee2f9b91c5a092e9e0797378c85816c7d666f1d Mon Sep 17 00:00:00 2001 From: Harry Law Date: Wed, 25 Jan 2023 11:35:29 +0000 Subject: [PATCH 31/56] Fix imports --- app/client/keybase/keybase.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/client/keybase/keybase.go b/app/client/keybase/keybase.go index a432fb07d..5ae7c0b2d 100644 --- a/app/client/keybase/keybase.go +++ b/app/client/keybase/keybase.go @@ -15,10 +15,10 @@ type Keybase interface { ImportFromJSON(jsonStr, passphrase string) error // Accessors - Get(address string) (KeyPair, error) + Get(address string) (crypto.KeyPair, error) GetPubKey(address string) (crypto.PublicKey, error) GetPrivKey(address, passphrase string) (crypto.PrivateKey, error) - GetAll() (addresses []string, keyPairs []KeyPair, err error) + GetAll() (addresses []string, keyPairs []crypto.KeyPair, err error) Exists(address string) (bool, error) // Exporters From beb2cb5ac80e8e6dd3afeeea4167735c3de0b8ad Mon Sep 17 00:00:00 2001 From: Harry Law Date: Wed, 25 Jan 2023 11:36:10 +0000 Subject: [PATCH 32/56] Fix imports, change Delete() logic to not use Update() wrapper, add comments, address review --- app/client/keybase/keystore.go | 93 +++++++++++++++++----------------- 1 file changed, 46 insertions(+), 47 deletions(-) diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index 1958fcb76..6999c1f24 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -12,14 +12,16 @@ import ( "github.com/pokt-network/pocket/shared/crypto" ) +// Errors func ErrorAddrNotFound(addr string) error { return fmt.Errorf("No key found with address: %s", addr) } +// Encoding is used to serialise the data to store the KeyPairs in the database func init() { gob.Register(crypto.Ed25519PublicKey{}) gob.Register(ed25519.PublicKey{}) - gob.Register(KeyPair{}) + gob.Register(crypto.KeyPair{}) } // badgerKeybase implements the KeyBase interface @@ -55,25 +57,25 @@ func (keybase *badgerKeybase) Stop() error { return keybase.db.Close() } -// Crate a new key and store it in the DB by encoding the KeyPair struct into a []byte +// Create a new key and store it in the DB by encoding the KeyPair struct into a []byte // Using the PublicKey.Address() return value as the key for storage func (keybase *badgerKeybase) Create(passphrase string) error { err := keybase.db.Update(func(tx *badger.Txn) error { - keyPair, err := CreateNewKey(passphrase) + keyPair, err := crypto.CreateNewKey(passphrase) if err != nil { return err } // Use key address as key in DB - key := keyPair.GetAddressBytes() + addrKey := keyPair.GetAddressBytes() // Encode entire KeyPair struct into []byte for value - bz := new(bytes.Buffer) - enc := gob.NewEncoder(bz) + keypairBz := new(bytes.Buffer) + enc := gob.NewEncoder(keypairBz) if err = enc.Encode(keyPair); err != nil { return err } - err = tx.Set(key, bz.Bytes()) + err = tx.Set(addrKey, keypairBz.Bytes()) if err != nil { return err } @@ -84,25 +86,25 @@ func (keybase *badgerKeybase) Create(passphrase string) error { return err } -// Crate a new KeyPair from the private key hex string and store it in the DB by encoding the KeyPair struct into a []byte +// Create a new KeyPair from the private key hex string and store it in the DB by encoding the KeyPair struct into a []byte // Using the PublicKey.Address() return value as the key for storage -func (keybase *badgerKeybase) ImportFromString(privStr, passphrase string) error { +func (keybase *badgerKeybase) ImportFromString(privKeyHex, passphrase string) error { err := keybase.db.Update(func(tx *badger.Txn) error { - keyPair, err := CreateNewKeyFromString(privStr, passphrase) + keyPair, err := crypto.CreateNewKeyFromString(privKeyHex, passphrase) if err != nil { return err } // Use key address as key in DB - key := keyPair.GetAddressBytes() + addrKey := keyPair.GetAddressBytes() // Encode entire KeyPair struct into []byte for value - bz := new(bytes.Buffer) - enc := gob.NewEncoder(bz) + keypairBz := new(bytes.Buffer) + enc := gob.NewEncoder(keypairBz) if err = enc.Encode(keyPair); err != nil { return err } - err = tx.Set(key, bz.Bytes()) + err = tx.Set(addrKey, keypairBz.Bytes()) if err != nil { return err } @@ -113,25 +115,25 @@ func (keybase *badgerKeybase) ImportFromString(privStr, passphrase string) error return err } -// Crate a new KeyPair from the private key JSON string and store it in the DB by encoding the KeyPair struct into a []byte +// Create a new KeyPair from the private key JSON string and store it in the DB by encoding the KeyPair struct into a []byte // Using the PublicKey.Address() return value as the key for storage func (keybase *badgerKeybase) ImportFromJSON(jsonStr, passphrase string) error { err := keybase.db.Update(func(tx *badger.Txn) error { - keyPair, err := ImportKeyFromJSON(jsonStr, passphrase) + keyPair, err := crypto.ImportKeyFromJSON(jsonStr, passphrase) if err != nil { return err } // Use key address as key in DB - key := keyPair.GetAddressBytes() + addrKey := keyPair.GetAddressBytes() // Encode entire KeyPair struct into []byte for value - bz := new(bytes.Buffer) - enc := gob.NewEncoder(bz) + keypairBz := new(bytes.Buffer) + enc := gob.NewEncoder(keypairBz) if err = enc.Encode(keyPair); err != nil { return err } - err = tx.Set(key, bz.Bytes()) + err = tx.Set(addrKey, keypairBz.Bytes()) if err != nil { return err } @@ -143,17 +145,17 @@ func (keybase *badgerKeybase) ImportFromJSON(jsonStr, passphrase string) error { } // Returns a KeyPair struct provided the address was found in the DB -func (keybase *badgerKeybase) Get(address string) (KeyPair, error) { - var kp KeyPair - bz := new(bytes.Buffer) +func (keybase *badgerKeybase) Get(address string) (crypto.KeyPair, error) { + var kp crypto.KeyPair + keypairBz := new(bytes.Buffer) addrBz, err := hex.DecodeString(address) if err != nil { - return KeyPair{}, err + return crypto.KeyPair{}, err } err = keybase.db.View(func(tx *badger.Txn) error { item, err := tx.Get(addrBz) - if err != nil && strings.Contains(err.Error(), "Key not found") { + if err != nil && strings.Contains(err.Error(), "not found") { return ErrorAddrNotFound(address) } else if err != nil { return err @@ -165,8 +167,8 @@ func (keybase *badgerKeybase) Get(address string) (KeyPair, error) { } // Decode []byte value back into KeyPair struct - bz.Write(value) - dec := gob.NewDecoder(bz) + keypairBz.Write(value) + dec := gob.NewDecoder(keypairBz) if err = dec.Decode(&kp); err != nil { return err } @@ -174,7 +176,7 @@ func (keybase *badgerKeybase) Get(address string) (KeyPair, error) { return nil }) if err != nil { - return KeyPair{}, err + return crypto.KeyPair{}, err } return kp, nil @@ -207,7 +209,7 @@ func (keybase *badgerKeybase) GetPrivKey(address, passphrase string) (crypto.Pri // Get all the addresses and key pairs stored in the keybase // Returns addresses stored and all the KeyPair structs stored in the DB -func (keybase *badgerKeybase) GetAll() (addresses []string, keyPairs []KeyPair, err error) { +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 { opts := badger.DefaultIteratorOptions @@ -220,10 +222,10 @@ func (keybase *badgerKeybase) GetAll() (addresses []string, keyPairs []KeyPair, b := make([]byte, len(val)) copy(b, val) // Decode []byte value back into KeyPair struct - var kp KeyPair - bz := new(bytes.Buffer) - bz.Write(b) - dec := gob.NewDecoder(bz) + var kp crypto.KeyPair + keypairBz := new(bytes.Buffer) + keypairBz.Write(b) + dec := gob.NewDecoder(keypairBz) if err = dec.Decode(&kp); err != nil { return err } @@ -252,7 +254,7 @@ func (keybase *badgerKeybase) Exists(address string) (bool, error) { if err != nil { return false, err } - return val != KeyPair{}, nil + return val != crypto.KeyPair{}, nil } // Export the Private Key string of the given address @@ -287,24 +289,24 @@ func (keybase *badgerKeybase) UpdatePassphrase(address, oldPassphrase, newPassph } err = keybase.db.Update(func(tx *badger.Txn) error { - keyPair, err := CreateNewKeyFromString(privStr, newPassphrase) + keyPair, err := crypto.CreateNewKeyFromString(privStr, newPassphrase) if err != nil { return err } // Use key address as key in DB - key := keyPair.GetAddressBytes() - if bytes.Compare(key, addrBz) != 0 { + addrKey := keyPair.GetAddressBytes() + if bytes.Compare(addrKey, addrBz) != 0 { return fmt.Errorf("Key address does not match previous address.") } // Encode entire KeyPair struct into []byte for value - bz := new(bytes.Buffer) - enc := gob.NewEncoder(bz) + keypairBz := new(bytes.Buffer) + enc := gob.NewEncoder(keypairBz) if err = enc.Encode(keyPair); err != nil { return err } - err = tx.Set(key, bz.Bytes()) + err = tx.Set(addrKey, keypairBz.Bytes()) if err != nil { return err } @@ -336,8 +338,7 @@ func (keybase *badgerKeybase) Verify(address string, msg, sig []byte) (bool, err // Remove a KeyPair from the DB given the address func (keybase *badgerKeybase) Delete(address, passphrase string) error { - _, err := keybase.GetPrivKey(address, passphrase) - if err != nil { + if _, err := keybase.GetPrivKey(address, passphrase); err != nil { return err } @@ -346,11 +347,9 @@ func (keybase *badgerKeybase) Delete(address, passphrase string) error { return err } - err = keybase.db.Update(func(tx *badger.Txn) error { - tx.Delete(addrBz) - return nil - }) - return err + tx := keybase.db.NewTransaction(true) + defer tx.Discard() + return tx.Delete(addrBz) } // Return badger.Options for the given DB path - disable logging From c3aef0750325f050bb67a50feed53a19d3e71291 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Wed, 25 Jan 2023 11:46:33 +0000 Subject: [PATCH 33/56] Combine if and error checking where appropriate - make delete use Update wrapper --- app/client/keybase/keystore.go | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index 6999c1f24..1ed96fb44 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -75,8 +75,7 @@ func (keybase *badgerKeybase) Create(passphrase string) error { return err } - err = tx.Set(addrKey, keypairBz.Bytes()) - if err != nil { + if err = tx.Set(addrKey, keypairBz.Bytes()); err != nil { return err } @@ -104,8 +103,7 @@ func (keybase *badgerKeybase) ImportFromString(privKeyHex, passphrase string) er return err } - err = tx.Set(addrKey, keypairBz.Bytes()) - if err != nil { + if err = tx.Set(addrKey, keypairBz.Bytes()); err != nil { return err } @@ -133,8 +131,7 @@ func (keybase *badgerKeybase) ImportFromJSON(jsonStr, passphrase string) error { return err } - err = tx.Set(addrKey, keypairBz.Bytes()) - if err != nil { + if err = tx.Set(addrKey, keypairBz.Bytes()); err != nil { return err } @@ -306,8 +303,7 @@ func (keybase *badgerKeybase) UpdatePassphrase(address, oldPassphrase, newPassph return err } - err = tx.Set(addrKey, keypairBz.Bytes()) - if err != nil { + if err = tx.Set(addrKey, keypairBz.Bytes()); err != nil { return err } @@ -347,9 +343,11 @@ func (keybase *badgerKeybase) Delete(address, passphrase string) error { return err } - tx := keybase.db.NewTransaction(true) - defer tx.Discard() - return tx.Delete(addrBz) + err = keybase.db.Update(func(tx *badger.Txn) error { + tx.Delete(addrBz) + return nil + }) + return err } // Return badger.Options for the given DB path - disable logging From 82e51b258c2504be38bec197fd1808c04c15cc63 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Wed, 25 Jan 2023 12:08:44 +0000 Subject: [PATCH 34/56] Move shared logic for import functions into a helper function --- app/client/keybase/keystore.go | 44 +++++++++++----------------------- 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index 1ed96fb44..5cbdbe8f9 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -57,11 +57,11 @@ func (keybase *badgerKeybase) Stop() error { return keybase.db.Close() } -// Create a new key and store it in the DB by encoding the KeyPair struct into a []byte -// Using the PublicKey.Address() return value as the key for storage -func (keybase *badgerKeybase) Create(passphrase string) error { +// Use the supplied function func(privStr, passphrase) (KeyPair, error) +// to create the new keypair and then insert it into the DB +func (keybase *badgerKeybase) newKeyPairFromString(fn func(string, string) (crypto.KeyPair, error), privStr, passphrase string) error { err := keybase.db.Update(func(tx *badger.Txn) error { - keyPair, err := crypto.CreateNewKey(passphrase) + keyPair, err := fn(privStr, passphrase) if err != nil { return err } @@ -85,11 +85,11 @@ func (keybase *badgerKeybase) Create(passphrase string) error { return err } -// Create a new KeyPair from the private key hex string and store it in the DB by encoding the KeyPair struct into a []byte +// Create a new key and store it in the DB by encoding the KeyPair struct into a []byte // Using the PublicKey.Address() return value as the key for storage -func (keybase *badgerKeybase) ImportFromString(privKeyHex, passphrase string) error { +func (keybase *badgerKeybase) Create(passphrase string) error { err := keybase.db.Update(func(tx *badger.Txn) error { - keyPair, err := crypto.CreateNewKeyFromString(privKeyHex, passphrase) + keyPair, err := crypto.CreateNewKey(passphrase) if err != nil { return err } @@ -113,32 +113,16 @@ func (keybase *badgerKeybase) ImportFromString(privKeyHex, passphrase string) er return err } +// Create a new KeyPair from the private key hex string and store it in the DB by encoding the KeyPair struct into a []byte +// Using the PublicKey.Address() return value as the key for storage +func (keybase *badgerKeybase) ImportFromString(privKeyHex, passphrase string) error { + return keybase.newKeyPairFromString(crypto.CreateNewKeyFromString, privKeyHex, passphrase) +} + // Create a new KeyPair from the private key JSON string and store it in the DB by encoding the KeyPair struct into a []byte // Using the PublicKey.Address() return value as the key for storage func (keybase *badgerKeybase) ImportFromJSON(jsonStr, passphrase string) error { - err := keybase.db.Update(func(tx *badger.Txn) error { - keyPair, err := crypto.ImportKeyFromJSON(jsonStr, passphrase) - if err != nil { - return err - } - - // Use key address as key in DB - addrKey := keyPair.GetAddressBytes() - // Encode entire KeyPair struct into []byte for value - keypairBz := new(bytes.Buffer) - enc := gob.NewEncoder(keypairBz) - if err = enc.Encode(keyPair); err != nil { - return err - } - - if err = tx.Set(addrKey, keypairBz.Bytes()); err != nil { - return err - } - - return nil - }) - - return err + return keybase.newKeyPairFromString(crypto.ImportKeyFromJSON, jsonStr, passphrase) } // Returns a KeyPair struct provided the address was found in the DB From 39e650e56487a291639671de0431ecfcc4d46874 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Wed, 25 Jan 2023 13:20:37 +0000 Subject: [PATCH 35/56] Make KeyPair an interface and EncKeyPair struct implement the interface --- app/client/keybase/keybase_test.go | 10 ++--- app/client/keybase/keystore.go | 16 ++++---- shared/crypto/armour.go | 4 +- shared/crypto/keypair.go | 66 ++++++++++++++++++++++-------- 4 files changed, 63 insertions(+), 33 deletions(-) diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go index 1b4ca94ff..10c811f52 100644 --- a/app/client/keybase/keybase_test.go +++ b/app/client/keybase/keybase_test.go @@ -81,7 +81,7 @@ func TestKeybase_ImportKeyFromString(t *testing.T) { require.Equal(t, len(kp.GetAddressBytes()), crypto.AddressLen) require.Equal(t, addr, kp.GetAddressString()) require.Equal(t, kp.GetAddressString(), testAddr) - require.Equal(t, kp.PublicKey.String(), testPubString) + require.Equal(t, kp.GetPublicKey().String(), testPubString) privKey, err := kp.Unarmour(testPassphrase) require.NoError(t, err) @@ -105,7 +105,7 @@ func TestKeybase_ImportKeyFromStringNoPassphrase(t *testing.T) { require.Equal(t, len(kp.GetAddressBytes()), crypto.AddressLen) require.Equal(t, addr, kp.GetAddressString()) require.Equal(t, kp.GetAddressString(), testAddr) - require.Equal(t, kp.PublicKey.String(), testPubString) + require.Equal(t, kp.GetPublicKey().String(), testPubString) privKey, err := kp.Unarmour("") require.NoError(t, err) @@ -144,7 +144,7 @@ func TestKeybase_ImportKeyFromJSON(t *testing.T) { require.Equal(t, len(kp.GetAddressBytes()), crypto.AddressLen) require.Equal(t, addr, kp.GetAddressString()) require.Equal(t, kp.GetAddressString(), testJSONAddr) - require.Equal(t, kp.PublicKey.String(), testJSONPubString) + require.Equal(t, kp.GetPublicKey().String(), testJSONPubString) privKey, err := kp.Unarmour(testPassphrase) require.NoError(t, err) @@ -181,7 +181,7 @@ func TestKeybase_GetKeyDoesntExist(t *testing.T) { kp, err := db.Get(testKey.Address().String()) require.EqualError(t, err, ErrorAddrNotFound(testKey.Address().String()).Error()) - require.Equal(t, kp, crypto.KeyPair{}) + require.Equal(t, kp, nil) } func TestKeybase_CheckKeyExists(t *testing.T) { @@ -346,7 +346,7 @@ func TestKeybase_DeleteKey(t *testing.T) { kp, err := db.Get(testKey.Address().String()) require.EqualError(t, err, ErrorAddrNotFound(testKey.Address().String()).Error()) - require.Equal(t, kp, crypto.KeyPair{}) + require.Equal(t, kp, nil) } func TestKeybase_DeleteKeyWrongPassphrase(t *testing.T) { diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index 5cbdbe8f9..739f98191 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -21,7 +21,7 @@ func ErrorAddrNotFound(addr string) error { func init() { gob.Register(crypto.Ed25519PublicKey{}) gob.Register(ed25519.PublicKey{}) - gob.Register(crypto.KeyPair{}) + gob.Register(crypto.EncKeyPair{}) } // badgerKeybase implements the KeyBase interface @@ -127,11 +127,11 @@ func (keybase *badgerKeybase) ImportFromJSON(jsonStr, passphrase string) error { // Returns a KeyPair struct provided the address was found in the DB func (keybase *badgerKeybase) Get(address string) (crypto.KeyPair, error) { - var kp crypto.KeyPair + var kp crypto.EncKeyPair keypairBz := new(bytes.Buffer) addrBz, err := hex.DecodeString(address) if err != nil { - return crypto.KeyPair{}, err + return nil, err } err = keybase.db.View(func(tx *badger.Txn) error { @@ -157,7 +157,7 @@ func (keybase *badgerKeybase) Get(address string) (crypto.KeyPair, error) { return nil }) if err != nil { - return crypto.KeyPair{}, err + return nil, err } return kp, nil @@ -170,7 +170,7 @@ func (keybase *badgerKeybase) GetPubKey(address string) (crypto.PublicKey, error return nil, err } - return kp.PublicKey, nil + return kp.GetPublicKey(), nil } // Returns a PrivateKey interface provided the address was found in the DB and the passphrase was correct @@ -203,7 +203,7 @@ func (keybase *badgerKeybase) GetAll() (addresses []string, keyPairs []crypto.Ke b := make([]byte, len(val)) copy(b, val) // Decode []byte value back into KeyPair struct - var kp crypto.KeyPair + var kp crypto.EncKeyPair keypairBz := new(bytes.Buffer) keypairBz.Write(b) dec := gob.NewDecoder(keypairBz) @@ -235,7 +235,7 @@ func (keybase *badgerKeybase) Exists(address string) (bool, error) { if err != nil { return false, err } - return val != crypto.KeyPair{}, nil + return val != nil, nil } // Export the Private Key string of the given address @@ -312,7 +312,7 @@ func (keybase *badgerKeybase) Verify(address string, msg, sig []byte) (bool, err if err != nil { return false, err } - pubKey := kp.PublicKey + pubKey := kp.GetPublicKey() return pubKey.Verify(msg, sig), nil } diff --git a/shared/crypto/armour.go b/shared/crypto/armour.go index 65eca1a90..9af79a0d4 100644 --- a/shared/crypto/armour.go +++ b/shared/crypto/armour.go @@ -185,7 +185,7 @@ func encryptAESGCM(key, plaintext []byte) ([]byte, error) { if err != nil { return nil, err } - nonce := key[:12] + nonce := key[:AESNonceSize] encBz := gcm.Seal(nil, nonce, plaintext, nil) return encBz, nil } @@ -200,7 +200,7 @@ func decryptAESGCM(key, encBz []byte) ([]byte, error) { if err != nil { return nil, err } - nonce := key[:12] + nonce := key[:AESNonceSize] result, err := gcm.Open(nil, nonce, encBz, nil) if err != nil && strings.Contains(err.Error(), "authentication failed") { return nil, ErrorWrongPassphrase diff --git a/shared/crypto/keypair.go b/shared/crypto/keypair.go index fa7169bdc..fd051dee6 100644 --- a/shared/crypto/keypair.go +++ b/shared/crypto/keypair.go @@ -1,36 +1,66 @@ package crypto +// The KeyPair interface exposes functions relating to public and encrypted private key pairs +type KeyPair interface { + // Accessors + GetPublicKey() PublicKey + GetPrivArmour() string + + // Public Key Address + GetAddressBytes() []byte + GetAddressString() string // hex string + + // Unarmour + Unarmour(passphrase string) (PrivateKey, error) + + // Export + ExportString(passphrase string) (string, error) + ExportJSON(passphrase string) (string, error) +} + +var _ KeyPair = &EncKeyPair{} + // KeyPair struct stores the public key and the passphrase encrypted private key -type KeyPair struct { +type EncKeyPair struct { PublicKey PublicKey PrivKeyArmour string } // Generate a new KeyPair struct given the public key and armoured private key func NewKeyPair(pub PublicKey, priv string) KeyPair { - return KeyPair{ + return EncKeyPair{ PublicKey: pub, PrivKeyArmour: priv, } } +// Return the public key +func (kp EncKeyPair) GetPublicKey() PublicKey { + return kp.PublicKey +} + +// Return private key armoured string +func (kp EncKeyPair) GetPrivArmour() string { + return kp.PrivKeyArmour +} + // Return the byte slice address of the public key -func (kp KeyPair) GetAddressBytes() []byte { +func (kp EncKeyPair) GetAddressBytes() []byte { return kp.PublicKey.Address().Bytes() } // Return the string address of the public key -func (kp KeyPair) GetAddressString() string { +func (kp EncKeyPair) GetAddressString() string { return kp.PublicKey.Address().String() } // Unarmour the private key with the passphrase provided -func (kp KeyPair) Unarmour(passphrase string) (PrivateKey, error) { +func (kp EncKeyPair) Unarmour(passphrase string) (PrivateKey, error) { return unarmourDecryptPrivKey(kp.PrivKeyArmour, passphrase) } // Export Private Key String -func (kp KeyPair) ExportString(passphrase string) (string, error) { +func (kp EncKeyPair) ExportString(passphrase string) (string, error) { privKey, err := unarmourDecryptPrivKey(kp.PrivKeyArmour, passphrase) if err != nil { return "", err @@ -39,7 +69,7 @@ func (kp KeyPair) ExportString(passphrase string) (string, error) { } // Export Private Key as armoured JSON string with fields to decrypt -func (kp KeyPair) ExportJSON(passphrase string) (string, error) { +func (kp EncKeyPair) ExportJSON(passphrase string) (string, error) { _, err := unarmourDecryptPrivKey(kp.PrivKeyArmour, passphrase) if err != nil { return "", err @@ -52,36 +82,36 @@ func (kp KeyPair) ExportJSON(passphrase string) (string, error) { func CreateNewKey(passphrase string) (KeyPair, error) { privKey, err := GeneratePrivateKey() if err != nil { - return KeyPair{}, err + return nil, err } privArmour, err := encryptArmourPrivKey(privKey, passphrase) if err != nil || privArmour == "" { - return KeyPair{}, err + return nil, err } pubKey := privKey.PublicKey() - keyPair := NewKeyPair(pubKey, privArmour) + kp := NewKeyPair(pubKey, privArmour) - return keyPair, nil + return kp, nil } // Generate new KeyPair from the hex string provided, encrypt and armour it as a string func CreateNewKeyFromString(privStrHex, passphrase string) (KeyPair, error) { privKey, err := NewPrivateKey(privStrHex) if err != nil { - return KeyPair{}, err + return nil, err } privArmour, err := encryptArmourPrivKey(privKey, passphrase) if err != nil || privArmour == "" { - return KeyPair{}, err + return nil, err } pubKey := privKey.PublicKey() - keyPair := NewKeyPair(pubKey, privArmour) + kp := NewKeyPair(pubKey, privArmour) - return keyPair, nil + return kp, nil } // Create new KeyPair from the JSON encoded privStr @@ -89,10 +119,10 @@ func ImportKeyFromJSON(jsonStr, passphrase string) (KeyPair, error) { // Get Private Key from armouredStr privKey, err := unarmourDecryptPrivKey(jsonStr, passphrase) if err != nil { - return KeyPair{}, err + return nil, err } pubKey := privKey.PublicKey() - keyPair := NewKeyPair(pubKey, jsonStr) + kp := NewKeyPair(pubKey, jsonStr) - return keyPair, nil + return kp, nil } From f1b6bda0df21a9448f162c7a8f17f42a2363c0c2 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Wed, 25 Jan 2023 13:27:33 +0000 Subject: [PATCH 36/56] Fix encryption with no passphrase --- app/client/keybase/keybase_test.go | 5 +++-- shared/crypto/armour.go | 17 ++--------------- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go index 10c811f52..297d6528c 100644 --- a/app/client/keybase/keybase_test.go +++ b/app/client/keybase/keybase_test.go @@ -26,6 +26,7 @@ const ( testJSONPubString = "408bec6320b540aa0cc86b3e633e214f2fd4dce4caa08f164fa3a9d3e577b46c" testJSONPrivString = "3554119cec1c0c8c5b3845a5d3fc6346eb44ed21aab5c063ae9b6b1d38bec275408bec6320b540aa0cc86b3e633e214f2fd4dce4caa08f164fa3a9d3e577b46c" testJSONString = `{"kdf":"scrypt","salt":"197d2754445a7e5ce3e6c8d7b1d0ff6f","secparam":"12","hint":"pocket wallet","ciphertext":"B/AORJrSeQrR5ewQGel4FeCCXscoCsMUzq9gXAAxDqjXMmMxa7TedBTuemtO82JyTCoQWFHbGxRx8A7IoETNh5T5yBAjNNrr7DDkVrcfSAM3ez9lQem17DsfowCvRtmbesDlvbSZMRy8mQgClLqWRN+c6W/fPQ/lxLUy1G1A965U/uImcMXzSwbfqYrBPEux"}` + testJSONPassphrase = "Testing@Testing123" ) func TestKeybase_CreateNewKey(t *testing.T) { @@ -131,7 +132,7 @@ func TestKeybase_ImportKeyFromJSON(t *testing.T) { db := initDB(t) defer db.Stop() - err := db.ImportFromJSON(testJSONString, testPassphrase) + err := db.ImportFromJSON(testJSONString, testJSONPassphrase) require.NoError(t, err) addresses, keypairs, err := db.GetAll() @@ -146,7 +147,7 @@ func TestKeybase_ImportKeyFromJSON(t *testing.T) { require.Equal(t, kp.GetAddressString(), testJSONAddr) require.Equal(t, kp.GetPublicKey().String(), testJSONPubString) - privKey, err := kp.Unarmour(testPassphrase) + privKey, err := kp.Unarmour(testJSONPassphrase) require.NoError(t, err) require.Equal(t, privKey.String(), testJSONPrivString) } diff --git a/shared/crypto/armour.go b/shared/crypto/armour.go index 9af79a0d4..97ff7628f 100644 --- a/shared/crypto/armour.go +++ b/shared/crypto/armour.go @@ -27,9 +27,6 @@ const ( klen = 32 // bytes // Sec param secParam = 12 - // An empty passphrase causes encryption to be unrecoverable - // When no passphrase is given a default passphrase is used instead - defaultPassphrase = "passphrase" ) // Errors @@ -67,7 +64,7 @@ func encryptArmourPrivKey(privKey PrivateKey, passphrase string) (string, error) } // Armour encrypted bytes by encoding into Base64 - armourStr := base64.StdEncoding.EncodeToString(encBz) + armourStr := base64.RawStdEncoding.EncodeToString(encBz) // Create ArmouredKey object so can unarmour later armoured := NewArmouredKey(kdf, hex.EncodeToString(saltBz), "", armourStr) @@ -84,11 +81,6 @@ func encryptArmourPrivKey(privKey PrivateKey, passphrase string) (string, error) // Encrypt the given privKey with the passphrase using a randomly // generated salt and the AES-256 GCM cipher func encryptPrivKey(privKey PrivateKey, passphrase string) (saltBz, encBz []byte, err error) { - // Use a default passphrase when none is given - if passphrase == "" { - passphrase = defaultPassphrase - } - // Get random bytes for salt saltBz = randBytes(randBz) @@ -132,7 +124,7 @@ func unarmourDecryptPrivKey(armourStr string, passphrase string) (privKey Privat } // Decoding the "armoured" ciphertext stored in base64 - encBz, err := base64.StdEncoding.DecodeString(armouredKey.CipherText) + encBz, err := base64.RawStdEncoding.DecodeString(armouredKey.CipherText) if err != nil { return nil, fmt.Errorf("Error decoding ciphertext: %v", err.Error()) } @@ -145,11 +137,6 @@ func unarmourDecryptPrivKey(armourStr string, passphrase string) (privKey Privat // Decrypt the AES-256 GCM encrypted bytes using the passphrase given func decryptPrivKey(saltBz, encBz []byte, passphrase string) (PrivateKey, error) { - // Use a default passphrase when none is given - if passphrase == "" { - passphrase = defaultPassphrase - } - // Derive key for decryption, see: https://pkg.go.dev/golang.org/x/crypto/scrypt#Key encryptionKey, err := scrypt.Key([]byte(passphrase), saltBz, n, r, p, klen) if err != nil { From ca143f5ab9381ee26dfb2607b5b46a97dba462c0 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Wed, 25 Jan 2023 14:44:42 +0000 Subject: [PATCH 37/56] Add README.md --- app/client/keybase/README.md | 191 +++++++++++++++++++++++++++++++++++ shared/crypto/keypair.go | 2 +- 2 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 app/client/keybase/README.md diff --git a/app/client/keybase/README.md b/app/client/keybase/README.md new file mode 100644 index 000000000..2e9a763b8 --- /dev/null +++ b/app/client/keybase/README.md @@ -0,0 +1,191 @@ +# Keybase + +This document is intended to outline the current Keybase implementation used by the V1 client, and is primarily focused on its design and implementation as well as testing. + +- [Backend Database](#backend-database) +- [Keybase Interface](#keybase-interface) + - [Code structure](#keybase-code-structure) + - [Makefile helper](#makefile-helper) +- [KeyPair Interface](#keypair-interface) + - [Code structure](#keypair-code-structure) +- [Encryption and Armouring](#encryption-and-armouring) +- [Testing](#testing) +- [TODO](#todo) + + +## Backend Database + +The Keybase uses the filesystem database `BadgerDB` as its backend to persistently store keys locally on the clients machine, in a key-value store pattern. + + +_The current keybase has not been integrated with any CLI endpoints, and as such is only accessible through the [keybase interface](#keybase-interface)_ + + +The DB stores the local key pairs in `EncKeyPair` structs encoded into `[]byte` using `encoding/gob` this is only used for internal storage in the DB. The `EncKeyPair` struct implements the [KeyPair interface](#keypair-interface) and as such has a number of methods that can be used on it. But relevent to the DB storage of these is the `GetAddressBytes()` function that returns the `[]byte` of the `PublicKey` field's hex address from the struct. The `[]byte` returned by the `GetAddressBytes()` function is used as the key in the key-value store and the value is the `gob` encoded `[]byte` of the `EncKeyPair` struct as a whole - which contains both the `PublicKey` and `PrivKeyArmour` (JSON encoded, encrypted private key string). + + +The Keybase DB layer then allows for a number of functions to be used which are exposed by the [Keybase interface](#keybase-interface) to fulfill CRUD operations on the DB itself. + + +## Keybase Interface + +The Keybase interface exposes the CRUD operations for interacting with the database layer. + +```go +// Keybase interface implements the CRUD operations for the keybase +type Keybase interface { + // Close the DB connection + Stop() error + + // Create new keypair entry in DB + Create(passphrase string) error + // Insert a new keypair from the private key hex string provided into the DB + ImportFromString(privStr, passphrase string) error + // Insert a new keypair from the JSON string of the encrypted private key into the DB + ImportFromJSON(jsonStr, passphrase string) error + + // Accessors + Get(address string) (crypto.KeyPair, error) + GetPubKey(address string) (crypto.PublicKey, error) + GetPrivKey(address, passphrase string) (crypto.PrivateKey, error) + GetAll() (addresses []string, keyPairs []crypto.KeyPair, err error) + Exists(address string) (bool, error) + + // Exporters + ExportPrivString(address, passphrase string) (string, error) + ExportPrivJSON(address, passphrase string) (string, error) + + // Updator + UpdatePassphrase(address, oldPassphrase, newPassphrase string) error + + // Sign Messages + Sign(address, passphrase string, msg []byte) ([]byte, error) + Verify(address string, msg, sig []byte) (bool, error) + + // Removals + Delete(address, passphrase string) error +} +``` + +The `Keybase` interface allows for the import/export of keys between V0<->V1. Meaning any key created in the V0 protocol can be imported in two ways to the V1 protocol. + 1. Via the JSON keyfile + - This method will take the JSON encoded, encrypted private key, and will import it into the V1 keybase - the `passphrase` supplied must be the same as the one use to encrypt the key in the first place or the key won't be able to be imported + 2. Via the private key hex string + - This method will directly import the private key from the hex string provided and then encrypt it with the passphrase provided - this does mean than the passphrase can be different from the original as this is a decrypted form of the private key + + +Although key pairs are stored in the local DB using the `[]byte` of the public key address as the key for retrieval all the accessing methods use the hex string of the public key's address to actually find the key for ease of use. + + +Keys can be created without the use of any password - in order to do this the `passphrase` supplied to the functions must be `""`. The private key will still be encrypted but will simply use the empty string as the key. + + +### Keybase Code Structure +``` +[ 90] app/client/keybase +├──── [ 8KB] README.md +├──── [ 1KB] keybase.go +├──── [ 13KB] keybase_test.go +└──── [ 8KB] keystore.go +``` + + +The interface itself is found in `app/client/keybase/keybase.go` whereas its implementation can be found in `app/client/keybase/keystore.go` + + +### Makefile Helper + + +To aid in the testing of the local keybase the following `Makefile` command has been exposed `make test_app` which will run the test suites from the `app` module alone, which includes the `app/client/keybase/keybase_test.go` file which covers the functionality of the `Keybase` implementation + + +## KeyPair Interface + + +The `KeyPair` interface exposes methods related to the operations used on the pairs of `PublicKey` types and JSON encoded, `PrivKeyArmour` strings. + +```go +// The KeyPair interface exposes functions relating to public and encrypted private key pairs +type KeyPair interface { + // Accessors + GetPublicKey() PublicKey + GetPrivArmour() string + + // Public Key Address + GetAddressBytes() []byte + GetAddressString() string // hex string + + // Unarmour + Unarmour(passphrase string) (PrivateKey, error) + + // Export + ExportString(passphrase string) (string, error) + ExportJSON(passphrase string) (string, error) +} +``` + +The `KeyPair` interface is implemented by the `EncKeyPair` struct + +```go +// EncKeyPair struct stores the public key and the passphrase encrypted private key +type EncKeyPair struct { + PublicKey PublicKey + PrivKeyArmour string +} +``` + +The `EncKeyPair` struct stores the `PublicKey` of the key pair and the JSON encoded, `"armoured"` key string. The armoured key string is built from the following struct + +```go +// Armoured Private Key struct with fields to unarmour it later +type ArmouredKey struct { + Kdf string `json:"kdf"` + Salt string `json:"salt"` + SecParam string `json:"secparam"` + Hint string `json:"hint"` + CipherText string `json:"ciphertext"` +} +``` + +This struct is created after the [encryption step](#encryption-and-armouring) has encrypted the private key string into the `CipherText` and the other fields are filled using the parameters used in the [encryption step](#encryption-and-armouring) so that the armoured key can later be unarmoured and decrypted provided the correct passphrase is supplied. This struct is then marshalled into a JSON string and stored in the `EncKeyPair`'s `PrivKeyArmour` field. + + +## KeyPair Code Structure + +The KeyPair code is seperated into two files `shared/crypto/keypair.go` and `shared/crypto/armour.go` + +``` +[ 146] shared +└──── [ 102] crypto + ├──── [ 5KB] armour.go + └──── [ 3KB] keypair.go +``` + + +## Encryption and Armouring + +Whenever a new key is created or imported it is encrypted using the passphrase provided (this can be `""` for no passphrase). + +The packages used for the encryption and armouring of a key are: `golang.org/x/crypto/scrypt` (key generation), `crypto/aes`, `crypto/cipher` (AES256-GCM encryption cipher) as well as `encoding/hex`, `encoding/base64` and `encoding/json` for the armouring of the encrypted key. + +1. First the OS's randomness is used to generate a `[]byte` of length `16` which is used as the salt for generating the key. +2. `scrypt` is then used to generate a `[]byte` of length 32, which is used as the key for the `AES256-GCM` encryption cipher. +3. The `AES256-GCM` cipher then uses the first `12` bytes of the key to encrypt the `[]byte` conversion of the private key hex string. + - _NOTE_ It is important that the cipher encrypts `[]byte(privateKeyHexString)` for interoperability with V0 keys. + +The armouring process then encodes the encrypted `[]byte` using the `base64.RawStdEncoding.EncodeToString()` function into a hex string. This is the `CipherText` field of the `ArmouredKey` struct. Along with this the salt bytes and other parameters used to encrypt and encode this key are put into an `ArmouredKey` struct before being marshalled into a JSON string and stored in the `EncKeyPair.PrivKeyArmour` field of a KeyPair. + + +When unarmouring the same process is done in reverse - the `EncKeyPair.PrivKeyArmour` JSON string is unmarshalled into the `AmouredKey` struct and the process is done in reverse. After the decryption of the `CipherText` the `[]byte` returned must be converted to a string and decoded from its hex from to be turned back into a `PrivateKey` type. This again is for interoperability with V0. + + +## Testing + +The full test suite can be run with `make test_app` where the [Keybase interface's](#keybase-interface) methods are tested with unit tests. + + +## TODO + +- [ ] Add better error catching and error messages for importing keys with invalid strings/invalid JSON +- [ ] Research and implement threshold signatures and threshold keys +- [ ] Look into a fully feature signature implementation beyond trivial `[]byte` messages diff --git a/shared/crypto/keypair.go b/shared/crypto/keypair.go index fd051dee6..51618ede3 100644 --- a/shared/crypto/keypair.go +++ b/shared/crypto/keypair.go @@ -20,7 +20,7 @@ type KeyPair interface { var _ KeyPair = &EncKeyPair{} -// KeyPair struct stores the public key and the passphrase encrypted private key +// EncKeyPair struct stores the public key and the passphrase encrypted private key type EncKeyPair struct { PublicKey PublicKey PrivKeyArmour string From eed8cd7f7d8a2a22e8981267cf86639fb88ebcc7 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sat, 28 Jan 2023 12:06:17 +0000 Subject: [PATCH 38/56] Update README.md --- app/client/keybase/README.md | 224 ++++++++++++++--------------------- 1 file changed, 92 insertions(+), 132 deletions(-) diff --git a/app/client/keybase/README.md b/app/client/keybase/README.md index 2e9a763b8..c596c4238 100644 --- a/app/client/keybase/README.md +++ b/app/client/keybase/README.md @@ -2,70 +2,39 @@ This document is intended to outline the current Keybase implementation used by the V1 client, and is primarily focused on its design and implementation as well as testing. -- [Backend Database](#backend-database) -- [Keybase Interface](#keybase-interface) - - [Code structure](#keybase-code-structure) - - [Makefile helper](#makefile-helper) -- [KeyPair Interface](#keypair-interface) - - [Code structure](#keypair-code-structure) -- [Encryption and Armouring](#encryption-and-armouring) -- [Testing](#testing) -- [TODO](#todo) + +* [1. Backend Database](#1-backend-database) +* [2. Keybase Interface](#2-keybase-interface) + * [2.1. Keybase Code Structure](#21-keybase-code-structure) + * [2.2. Makefile Helper](#22-makefile-helper) +* [3. KeyPair Interface](#3-keypair-interface) + * [3.1. KeyPair Code Structure](#31-keypair-code-structure) +* [4. Encryption and Armouring](#4-encryption-and-armouring) +* [5. Testing](#5-testing) +* [6. TODO](#6-todo) + +## 1. Backend Database -## Backend Database - -The Keybase uses the filesystem database `BadgerDB` as its backend to persistently store keys locally on the clients machine, in a key-value store pattern. - - -_The current keybase has not been integrated with any CLI endpoints, and as such is only accessible through the [keybase interface](#keybase-interface)_ +The Keybase package uses a filesystem key-value database, `BadgerDB`, as its backend to persistently store keys locally on the client machine. +_The current keybase has not been integrated with any CLI endpoints, and as such is only accessible through the [keybase interface](#keybase-interface); tracked by #150_ The DB stores the local key pairs in `EncKeyPair` structs encoded into `[]byte` using `encoding/gob` this is only used for internal storage in the DB. The `EncKeyPair` struct implements the [KeyPair interface](#keypair-interface) and as such has a number of methods that can be used on it. But relevent to the DB storage of these is the `GetAddressBytes()` function that returns the `[]byte` of the `PublicKey` field's hex address from the struct. The `[]byte` returned by the `GetAddressBytes()` function is used as the key in the key-value store and the value is the `gob` encoded `[]byte` of the `EncKeyPair` struct as a whole - which contains both the `PublicKey` and `PrivKeyArmour` (JSON encoded, encrypted private key string). - The Keybase DB layer then allows for a number of functions to be used which are exposed by the [Keybase interface](#keybase-interface) to fulfill CRUD operations on the DB itself. -## Keybase Interface - -The Keybase interface exposes the CRUD operations for interacting with the database layer. - -```go -// Keybase interface implements the CRUD operations for the keybase -type Keybase interface { - // Close the DB connection - Stop() error - - // Create new keypair entry in DB - Create(passphrase string) error - // Insert a new keypair from the private key hex string provided into the DB - ImportFromString(privStr, passphrase string) error - // Insert a new keypair from the JSON string of the encrypted private key into the DB - ImportFromJSON(jsonStr, passphrase string) error - - // Accessors - Get(address string) (crypto.KeyPair, error) - GetPubKey(address string) (crypto.PublicKey, error) - GetPrivKey(address, passphrase string) (crypto.PrivateKey, error) - GetAll() (addresses []string, keyPairs []crypto.KeyPair, err error) - Exists(address string) (bool, error) +## 2. Keybase Interface - // Exporters - ExportPrivString(address, passphrase string) (string, error) - ExportPrivJSON(address, passphrase string) (string, error) - - // Updator - UpdatePassphrase(address, oldPassphrase, newPassphrase string) error - - // Sign Messages - Sign(address, passphrase string, msg []byte) ([]byte, error) - Verify(address string, msg, sig []byte) (bool, error) - - // Removals - Delete(address, passphrase string) error -} -``` +The [Keybase interface](./keybase.go) exposes the CRUD operations to operate on keys, and supports the following operations: +- Create password protected private keys +- Export/Import string/json keypairs +- Retrieve public/private keys or keypairs +- List all keys stored +- Check keys exist in the keybase +- Update passphrase on a private key +- Message signing and verification The `Keybase` interface allows for the import/export of keys between V0<->V1. Meaning any key created in the V0 protocol can be imported in two ways to the V1 protocol. 1. Via the JSON keyfile @@ -73,118 +42,109 @@ The `Keybase` interface allows for the import/export of keys between V0<->V1. Me 2. Via the private key hex string - This method will directly import the private key from the hex string provided and then encrypt it with the passphrase provided - this does mean than the passphrase can be different from the original as this is a decrypted form of the private key - Although key pairs are stored in the local DB using the `[]byte` of the public key address as the key for retrieval all the accessing methods use the hex string of the public key's address to actually find the key for ease of use. - Keys can be created without the use of any password - in order to do this the `passphrase` supplied to the functions must be `""`. The private key will still be encrypted but will simply use the empty string as the key. -### Keybase Code Structure +### 2.1. Keybase Code Structure ``` -[ 90] app/client/keybase -├──── [ 8KB] README.md -├──── [ 1KB] keybase.go -├──── [ 13KB] keybase_test.go -└──── [ 8KB] keystore.go +app +└── client + └── keybase + ├── README.md + ├── keybase.go + ├── keybase_test.go + └── keystore.go ``` +The interface itself is found in [keybase.go](./keybase.go) whereas its implementation can be found in [keystore.go](./keystore.go) -The interface itself is found in `app/client/keybase/keybase.go` whereas its implementation can be found in `app/client/keybase/keystore.go` +### 2.2. Makefile Helper -### Makefile Helper +To aid in the testing of the local keybase the following `Makefile` command has been exposed `make test_app` which will run the test suites from the `app` module alone, which includes the [keybase_test.go](./keybase_test.go) file which covers the functionality of the `Keybase` implementation -To aid in the testing of the local keybase the following `Makefile` command has been exposed `make test_app` which will run the test suites from the `app` module alone, which includes the `app/client/keybase/keybase_test.go` file which covers the functionality of the `Keybase` implementation +## 3. KeyPair Interface +The [KeyPair interface](../../../shared/crypto/keypair.go) exposes methods related to the operations used on the pairs of `PublicKey` types and JSON encoded, `PrivKeyArmour` strings., such as: +- Retrieve the public key or armoured private key JSON string +- Get Public key address `[]byte` or hex `string` +- Unarmour the private key JSON string +- Export the private key hex string/JSON armoured string -## KeyPair Interface +The [KeyPair](../../../shared/crypto/keypair.go) interface is implemented by the `EncKeyPair` struct, which stores the `PublicKey` of the key pair and the JSON encoded, `armoured` key string. +The private key armoured JSON string is created after the [encryption step](#encryption-and-armouring) has encrypted the private key and marshalled it into a JSON string. -The `KeyPair` interface exposes methods related to the operations used on the pairs of `PublicKey` types and JSON encoded, `PrivKeyArmour` strings. -```go -// The KeyPair interface exposes functions relating to public and encrypted private key pairs -type KeyPair interface { - // Accessors - GetPublicKey() PublicKey - GetPrivArmour() string +### 3.1. KeyPair Code Structure - // Public Key Address - GetAddressBytes() []byte - GetAddressString() string // hex string +The KeyPair code is seperated into two files [keypair.go](../../../shared/crypto/keypair.go) and [armour.go](../../../shared/crypto/armour.go) - // Unarmour - Unarmour(passphrase string) (PrivateKey, error) - - // Export - ExportString(passphrase string) (string, error) - ExportJSON(passphrase string) (string, error) -} ``` - -The `KeyPair` interface is implemented by the `EncKeyPair` struct - -```go -// EncKeyPair struct stores the public key and the passphrase encrypted private key -type EncKeyPair struct { - PublicKey PublicKey - PrivKeyArmour string -} -``` - -The `EncKeyPair` struct stores the `PublicKey` of the key pair and the JSON encoded, `"armoured"` key string. The armoured key string is built from the following struct - -```go -// Armoured Private Key struct with fields to unarmour it later -type ArmouredKey struct { - Kdf string `json:"kdf"` - Salt string `json:"salt"` - SecParam string `json:"secparam"` - Hint string `json:"hint"` - CipherText string `json:"ciphertext"` -} +shared +└── crypto + ├── armour.go + └── keypair.go ``` -This struct is created after the [encryption step](#encryption-and-armouring) has encrypted the private key string into the `CipherText` and the other fields are filled using the parameters used in the [encryption step](#encryption-and-armouring) so that the armoured key can later be unarmoured and decrypted provided the correct passphrase is supplied. This struct is then marshalled into a JSON string and stored in the `EncKeyPair`'s `PrivKeyArmour` field. - -## KeyPair Code Structure - -The KeyPair code is seperated into two files `shared/crypto/keypair.go` and `shared/crypto/armour.go` - -``` -[ 146] shared -└──── [ 102] crypto - ├──── [ 5KB] armour.go - └──── [ 3KB] keypair.go -``` - - -## Encryption and Armouring +## 4. Encryption and Armouring Whenever a new key is created or imported it is encrypted using the passphrase provided (this can be `""` for no passphrase). -The packages used for the encryption and armouring of a key are: `golang.org/x/crypto/scrypt` (key generation), `crypto/aes`, `crypto/cipher` (AES256-GCM encryption cipher) as well as `encoding/hex`, `encoding/base64` and `encoding/json` for the armouring of the encrypted key. - -1. First the OS's randomness is used to generate a `[]byte` of length `16` which is used as the salt for generating the key. -2. `scrypt` is then used to generate a `[]byte` of length 32, which is used as the key for the `AES256-GCM` encryption cipher. -3. The `AES256-GCM` cipher then uses the first `12` bytes of the key to encrypt the `[]byte` conversion of the private key hex string. - - _NOTE_ It is important that the cipher encrypts `[]byte(privateKeyHexString)` for interoperability with V0 keys. - -The armouring process then encodes the encrypted `[]byte` using the `base64.RawStdEncoding.EncodeToString()` function into a hex string. This is the `CipherText` field of the `ArmouredKey` struct. Along with this the salt bytes and other parameters used to encrypt and encode this key are put into an `ArmouredKey` struct before being marshalled into a JSON string and stored in the `EncKeyPair.PrivKeyArmour` field of a KeyPair. - +```mermaid +flowchart LR + subgraph C[core lib] + A["rand([16]byte)"] + end + subgraph S[scrypt lib] + B["key(salt, pass, ...)"] + end + subgraph AES-GCM + direction TB + D["Cipher(key)"] + E["GCM(block)"] + F["Seal(plaintext, nonce)"] + D--Block-->E + E--Nonce-->F + end + C--Salt-->S + S--Key-->AES-GCM +``` -When unarmouring the same process is done in reverse - the `EncKeyPair.PrivKeyArmour` JSON string is unmarshalled into the `AmouredKey` struct and the process is done in reverse. After the decryption of the `CipherText` the `[]byte` returned must be converted to a string and decoded from its hex from to be turned back into a `PrivateKey` type. This again is for interoperability with V0. +When unarmouring and decrypting the same process is done in reverse. + +```mermaid +flowchart LR + subgraph C[core lib] + A["decode(privateKeyArmouredString)"] + end + subgraph S[scrypt lib] + B["key(salt, pass, ...)"] + end + subgraph AES-GCM + direction TB + D["Cipher(key)"] + E["GCM(block)"] + F["Open(encryptedBytes, nonce)"] + D--Block-->E + E--Nonce-->F + end + C--Salt-->S + C--EncryptedBytes-->AES-GCM + S--Key-->AES-GCM +``` -## Testing +## 5. Testing The full test suite can be run with `make test_app` where the [Keybase interface's](#keybase-interface) methods are tested with unit tests. -## TODO +## 6. TODO - [ ] Add better error catching and error messages for importing keys with invalid strings/invalid JSON - [ ] Research and implement threshold signatures and threshold keys From 55447e5db4ef53e196cfb50639fdc0364dd1be1b Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sat, 28 Jan 2023 12:06:46 +0000 Subject: [PATCH 39/56] Add hint to new keys created --- app/client/keybase/keybase.go | 2 +- app/client/keybase/keystore.go | 4 ++-- shared/crypto/armour.go | 6 +++--- shared/crypto/keypair.go | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/client/keybase/keybase.go b/app/client/keybase/keybase.go index 5ae7c0b2d..5329c8e0c 100644 --- a/app/client/keybase/keybase.go +++ b/app/client/keybase/keybase.go @@ -8,7 +8,7 @@ type Keybase interface { Stop() error // Create new keypair entry in DB - Create(passphrase string) error + Create(passphrase, hint string) error // Insert a new keypair from the private key hex string provided into the DB ImportFromString(privStr, passphrase string) error // Insert a new keypair from the JSON string of the encrypted private key into the DB diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index 739f98191..28ded2861 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -87,9 +87,9 @@ func (keybase *badgerKeybase) newKeyPairFromString(fn func(string, string) (cryp // Create a new key and store it in the DB by encoding the KeyPair struct into a []byte // Using the PublicKey.Address() return value as the key for storage -func (keybase *badgerKeybase) Create(passphrase string) error { +func (keybase *badgerKeybase) Create(passphrase, hint string) error { err := keybase.db.Update(func(tx *badger.Txn) error { - keyPair, err := crypto.CreateNewKey(passphrase) + keyPair, err := crypto.CreateNewKey(passphrase, hint) if err != nil { return err } diff --git a/shared/crypto/armour.go b/shared/crypto/armour.go index 97ff7628f..24a585da0 100644 --- a/shared/crypto/armour.go +++ b/shared/crypto/armour.go @@ -54,9 +54,9 @@ func NewArmouredKey(kdf, salt, hint, cipher string) ArmouredKey { } } -// Encrypt the given privKey with the passphrase, armour it by encoding the ecnrypted +// Encrypt the given privKey with the passphrase, armour it by encoding the encrypted // []byte into base64, and convert into a json string with the parameters for unarmouring -func encryptArmourPrivKey(privKey PrivateKey, passphrase string) (string, error) { +func encryptArmourPrivKey(privKey PrivateKey, passphrase, hint string) (string, error) { // Encrypt privKey usign AES-256 GCM Cipher saltBz, encBz, err := encryptPrivKey(privKey, passphrase) if err != nil { @@ -67,7 +67,7 @@ func encryptArmourPrivKey(privKey PrivateKey, passphrase string) (string, error) armourStr := base64.RawStdEncoding.EncodeToString(encBz) // Create ArmouredKey object so can unarmour later - armoured := NewArmouredKey(kdf, hex.EncodeToString(saltBz), "", armourStr) + armoured := NewArmouredKey(kdf, hex.EncodeToString(saltBz), hint, armourStr) // Encode armoured struct into []byte js, err := json.Marshal(armoured) diff --git a/shared/crypto/keypair.go b/shared/crypto/keypair.go index 51618ede3..067716ab4 100644 --- a/shared/crypto/keypair.go +++ b/shared/crypto/keypair.go @@ -79,13 +79,13 @@ func (kp EncKeyPair) ExportJSON(passphrase string) (string, error) { // Generate new private ED25519 key and encrypt and armour it as a string // Returns a KeyPair struct of the Public Key and Armoured String -func CreateNewKey(passphrase string) (KeyPair, error) { +func CreateNewKey(passphrase, hint string) (KeyPair, error) { privKey, err := GeneratePrivateKey() if err != nil { return nil, err } - privArmour, err := encryptArmourPrivKey(privKey, passphrase) + privArmour, err := encryptArmourPrivKey(privKey, passphrase, hint) if err != nil || privArmour == "" { return nil, err } From 1e060149ffad144afb937b18fc3f9d9feb586e09 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sat, 28 Jan 2023 12:10:15 +0000 Subject: [PATCH 40/56] Add hint to imported string keys --- app/client/keybase/keybase.go | 2 +- app/client/keybase/keystore.go | 44 +++++++++++++++++++++++----------- shared/crypto/keypair.go | 4 ++-- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/app/client/keybase/keybase.go b/app/client/keybase/keybase.go index 5329c8e0c..a49127ffa 100644 --- a/app/client/keybase/keybase.go +++ b/app/client/keybase/keybase.go @@ -10,7 +10,7 @@ type Keybase interface { // Create new keypair entry in DB Create(passphrase, hint string) error // Insert a new keypair from the private key hex string provided into the DB - ImportFromString(privStr, passphrase string) error + ImportFromString(privStr, passphrase, hint string) error // Insert a new keypair from the JSON string of the encrypted private key into the DB ImportFromJSON(jsonStr, passphrase string) error diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index 28ded2861..c0e8ae61c 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -57,11 +57,11 @@ func (keybase *badgerKeybase) Stop() error { return keybase.db.Close() } -// Use the supplied function func(privStr, passphrase) (KeyPair, error) -// to create the new keypair and then insert it into the DB -func (keybase *badgerKeybase) newKeyPairFromString(fn func(string, string) (crypto.KeyPair, error), privStr, passphrase string) error { +// Create a new key and store it in the DB by encoding the KeyPair struct into a []byte +// Using the PublicKey.Address() return value as the key for storage +func (keybase *badgerKeybase) Create(passphrase, hint string) error { err := keybase.db.Update(func(tx *badger.Txn) error { - keyPair, err := fn(privStr, passphrase) + keyPair, err := crypto.CreateNewKey(passphrase, hint) if err != nil { return err } @@ -85,11 +85,11 @@ func (keybase *badgerKeybase) newKeyPairFromString(fn func(string, string) (cryp return err } -// Create a new key and store it in the DB by encoding the KeyPair struct into a []byte +// Create a new KeyPair from the private key hex string and store it in the DB by encoding the KeyPair struct into a []byte // Using the PublicKey.Address() return value as the key for storage -func (keybase *badgerKeybase) Create(passphrase, hint string) error { +func (keybase *badgerKeybase) ImportFromString(privKeyHex, passphrase, hint string) error { err := keybase.db.Update(func(tx *badger.Txn) error { - keyPair, err := crypto.CreateNewKey(passphrase, hint) + keyPair, err := crypto.CreateNewKeyFromString(privKeyHex, passphrase, hint) if err != nil { return err } @@ -113,16 +113,32 @@ func (keybase *badgerKeybase) Create(passphrase, hint string) error { return err } -// Create a new KeyPair from the private key hex string and store it in the DB by encoding the KeyPair struct into a []byte -// Using the PublicKey.Address() return value as the key for storage -func (keybase *badgerKeybase) ImportFromString(privKeyHex, passphrase string) error { - return keybase.newKeyPairFromString(crypto.CreateNewKeyFromString, privKeyHex, passphrase) -} - // Create a new KeyPair from the private key JSON string and store it in the DB by encoding the KeyPair struct into a []byte // Using the PublicKey.Address() return value as the key for storage func (keybase *badgerKeybase) ImportFromJSON(jsonStr, passphrase string) error { - return keybase.newKeyPairFromString(crypto.ImportKeyFromJSON, jsonStr, passphrase) + err := keybase.db.Update(func(tx *badger.Txn) error { + keyPair, err := crypto.ImportKeyFromJSON(jsonStr, passphrase) + if err != nil { + return err + } + + // Use key address as key in DB + addrKey := keyPair.GetAddressBytes() + // Encode entire KeyPair struct into []byte for value + keypairBz := new(bytes.Buffer) + enc := gob.NewEncoder(keypairBz) + if err = enc.Encode(keyPair); err != nil { + return err + } + + if err = tx.Set(addrKey, keypairBz.Bytes()); err != nil { + return err + } + + return nil + }) + + return err } // Returns a KeyPair struct provided the address was found in the DB diff --git a/shared/crypto/keypair.go b/shared/crypto/keypair.go index 067716ab4..e8288651e 100644 --- a/shared/crypto/keypair.go +++ b/shared/crypto/keypair.go @@ -97,13 +97,13 @@ func CreateNewKey(passphrase, hint string) (KeyPair, error) { } // Generate new KeyPair from the hex string provided, encrypt and armour it as a string -func CreateNewKeyFromString(privStrHex, passphrase string) (KeyPair, error) { +func CreateNewKeyFromString(privStrHex, passphrase, hint string) (KeyPair, error) { privKey, err := NewPrivateKey(privStrHex) if err != nil { return nil, err } - privArmour, err := encryptArmourPrivKey(privKey, passphrase) + privArmour, err := encryptArmourPrivKey(privKey, passphrase, hint) if err != nil || privArmour == "" { return nil, err } From 67b9e01a9d48513c1c7a8f78bd61c121f936bea2 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sat, 28 Jan 2023 12:11:01 +0000 Subject: [PATCH 41/56] Add hint to update passphrase --- app/client/keybase/keybase.go | 2 +- app/client/keybase/keystore.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/client/keybase/keybase.go b/app/client/keybase/keybase.go index a49127ffa..567fd2e7f 100644 --- a/app/client/keybase/keybase.go +++ b/app/client/keybase/keybase.go @@ -26,7 +26,7 @@ type Keybase interface { ExportPrivJSON(address, passphrase string) (string, error) // Updator - UpdatePassphrase(address, oldPassphrase, newPassphrase string) error + UpdatePassphrase(address, oldPassphrase, newPassphrase, hint string) error // Sign Messages Sign(address, passphrase string, msg []byte) ([]byte, error) diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index c0e8ae61c..c62b8e45e 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -272,7 +272,7 @@ func (keybase *badgerKeybase) ExportPrivJSON(address, passphrase string) (string return kp.ExportJSON(passphrase) } -func (keybase *badgerKeybase) UpdatePassphrase(address, oldPassphrase, newPassphrase string) error { +func (keybase *badgerKeybase) UpdatePassphrase(address, oldPassphrase, newPassphrase, hint string) error { // Check the oldPassphrase is correct privKey, err := keybase.GetPrivKey(address, oldPassphrase) if err != nil { @@ -286,7 +286,7 @@ func (keybase *badgerKeybase) UpdatePassphrase(address, oldPassphrase, newPassph } err = keybase.db.Update(func(tx *badger.Txn) error { - keyPair, err := crypto.CreateNewKeyFromString(privStr, newPassphrase) + keyPair, err := crypto.CreateNewKeyFromString(privStr, newPassphrase, hint) if err != nil { return err } From 89f27766ac66d4cf988e2e95bd05b48a9faa474d Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sat, 28 Jan 2023 13:08:41 +0000 Subject: [PATCH 42/56] Reduce scope of EncKeyPair add Marshal and Unmarshal functions to KeyPair interface --- app/client/keybase/keybase_test.go | 51 +++++++++++------------ app/client/keybase/keystore.go | 55 +++++++++--------------- shared/crypto/keypair.go | 67 +++++++++++++++++++++++++----- 3 files changed, 101 insertions(+), 72 deletions(-) diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go index 297d6528c..3962fe2b6 100644 --- a/app/client/keybase/keybase_test.go +++ b/app/client/keybase/keybase_test.go @@ -14,11 +14,11 @@ const ( testPrivString = "045e8380086abc6f6e941d6fe47ca93b86723bc246ec8c4beee411b410028675ed78c49592f836f7a4d47d4fb6a0e6b19f07aebc201d005f6b2c6afe389086e9" testPubString = "ed78c49592f836f7a4d47d4fb6a0e6b19f07aebc201d005f6b2c6afe389086e9" testAddr = "26e16ccab7a898400022476332e2972b8199f2f9" - testJSON = `{"kdf":"scrypt","salt":"AD165E014A42709A7EBF03594867C970","secparam":"12","hint":"","ciphertext":"BJ49gRklmHmnZP9cMBfExmpbQ7aGxyVgq/6ItDqbY5gFoses2+g3+aaHdgFv2PIWIAEojfqX3k1A6tBWF47Yw3OJ+vaTzje8sN/roKFJop4="}` - testPassphrase = "Testing@Testing123" // Other + testPassphrase = "Testing@Testing123" testNewPassphrase = "321gnitsetgnitset" + testHint = "testing" testTx = "79fca587bbcfd5da86d73e1d849769017b1c91cc8177dec0fc0e3e0d345f2b35" // JSON account @@ -26,14 +26,13 @@ const ( testJSONPubString = "408bec6320b540aa0cc86b3e633e214f2fd4dce4caa08f164fa3a9d3e577b46c" testJSONPrivString = "3554119cec1c0c8c5b3845a5d3fc6346eb44ed21aab5c063ae9b6b1d38bec275408bec6320b540aa0cc86b3e633e214f2fd4dce4caa08f164fa3a9d3e577b46c" testJSONString = `{"kdf":"scrypt","salt":"197d2754445a7e5ce3e6c8d7b1d0ff6f","secparam":"12","hint":"pocket wallet","ciphertext":"B/AORJrSeQrR5ewQGel4FeCCXscoCsMUzq9gXAAxDqjXMmMxa7TedBTuemtO82JyTCoQWFHbGxRx8A7IoETNh5T5yBAjNNrr7DDkVrcfSAM3ez9lQem17DsfowCvRtmbesDlvbSZMRy8mQgClLqWRN+c6W/fPQ/lxLUy1G1A965U/uImcMXzSwbfqYrBPEux"}` - testJSONPassphrase = "Testing@Testing123" ) func TestKeybase_CreateNewKey(t *testing.T) { db := initDB(t) defer db.Stop() - err := db.Create(testPassphrase) + err := db.Create(testPassphrase, testHint) require.NoError(t, err) addresses, keypairs, err := db.GetAll() @@ -51,7 +50,7 @@ func TestKeybase_CreateNewKeyNoPassphrase(t *testing.T) { db := initDB(t) defer db.Stop() - err := db.Create("") + err := db.Create("", "") require.NoError(t, err) addresses, keypairs, err := db.GetAll() @@ -69,7 +68,7 @@ func TestKeybase_ImportKeyFromString(t *testing.T) { db := initDB(t) defer db.Stop() - err := db.ImportFromString(testPrivString, testPassphrase) + err := db.ImportFromString(testPrivString, testPassphrase, testHint) require.NoError(t, err) addresses, keypairs, err := db.GetAll() @@ -93,7 +92,7 @@ func TestKeybase_ImportKeyFromStringNoPassphrase(t *testing.T) { db := initDB(t) defer db.Stop() - err := db.ImportFromString(testPrivString, "") + err := db.ImportFromString(testPrivString, "", "") require.NoError(t, err) addresses, keypairs, err := db.GetAll() @@ -124,7 +123,7 @@ func TestKeybase_ImportKeyFromStringInvalidString(t *testing.T) { falseBz, err := hex.DecodeString(falseAddr) require.NoError(t, err) - err = db.ImportFromString(falseAddr, testPassphrase) + err = db.ImportFromString(falseAddr, testPassphrase, testHint) require.EqualError(t, err, crypto.ErrInvalidPrivateKeyLen(len(falseBz)).Error()) } @@ -132,7 +131,7 @@ func TestKeybase_ImportKeyFromJSON(t *testing.T) { db := initDB(t) defer db.Stop() - err := db.ImportFromJSON(testJSONString, testJSONPassphrase) + err := db.ImportFromJSON(testJSONString, testPassphrase) require.NoError(t, err) addresses, keypairs, err := db.GetAll() @@ -147,7 +146,7 @@ func TestKeybase_ImportKeyFromJSON(t *testing.T) { require.Equal(t, kp.GetAddressString(), testJSONAddr) require.Equal(t, kp.GetPublicKey().String(), testJSONPubString) - privKey, err := kp.Unarmour(testJSONPassphrase) + privKey, err := kp.Unarmour(testPassphrase) require.NoError(t, err) require.Equal(t, privKey.String(), testJSONPrivString) } @@ -158,7 +157,7 @@ func TestKeybase_GetKey(t *testing.T) { testKey := createTestKeys(t, 1)[0] - err := db.ImportFromString(testKey.String(), testPassphrase) + err := db.ImportFromString(testKey.String(), testPassphrase, testHint) require.NoError(t, err) kp, err := db.Get(testKey.Address().String()) @@ -191,7 +190,7 @@ func TestKeybase_CheckKeyExists(t *testing.T) { testKey := createTestKeys(t, 1)[0] - err := db.ImportFromString(testKey.String(), testPassphrase) + err := db.ImportFromString(testKey.String(), testPassphrase, testHint) require.NoError(t, err) exists, err := db.Exists(testKey.Address().String()) @@ -217,7 +216,7 @@ func TestKeybase_GetAllKeys(t *testing.T) { pkm := make(map[string]crypto.PrivateKey, 0) pks := createTestKeys(t, 5) for i := 0; i < 5; i++ { - err := db.ImportFromString(pks[i].String(), testPassphrase) + err := db.ImportFromString(pks[i].String(), testPassphrase, testHint) require.NoError(t, err) pkm[pks[i].Address().String()] = pks[i] } @@ -244,7 +243,7 @@ func TestKeybase_GetPubKey(t *testing.T) { testKey := createTestKeys(t, 1)[0] - err := db.ImportFromString(testKey.String(), testPassphrase) + err := db.ImportFromString(testKey.String(), testPassphrase, testHint) require.NoError(t, err) pubKey, err := db.GetPubKey(testKey.Address().String()) @@ -262,7 +261,7 @@ func TestKeybase_GetPrivKey(t *testing.T) { testKey := createTestKeys(t, 1)[0] - err := db.ImportFromString(testKey.String(), testPassphrase) + err := db.ImportFromString(testKey.String(), testPassphrase, testHint) require.NoError(t, err) privKey, err := db.GetPrivKey(testKey.Address().String(), testPassphrase) @@ -281,7 +280,7 @@ func TestKeybase_GetPrivKeyWrongPassphrase(t *testing.T) { testKey := createTestKeys(t, 1)[0] - err := db.ImportFromString(testKey.String(), testPassphrase) + err := db.ImportFromString(testKey.String(), testPassphrase, testHint) require.NoError(t, err) privKey, err := db.GetPrivKey(testKey.Address().String(), testNewPassphrase) @@ -295,13 +294,13 @@ func TestKeybase_UpdatePassphrase(t *testing.T) { testKey := createTestKeys(t, 1)[0] - err := db.ImportFromString(testKey.String(), testPassphrase) + err := db.ImportFromString(testKey.String(), testPassphrase, testHint) require.NoError(t, err) _, err = db.GetPrivKey(testKey.Address().String(), testPassphrase) require.NoError(t, err) - err = db.UpdatePassphrase(testKey.Address().String(), testPassphrase, testNewPassphrase) + err = db.UpdatePassphrase(testKey.Address().String(), testPassphrase, testNewPassphrase, testHint) require.NoError(t, err) privKey, err := db.GetPrivKey(testKey.Address().String(), testNewPassphrase) @@ -320,13 +319,13 @@ func TestKeybase_UpdatePassphraseWrongPassphrase(t *testing.T) { testKey := createTestKeys(t, 1)[0] - err := db.ImportFromString(testKey.String(), testPassphrase) + err := db.ImportFromString(testKey.String(), testPassphrase, testHint) require.NoError(t, err) _, err = db.GetPrivKey(testKey.Address().String(), testPassphrase) require.NoError(t, err) - err = db.UpdatePassphrase(testKey.Address().String(), testNewPassphrase, testNewPassphrase) + err = db.UpdatePassphrase(testKey.Address().String(), testNewPassphrase, testNewPassphrase, testHint) require.ErrorIs(t, err, crypto.ErrorWrongPassphrase) } @@ -336,7 +335,7 @@ func TestKeybase_DeleteKey(t *testing.T) { testKey := createTestKeys(t, 1)[0] - err := db.ImportFromString(testKey.String(), testPassphrase) + err := db.ImportFromString(testKey.String(), testPassphrase, testHint) require.NoError(t, err) _, err = db.GetPrivKey(testKey.Address().String(), testPassphrase) @@ -356,7 +355,7 @@ func TestKeybase_DeleteKeyWrongPassphrase(t *testing.T) { testKey := createTestKeys(t, 1)[0] - err := db.ImportFromString(testKey.String(), testPassphrase) + err := db.ImportFromString(testKey.String(), testPassphrase, testHint) require.NoError(t, err) _, err = db.GetPrivKey(testKey.Address().String(), testPassphrase) @@ -372,7 +371,7 @@ func TestKeybase_SignMessage(t *testing.T) { pk := createTestKeyFromString(t, testPrivString) - err := db.ImportFromString(testPrivString, testPassphrase) + err := db.ImportFromString(testPrivString, testPassphrase, testHint) require.NoError(t, err) privKey, err := db.GetPrivKey(pk.Address().String(), testPassphrase) @@ -395,7 +394,7 @@ func TestKeybase_SignMessageWrongPassphrase(t *testing.T) { pk := createTestKeyFromString(t, testPrivString) - err := db.ImportFromString(testPrivString, testPassphrase) + err := db.ImportFromString(testPrivString, testPassphrase, testHint) require.NoError(t, err) privKey, err := db.GetPrivKey(pk.Address().String(), testPassphrase) @@ -413,7 +412,7 @@ func TestKeybase_ExportString(t *testing.T) { db := initDB(t) defer db.Stop() - err := db.ImportFromString(testPrivString, testPassphrase) + err := db.ImportFromString(testPrivString, testPassphrase, testHint) require.NoError(t, err) privStr, err := db.ExportPrivString(testAddr, testPassphrase) @@ -425,7 +424,7 @@ func TestKeybase_ExportJSON(t *testing.T) { db := initDB(t) defer db.Stop() - err := db.ImportFromString(testPrivString, testPassphrase) + err := db.ImportFromString(testPrivString, testPassphrase, testHint) require.NoError(t, err) jsonStr, err := db.ExportPrivJSON(testAddr, testPassphrase) diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index c62b8e45e..f971cea75 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -2,8 +2,6 @@ package keybase import ( "bytes" - "crypto/ed25519" - "encoding/gob" "encoding/hex" "fmt" "strings" @@ -17,13 +15,6 @@ func ErrorAddrNotFound(addr string) error { return fmt.Errorf("No key found with address: %s", addr) } -// Encoding is used to serialise the data to store the KeyPairs in the database -func init() { - gob.Register(crypto.Ed25519PublicKey{}) - gob.Register(ed25519.PublicKey{}) - gob.Register(crypto.EncKeyPair{}) -} - // badgerKeybase implements the KeyBase interface var _ Keybase = &badgerKeybase{} @@ -68,14 +59,14 @@ func (keybase *badgerKeybase) Create(passphrase, hint string) error { // Use key address as key in DB addrKey := keyPair.GetAddressBytes() + // Encode entire KeyPair struct into []byte for value - keypairBz := new(bytes.Buffer) - enc := gob.NewEncoder(keypairBz) - if err = enc.Encode(keyPair); err != nil { + keypairBz, err := keyPair.Marshal() + if err != nil { return err } - if err = tx.Set(addrKey, keypairBz.Bytes()); err != nil { + if err = tx.Set(addrKey, keypairBz); err != nil { return err } @@ -96,14 +87,14 @@ func (keybase *badgerKeybase) ImportFromString(privKeyHex, passphrase, hint stri // Use key address as key in DB addrKey := keyPair.GetAddressBytes() + // Encode entire KeyPair struct into []byte for value - keypairBz := new(bytes.Buffer) - enc := gob.NewEncoder(keypairBz) - if err = enc.Encode(keyPair); err != nil { + keypairBz, err := keyPair.Marshal() + if err != nil { return err } - if err = tx.Set(addrKey, keypairBz.Bytes()); err != nil { + if err = tx.Set(addrKey, keypairBz); err != nil { return err } @@ -124,14 +115,14 @@ func (keybase *badgerKeybase) ImportFromJSON(jsonStr, passphrase string) error { // Use key address as key in DB addrKey := keyPair.GetAddressBytes() + // Encode entire KeyPair struct into []byte for value - keypairBz := new(bytes.Buffer) - enc := gob.NewEncoder(keypairBz) - if err = enc.Encode(keyPair); err != nil { + keypairBz, err := keyPair.Marshal() + if err != nil { return err } - if err = tx.Set(addrKey, keypairBz.Bytes()); err != nil { + if err = tx.Set(addrKey, keypairBz); err != nil { return err } @@ -143,8 +134,7 @@ func (keybase *badgerKeybase) ImportFromJSON(jsonStr, passphrase string) error { // Returns a KeyPair struct provided the address was found in the DB func (keybase *badgerKeybase) Get(address string) (crypto.KeyPair, error) { - var kp crypto.EncKeyPair - keypairBz := new(bytes.Buffer) + kp := crypto.GetKeypair() addrBz, err := hex.DecodeString(address) if err != nil { return nil, err @@ -164,9 +154,7 @@ func (keybase *badgerKeybase) Get(address string) (crypto.KeyPair, error) { } // Decode []byte value back into KeyPair struct - keypairBz.Write(value) - dec := gob.NewDecoder(keypairBz) - if err = dec.Decode(&kp); err != nil { + if err := kp.Unmarshal(value); err != nil { return err } @@ -219,11 +207,8 @@ func (keybase *badgerKeybase) GetAll() (addresses []string, keyPairs []crypto.Ke b := make([]byte, len(val)) copy(b, val) // Decode []byte value back into KeyPair struct - var kp crypto.EncKeyPair - keypairBz := new(bytes.Buffer) - keypairBz.Write(b) - dec := gob.NewDecoder(keypairBz) - if err = dec.Decode(&kp); err != nil { + kp := crypto.GetKeypair() + if err := kp.Unmarshal(b); err != nil { return err } @@ -296,14 +281,14 @@ func (keybase *badgerKeybase) UpdatePassphrase(address, oldPassphrase, newPassph if bytes.Compare(addrKey, addrBz) != 0 { return fmt.Errorf("Key address does not match previous address.") } + // Encode entire KeyPair struct into []byte for value - keypairBz := new(bytes.Buffer) - enc := gob.NewEncoder(keypairBz) - if err = enc.Encode(keyPair); err != nil { + keypairBz, err := keyPair.Marshal() + if err != nil { return err } - if err = tx.Set(addrKey, keypairBz.Bytes()); err != nil { + if err = tx.Set(addrKey, keypairBz); err != nil { return err } diff --git a/shared/crypto/keypair.go b/shared/crypto/keypair.go index e8288651e..a3d747f83 100644 --- a/shared/crypto/keypair.go +++ b/shared/crypto/keypair.go @@ -1,5 +1,18 @@ package crypto +import ( + "bytes" + "crypto/ed25519" + "encoding/gob" +) + +// Encoding is used to serialise the data to store the KeyPairs in the 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 type KeyPair interface { // Accessors @@ -16,51 +29,60 @@ type KeyPair interface { // Export ExportString(passphrase string) (string, error) ExportJSON(passphrase string) (string, error) + + // Marshalling + Marshal() ([]byte, error) + Unmarshal([]byte) error } -var _ KeyPair = &EncKeyPair{} +var _ KeyPair = &encKeyPair{} -// EncKeyPair struct stores the public key and the passphrase encrypted private key -type EncKeyPair struct { +// encKeyPair struct stores the public key and the passphrase encrypted private key +type encKeyPair struct { PublicKey PublicKey PrivKeyArmour string } // Generate a new KeyPair struct given the public key and armoured private key func NewKeyPair(pub PublicKey, priv string) KeyPair { - return EncKeyPair{ + return &encKeyPair{ PublicKey: pub, PrivKeyArmour: priv, } } +// Return an empty KeyPair interface +func GetKeypair() KeyPair { + return &encKeyPair{} +} + // Return the public key -func (kp EncKeyPair) GetPublicKey() PublicKey { +func (kp encKeyPair) GetPublicKey() PublicKey { return kp.PublicKey } // Return private key armoured string -func (kp EncKeyPair) GetPrivArmour() string { +func (kp encKeyPair) GetPrivArmour() string { return kp.PrivKeyArmour } // Return the byte slice address of the public key -func (kp EncKeyPair) GetAddressBytes() []byte { +func (kp encKeyPair) GetAddressBytes() []byte { return kp.PublicKey.Address().Bytes() } // Return the string address of the public key -func (kp EncKeyPair) GetAddressString() string { +func (kp encKeyPair) GetAddressString() string { return kp.PublicKey.Address().String() } // Unarmour the private key with the passphrase provided -func (kp EncKeyPair) Unarmour(passphrase string) (PrivateKey, error) { +func (kp encKeyPair) Unarmour(passphrase string) (PrivateKey, error) { return unarmourDecryptPrivKey(kp.PrivKeyArmour, passphrase) } // Export Private Key String -func (kp EncKeyPair) ExportString(passphrase string) (string, error) { +func (kp encKeyPair) ExportString(passphrase string) (string, error) { privKey, err := unarmourDecryptPrivKey(kp.PrivKeyArmour, passphrase) if err != nil { return "", err @@ -69,7 +91,7 @@ func (kp EncKeyPair) ExportString(passphrase string) (string, error) { } // Export Private Key as armoured JSON string with fields to decrypt -func (kp EncKeyPair) ExportJSON(passphrase string) (string, error) { +func (kp encKeyPair) ExportJSON(passphrase string) (string, error) { _, err := unarmourDecryptPrivKey(kp.PrivKeyArmour, passphrase) if err != nil { return "", err @@ -77,6 +99,29 @@ func (kp EncKeyPair) ExportJSON(passphrase string) (string, error) { return kp.PrivKeyArmour, nil } +// Marshal KeyPair into a []byte +func (kp encKeyPair) Marshal() ([]byte, error) { + buf := new(bytes.Buffer) + enc := gob.NewEncoder(buf) + if err := enc.Encode(kp); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// Unmarshal []byte into an encKeyPair struct +func (kp *encKeyPair) Unmarshal(bz []byte) error { + var keyPair encKeyPair + keypairBz := new(bytes.Buffer) + keypairBz.Write(bz) + dec := gob.NewDecoder(keypairBz) + if err := dec.Decode(&keyPair); err != nil { + return err + } + *kp = keyPair + 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 func CreateNewKey(passphrase, hint string) (KeyPair, error) { From 07a605f471d5e1d4ce139574f8248862d4110850 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sat, 28 Jan 2023 13:11:03 +0000 Subject: [PATCH 43/56] Fix comments --- app/client/keybase/keystore.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index f971cea75..75e122612 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -60,7 +60,7 @@ func (keybase *badgerKeybase) Create(passphrase, hint string) error { // Use key address as key in DB addrKey := keyPair.GetAddressBytes() - // Encode entire KeyPair struct into []byte for value + // Encode KeyPair into []byte for value keypairBz, err := keyPair.Marshal() if err != nil { return err @@ -88,7 +88,7 @@ func (keybase *badgerKeybase) ImportFromString(privKeyHex, passphrase, hint stri // Use key address as key in DB addrKey := keyPair.GetAddressBytes() - // Encode entire KeyPair struct into []byte for value + // Encode KeyPair into []byte for value keypairBz, err := keyPair.Marshal() if err != nil { return err @@ -116,7 +116,7 @@ func (keybase *badgerKeybase) ImportFromJSON(jsonStr, passphrase string) error { // Use key address as key in DB addrKey := keyPair.GetAddressBytes() - // Encode entire KeyPair struct into []byte for value + // Encode KeyPair into []byte for value keypairBz, err := keyPair.Marshal() if err != nil { return err @@ -153,7 +153,7 @@ func (keybase *badgerKeybase) Get(address string) (crypto.KeyPair, error) { return err } - // Decode []byte value back into KeyPair struct + // Decode []byte value back into KeyPair if err := kp.Unmarshal(value); err != nil { return err } @@ -206,7 +206,8 @@ func (keybase *badgerKeybase) GetAll() (addresses []string, keyPairs []crypto.Ke err := item.Value(func(val []byte) error { b := make([]byte, len(val)) copy(b, val) - // Decode []byte value back into KeyPair struct + + // Decode []byte value back into KeyPair kp := crypto.GetKeypair() if err := kp.Unmarshal(b); err != nil { return err @@ -282,7 +283,7 @@ func (keybase *badgerKeybase) UpdatePassphrase(address, oldPassphrase, newPassph return fmt.Errorf("Key address does not match previous address.") } - // Encode entire KeyPair struct into []byte for value + // Encode KeyPair into []byte for value keypairBz, err := keyPair.Marshal() if err != nil { return err From cc92bb0182de8aee111f8c12e055108d79b1b20d Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sat, 28 Jan 2023 13:16:48 +0000 Subject: [PATCH 44/56] Add db path directory exists check --- app/client/keybase/keystore.go | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index 75e122612..7a56cdf78 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/hex" "fmt" + "os" "strings" "github.com/dgraph-io/badger/v3" @@ -24,8 +25,12 @@ type badgerKeybase struct { } // Creates/Opens the DB at the specified path -// WARNING: path must be a valid directory that already exists +// DISCUSS: Should this create the directory if it doesn't exist? func NewKeybase(path string) (Keybase, error) { + pathExists, err := dirExists(path) + if err != nil || !pathExists { + return nil, err + } db, err := badger.Open(badgerOptions(path)) if err != nil { return nil, err @@ -342,3 +347,19 @@ func badgerOptions(path string) badger.Options { opts.Logger = nil // Badger logger is very noisy return opts } + +// Check directory exists / create if not +// Check that a file exists at the given path +func dirExists(path string) (bool, error) { + stat, err := os.Stat(path) + if err == nil { + if !stat.IsDir() { + return false, fmt.Errorf("Keybase path is not a directory: %s", path) + } + return true, nil + } + if os.IsNotExist(err) { + return false, nil + } + return false, err +} From 4b1712c030473ffc9b091b679508e59b7d78084d Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sat, 28 Jan 2023 13:23:22 +0000 Subject: [PATCH 45/56] Reduce scope of ArmouredKey --- shared/crypto/armour.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/shared/crypto/armour.go b/shared/crypto/armour.go index 24a585da0..03a3b8c98 100644 --- a/shared/crypto/armour.go +++ b/shared/crypto/armour.go @@ -35,7 +35,7 @@ var ( ) // Armoured Private Key struct with fields to unarmour it later -type ArmouredKey struct { +type armouredKey struct { Kdf string `json:"kdf"` Salt string `json:"salt"` SecParam string `json:"secparam"` @@ -44,8 +44,8 @@ type ArmouredKey struct { } // Generate new armoured private key struct with parameters for unarmouring -func NewArmouredKey(kdf, salt, hint, cipher string) ArmouredKey { - return ArmouredKey{ +func newArmouredKey(kdf, salt, hint, cipher string) armouredKey { + return armouredKey{ Kdf: kdf, Salt: salt, SecParam: strconv.Itoa(secParam), @@ -67,7 +67,7 @@ func encryptArmourPrivKey(privKey PrivateKey, passphrase, hint string) (string, armourStr := base64.RawStdEncoding.EncodeToString(encBz) // Create ArmouredKey object so can unarmour later - armoured := NewArmouredKey(kdf, hex.EncodeToString(saltBz), hint, armourStr) + armoured := newArmouredKey(kdf, hex.EncodeToString(saltBz), hint, armourStr) // Encode armoured struct into []byte js, err := json.Marshal(armoured) @@ -103,28 +103,28 @@ func encryptPrivKey(privKey PrivateKey, passphrase string) (saltBz, encBz []byte // Unarmor and decrypt the private key using the passphrase provided func unarmourDecryptPrivKey(armourStr string, passphrase string) (privKey PrivateKey, err error) { // Decode armourStr back into ArmouredKey struct - armouredKey := ArmouredKey{} - err = json.Unmarshal([]byte(armourStr), &armouredKey) + ak := armouredKey{} + err = json.Unmarshal([]byte(armourStr), &ak) if err != nil { return nil, err } // Check the ArmouredKey for the correct parameters on kdf and salt - if armouredKey.Kdf != kdf { - return nil, fmt.Errorf("Unrecognized KDF type: %v", armouredKey.Kdf) + if ak.Kdf != kdf { + return nil, fmt.Errorf("Unrecognized KDF type: %v", ak.Kdf) } - if armouredKey.Salt == "" { + if ak.Salt == "" { return nil, fmt.Errorf("Missing salt bytes") } // Decoding the salt - saltBz, err := hex.DecodeString(armouredKey.Salt) + saltBz, err := hex.DecodeString(ak.Salt) if err != nil { return nil, fmt.Errorf("Error decoding salt: %v", err.Error()) } // Decoding the "armoured" ciphertext stored in base64 - encBz, err := base64.RawStdEncoding.DecodeString(armouredKey.CipherText) + encBz, err := base64.RawStdEncoding.DecodeString(ak.CipherText) if err != nil { return nil, fmt.Errorf("Error decoding ciphertext: %v", err.Error()) } From 598ef4852b0a9755abf2b8e9d1c0e59b83ef6976 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sat, 28 Jan 2023 13:25:13 +0000 Subject: [PATCH 46/56] Reduce scope of NewKeyPair --- shared/crypto/keypair.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shared/crypto/keypair.go b/shared/crypto/keypair.go index a3d747f83..ee6008e7b 100644 --- a/shared/crypto/keypair.go +++ b/shared/crypto/keypair.go @@ -44,7 +44,7 @@ type encKeyPair struct { } // Generate a new KeyPair struct given the public key and armoured private key -func NewKeyPair(pub PublicKey, priv string) KeyPair { +func newKeyPair(pub PublicKey, priv string) KeyPair { return &encKeyPair{ PublicKey: pub, PrivKeyArmour: priv, @@ -136,7 +136,7 @@ func CreateNewKey(passphrase, hint string) (KeyPair, error) { } pubKey := privKey.PublicKey() - kp := NewKeyPair(pubKey, privArmour) + kp := newKeyPair(pubKey, privArmour) return kp, nil } @@ -154,7 +154,7 @@ func CreateNewKeyFromString(privStrHex, passphrase, hint string) (KeyPair, error } pubKey := privKey.PublicKey() - kp := NewKeyPair(pubKey, privArmour) + kp := newKeyPair(pubKey, privArmour) return kp, nil } @@ -167,7 +167,7 @@ func ImportKeyFromJSON(jsonStr, passphrase string) (KeyPair, error) { return nil, err } pubKey := privKey.PublicKey() - kp := NewKeyPair(pubKey, jsonStr) + kp := newKeyPair(pubKey, jsonStr) return kp, nil } From a86654f980029513908a87a8c37fcf0ad23d4eeb Mon Sep 17 00:00:00 2001 From: Harry Law Date: Sat, 28 Jan 2023 13:32:04 +0000 Subject: [PATCH 47/56] Update README.md --- app/client/keybase/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/client/keybase/README.md b/app/client/keybase/README.md index c596c4238..ab47e76c1 100644 --- a/app/client/keybase/README.md +++ b/app/client/keybase/README.md @@ -20,7 +20,7 @@ The Keybase package uses a filesystem key-value database, `BadgerDB`, as its bac _The current keybase has not been integrated with any CLI endpoints, and as such is only accessible through the [keybase interface](#keybase-interface); tracked by #150_ -The DB stores the local key pairs in `EncKeyPair` structs encoded into `[]byte` using `encoding/gob` this is only used for internal storage in the DB. The `EncKeyPair` struct implements the [KeyPair interface](#keypair-interface) and as such has a number of methods that can be used on it. But relevent to the DB storage of these is the `GetAddressBytes()` function that returns the `[]byte` of the `PublicKey` field's hex address from the struct. The `[]byte` returned by the `GetAddressBytes()` function is used as the key in the key-value store and the value is the `gob` encoded `[]byte` of the `EncKeyPair` struct as a whole - which contains both the `PublicKey` and `PrivKeyArmour` (JSON encoded, encrypted private key string). +The DB stores the local keys encoded into `[]byte` using `encoding/gob` this is only used for internal storage in the DB. The [KeyPair interface](#keypair-interface) has a number of methods that can be used on it. But relevent to the DB storage of these is the `GetAddressBytes()` function that returns the `[]byte` of the `PublicKey` field's hex address from the struct. The `[]byte` returned by the `GetAddressBytes()` function is used as the key in the key-value store and the value is the `gob` encoded `[]byte` of the `KeyPair` interface as a whole - which contains both the `PublicKey` and `PrivKeyArmour` (JSON encoded, encrypted private key string). The Keybase DB layer then allows for a number of functions to be used which are exposed by the [Keybase interface](#keybase-interface) to fulfill CRUD operations on the DB itself. @@ -73,8 +73,9 @@ The [KeyPair interface](../../../shared/crypto/keypair.go) exposes methods relat - Get Public key address `[]byte` or hex `string` - Unarmour the private key JSON string - Export the private key hex string/JSON armoured string +- Marshal and unmarshal the KeyPair in and out of a `[]byte` -The [KeyPair](../../../shared/crypto/keypair.go) interface is implemented by the `EncKeyPair` struct, which stores the `PublicKey` of the key pair and the JSON encoded, `armoured` key string. +The [KeyPair](../../../shared/crypto/keypair.go) interface is implemented by the `encKeyPair` struct, which stores the `PublicKey` of the key pair and the JSON encoded, `armoured` key string. The private key armoured JSON string is created after the [encryption step](#encryption-and-armouring) has encrypted the private key and marshalled it into a JSON string. From cfdd92f7127851f9425b96a6e22e6c5b8155ba1f Mon Sep 17 00:00:00 2001 From: Harry Law Date: Mon, 30 Jan 2023 15:25:30 +0000 Subject: [PATCH 48/56] Change EncryptedBytes to CipherText within diagram in README.md --- app/client/keybase/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/client/keybase/README.md b/app/client/keybase/README.md index ab47e76c1..94bed415b 100644 --- a/app/client/keybase/README.md +++ b/app/client/keybase/README.md @@ -130,12 +130,12 @@ flowchart LR direction TB D["Cipher(key)"] E["GCM(block)"] - F["Open(encryptedBytes, nonce)"] + F["Open(ciphertext, nonce)"] D--Block-->E E--Nonce-->F end C--Salt-->S - C--EncryptedBytes-->AES-GCM + C--CipherText-->AES-GCM S--Key-->AES-GCM ``` From 5654f0a17f026def1457f755027bb8ccc383dfec Mon Sep 17 00:00:00 2001 From: Daniel Olshansky Date: Wed, 1 Feb 2023 16:12:43 -0800 Subject: [PATCH 49/56] Automatic VSCode formatting --- app/client/keybase/README.md | 54 ++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/app/client/keybase/README.md b/app/client/keybase/README.md index 94bed415b..33ba12b8a 100644 --- a/app/client/keybase/README.md +++ b/app/client/keybase/README.md @@ -1,20 +1,18 @@ -# Keybase +# Keybase This document is intended to outline the current Keybase implementation used by the V1 client, and is primarily focused on its design and implementation as well as testing. - -* [1. Backend Database](#1-backend-database) -* [2. Keybase Interface](#2-keybase-interface) - * [2.1. Keybase Code Structure](#21-keybase-code-structure) - * [2.2. Makefile Helper](#22-makefile-helper) -* [3. KeyPair Interface](#3-keypair-interface) - * [3.1. KeyPair Code Structure](#31-keypair-code-structure) -* [4. Encryption and Armouring](#4-encryption-and-armouring) -* [5. Testing](#5-testing) -* [6. TODO](#6-todo) - +- [1. Backend Database](#1-backend-database) +- [2. Keybase Interface](#2-keybase-interface) + - [2.1. Keybase Code Structure](#21-keybase-code-structure) + - [2.2. Makefile Helper](#22-makefile-helper) +- [3. KeyPair Interface](#3-keypair-interface) + - [3.1. KeyPair Code Structure](#31-keypair-code-structure) +- [4. Encryption and Armouring](#4-encryption-and-armouring) +- [5. Testing](#5-testing) +- [6. TODO](#6-todo) -## 1. Backend Database +## 1. Backend Database The Keybase package uses a filesystem key-value database, `BadgerDB`, as its backend to persistently store keys locally on the client machine. @@ -24,10 +22,10 @@ The DB stores the local keys encoded into `[]byte` using `encoding/gob` this is The Keybase DB layer then allows for a number of functions to be used which are exposed by the [Keybase interface](#keybase-interface) to fulfill CRUD operations on the DB itself. - -## 2. Keybase Interface +## 2. Keybase Interface The [Keybase interface](./keybase.go) exposes the CRUD operations to operate on keys, and supports the following operations: + - Create password protected private keys - Export/Import string/json keypairs - Retrieve public/private keys or keypairs @@ -37,17 +35,18 @@ The [Keybase interface](./keybase.go) exposes the CRUD operations to operate on - Message signing and verification The `Keybase` interface allows for the import/export of keys between V0<->V1. Meaning any key created in the V0 protocol can be imported in two ways to the V1 protocol. - 1. Via the JSON keyfile + +1. Via the JSON keyfile - This method will take the JSON encoded, encrypted private key, and will import it into the V1 keybase - the `passphrase` supplied must be the same as the one use to encrypt the key in the first place or the key won't be able to be imported - 2. Via the private key hex string +2. Via the private key hex string - This method will directly import the private key from the hex string provided and then encrypt it with the passphrase provided - this does mean than the passphrase can be different from the original as this is a decrypted form of the private key Although key pairs are stored in the local DB using the `[]byte` of the public key address as the key for retrieval all the accessing methods use the hex string of the public key's address to actually find the key for ease of use. Keys can be created without the use of any password - in order to do this the `passphrase` supplied to the functions must be `""`. The private key will still be encrypted but will simply use the empty string as the key. +### 2.1. Keybase Code Structure -### 2.1. Keybase Code Structure ``` app └── client @@ -60,15 +59,14 @@ app The interface itself is found in [keybase.go](./keybase.go) whereas its implementation can be found in [keystore.go](./keystore.go) - -### 2.2. Makefile Helper +### 2.2. Makefile Helper To aid in the testing of the local keybase the following `Makefile` command has been exposed `make test_app` which will run the test suites from the `app` module alone, which includes the [keybase_test.go](./keybase_test.go) file which covers the functionality of the `Keybase` implementation - -## 3. KeyPair Interface +## 3. KeyPair Interface The [KeyPair interface](../../../shared/crypto/keypair.go) exposes methods related to the operations used on the pairs of `PublicKey` types and JSON encoded, `PrivKeyArmour` strings., such as: + - Retrieve the public key or armoured private key JSON string - Get Public key address `[]byte` or hex `string` - Unarmour the private key JSON string @@ -79,8 +77,7 @@ The [KeyPair](../../../shared/crypto/keypair.go) interface is implemented by the The private key armoured JSON string is created after the [encryption step](#encryption-and-armouring) has encrypted the private key and marshalled it into a JSON string. - -### 3.1. KeyPair Code Structure +### 3.1. KeyPair Code Structure The KeyPair code is seperated into two files [keypair.go](../../../shared/crypto/keypair.go) and [armour.go](../../../shared/crypto/armour.go) @@ -91,8 +88,7 @@ shared └── keypair.go ``` - -## 4. Encryption and Armouring +## 4. Encryption and Armouring Whenever a new key is created or imported it is encrypted using the passphrase provided (this can be `""` for no passphrase). @@ -139,13 +135,11 @@ flowchart LR S--Key-->AES-GCM ``` - -## 5. Testing +## 5. Testing The full test suite can be run with `make test_app` where the [Keybase interface's](#keybase-interface) methods are tested with unit tests. - -## 6. TODO +## 6. TODO - [ ] Add better error catching and error messages for importing keys with invalid strings/invalid JSON - [ ] Research and implement threshold signatures and threshold keys From ca47d53b91132f2d6621586c71d9fed475789d20 Mon Sep 17 00:00:00 2001 From: Daniel Olshansky Date: Wed, 1 Feb 2023 17:27:58 -0800 Subject: [PATCH 50/56] Update the READMEs --- app/client/keybase/README.md | 139 ++++++++++------------------------- shared/crypto/README.md | 96 ++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 102 deletions(-) create mode 100644 shared/crypto/README.md diff --git a/app/client/keybase/README.md b/app/client/keybase/README.md index 33ba12b8a..68e370ad1 100644 --- a/app/client/keybase/README.md +++ b/app/client/keybase/README.md @@ -2,27 +2,28 @@ This document is intended to outline the current Keybase implementation used by the V1 client, and is primarily focused on its design and implementation as well as testing. -- [1. Backend Database](#1-backend-database) -- [2. Keybase Interface](#2-keybase-interface) - - [2.1. Keybase Code Structure](#21-keybase-code-structure) - - [2.2. Makefile Helper](#22-makefile-helper) -- [3. KeyPair Interface](#3-keypair-interface) - - [3.1. KeyPair Code Structure](#31-keypair-code-structure) -- [4. Encryption and Armouring](#4-encryption-and-armouring) -- [5. Testing](#5-testing) -- [6. TODO](#6-todo) +- [Backend Database](#backend-database) +- [Keybase Interface](#keybase-interface) + - [V0\<-\>V1 Interoperability](#v0-v1-interoperability) + - [Keybase Code Structure](#keybase-code-structure) +- [Makefile Testing Helper](#makefile-testing-helper) +- [KeyPair Encryption \& Armouring](#keypair-encryption--armouring) +- [TODO: Future Work](#todo-future-work) -## 1. Backend Database +_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)_ -The Keybase package uses a filesystem key-value database, `BadgerDB`, as its backend to persistently store keys locally on the client machine. +## Backend Database -_The current keybase has not been integrated with any CLI endpoints, and as such is only accessible through the [keybase interface](#keybase-interface); tracked by #150_ +The Keybase package uses a filesystem key-value database, `BadgerDB`, as its backend to persistently store keys locally on the client machine. The DB stores the local keys encoded as `[]byte` using `encoding/gob`. -The DB stores the local keys encoded into `[]byte` using `encoding/gob` this is only used for internal storage in the DB. The [KeyPair interface](#keypair-interface) has a number of methods that can be used on it. But relevent to the DB storage of these is the `GetAddressBytes()` function that returns the `[]byte` of the `PublicKey` field's hex address from the struct. The `[]byte` returned by the `GetAddressBytes()` function is used as the key in the key-value store and the value is the `gob` encoded `[]byte` of the `KeyPair` interface as a whole - which contains both the `PublicKey` and `PrivKeyArmour` (JSON encoded, encrypted private key string). +The `KeyPair` defined in [crypto package](../../../shared/core/crypto) is the data structure that's stored in the DB. Specifically: -The Keybase DB layer then allows for a number of functions to be used which are exposed by the [Keybase interface](#keybase-interface) to fulfill CRUD operations on the DB itself. +- **Key**: The `[]byte` returned by the `GetAddressBytes()` function is used as the key in the key-value store. +- **Value**: The `gob` encoded struct of the entire `KeyPair`, containing both the `PublicKey` and `PrivKeyArmour` (JSON encoded, encrypted private key string), is the value. -## 2. Keybase Interface +The Keybase DB layer exposes several functions, defined by the [Keybase interface](#keybase-interface), to fulfill CRUD operations on the DB itself and oeprate with the Keypairs. + +## Keybase Interface The [Keybase interface](./keybase.go) exposes the CRUD operations to operate on keys, and supports the following operations: @@ -34,20 +35,23 @@ The [Keybase interface](./keybase.go) exposes the CRUD operations to operate on - Update passphrase on a private key - Message signing and verification -The `Keybase` interface allows for the import/export of keys between V0<->V1. Meaning any key created in the V0 protocol can be imported in two ways to the V1 protocol. +### V0<->V1 Interoperability -1. Via the JSON keyfile - - This method will take the JSON encoded, encrypted private key, and will import it into the V1 keybase - the `passphrase` supplied must be the same as the one use to encrypt the key in the first place or the key won't be able to be imported -2. Via the private key hex string - - This method will directly import the private key from the hex string provided and then encrypt it with the passphrase provided - this does mean than the passphrase can be different from the original as this is a decrypted form of the private key +The `Keybase` interface supports full interoperability of key export & import between Pocket [V0](https://github.com/pokt-network/pocket-core)<->[V1](https://github.com/pokt-network/pocket). -Although key pairs are stored in the local DB using the `[]byte` of the public key address as the key for retrieval all the accessing methods use the hex string of the public key's address to actually find the key for ease of use. +Any private key created in the V0 protocol can be imported into V1 via one of the following two ways: -Keys can be created without the use of any password - in order to do this the `passphrase` supplied to the functions must be `""`. The private key will still be encrypted but will simply use the empty string as the key. +1. **JSON keyfile**: This method will take the JSON encoded, encrypted private key, and will import it into the V1 keybase. The `passphrase` supplied must be the same as the one use to encrypt the key in the first place or the key won't be importable. -### 2.1. Keybase Code Structure +2. **Private Key Hex String**: This method will directly import the private key from the hex string provided and encrypt it with the passphrase provided. This enables the passphrase to be different from the original as the provided plaintext is already decrypted. -``` +Although key pairs are stored in the local DB using the serialized (`[]byte`) representation of the public key, the associated address can be used for accessing the record in the DB for simplicity. + +Keys can be created without a password by specifying an empty (`""`) passphrase. The private key will still be encrypted at rest but will use the empty string as the passphrase for decryption. + +### Keybase Code Structure + +```bash app └── client └── keybase @@ -57,90 +61,21 @@ app └── keystore.go ``` -The interface itself is found in [keybase.go](./keybase.go) whereas its implementation can be found in [keystore.go](./keystore.go) - -### 2.2. Makefile Helper - -To aid in the testing of the local keybase the following `Makefile` command has been exposed `make test_app` which will run the test suites from the `app` module alone, which includes the [keybase_test.go](./keybase_test.go) file which covers the functionality of the `Keybase` implementation - -## 3. KeyPair Interface - -The [KeyPair interface](../../../shared/crypto/keypair.go) exposes methods related to the operations used on the pairs of `PublicKey` types and JSON encoded, `PrivKeyArmour` strings., such as: +The interface is found in [keybase.go](./keybase.go) whereas its implementation can be found in [keystore.go](./keystore.go) -- Retrieve the public key or armoured private key JSON string -- Get Public key address `[]byte` or hex `string` -- Unarmour the private key JSON string -- Export the private key hex string/JSON armoured string -- Marshal and unmarshal the KeyPair in and out of a `[]byte` +## Makefile Testing Helper -The [KeyPair](../../../shared/crypto/keypair.go) interface is implemented by the `encKeyPair` struct, which stores the `PublicKey` of the key pair and the JSON encoded, `armoured` key string. +The unit tests for the keybase are defined in [keybase_test.go](./keybase_test.go) and can therefore be executed alongside other application specific tests by running `make test_app`. -The private key armoured JSON string is created after the [encryption step](#encryption-and-armouring) has encrypted the private key and marshalled it into a JSON string. - -### 3.1. KeyPair Code Structure - -The KeyPair code is seperated into two files [keypair.go](../../../shared/crypto/keypair.go) and [armour.go](../../../shared/crypto/armour.go) - -``` -shared -└── crypto - ├── armour.go - └── keypair.go -``` - -## 4. Encryption and Armouring - -Whenever a new key is created or imported it is encrypted using the passphrase provided (this can be `""` for no passphrase). - -```mermaid -flowchart LR - subgraph C[core lib] - A["rand([16]byte)"] - end - subgraph S[scrypt lib] - B["key(salt, pass, ...)"] - end - subgraph AES-GCM - direction TB - D["Cipher(key)"] - E["GCM(block)"] - F["Seal(plaintext, nonce)"] - D--Block-->E - E--Nonce-->F - end - C--Salt-->S - S--Key-->AES-GCM -``` - -When unarmouring and decrypting the same process is done in reverse. - -```mermaid -flowchart LR - subgraph C[core lib] - A["decode(privateKeyArmouredString)"] - end - subgraph S[scrypt lib] - B["key(salt, pass, ...)"] - end - subgraph AES-GCM - direction TB - D["Cipher(key)"] - E["GCM(block)"] - F["Open(ciphertext, nonce)"] - D--Block-->E - E--Nonce-->F - end - C--Salt-->S - C--CipherText-->AES-GCM - S--Key-->AES-GCM -``` +## KeyPair Encryption & Armouring -## 5. Testing +The [documentation in the crypto library](../../../shared/crypto/README.md) covers all of the details related to the `KeyPair` interface, as well as `PrivateKey` encryption, armouring and unarmouring. -The full test suite can be run with `make test_app` where the [Keybase interface's](#keybase-interface) methods are tested with unit tests. +The primitives and functions defined there are heavily used throughout this package. -## 6. TODO +## TODO: Future Work -- [ ] Add better error catching and error messages for importing keys with invalid strings/invalid JSON +- [ ] Improve error handling and error messages for importing keys with invalid strings/invalid JSON - [ ] Research and implement threshold signatures and threshold keys - [ ] Look into a fully feature signature implementation beyond trivial `[]byte` messages +- [ ] Integrate the keybase with the CLI (#150) diff --git a/shared/crypto/README.md b/shared/crypto/README.md new file mode 100644 index 000000000..3475e0ccb --- /dev/null +++ b/shared/crypto/README.md @@ -0,0 +1,96 @@ +# Pocket Crypto + +- [KeyPair Interface](#keypair-interface) + - [KeyPair Code Structure](#keypair-code-structure) +- [Encryption and Armouring](#encryption-and-armouring) + +_DOCUMENT: Note that this README is a WIP and does not exhaustively document all the current types in this package_ + +## KeyPair Interface + +The [KeyPair interface](./keypair.go) exposes methods related to operating on `PublicKey` types and `PrivKeyArmour` strings, such as: + +- Retrieve the PublicKey or armoured PrivateKey JSON string +- Get PublicKey address `[]byte` or hex `string` +- Unarmour the PrivateKey JSON string +- Export the PrivateKey hex string or JSON as an armoured string +- Marshal or unmarshal the KeyPair to/from a `[]byte` + +The [KeyPair](./keypair.go) interface is implemented by the `encKeyPair` struct which stores: + +1. `PublicKey` of the KeyPair +2. `PrivateKey` armoured JSON string + +The PrivateKey armoured JSON string is created after the [encryption step](#encryption-and-armouring) has encrypted the PrivateKey and marshalled it into a JSON string. + +### KeyPair Code Structure + +The KeyPair code is separated into two files: [keypair.go](./keypair.go) and [armour.go](./armour.go) + +```bash +shared +└── crypto + ├── armour.go + └── keypair.go +``` + +## Encryption and Armouring + +The passphrase provided or `""` (default) is used for encrypting and armouring new or imported keys. + +The following flowchart shows this process: + +```mermaid +flowchart LR + subgraph C[core lib] + A["rand([16]byte)"] + end + subgraph S[scrypt lib] + B["key(salt, pass, ...)"] + end + subgraph AES-GCM + direction TB + D["Cipher(key)"] + E["GCM(block)"] + F["Seal(plaintext, nonce)"] + D--Block-->E + E--Nonce-->F + end + subgraph Armour + direction LR + G["base64(encryptedPrivateKey)"] + H["base64(Salt)"] + G --> armoured + H --> armoured + end + C--Salt-->S + S--Key-->AES-GCM + AES-GCM--encryptedPrivateKey-->Armour + C--Salt-->Armour + kdf --> Armour + hint --> Armour + Armour--Marshal-->Return(encryptedArmouredPrivateKey) +``` + +The process above is reversed when unarmouring and decrypting a key in the keybase: + +```mermaid +flowchart LR + subgraph C[core lib] + A["decode(privateKeyArmouredString)"] + end + subgraph S[scrypt lib] + B["key(salt, pass, ...)"] + end + subgraph AES-GCM + direction TB + D["Cipher(key)"] + E["GCM(block)"] + F["Open(ciphertext, nonce)"] + D--Block-->E + E--Nonce-->F + end + C--Salt-->S + C--CipherText-->AES-GCM + S--Key-->AES-GCM +``` From 697d0317ad2119e7c5f5e27fb153caee3517ba35 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Thu, 2 Feb 2023 10:03:23 +0000 Subject: [PATCH 51/56] Update unarmour diagram --- shared/crypto/README.md | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/shared/crypto/README.md b/shared/crypto/README.md index 3475e0ccb..218934afa 100644 --- a/shared/crypto/README.md +++ b/shared/crypto/README.md @@ -58,8 +58,8 @@ flowchart LR end subgraph Armour direction LR - G["base64(encryptedPrivateKey)"] - H["base64(Salt)"] + G["base64Encode(encryptedPrivateKey)"] + H["hexEncode(Salt)"] G --> armoured H --> armoured end @@ -76,21 +76,27 @@ The process above is reversed when unarmouring and decrypting a key in the keyba ```mermaid flowchart LR - subgraph C[core lib] - A["decode(privateKeyArmouredString)"] + subgraph U[Unarmour] + armoured + B["hexDecode(salt)"] + C["base64Decode(cipherText)"] + armoured-->B + armoured-->C end subgraph S[scrypt lib] - B["key(salt, pass, ...)"] + E["key(salt, pass, ...)"] end subgraph AES-GCM direction TB - D["Cipher(key)"] - E["GCM(block)"] - F["Open(ciphertext, nonce)"] - D--Block-->E - E--Nonce-->F + F["Cipher(key)"] + G["GCM(block)"] + H["Open(encryptedBytes, nonce)"] + F--Block-->G + G--Nonce-->H end - C--Salt-->S - C--CipherText-->AES-GCM + encryptedArmouredPrivateKey --Unmarshal--> U + B--Salt-->S + C--encryptedBytes-->AES-GCM S--Key-->AES-GCM + AES-GCM-->PrivateKey ``` From c707aadf6610aa176382d79e8ed9ffc9f2cf5b57 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Thu, 2 Feb 2023 11:16:42 +0000 Subject: [PATCH 52/56] Update comments and readme --- app/client/keybase/keystore.go | 6 +++--- shared/crypto/README.md | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index 7a56cdf78..c82ebe9f0 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -53,7 +53,7 @@ func (keybase *badgerKeybase) Stop() error { return keybase.db.Close() } -// Create a new key and store it in the DB by encoding the KeyPair struct into a []byte +// Create a new key and store the serialised KeyPair encoding in the DB // Using the PublicKey.Address() return value as the key for storage func (keybase *badgerKeybase) Create(passphrase, hint string) error { err := keybase.db.Update(func(tx *badger.Txn) error { @@ -81,7 +81,7 @@ func (keybase *badgerKeybase) Create(passphrase, hint string) error { return err } -// Create a new KeyPair from the private key hex string and store it in the DB by encoding the KeyPair struct into a []byte +// Create 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 func (keybase *badgerKeybase) ImportFromString(privKeyHex, passphrase, hint string) error { err := keybase.db.Update(func(tx *badger.Txn) error { @@ -109,7 +109,7 @@ func (keybase *badgerKeybase) ImportFromString(privKeyHex, passphrase, hint stri return err } -// Create a new KeyPair from the private key JSON string and store it in the DB by encoding the KeyPair struct into a []byte +// Create 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 func (keybase *badgerKeybase) ImportFromJSON(jsonStr, passphrase string) error { err := keybase.db.Update(func(tx *badger.Txn) error { diff --git a/shared/crypto/README.md b/shared/crypto/README.md index 218934afa..a7a1da699 100644 --- a/shared/crypto/README.md +++ b/shared/crypto/README.md @@ -80,8 +80,11 @@ flowchart LR armoured B["hexDecode(salt)"] C["base64Decode(cipherText)"] - armoured-->B - armoured-->C + D["verify"] + armoured--salt-->B + armoured--cipherText-->C + armoured--kdf-->D + end subgraph S[scrypt lib] E["key(salt, pass, ...)"] From 86deecc70e8c63893ce9cdde3547b637d767de5b Mon Sep 17 00:00:00 2001 From: Harry Law Date: Thu, 2 Feb 2023 11:45:03 +0000 Subject: [PATCH 53/56] Create DB path if it doesn't exist --- app/client/keybase/keystore.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index c82ebe9f0..b622e2889 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -25,9 +25,8 @@ type badgerKeybase struct { } // Creates/Opens the DB at the specified path -// DISCUSS: Should this create the directory if it doesn't exist? func NewKeybase(path string) (Keybase, error) { - pathExists, err := dirExists(path) + pathExists, err := dirExists(path) // Creates path if it doesn't exist if err != nil || !pathExists { return nil, err } @@ -349,17 +348,21 @@ func badgerOptions(path string) badger.Options { } // Check directory exists / create if not -// Check that a file exists at the given path func dirExists(path string) (bool, error) { stat, err := os.Stat(path) if err == nil { + // Exists but not directory if !stat.IsDir() { return false, fmt.Errorf("Keybase path is not a directory: %s", path) } return true, nil } if os.IsNotExist(err) { - return false, nil + // Create directories in path recursively + if err := os.MkdirAll(path, os.ModePerm); err != nil { + return false, fmt.Errorf("Error creating directory at path: %s, (%v)", path, err.Error()) + } + return true, nil } return false, err } From adbd983bd6a0695605f0f4c5297431d48ffaffa0 Mon Sep 17 00:00:00 2001 From: Harry Law Date: Thu, 2 Feb 2023 15:11:38 +0000 Subject: [PATCH 54/56] Address golangci-lint errors --- app/client/keybase/keybase_test.go | 50 +++++++++++++++++------------- app/client/keybase/keystore.go | 12 ++++--- shared/crypto/armour.go | 6 ++-- 3 files changed, 38 insertions(+), 30 deletions(-) diff --git a/app/client/keybase/keybase_test.go b/app/client/keybase/keybase_test.go index 3962fe2b6..aafbf0865 100644 --- a/app/client/keybase/keybase_test.go +++ b/app/client/keybase/keybase_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/require" ) +//nolint:gosec // G101 Credentials are for tests const ( // Example account testPrivString = "045e8380086abc6f6e941d6fe47ca93b86723bc246ec8c4beee411b410028675ed78c49592f836f7a4d47d4fb6a0e6b19f07aebc201d005f6b2c6afe389086e9" @@ -30,7 +31,7 @@ const ( func TestKeybase_CreateNewKey(t *testing.T) { db := initDB(t) - defer db.Stop() + defer stopDB(t, db) err := db.Create(testPassphrase, testHint) require.NoError(t, err) @@ -48,7 +49,7 @@ func TestKeybase_CreateNewKey(t *testing.T) { func TestKeybase_CreateNewKeyNoPassphrase(t *testing.T) { db := initDB(t) - defer db.Stop() + defer stopDB(t, db) err := db.Create("", "") require.NoError(t, err) @@ -66,7 +67,7 @@ func TestKeybase_CreateNewKeyNoPassphrase(t *testing.T) { func TestKeybase_ImportKeyFromString(t *testing.T) { db := initDB(t) - defer db.Stop() + defer stopDB(t, db) err := db.ImportFromString(testPrivString, testPassphrase, testHint) require.NoError(t, err) @@ -90,7 +91,7 @@ func TestKeybase_ImportKeyFromString(t *testing.T) { func TestKeybase_ImportKeyFromStringNoPassphrase(t *testing.T) { db := initDB(t) - defer db.Stop() + defer stopDB(t, db) err := db.ImportFromString(testPrivString, "", "") require.NoError(t, err) @@ -115,7 +116,7 @@ func TestKeybase_ImportKeyFromStringNoPassphrase(t *testing.T) { // TODO: Improve this test/create functions to check string validity func TestKeybase_ImportKeyFromStringInvalidString(t *testing.T) { db := initDB(t) - defer db.Stop() + defer stopDB(t, db) testKey := createTestKeys(t, 1)[0] @@ -129,7 +130,7 @@ func TestKeybase_ImportKeyFromStringInvalidString(t *testing.T) { func TestKeybase_ImportKeyFromJSON(t *testing.T) { db := initDB(t) - defer db.Stop() + defer stopDB(t, db) err := db.ImportFromJSON(testJSONString, testPassphrase) require.NoError(t, err) @@ -153,7 +154,7 @@ func TestKeybase_ImportKeyFromJSON(t *testing.T) { func TestKeybase_GetKey(t *testing.T) { db := initDB(t) - defer db.Stop() + defer stopDB(t, db) testKey := createTestKeys(t, 1)[0] @@ -175,7 +176,7 @@ func TestKeybase_GetKey(t *testing.T) { func TestKeybase_GetKeyDoesntExist(t *testing.T) { db := initDB(t) - defer db.Stop() + defer stopDB(t, db) testKey := createTestKeys(t, 1)[0] @@ -186,7 +187,7 @@ func TestKeybase_GetKeyDoesntExist(t *testing.T) { func TestKeybase_CheckKeyExists(t *testing.T) { db := initDB(t) - defer db.Stop() + defer stopDB(t, db) testKey := createTestKeys(t, 1)[0] @@ -200,7 +201,7 @@ func TestKeybase_CheckKeyExists(t *testing.T) { func TestKeybase_CheckKeyExistsDoesntExist(t *testing.T) { db := initDB(t) - defer db.Stop() + defer stopDB(t, db) testKey := createTestKeys(t, 1)[0] @@ -211,7 +212,7 @@ func TestKeybase_CheckKeyExistsDoesntExist(t *testing.T) { func TestKeybase_GetAllKeys(t *testing.T) { db := initDB(t) - defer db.Stop() + defer stopDB(t, db) pkm := make(map[string]crypto.PrivateKey, 0) pks := createTestKeys(t, 5) @@ -239,7 +240,7 @@ func TestKeybase_GetAllKeys(t *testing.T) { func TestKeybase_GetPubKey(t *testing.T) { db := initDB(t) - defer db.Stop() + defer stopDB(t, db) testKey := createTestKeys(t, 1)[0] @@ -257,7 +258,7 @@ func TestKeybase_GetPubKey(t *testing.T) { func TestKeybase_GetPrivKey(t *testing.T) { db := initDB(t) - defer db.Stop() + defer stopDB(t, db) testKey := createTestKeys(t, 1)[0] @@ -276,7 +277,7 @@ func TestKeybase_GetPrivKey(t *testing.T) { func TestKeybase_GetPrivKeyWrongPassphrase(t *testing.T) { db := initDB(t) - defer db.Stop() + defer stopDB(t, db) testKey := createTestKeys(t, 1)[0] @@ -290,7 +291,7 @@ func TestKeybase_GetPrivKeyWrongPassphrase(t *testing.T) { func TestKeybase_UpdatePassphrase(t *testing.T) { db := initDB(t) - defer db.Stop() + defer stopDB(t, db) testKey := createTestKeys(t, 1)[0] @@ -315,7 +316,7 @@ func TestKeybase_UpdatePassphrase(t *testing.T) { func TestKeybase_UpdatePassphraseWrongPassphrase(t *testing.T) { db := initDB(t) - defer db.Stop() + defer stopDB(t, db) testKey := createTestKeys(t, 1)[0] @@ -331,7 +332,7 @@ func TestKeybase_UpdatePassphraseWrongPassphrase(t *testing.T) { func TestKeybase_DeleteKey(t *testing.T) { db := initDB(t) - defer db.Stop() + defer stopDB(t, db) testKey := createTestKeys(t, 1)[0] @@ -351,7 +352,7 @@ func TestKeybase_DeleteKey(t *testing.T) { func TestKeybase_DeleteKeyWrongPassphrase(t *testing.T) { db := initDB(t) - defer db.Stop() + defer stopDB(t, db) testKey := createTestKeys(t, 1)[0] @@ -367,7 +368,7 @@ func TestKeybase_DeleteKeyWrongPassphrase(t *testing.T) { func TestKeybase_SignMessage(t *testing.T) { db := initDB(t) - defer db.Stop() + defer stopDB(t, db) pk := createTestKeyFromString(t, testPrivString) @@ -390,7 +391,7 @@ func TestKeybase_SignMessage(t *testing.T) { func TestKeybase_SignMessageWrongPassphrase(t *testing.T) { db := initDB(t) - defer db.Stop() + defer stopDB(t, db) pk := createTestKeyFromString(t, testPrivString) @@ -410,7 +411,7 @@ func TestKeybase_SignMessageWrongPassphrase(t *testing.T) { func TestKeybase_ExportString(t *testing.T) { db := initDB(t) - defer db.Stop() + defer stopDB(t, db) err := db.ImportFromString(testPrivString, testPassphrase, testHint) require.NoError(t, err) @@ -422,7 +423,7 @@ func TestKeybase_ExportString(t *testing.T) { func TestKeybase_ExportJSON(t *testing.T) { db := initDB(t) - defer db.Stop() + defer stopDB(t, db) err := db.ImportFromString(testPrivString, testPassphrase, testHint) require.NoError(t, err) @@ -465,3 +466,8 @@ func createTestKeyFromString(t *testing.T, str string) crypto.PrivateKey { require.NoError(t, err) return privKey } + +func stopDB(t *testing.T, db Keybase) { + err := db.Stop() + require.NoError(t, err) +} diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index b622e2889..c51db1216 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -70,7 +70,7 @@ func (keybase *badgerKeybase) Create(passphrase, hint string) error { return err } - if err = tx.Set(addrKey, keypairBz); err != nil { + if err := tx.Set(addrKey, keypairBz); err != nil { return err } @@ -98,7 +98,7 @@ func (keybase *badgerKeybase) ImportFromString(privKeyHex, passphrase, hint stri return err } - if err = tx.Set(addrKey, keypairBz); err != nil { + if err := tx.Set(addrKey, keypairBz); err != nil { return err } @@ -126,7 +126,7 @@ func (keybase *badgerKeybase) ImportFromJSON(jsonStr, passphrase string) error { return err } - if err = tx.Set(addrKey, keypairBz); err != nil { + if err := tx.Set(addrKey, keypairBz); err != nil { return err } @@ -283,7 +283,7 @@ func (keybase *badgerKeybase) UpdatePassphrase(address, oldPassphrase, newPassph // Use key address as key in DB addrKey := keyPair.GetAddressBytes() - if bytes.Compare(addrKey, addrBz) != 0 { + if !bytes.Equal(addrKey, addrBz) { return fmt.Errorf("Key address does not match previous address.") } @@ -334,7 +334,9 @@ func (keybase *badgerKeybase) Delete(address, passphrase string) error { } err = keybase.db.Update(func(tx *badger.Txn) error { - tx.Delete(addrBz) + if err := tx.Delete(addrBz); err != nil { + return err + } return nil }) return err diff --git a/shared/crypto/armour.go b/shared/crypto/armour.go index 03a3b8c98..5adb825ad 100644 --- a/shared/crypto/armour.go +++ b/shared/crypto/armour.go @@ -44,13 +44,13 @@ type armouredKey struct { } // Generate new armoured private key struct with parameters for unarmouring -func newArmouredKey(kdf, salt, hint, cipher string) armouredKey { +func newArmouredKey(kdf, salt, hint, cipherText string) armouredKey { return armouredKey{ Kdf: kdf, Salt: salt, SecParam: strconv.Itoa(secParam), Hint: hint, - CipherText: cipher, + CipherText: cipherText, } } @@ -101,7 +101,7 @@ func encryptPrivKey(privKey PrivateKey, passphrase string) (saltBz, encBz []byte } // Unarmor and decrypt the private key using the passphrase provided -func unarmourDecryptPrivKey(armourStr string, passphrase string) (privKey PrivateKey, err error) { +func unarmourDecryptPrivKey(armourStr, passphrase string) (privKey PrivateKey, err error) { // Decode armourStr back into ArmouredKey struct ak := armouredKey{} err = json.Unmarshal([]byte(armourStr), &ak) From df3b575f9816f29e8c08c88a628d64129612981e Mon Sep 17 00:00:00 2001 From: Harry Law Date: Thu, 2 Feb 2023 22:18:26 +0000 Subject: [PATCH 55/56] Simplify txn returns --- app/client/keybase/keystore.go | 31 ++++++------------------------- 1 file changed, 6 insertions(+), 25 deletions(-) diff --git a/app/client/keybase/keystore.go b/app/client/keybase/keystore.go index c51db1216..418816979 100644 --- a/app/client/keybase/keystore.go +++ b/app/client/keybase/keystore.go @@ -70,11 +70,7 @@ func (keybase *badgerKeybase) Create(passphrase, hint string) error { return err } - if err := tx.Set(addrKey, keypairBz); err != nil { - return err - } - - return nil + return tx.Set(addrKey, keypairBz) }) return err @@ -98,11 +94,7 @@ func (keybase *badgerKeybase) ImportFromString(privKeyHex, passphrase, hint stri return err } - if err := tx.Set(addrKey, keypairBz); err != nil { - return err - } - - return nil + return tx.Set(addrKey, keypairBz) }) return err @@ -126,11 +118,7 @@ func (keybase *badgerKeybase) ImportFromJSON(jsonStr, passphrase string) error { return err } - if err := tx.Set(addrKey, keypairBz); err != nil { - return err - } - - return nil + return tx.Set(addrKey, keypairBz) }) return err @@ -293,11 +281,7 @@ func (keybase *badgerKeybase) UpdatePassphrase(address, oldPassphrase, newPassph return err } - if err = tx.Set(addrKey, keypairBz); err != nil { - return err - } - - return nil + return tx.Set(addrKey, keypairBz) }) return err @@ -334,10 +318,7 @@ func (keybase *badgerKeybase) Delete(address, passphrase string) error { } err = keybase.db.Update(func(tx *badger.Txn) error { - if err := tx.Delete(addrBz); err != nil { - return err - } - return nil + return tx.Delete(addrBz) }) return err } @@ -349,7 +330,7 @@ func badgerOptions(path string) badger.Options { return opts } -// Check directory exists / create if not +// Check directory exists and creates path if it doesn't exist func dirExists(path string) (bool, error) { stat, err := os.Stat(path) if err == nil { From f39d5e6017bf69365a97d7bcdad32898c81345eb Mon Sep 17 00:00:00 2001 From: Harry Law Date: Thu, 2 Feb 2023 22:34:39 +0000 Subject: [PATCH 56/56] Update CHANGELOG.md files and move app/client/cli/doc to app/client/doc --- app/client/{cli => }/doc/CHANGELOG.md | 9 +++++++++ app/client/{cli => }/doc/README.md | 0 app/client/{cli => }/doc/commands/.gitkeep | 0 app/client/{cli => }/doc/commands/client.md | 0 app/client/{cli => }/doc/commands/client_Account.md | 0 app/client/{cli => }/doc/commands/client_Account_Send.md | 0 app/client/{cli => }/doc/commands/client_Application.md | 0 .../doc/commands/client_Application_EditStake.md | 0 .../{cli => }/doc/commands/client_Application_Stake.md | 0 .../{cli => }/doc/commands/client_Application_Unpause.md | 0 .../{cli => }/doc/commands/client_Application_Unstake.md | 0 app/client/{cli => }/doc/commands/client_Consensus.md | 0 .../{cli => }/doc/commands/client_Consensus_Height.md | 0 .../{cli => }/doc/commands/client_Consensus_Round.md | 0 .../{cli => }/doc/commands/client_Consensus_State.md | 0 .../{cli => }/doc/commands/client_Consensus_Step.md | 0 app/client/{cli => }/doc/commands/client_Fisherman.md | 0 .../{cli => }/doc/commands/client_Fisherman_EditStake.md | 0 .../{cli => }/doc/commands/client_Fisherman_Stake.md | 0 .../{cli => }/doc/commands/client_Fisherman_Unpause.md | 0 .../{cli => }/doc/commands/client_Fisherman_Unstake.md | 0 app/client/{cli => }/doc/commands/client_Governance.md | 0 .../doc/commands/client_Governance_ChangeParameter.md | 0 app/client/{cli => }/doc/commands/client_Node.md | 0 .../{cli => }/doc/commands/client_Node_EditStake.md | 0 app/client/{cli => }/doc/commands/client_Node_Stake.md | 0 app/client/{cli => }/doc/commands/client_Node_Unpause.md | 0 app/client/{cli => }/doc/commands/client_Node_Unstake.md | 0 app/client/{cli => }/doc/commands/client_System.md | 0 .../{cli => }/doc/commands/client_System_Health.md | 0 .../{cli => }/doc/commands/client_System_Version.md | 0 app/client/{cli => }/doc/commands/client_Validator.md | 0 .../{cli => }/doc/commands/client_Validator_EditStake.md | 0 .../{cli => }/doc/commands/client_Validator_Stake.md | 0 .../{cli => }/doc/commands/client_Validator_Unpause.md | 0 .../{cli => }/doc/commands/client_Validator_Unstake.md | 0 shared/CHANGELOG.md | 5 +++++ 37 files changed, 14 insertions(+) rename app/client/{cli => }/doc/CHANGELOG.md (84%) rename app/client/{cli => }/doc/README.md (100%) rename app/client/{cli => }/doc/commands/.gitkeep (100%) rename app/client/{cli => }/doc/commands/client.md (100%) rename app/client/{cli => }/doc/commands/client_Account.md (100%) rename app/client/{cli => }/doc/commands/client_Account_Send.md (100%) rename app/client/{cli => }/doc/commands/client_Application.md (100%) rename app/client/{cli => }/doc/commands/client_Application_EditStake.md (100%) rename app/client/{cli => }/doc/commands/client_Application_Stake.md (100%) rename app/client/{cli => }/doc/commands/client_Application_Unpause.md (100%) rename app/client/{cli => }/doc/commands/client_Application_Unstake.md (100%) rename app/client/{cli => }/doc/commands/client_Consensus.md (100%) rename app/client/{cli => }/doc/commands/client_Consensus_Height.md (100%) rename app/client/{cli => }/doc/commands/client_Consensus_Round.md (100%) rename app/client/{cli => }/doc/commands/client_Consensus_State.md (100%) rename app/client/{cli => }/doc/commands/client_Consensus_Step.md (100%) rename app/client/{cli => }/doc/commands/client_Fisherman.md (100%) rename app/client/{cli => }/doc/commands/client_Fisherman_EditStake.md (100%) rename app/client/{cli => }/doc/commands/client_Fisherman_Stake.md (100%) rename app/client/{cli => }/doc/commands/client_Fisherman_Unpause.md (100%) rename app/client/{cli => }/doc/commands/client_Fisherman_Unstake.md (100%) rename app/client/{cli => }/doc/commands/client_Governance.md (100%) rename app/client/{cli => }/doc/commands/client_Governance_ChangeParameter.md (100%) rename app/client/{cli => }/doc/commands/client_Node.md (100%) rename app/client/{cli => }/doc/commands/client_Node_EditStake.md (100%) rename app/client/{cli => }/doc/commands/client_Node_Stake.md (100%) rename app/client/{cli => }/doc/commands/client_Node_Unpause.md (100%) rename app/client/{cli => }/doc/commands/client_Node_Unstake.md (100%) rename app/client/{cli => }/doc/commands/client_System.md (100%) rename app/client/{cli => }/doc/commands/client_System_Health.md (100%) rename app/client/{cli => }/doc/commands/client_System_Version.md (100%) rename app/client/{cli => }/doc/commands/client_Validator.md (100%) rename app/client/{cli => }/doc/commands/client_Validator_EditStake.md (100%) rename app/client/{cli => }/doc/commands/client_Validator_Stake.md (100%) rename app/client/{cli => }/doc/commands/client_Validator_Unpause.md (100%) rename app/client/{cli => }/doc/commands/client_Validator_Unstake.md (100%) diff --git a/app/client/cli/doc/CHANGELOG.md b/app/client/doc/CHANGELOG.md similarity index 84% rename from app/client/cli/doc/CHANGELOG.md rename to app/client/doc/CHANGELOG.md index 2cb25caad..2004287cf 100644 --- a/app/client/cli/doc/CHANGELOG.md +++ b/app/client/doc/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.0.0.5] - 2023-02-02 + +### Added + +- Create `Keybase` interface to handle CRUD operations for `KeyPairs` with a `BadgerDB` backend +- Add logic to create, import, export, list, delete and update (passphrase) key pairs +- Add logic to sign and verify arbitrary messages +- Add unit tests for the keybase + ## [0.0.0.4] - 2023-01-10 - The `client` (i.e. CLI) no longer instantiates a `P2P` module along with a bus of optional modules. Instead, it instantiates a `client-only` `P2P` module that is disconnected from consensus and persistence. Interactions with the persistence & consensus layer happen via RPC. diff --git a/app/client/cli/doc/README.md b/app/client/doc/README.md similarity index 100% rename from app/client/cli/doc/README.md rename to app/client/doc/README.md diff --git a/app/client/cli/doc/commands/.gitkeep b/app/client/doc/commands/.gitkeep similarity index 100% rename from app/client/cli/doc/commands/.gitkeep rename to app/client/doc/commands/.gitkeep diff --git a/app/client/cli/doc/commands/client.md b/app/client/doc/commands/client.md similarity index 100% rename from app/client/cli/doc/commands/client.md rename to app/client/doc/commands/client.md diff --git a/app/client/cli/doc/commands/client_Account.md b/app/client/doc/commands/client_Account.md similarity index 100% rename from app/client/cli/doc/commands/client_Account.md rename to app/client/doc/commands/client_Account.md diff --git a/app/client/cli/doc/commands/client_Account_Send.md b/app/client/doc/commands/client_Account_Send.md similarity index 100% rename from app/client/cli/doc/commands/client_Account_Send.md rename to app/client/doc/commands/client_Account_Send.md diff --git a/app/client/cli/doc/commands/client_Application.md b/app/client/doc/commands/client_Application.md similarity index 100% rename from app/client/cli/doc/commands/client_Application.md rename to app/client/doc/commands/client_Application.md diff --git a/app/client/cli/doc/commands/client_Application_EditStake.md b/app/client/doc/commands/client_Application_EditStake.md similarity index 100% rename from app/client/cli/doc/commands/client_Application_EditStake.md rename to app/client/doc/commands/client_Application_EditStake.md diff --git a/app/client/cli/doc/commands/client_Application_Stake.md b/app/client/doc/commands/client_Application_Stake.md similarity index 100% rename from app/client/cli/doc/commands/client_Application_Stake.md rename to app/client/doc/commands/client_Application_Stake.md diff --git a/app/client/cli/doc/commands/client_Application_Unpause.md b/app/client/doc/commands/client_Application_Unpause.md similarity index 100% rename from app/client/cli/doc/commands/client_Application_Unpause.md rename to app/client/doc/commands/client_Application_Unpause.md diff --git a/app/client/cli/doc/commands/client_Application_Unstake.md b/app/client/doc/commands/client_Application_Unstake.md similarity index 100% rename from app/client/cli/doc/commands/client_Application_Unstake.md rename to app/client/doc/commands/client_Application_Unstake.md diff --git a/app/client/cli/doc/commands/client_Consensus.md b/app/client/doc/commands/client_Consensus.md similarity index 100% rename from app/client/cli/doc/commands/client_Consensus.md rename to app/client/doc/commands/client_Consensus.md diff --git a/app/client/cli/doc/commands/client_Consensus_Height.md b/app/client/doc/commands/client_Consensus_Height.md similarity index 100% rename from app/client/cli/doc/commands/client_Consensus_Height.md rename to app/client/doc/commands/client_Consensus_Height.md diff --git a/app/client/cli/doc/commands/client_Consensus_Round.md b/app/client/doc/commands/client_Consensus_Round.md similarity index 100% rename from app/client/cli/doc/commands/client_Consensus_Round.md rename to app/client/doc/commands/client_Consensus_Round.md diff --git a/app/client/cli/doc/commands/client_Consensus_State.md b/app/client/doc/commands/client_Consensus_State.md similarity index 100% rename from app/client/cli/doc/commands/client_Consensus_State.md rename to app/client/doc/commands/client_Consensus_State.md diff --git a/app/client/cli/doc/commands/client_Consensus_Step.md b/app/client/doc/commands/client_Consensus_Step.md similarity index 100% rename from app/client/cli/doc/commands/client_Consensus_Step.md rename to app/client/doc/commands/client_Consensus_Step.md diff --git a/app/client/cli/doc/commands/client_Fisherman.md b/app/client/doc/commands/client_Fisherman.md similarity index 100% rename from app/client/cli/doc/commands/client_Fisherman.md rename to app/client/doc/commands/client_Fisherman.md diff --git a/app/client/cli/doc/commands/client_Fisherman_EditStake.md b/app/client/doc/commands/client_Fisherman_EditStake.md similarity index 100% rename from app/client/cli/doc/commands/client_Fisherman_EditStake.md rename to app/client/doc/commands/client_Fisherman_EditStake.md diff --git a/app/client/cli/doc/commands/client_Fisherman_Stake.md b/app/client/doc/commands/client_Fisherman_Stake.md similarity index 100% rename from app/client/cli/doc/commands/client_Fisherman_Stake.md rename to app/client/doc/commands/client_Fisherman_Stake.md diff --git a/app/client/cli/doc/commands/client_Fisherman_Unpause.md b/app/client/doc/commands/client_Fisherman_Unpause.md similarity index 100% rename from app/client/cli/doc/commands/client_Fisherman_Unpause.md rename to app/client/doc/commands/client_Fisherman_Unpause.md diff --git a/app/client/cli/doc/commands/client_Fisherman_Unstake.md b/app/client/doc/commands/client_Fisherman_Unstake.md similarity index 100% rename from app/client/cli/doc/commands/client_Fisherman_Unstake.md rename to app/client/doc/commands/client_Fisherman_Unstake.md diff --git a/app/client/cli/doc/commands/client_Governance.md b/app/client/doc/commands/client_Governance.md similarity index 100% rename from app/client/cli/doc/commands/client_Governance.md rename to app/client/doc/commands/client_Governance.md diff --git a/app/client/cli/doc/commands/client_Governance_ChangeParameter.md b/app/client/doc/commands/client_Governance_ChangeParameter.md similarity index 100% rename from app/client/cli/doc/commands/client_Governance_ChangeParameter.md rename to app/client/doc/commands/client_Governance_ChangeParameter.md diff --git a/app/client/cli/doc/commands/client_Node.md b/app/client/doc/commands/client_Node.md similarity index 100% rename from app/client/cli/doc/commands/client_Node.md rename to app/client/doc/commands/client_Node.md diff --git a/app/client/cli/doc/commands/client_Node_EditStake.md b/app/client/doc/commands/client_Node_EditStake.md similarity index 100% rename from app/client/cli/doc/commands/client_Node_EditStake.md rename to app/client/doc/commands/client_Node_EditStake.md diff --git a/app/client/cli/doc/commands/client_Node_Stake.md b/app/client/doc/commands/client_Node_Stake.md similarity index 100% rename from app/client/cli/doc/commands/client_Node_Stake.md rename to app/client/doc/commands/client_Node_Stake.md diff --git a/app/client/cli/doc/commands/client_Node_Unpause.md b/app/client/doc/commands/client_Node_Unpause.md similarity index 100% rename from app/client/cli/doc/commands/client_Node_Unpause.md rename to app/client/doc/commands/client_Node_Unpause.md diff --git a/app/client/cli/doc/commands/client_Node_Unstake.md b/app/client/doc/commands/client_Node_Unstake.md similarity index 100% rename from app/client/cli/doc/commands/client_Node_Unstake.md rename to app/client/doc/commands/client_Node_Unstake.md diff --git a/app/client/cli/doc/commands/client_System.md b/app/client/doc/commands/client_System.md similarity index 100% rename from app/client/cli/doc/commands/client_System.md rename to app/client/doc/commands/client_System.md diff --git a/app/client/cli/doc/commands/client_System_Health.md b/app/client/doc/commands/client_System_Health.md similarity index 100% rename from app/client/cli/doc/commands/client_System_Health.md rename to app/client/doc/commands/client_System_Health.md diff --git a/app/client/cli/doc/commands/client_System_Version.md b/app/client/doc/commands/client_System_Version.md similarity index 100% rename from app/client/cli/doc/commands/client_System_Version.md rename to app/client/doc/commands/client_System_Version.md diff --git a/app/client/cli/doc/commands/client_Validator.md b/app/client/doc/commands/client_Validator.md similarity index 100% rename from app/client/cli/doc/commands/client_Validator.md rename to app/client/doc/commands/client_Validator.md diff --git a/app/client/cli/doc/commands/client_Validator_EditStake.md b/app/client/doc/commands/client_Validator_EditStake.md similarity index 100% rename from app/client/cli/doc/commands/client_Validator_EditStake.md rename to app/client/doc/commands/client_Validator_EditStake.md diff --git a/app/client/cli/doc/commands/client_Validator_Stake.md b/app/client/doc/commands/client_Validator_Stake.md similarity index 100% rename from app/client/cli/doc/commands/client_Validator_Stake.md rename to app/client/doc/commands/client_Validator_Stake.md diff --git a/app/client/cli/doc/commands/client_Validator_Unpause.md b/app/client/doc/commands/client_Validator_Unpause.md similarity index 100% rename from app/client/cli/doc/commands/client_Validator_Unpause.md rename to app/client/doc/commands/client_Validator_Unpause.md diff --git a/app/client/cli/doc/commands/client_Validator_Unstake.md b/app/client/doc/commands/client_Validator_Unstake.md similarity index 100% rename from app/client/cli/doc/commands/client_Validator_Unstake.md rename to app/client/doc/commands/client_Validator_Unstake.md diff --git a/shared/CHANGELOG.md b/shared/CHANGELOG.md index b4157de8d..b6c0252b0 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.19] - 2023-02-02 + +- Add `KeyPair` interface +- Add logic to create new keypairs, encrypt/armour them and decrypt/unarmour them + ## [0.0.0.18] - 2023-01-31 - Match naming conventions in `Param` protobuf file