github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/btcutil/wif.go (about) 1 // Copyright (c) 2013-2016 The btcsuite developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package btcutil 6 7 import ( 8 "bytes" 9 "errors" 10 "github.com/mit-dci/lit/btcutil/base58" 11 "github.com/mit-dci/lit/btcutil/chaincfg/chainhash" 12 "github.com/mit-dci/lit/coinparam" 13 "github.com/mit-dci/lit/crypto/koblitz" 14 ) 15 16 // ErrMalformedPrivateKey describes an error where a WIF-encoded private 17 // key cannot be decoded due to being improperly formatted. This may occur 18 // if the byte length is incorrect or an unexpected magic number was 19 // encountered. 20 var ErrMalformedPrivateKey = errors.New("malformed private key") 21 22 // compressMagic is the magic byte used to identify a WIF encoding for 23 // an address created from a compressed serialized public key. 24 const compressMagic byte = 0x01 25 26 // WIF contains the individual components described by the Wallet Import Format 27 // (WIF). A WIF string is typically used to represent a private key and its 28 // associated address in a way that may be easily copied and imported into or 29 // exported from wallet software. WIF strings may be decoded into this 30 // structure by calling DecodeWIF or created with a user-provided private key 31 // by calling NewWIF. 32 type WIF struct { 33 // PrivKey is the private key being imported or exported. 34 PrivKey *koblitz.PrivateKey 35 36 // CompressPubKey specifies whether the address controlled by the 37 // imported or exported private key was created by hashing a 38 // compressed (33-byte) serialized public key, rather than an 39 // uncompressed (65-byte) one. 40 CompressPubKey bool 41 42 // netID is the bitcoin network identifier byte used when 43 // WIF encoding the private key. 44 NetID byte 45 } 46 47 // NewWIF creates a new WIF structure to export an address and its private key 48 // as a string encoded in the Wallet Import Format. The compress argument 49 // specifies whether the address intended to be imported or exported was created 50 // by serializing the public key compressed rather than uncompressed. 51 func NewWIF(privKey *koblitz.PrivateKey, net *coinparam.Params, compress bool) (*WIF, error) { 52 if net == nil { 53 return nil, errors.New("no network") 54 } 55 return &WIF{privKey, compress, net.PrivateKeyID}, nil 56 } 57 58 // IsForNet returns whether or not the decoded WIF structure is associated 59 // with the passed bitcoin network. 60 func (w *WIF) IsForNet(net *coinparam.Params) bool { 61 return w.NetID == net.PrivateKeyID 62 } 63 64 // DecodeWIF creates a new WIF structure by decoding the string encoding of 65 // the import format. 66 // 67 // The WIF string must be a base58-encoded string of the following byte 68 // sequence: 69 // 70 // * 1 byte to identify the network, must be 0x80 for mainnet or 0xef for 71 // either testnet3 or the regression test network 72 // * 32 bytes of a binary-encoded, big-endian, zero-padded private key 73 // * Optional 1 byte (equal to 0x01) if the address being imported or exported 74 // was created by taking the RIPEMD160 after SHA256 hash of a serialized 75 // compressed (33-byte) public key 76 // * 4 bytes of checksum, must equal the first four bytes of the double SHA256 77 // of every byte before the checksum in this sequence 78 // 79 // If the base58-decoded byte sequence does not match this, DecodeWIF will 80 // return a non-nil error. ErrMalformedPrivateKey is returned when the WIF 81 // is of an impossible length or the expected compressed pubkey magic number 82 // does not equal the expected value of 0x01. ErrChecksumMismatch is returned 83 // if the expected WIF checksum does not match the calculated checksum. 84 func DecodeWIF(wif string) (*WIF, error) { 85 decoded := base58.Decode(wif) 86 decodedLen := len(decoded) 87 var compress bool 88 89 // Length of base58 decoded WIF must be 32 bytes + an optional 1 byte 90 // (0x01) if compressed, plus 1 byte for netID + 4 bytes of checksum. 91 switch decodedLen { 92 case 1 + koblitz.PrivKeyBytesLen + 1 + 4: 93 if decoded[33] != compressMagic { 94 return nil, ErrMalformedPrivateKey 95 } 96 compress = true 97 case 1 + koblitz.PrivKeyBytesLen + 4: 98 compress = false 99 default: 100 return nil, ErrMalformedPrivateKey 101 } 102 103 // Checksum is first four bytes of double SHA256 of the identifier byte 104 // and privKey. Verify this matches the final 4 bytes of the decoded 105 // private key. 106 var tosum []byte 107 if compress { 108 tosum = decoded[:1+koblitz.PrivKeyBytesLen+1] 109 } else { 110 tosum = decoded[:1+koblitz.PrivKeyBytesLen] 111 } 112 cksum := chainhash.DoubleHashB(tosum)[:4] 113 if !bytes.Equal(cksum, decoded[decodedLen-4:]) { 114 return nil, ErrChecksumMismatch 115 } 116 117 netID := decoded[0] 118 privKeyBytes := decoded[1 : 1+koblitz.PrivKeyBytesLen] 119 privKey, _ := koblitz.PrivKeyFromBytes(koblitz.S256(), privKeyBytes) 120 return &WIF{privKey, compress, netID}, nil 121 } 122 123 // String creates the Wallet Import Format string encoding of a WIF structure. 124 // See DecodeWIF for a detailed breakdown of the format and requirements of 125 // a valid WIF string. 126 func (w *WIF) String() string { 127 // Precalculate size. Maximum number of bytes before base58 encoding 128 // is one byte for the network, 32 bytes of private key, possibly one 129 // extra byte if the pubkey is to be compressed, and finally four 130 // bytes of checksum. 131 encodeLen := 1 + koblitz.PrivKeyBytesLen + 4 132 if w.CompressPubKey { 133 encodeLen++ 134 } 135 136 a := make([]byte, 0, encodeLen) 137 a = append(a, w.NetID) 138 // Pad and append bytes manually, instead of using Serialize, to 139 // avoid another call to make. 140 a = paddedAppend(koblitz.PrivKeyBytesLen, a, w.PrivKey.D.Bytes()) 141 if w.CompressPubKey { 142 a = append(a, compressMagic) 143 } 144 cksum := chainhash.DoubleHashB(a)[:4] 145 a = append(a, cksum...) 146 return base58.Encode(a) 147 } 148 149 // SerializePubKey serializes the associated public key of the imported or 150 // exported private key in either a compressed or uncompressed format. The 151 // serialization format chosen depends on the value of w.CompressPubKey. 152 func (w *WIF) SerializePubKey() []byte { 153 pk := (*koblitz.PublicKey)(&w.PrivKey.PublicKey) 154 if w.CompressPubKey { 155 return pk.SerializeCompressed() 156 } 157 return pk.SerializeUncompressed() 158 } 159 160 // paddedAppend appends the src byte slice to dst, returning the new slice. 161 // If the length of the source is smaller than the passed size, leading zero 162 // bytes are appended to the dst slice before appending src. 163 func paddedAppend(size uint, dst, src []byte) []byte { 164 for i := 0; i < int(size)-len(src); i++ { 165 dst = append(dst, 0) 166 } 167 return append(dst, src...) 168 }