mirror of
https://github.com/btcsuite/btcd.git
synced 2024-11-19 01:40:07 +01:00
ba5407615d
The doc formatting changes introduced in the recent go version is increasing the diff for all of the new commits. Formatting it all in this commit will help the readability of future PRs by reducing the diff.
158 lines
5.7 KiB
Go
158 lines
5.7 KiB
Go
// Copyright (c) 2013-2016 The btcsuite developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package btcutil
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
|
|
"github.com/btcsuite/btcd/btcec/v2"
|
|
"github.com/btcsuite/btcd/btcutil/base58"
|
|
"github.com/btcsuite/btcd/chaincfg"
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
)
|
|
|
|
// ErrMalformedPrivateKey describes an error where a WIF-encoded private
|
|
// key cannot be decoded due to being improperly formatted. This may occur
|
|
// if the byte length is incorrect or an unexpected magic number was
|
|
// encountered.
|
|
var ErrMalformedPrivateKey = errors.New("malformed private key")
|
|
|
|
// compressMagic is the magic byte used to identify a WIF encoding for
|
|
// an address created from a compressed serialized public key.
|
|
const compressMagic byte = 0x01
|
|
|
|
// WIF contains the individual components described by the Wallet Import Format
|
|
// (WIF). A WIF string is typically used to represent a private key and its
|
|
// associated address in a way that may be easily copied and imported into or
|
|
// exported from wallet software. WIF strings may be decoded into this
|
|
// structure by calling DecodeWIF or created with a user-provided private key
|
|
// by calling NewWIF.
|
|
type WIF struct {
|
|
// PrivKey is the private key being imported or exported.
|
|
PrivKey *btcec.PrivateKey
|
|
|
|
// CompressPubKey specifies whether the address controlled by the
|
|
// imported or exported private key was created by hashing a
|
|
// compressed (33-byte) serialized public key, rather than an
|
|
// uncompressed (65-byte) one.
|
|
CompressPubKey bool
|
|
|
|
// netID is the bitcoin network identifier byte used when
|
|
// WIF encoding the private key.
|
|
netID byte
|
|
}
|
|
|
|
// NewWIF creates a new WIF structure to export an address and its private key
|
|
// as a string encoded in the Wallet Import Format. The compress argument
|
|
// specifies whether the address intended to be imported or exported was created
|
|
// by serializing the public key compressed rather than uncompressed.
|
|
func NewWIF(privKey *btcec.PrivateKey, net *chaincfg.Params, compress bool) (*WIF, error) {
|
|
if net == nil {
|
|
return nil, errors.New("no network")
|
|
}
|
|
return &WIF{privKey, compress, net.PrivateKeyID}, nil
|
|
}
|
|
|
|
// IsForNet returns whether or not the decoded WIF structure is associated
|
|
// with the passed bitcoin network.
|
|
func (w *WIF) IsForNet(net *chaincfg.Params) bool {
|
|
return w.netID == net.PrivateKeyID
|
|
}
|
|
|
|
// DecodeWIF creates a new WIF structure by decoding the string encoding of
|
|
// the import format.
|
|
//
|
|
// The WIF string must be a base58-encoded string of the following byte
|
|
// sequence:
|
|
//
|
|
// - 1 byte to identify the network, must be 0x80 for mainnet or 0xef for
|
|
// either testnet3 or the regression test network
|
|
// - 32 bytes of a binary-encoded, big-endian, zero-padded private key
|
|
// - Optional 1 byte (equal to 0x01) if the address being imported or exported
|
|
// was created by taking the RIPEMD160 after SHA256 hash of a serialized
|
|
// compressed (33-byte) public key
|
|
// - 4 bytes of checksum, must equal the first four bytes of the double SHA256
|
|
// of every byte before the checksum in this sequence
|
|
//
|
|
// If the base58-decoded byte sequence does not match this, DecodeWIF will
|
|
// return a non-nil error. ErrMalformedPrivateKey is returned when the WIF
|
|
// is of an impossible length or the expected compressed pubkey magic number
|
|
// does not equal the expected value of 0x01. ErrChecksumMismatch is returned
|
|
// if the expected WIF checksum does not match the calculated checksum.
|
|
func DecodeWIF(wif string) (*WIF, error) {
|
|
decoded := base58.Decode(wif)
|
|
decodedLen := len(decoded)
|
|
var compress bool
|
|
|
|
// Length of base58 decoded WIF must be 32 bytes + an optional 1 byte
|
|
// (0x01) if compressed, plus 1 byte for netID + 4 bytes of checksum.
|
|
switch decodedLen {
|
|
case 1 + btcec.PrivKeyBytesLen + 1 + 4:
|
|
if decoded[33] != compressMagic {
|
|
return nil, ErrMalformedPrivateKey
|
|
}
|
|
compress = true
|
|
case 1 + btcec.PrivKeyBytesLen + 4:
|
|
compress = false
|
|
default:
|
|
return nil, ErrMalformedPrivateKey
|
|
}
|
|
|
|
// Checksum is first four bytes of double SHA256 of the identifier byte
|
|
// and privKey. Verify this matches the final 4 bytes of the decoded
|
|
// private key.
|
|
var tosum []byte
|
|
if compress {
|
|
tosum = decoded[:1+btcec.PrivKeyBytesLen+1]
|
|
} else {
|
|
tosum = decoded[:1+btcec.PrivKeyBytesLen]
|
|
}
|
|
cksum := chainhash.DoubleHashB(tosum)[:4]
|
|
if !bytes.Equal(cksum, decoded[decodedLen-4:]) {
|
|
return nil, ErrChecksumMismatch
|
|
}
|
|
|
|
netID := decoded[0]
|
|
privKeyBytes := decoded[1 : 1+btcec.PrivKeyBytesLen]
|
|
privKey, _ := btcec.PrivKeyFromBytes(privKeyBytes)
|
|
return &WIF{privKey, compress, netID}, nil
|
|
}
|
|
|
|
// String creates the Wallet Import Format string encoding of a WIF structure.
|
|
// See DecodeWIF for a detailed breakdown of the format and requirements of
|
|
// a valid WIF string.
|
|
func (w *WIF) String() string {
|
|
// Precalculate size. Maximum number of bytes before base58 encoding
|
|
// is one byte for the network, 32 bytes of private key, possibly one
|
|
// extra byte if the pubkey is to be compressed, and finally four
|
|
// bytes of checksum.
|
|
encodeLen := 1 + btcec.PrivKeyBytesLen + 4
|
|
if w.CompressPubKey {
|
|
encodeLen++
|
|
}
|
|
|
|
a := make([]byte, 0, encodeLen)
|
|
a = append(a, w.netID)
|
|
a = append(a, w.PrivKey.Serialize()...)
|
|
if w.CompressPubKey {
|
|
a = append(a, compressMagic)
|
|
}
|
|
cksum := chainhash.DoubleHashB(a)[:4]
|
|
a = append(a, cksum...)
|
|
return base58.Encode(a)
|
|
}
|
|
|
|
// SerializePubKey serializes the associated public key of the imported or
|
|
// exported private key in either a compressed or uncompressed format. The
|
|
// serialization format chosen depends on the value of w.CompressPubKey.
|
|
func (w *WIF) SerializePubKey() []byte {
|
|
pk := w.PrivKey.PubKey()
|
|
if w.CompressPubKey {
|
|
return pk.SerializeCompressed()
|
|
}
|
|
return pk.SerializeUncompressed()
|
|
}
|