mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-05-20 22:06:42 +00:00

* Add send and sign commands to protobuf * Added Send and Sign stubs in kaspawalletd server * Implement Sign * Implemented Send * Allow Broadcast command to supply multiple transactions * No longer prompt for password in DecryptMnemonics * Rename TransactionIDs -> TxIDs to keep consistency with Broadcast * Add some comments and formatting Co-authored-by: Ori Newman <orinewman1@gmail.com>
120 lines
3.5 KiB
Go
120 lines
3.5 KiB
Go
package keys
|
|
|
|
import (
|
|
"bufio"
|
|
"crypto/rand"
|
|
"crypto/subtle"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet"
|
|
"github.com/kaspanet/kaspad/cmd/kaspawallet/utils"
|
|
"github.com/kaspanet/kaspad/domain/dagconfig"
|
|
"github.com/pkg/errors"
|
|
"github.com/tyler-smith/go-bip39"
|
|
)
|
|
|
|
// CreateMnemonics generates `numKeys` number of mnemonics.
|
|
func CreateMnemonics(params *dagconfig.Params, numKeys uint32, cmdLinePassword string, isMultisig bool) (encryptedPrivateKeys []*EncryptedMnemonic, extendedPublicKeys []string, err error) {
|
|
mnemonics := make([]string, numKeys)
|
|
for i := uint32(0); i < numKeys; i++ {
|
|
var err error
|
|
mnemonics[i], err = libkaspawallet.CreateMnemonic()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
|
|
return encryptedMnemonicExtendedPublicKeyPairs(params, mnemonics, cmdLinePassword, isMultisig)
|
|
}
|
|
|
|
// ImportMnemonics imports a `numKeys` of mnemonics.
|
|
func ImportMnemonics(params *dagconfig.Params, numKeys uint32, cmdLinePassword string, isMultisig bool) (encryptedPrivateKeys []*EncryptedMnemonic, extendedPublicKeys []string, err error) {
|
|
mnemonics := make([]string, numKeys)
|
|
for i := uint32(0); i < numKeys; i++ {
|
|
fmt.Printf("Enter mnemonic #%d here:\n", i+1)
|
|
reader := bufio.NewReader(os.Stdin)
|
|
mnemonic, err := utils.ReadLine(reader)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
if !bip39.IsMnemonicValid(string(mnemonic)) {
|
|
return nil, nil, errors.Errorf("mnemonic is invalid")
|
|
}
|
|
|
|
mnemonics[i] = string(mnemonic)
|
|
}
|
|
return encryptedMnemonicExtendedPublicKeyPairs(params, mnemonics, cmdLinePassword, isMultisig)
|
|
}
|
|
|
|
func encryptedMnemonicExtendedPublicKeyPairs(params *dagconfig.Params, mnemonics []string, cmdLinePassword string, isMultisig bool) (
|
|
encryptedPrivateKeys []*EncryptedMnemonic, extendedPublicKeys []string, err error) {
|
|
password := []byte(cmdLinePassword)
|
|
if len(password) == 0 {
|
|
|
|
password = []byte(GetPassword("Enter password for the key file:"))
|
|
confirmPassword := []byte(GetPassword("Confirm password:"))
|
|
|
|
if subtle.ConstantTimeCompare(password, confirmPassword) != 1 {
|
|
return nil, nil, errors.New("Passwords are not identical")
|
|
}
|
|
}
|
|
|
|
encryptedPrivateKeys = make([]*EncryptedMnemonic, 0, len(mnemonics))
|
|
for _, mnemonic := range mnemonics {
|
|
extendedPublicKey, err := libkaspawallet.MasterPublicKeyFromMnemonic(params, mnemonic, isMultisig)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
extendedPublicKeys = append(extendedPublicKeys, extendedPublicKey)
|
|
|
|
encryptedPrivateKey, err := encryptMnemonic(mnemonic, password)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
encryptedPrivateKeys = append(encryptedPrivateKeys, encryptedPrivateKey)
|
|
}
|
|
|
|
return encryptedPrivateKeys, extendedPublicKeys, nil
|
|
}
|
|
|
|
func generateSalt() ([]byte, error) {
|
|
salt := make([]byte, 16)
|
|
_, err := rand.Read(salt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return salt, nil
|
|
}
|
|
|
|
func encryptMnemonic(mnemonic string, password []byte) (*EncryptedMnemonic, error) {
|
|
mnemonicBytes := []byte(mnemonic)
|
|
|
|
salt, err := generateSalt()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
aead, err := getAEAD(defaultNumThreads, password, salt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Select a random nonce, and leave capacity for the ciphertext.
|
|
nonce := make([]byte, aead.NonceSize(), aead.NonceSize()+len(mnemonicBytes)+aead.Overhead())
|
|
if _, err := rand.Read(nonce); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Encrypt the message and append the ciphertext to the nonce.
|
|
cipher := aead.Seal(nonce, nonce, []byte(mnemonicBytes), nil)
|
|
|
|
return &EncryptedMnemonic{
|
|
cipher: cipher,
|
|
salt: salt,
|
|
}, nil
|
|
}
|