Add dump unencrypted data sub command to the wallet (#1661)

This commit is contained in:
Ori Newman 2021-04-06 12:29:13 +03:00 committed by GitHub
parent 73b36f12f0
commit 6dd3d4a9e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 127 additions and 12 deletions

View File

@ -12,10 +12,11 @@ const (
createSubCmd = "create" createSubCmd = "create"
balanceSubCmd = "balance" balanceSubCmd = "balance"
sendSubCmd = "send" sendSubCmd = "send"
createUnsignedTransactionSubCmd = "createUnsignedTransaction" createUnsignedTransactionSubCmd = "create-unsigned-transaction"
signSubCmd = "sign" signSubCmd = "sign"
broadcastSubCmd = "broadcast" broadcastSubCmd = "broadcast"
showAddressSubCmd = "show-address" showAddressSubCmd = "show-address"
dumpUnencryptedDataSubCmd = "dump-unencrypted-data"
) )
type configFlags struct { type configFlags struct {
@ -70,6 +71,11 @@ type showAddressConfig struct {
config.NetworkFlags config.NetworkFlags
} }
type dumpUnencryptedDataConfig struct {
KeysFile string `long:"keys-file" short:"f" description:"Keys file location (default: ~/.kaspawallet/keys.json (*nix), %USERPROFILE%\\AppData\\Local\\Kaspawallet\\key.json (Windows))"`
config.NetworkFlags
}
func parseCommandLine() (subCommand string, config interface{}) { func parseCommandLine() (subCommand string, config interface{}) {
cfg := &configFlags{} cfg := &configFlags{}
parser := flags.NewParser(cfg, flags.PrintErrors|flags.HelpFlag) parser := flags.NewParser(cfg, flags.PrintErrors|flags.HelpFlag)
@ -102,6 +108,11 @@ func parseCommandLine() (subCommand string, config interface{}) {
parser.AddCommand(showAddressSubCmd, "Shows the public address of the current wallet", parser.AddCommand(showAddressSubCmd, "Shows the public address of the current wallet",
"Shows the public address of the current wallet", showAddressConf) "Shows the public address of the current wallet", showAddressConf)
dumpUnencryptedDataConf := &dumpUnencryptedDataConfig{}
parser.AddCommand(dumpUnencryptedDataSubCmd, "Prints the unencrypted wallet data",
"Prints the unencrypted wallet data including its private keys. Anyone that sees it can access "+
"the funds. Use only on safe environment.", dumpUnencryptedDataConf)
_, err := parser.Parse() _, err := parser.Parse()
if err != nil { if err != nil {
@ -164,6 +175,13 @@ func parseCommandLine() (subCommand string, config interface{}) {
printErrorAndExit(err) printErrorAndExit(err)
} }
config = showAddressConf config = showAddressConf
case dumpUnencryptedDataSubCmd:
combineNetworkFlags(&dumpUnencryptedDataConf.NetworkFlags, &cfg.NetworkFlags)
err := dumpUnencryptedDataConf.ResolveNetwork(parser)
if err != nil {
printErrorAndExit(err)
}
config = dumpUnencryptedDataConf
} }
return parser.Command.Active.Name, config return parser.Command.Active.Name, config

View File

@ -0,0 +1,69 @@
package main
import (
"bufio"
"fmt"
"github.com/kaspanet/kaspad/cmd/kaspawallet/keys"
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
"github.com/pkg/errors"
"os"
)
func dumpUnencryptedData(conf *dumpUnencryptedDataConfig) error {
err := confirmDump()
if err != nil {
return err
}
keysFile, err := keys.ReadKeysFile(conf.KeysFile)
if err != nil {
return err
}
privateKeys, err := keysFile.DecryptPrivateKeys()
if err != nil {
return err
}
privateKeysPublicKeys := make(map[string]struct{})
for i, privateKey := range privateKeys {
fmt.Printf("Private key #%d:\n%x\n\n", i+1, privateKey)
publicKey, err := libkaspawallet.PublicKeyFromPrivateKey(privateKey)
if err != nil {
return err
}
privateKeysPublicKeys[string(publicKey)] = struct{}{}
}
i := 1
for _, publicKey := range keysFile.PublicKeys {
if _, exists := privateKeysPublicKeys[string(publicKey)]; exists {
continue
}
fmt.Printf("Public key #%d:\n%x\n\n", i, publicKey)
i++
}
fmt.Printf("Minimum number of signatures: %d\n", keysFile.MinimumSignatures)
return nil
}
func confirmDump() error {
reader := bufio.NewReader(os.Stdin)
fmt.Printf("This operation will print your unencrypted keys on the screen. Anyone that sees this information " +
"will be able to steal your funds. Are you sure you want to proceed (y/N)? ")
line, isPrefix, err := reader.ReadLine()
if err != nil {
return err
}
fmt.Println()
if isPrefix || string(line) != "y" {
return errors.Errorf("Dump aborted by user")
}
return nil
}

View File

@ -4,6 +4,7 @@ import (
"bufio" "bufio"
"crypto/rand" "crypto/rand"
"crypto/subtle" "crypto/subtle"
"encoding/hex"
"fmt" "fmt"
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet" "github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -29,7 +30,17 @@ func ImportKeyPairs(numKeys uint32) (encryptedPrivateKeys []*EncryptedPrivateKey
if isPrefix { if isPrefix {
return nil, nil, errors.Errorf("Private key is too long") return nil, nil, errors.Errorf("Private key is too long")
} }
return libkaspawallet.KeyPairFromPrivateKeyHex(string(line)) privateKey, err := hex.DecodeString(string(line))
if err != nil {
return nil, nil, err
}
publicKey, err := libkaspawallet.PublicKeyFromPrivateKey(privateKey)
if err != nil {
return nil, nil, err
}
return privateKey, publicKey, nil
}) })
} }

