mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-05-21 06:16:45 +00:00
111 lines
3.0 KiB
Go
111 lines
3.0 KiB
Go
package keys
|
|
|
|
import (
|
|
"bufio"
|
|
"crypto/rand"
|
|
"crypto/subtle"
|
|
"encoding/hex"
|
|
"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")
|
|
}
|
|
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
|
|
})
|
|
}
|
|
|
|
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:")
|
|
|
|
if subtle.ConstantTimeCompare(password, confirmPassword) != 1 {
|
|
return nil, nil, errors.New("Passwords are not identical")
|
|
}
|
|
|
|
encryptedPrivateKeys = make([]*EncryptedPrivateKey, 0, numKeys)
|
|
for i := uint32(0); i < numKeys; i++ {
|
|
privateKey, publicKey, err := keyPairFunction(i)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
publicKeys = append(publicKeys, publicKey)
|
|
|
|
encryptedPrivateKey, err := encryptPrivateKey(privateKey, password)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
encryptedPrivateKeys = append(encryptedPrivateKeys, encryptedPrivateKey)
|
|
}
|
|
|
|
return encryptedPrivateKeys, publicKeys, nil
|
|
}
|
|
|
|
func generateSalt() ([]byte, error) {
|
|
salt := make([]byte, 16)
|
|
_, err := rand.Read(salt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return salt, nil
|
|
}
|
|
|
|
func encryptPrivateKey(privateKey []byte, password []byte) (*EncryptedPrivateKey, error) {
|
|
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(privateKey)+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, privateKey, nil)
|
|
|
|
return &EncryptedPrivateKey{
|
|
cipher: cipher,
|
|
salt: salt,
|
|
}, nil
|
|
}
|