mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-05-21 06:16:45 +00:00

* Naive bip39 with address reuse * Avoid address reuse in libkaspawallet * Add wallet daemon * Use daemon everywhere * Add forceOverride * Make CreateUnsignedTransaction endpoint receive amount in sompis * Collect close UTXOs * Filter out non-spendable UTXOs from selectUTXOs * Use different paths for multisig and non multisig * Fix tests to use non zero path * Fix multisig cosigner index detection * Add comments * Fix dump_unencrypted_data.go according to bip39 and bip32 * Fix wrong derivation path for multisig on wallet creation * Remove IsSynced endpoint and add validation if wallet is synced for the relevant endpoints * Rename server address to daemon address * Fix capacity for extendedPublicKeys * Use ReadBytes instead of ReadLine * Add validation when importing * Increment before using index value, and use it as is * Save keys file exactly where needed * Use %+v printErrorAndExit * Remove redundant consts * Rnemae collectCloseUTXOs and collectFarUTXOs * Move typedefs around * Add comment to addressesToQuery * Update collectUTXOsFromRecentAddresses comment about locks * Split collectUTXOs to small functions * Add sanity check * Add addEntryToUTXOSet function * Change validateIsSynced to isSynced * Simplify createKeyPairsFromFunction logic * Rename .Sync() to .Save() * Fix typo * Create bip39BitSize const * Add consts to purposes * Add multisig check for 'send' * Rename updatedPSTxBytes to partiallySignedTransaction * Change collectUTXOsFromFarAddresses's comment * Use setters for last used indexes * Don't use the pstx acronym * Fix SetPath * Remove spaces when reading lines * Fix walletserver to daemonaddress * Fix isUTXOSpendable to use DAA score Co-authored-by: Svarog <feanorr@gmail.com>
116 lines
3.3 KiB
Go
116 lines
3.3 KiB
Go
package keys
|
|
|
|
import (
|
|
"bufio"
|
|
"crypto/rand"
|
|
"crypto/subtle"
|
|
"fmt"
|
|
"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"
|
|
"os"
|
|
)
|
|
|
|
// CreateMnemonics generates `numKeys` number of mnemonics.
|
|
func CreateMnemonics(params *dagconfig.Params, numKeys uint32, 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, isMultisig)
|
|
}
|
|
|
|
// ImportMnemonics imports a `numKeys` of mnemonics.
|
|
func ImportMnemonics(params *dagconfig.Params, numKeys uint32, 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, isMultisig)
|
|
}
|
|
|
|
func encryptedMnemonicExtendedPublicKeyPairs(params *dagconfig.Params, mnemonics []string, isMultisig bool) (
|
|
encryptedPrivateKeys []*EncryptedMnemonic, extendedPublicKeys []string, err error) {
|
|
|
|
password := getPassword("Enter password for the key file:")
|
|
confirmPassword := 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(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
|
|
}
|