2020-11-09 10:05:29 -03:00

110 lines
2.7 KiB
Go

package libwallet
import (
"bytes"
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"github.com/btcsuite/btcd/btcec"
"github.com/btcsuite/btcutil/base58"
"github.com/pkg/errors"
)
type ChallengePrivateKey struct {
key *btcec.PrivateKey
}
type DecryptedPrivateKey struct {
Key *HDPrivateKey
Birthday int
}
func NewChallengePrivateKey(input, salt []byte) *ChallengePrivateKey {
key := Scrypt256(input, salt)
// 2nd return value is the pub key which we don't need right now
priv, _ := btcec.PrivKeyFromBytes(btcec.S256(), key)
return &ChallengePrivateKey{key: priv}
}
// SignSha computes the SHA-256 digest of the given payload and signs it.
func (k *ChallengePrivateKey) SignSha(payload []byte) ([]byte, error) {
hash := sha256.Sum256(payload)
sig, err := k.key.Sign(hash[:])
if err != nil {
return nil, errors.Wrapf(err, "failed to sign payload")
}
return sig.Serialize(), nil
}
func (k *ChallengePrivateKey) PubKeyHex() string {
rawKey := k.key.PubKey().SerializeCompressed()
return hex.EncodeToString(rawKey)
}
func (k *ChallengePrivateKey) PubKey() *ChallengePublicKey {
return &ChallengePublicKey{pubKey: k.key.PubKey()}
}
func (k *ChallengePrivateKey) DecryptKey(encryptedKey string, network *Network) (*DecryptedPrivateKey, error) {
reader := bytes.NewReader(base58.Decode(encryptedKey))
version, err := reader.ReadByte()
if err != nil {
return nil, errors.Wrapf(err, "decrypting key")
}
if version != 2 {
return nil, errors.Errorf("decrypting key: found key version %v, expected 2", version)
}
birthdayBytes := make([]byte, 2)
rawPubEph := make([]byte, serializedPublicKeyLength)
ciphertext := make([]byte, 64)
recoveryCodeSalt := make([]byte, 8)
n, err := reader.Read(birthdayBytes)
if err != nil || n != 2 {
return nil, errors.Errorf("decrypting key: failed to read birthday")
}
birthday := binary.BigEndian.Uint16(birthdayBytes)
n, err = reader.Read(rawPubEph)
if err != nil || n != serializedPublicKeyLength {
return nil, errors.Errorf("decrypting key: failed to read pubeph")
}
n, err = reader.Read(ciphertext)
if err != nil || n != 64 {
return nil, errors.Errorf("decrypting key: failed to read ciphertext")
}
n, err = reader.Read(recoveryCodeSalt)
if err != nil || n != 8 {
return nil, errors.Errorf("decrypting key: failed to read recoveryCodeSalt")
}
plaintext, err := decryptWithPrivKey(k.key, rawPubEph, ciphertext)
if err != nil {
return nil, err
}
rawPrivKey := plaintext[0:32]
rawChainCode := plaintext[32:]
privKey, err := NewHDPrivateKeyFromBytes(rawPrivKey, rawChainCode, network)
if err != nil {
return nil, errors.Wrapf(err, "decrypting key: failed to parse key")
}
return &DecryptedPrivateKey{
privKey,
int(birthday),
}, nil
}