From 73b36f12f0a83f437698ee42b3aa7e89dfee4216 Mon Sep 17 00:00:00 2001 From: stasatdaglabs <39559713+stasatdaglabs@users.noreply.github.com> Date: Mon, 5 Apr 2021 18:10:33 +0300 Subject: [PATCH] Implement importing private keys into the wallet (#1655) * Implement importing private keys into the wallet. * Fix bad --import default. * Fix typo in --import annotation. * Make go lint happy. * Make go lint happier. Co-authored-by: Ori Newman --- cmd/kaspawallet/config.go | 3 ++- cmd/kaspawallet/create.go | 9 ++++++- cmd/kaspawallet/keys/create.go | 29 ++++++++++++++++++++++- cmd/kaspawallet/libkaspawallet/keypair.go | 22 +++++++++++++++-- 4 files changed, 58 insertions(+), 5 deletions(-) diff --git a/cmd/kaspawallet/config.go b/cmd/kaspawallet/config.go index 3440445ee..4f5879def 100644 --- a/cmd/kaspawallet/config.go +++ b/cmd/kaspawallet/config.go @@ -25,8 +25,9 @@ type configFlags struct { type createConfig struct { KeysFile string `long:"keys-file" short:"f" description:"Keys file location (default: ~/.kaspawallet/keys.json (*nix), %USERPROFILE%\\AppData\\Local\\Kaspawallet\\key.json (Windows))"` MinimumSignatures uint32 `long:"min-signatures" short:"m" description:"Minimum required signatures" default:"1"` - NumPrivateKeys uint32 `long:"num-private-keys" short:"k" description:"Number of private keys to generate" default:"1"` + NumPrivateKeys uint32 `long:"num-private-keys" short:"k" description:"Number of private keys" default:"1"` NumPublicKeys uint32 `long:"num-public-keys" short:"n" description:"Total number of keys" default:"1"` + Import bool `long:"import" short:"i" description:"Import private keys (as opposed to generating them)"` config.NetworkFlags } diff --git a/cmd/kaspawallet/create.go b/cmd/kaspawallet/create.go index 19ab0bb31..b0644e459 100644 --- a/cmd/kaspawallet/create.go +++ b/cmd/kaspawallet/create.go @@ -11,7 +11,14 @@ import ( ) func create(conf *createConfig) error { - encryptedPrivateKeys, publicKeys, err := keys.CreateKeyPairs(conf.NumPrivateKeys) + var encryptedPrivateKeys []*keys.EncryptedPrivateKey + var publicKeys [][]byte + var err error + if !conf.Import { + encryptedPrivateKeys, publicKeys, err = keys.CreateKeyPairs(conf.NumPrivateKeys) + } else { + encryptedPrivateKeys, publicKeys, err = keys.ImportKeyPairs(conf.NumPrivateKeys) + } if err != nil { return err } diff --git a/cmd/kaspawallet/keys/create.go b/cmd/kaspawallet/keys/create.go index efaaba06e..c6a48bad4 100644 --- a/cmd/kaspawallet/keys/create.go +++ b/cmd/kaspawallet/keys/create.go @@ -1,14 +1,41 @@ package keys import ( + "bufio" "crypto/rand" "crypto/subtle" + "fmt" "github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet" "github.com/pkg/errors" + "os" ) // CreateKeyPairs generates `numKeys` number of key pairs. func CreateKeyPairs(numKeys uint32) (encryptedPrivateKeys []*EncryptedPrivateKey, publicKeys [][]byte, err error) { + return createKeyPairsFromFunction(numKeys, func(_ uint32) ([]byte, []byte, error) { + return libkaspawallet.CreateKeyPair() + }) +} + +// ImportKeyPairs imports a `numKeys` of private keys and generates key pairs out of them. +func ImportKeyPairs(numKeys uint32) (encryptedPrivateKeys []*EncryptedPrivateKey, publicKeys [][]byte, err error) { + return createKeyPairsFromFunction(numKeys, func(keyIndex uint32) ([]byte, []byte, error) { + fmt.Printf("Enter private key #%d here:\n", keyIndex+1) + reader := bufio.NewReader(os.Stdin) + line, isPrefix, err := reader.ReadLine() + if err != nil { + return nil, nil, err + } + if isPrefix { + return nil, nil, errors.Errorf("Private key is too long") + } + return libkaspawallet.KeyPairFromPrivateKeyHex(string(line)) + }) +} + +func createKeyPairsFromFunction(numKeys uint32, keyPairFunction func(keyIndex uint32) ([]byte, []byte, error)) ( + encryptedPrivateKeys []*EncryptedPrivateKey, publicKeys [][]byte, err error) { + password := getPassword("Enter password for the key file:") confirmPassword := getPassword("Confirm password:") @@ -18,7 +45,7 @@ func CreateKeyPairs(numKeys uint32) (encryptedPrivateKeys []*EncryptedPrivateKey encryptedPrivateKeys = make([]*EncryptedPrivateKey, 0, numKeys) for i := uint32(0); i < numKeys; i++ { - privateKey, publicKey, err := libkaspawallet.CreateKeyPair() + privateKey, publicKey, err := keyPairFunction(i) if err != nil { return nil, nil, err } diff --git a/cmd/kaspawallet/libkaspawallet/keypair.go b/cmd/kaspawallet/libkaspawallet/keypair.go index 3bf5aefe6..1043fdb40 100644 --- a/cmd/kaspawallet/libkaspawallet/keypair.go +++ b/cmd/kaspawallet/libkaspawallet/keypair.go @@ -1,6 +1,7 @@ package libkaspawallet import ( + "encoding/hex" "github.com/kaspanet/go-secp256k1" "github.com/kaspanet/kaspad/domain/dagconfig" "github.com/kaspanet/kaspad/util" @@ -13,7 +14,24 @@ func CreateKeyPair() ([]byte, []byte, error) { if err != nil { return nil, nil, errors.Wrap(err, "Failed to generate private key") } - publicKey, err := privateKey.SchnorrPublicKey() + return keyPairBytes(privateKey) +} + +// KeyPairFromPrivateKeyHex decodes a private-public key pair out of `privateKeyHex` +func KeyPairFromPrivateKeyHex(privateKeyHex string) ([]byte, []byte, error) { + privateKeyBytes, err := hex.DecodeString(privateKeyHex) + if err != nil { + return nil, nil, errors.Wrap(err, "Failed to deserialized private key") + } + privateKey, err := secp256k1.DeserializeSchnorrPrivateKeyFromSlice(privateKeyBytes) + if err != nil { + return nil, nil, errors.Wrap(err, "Failed to deserialized private key") + } + return keyPairBytes(privateKey) +} + +func keyPairBytes(keyPair *secp256k1.SchnorrKeyPair) ([]byte, []byte, error) { + publicKey, err := keyPair.SchnorrPublicKey() if err != nil { return nil, nil, errors.Wrap(err, "Failed to generate public key") } @@ -22,7 +40,7 @@ func CreateKeyPair() ([]byte, []byte, error) { return nil, nil, errors.Wrap(err, "Failed to serialize public key") } - return privateKey.SerializePrivateKey()[:], publicKeySerialized[:], nil + return keyPair.SerializePrivateKey()[:], publicKeySerialized[:], nil } func addressFromPublicKey(params *dagconfig.Params, publicKeySerialized []byte) (util.Address, error) {