View File

@ -1,7 +1,6 @@
package libkaspawallet package libkaspawallet
import ( import (
"encoding/hex"
"github.com/kaspanet/go-secp256k1" "github.com/kaspanet/go-secp256k1"
"github.com/kaspanet/kaspad/domain/dagconfig" "github.com/kaspanet/kaspad/domain/dagconfig"
"github.com/kaspanet/kaspad/util" "github.com/kaspanet/kaspad/util"
@ -10,24 +9,40 @@ import (
// CreateKeyPair generates a private-public key pair // CreateKeyPair generates a private-public key pair
func CreateKeyPair() ([]byte, []byte, error) { func CreateKeyPair() ([]byte, []byte, error) {
privateKey, err := secp256k1.GenerateSchnorrKeyPair() keyPair, err := secp256k1.GenerateSchnorrKeyPair()
if err != nil { if err != nil {
return nil, nil, errors.Wrap(err, "Failed to generate private key") return nil, nil, errors.Wrap(err, "Failed to generate private key")
} }
return keyPairBytes(privateKey) publicKey, err := keyPair.SchnorrPublicKey()
if err != nil {
return nil, nil, errors.Wrap(err, "Failed to generate public key")
}
publicKeySerialized, err := publicKey.Serialize()
if err != nil {
return nil, nil, errors.Wrap(err, "Failed to serialize public key")
} }
// KeyPairFromPrivateKeyHex decodes a private-public key pair out of `privateKeyHex` return keyPair.SerializePrivateKey()[:], publicKeySerialized[:], nil
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)
// PublicKeyFromPrivateKey returns the public key associated with a private key
func PublicKeyFromPrivateKey(privateKeyBytes []byte) ([]byte, error) {
keyPair, err := secp256k1.DeserializeSchnorrPrivateKeyFromSlice(privateKeyBytes)
if err != nil { if err != nil {
return nil, nil, errors.Wrap(err, "Failed to deserialized private key") return nil, errors.Wrap(err, "Failed to deserialized private key")
} }
return keyPairBytes(privateKey)
publicKey, err := keyPair.SchnorrPublicKey()
if err != nil {
return nil, errors.Wrap(err, "Failed to generate public key")
}
publicKeySerialized, err := publicKey.Serialize()
if err != nil {
return nil, errors.Wrap(err, "Failed to serialize public key")
}
return publicKeySerialized[:], nil
} }
func keyPairBytes(keyPair *secp256k1.SchnorrKeyPair) ([]byte, []byte, error) { func keyPairBytes(keyPair *secp256k1.SchnorrKeyPair) ([]byte, []byte, error) {

View File

@ -21,6 +21,8 @@ func main() {
err = broadcast(config.(*broadcastConfig)) err = broadcast(config.(*broadcastConfig))
case showAddressSubCmd: case showAddressSubCmd:
err = showAddress(config.(*showAddressConfig)) err = showAddress(config.(*showAddressConfig))
case dumpUnencryptedDataSubCmd:
err = dumpUnencryptedData(config.(*dumpUnencryptedDataConfig))
default: default:
err = errors.Errorf("Unknown sub-command '%s'\n", subCmd) err = errors.Errorf("Unknown sub-command '%s'\n", subCmd)
} }