Skip to content

Commit 710724f

Browse files
Weston SiegenthalerWeston Siegenthaler
Weston Siegenthaler
authored and
Weston Siegenthaler
committed
Initial commit of improvements resulting from bitaddress.org
1 parent 6fb2c77 commit 710724f

File tree

3 files changed

+195
-29
lines changed

3 files changed

+195
-29
lines changed

src/address.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ Bitcoin.Address = function (bytes) {
44
}
55
this.hash = bytes;
66

7-
this.version = 0x00;
7+
this.version = Bitcoin.Address.networkVersion;
88
};
99

10+
Bitcoin.Address.networkVersion = 0x00; // mainnet
11+
1012
/**
1113
* Serialize this object as a standard Bitcoin address.
1214
*

src/eckey.js

+183-28
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,33 @@ Bitcoin.ECKey = (function () {
1515
// Prepend zero byte to prevent interpretation as negative integer
1616
this.priv = BigInteger.fromByteArrayUnsigned(input);
1717
} else if ("string" == typeof input) {
18-
if (input.length == 51 && input[0] == '5') {
19-
// Base58 encoded private key
20-
this.priv = BigInteger.fromByteArrayUnsigned(ECKey.decodeString(input));
18+
var bytes = null;
19+
if (ECKey.isWalletImportFormat(input)) {
20+
bytes = ECKey.decodeWalletImportFormat(input);
21+
} else if (ECKey.isCompressedWalletImportFormat(input)) {
22+
bytes = ECKey.decodeCompressedWalletImportFormat(input);
23+
this.compressed = true;
24+
} else if (ECKey.isMiniFormat(input)) {
25+
bytes = Crypto.SHA256(input, { asBytes: true });
26+
} else if (ECKey.isHexFormat(input)) {
27+
bytes = Crypto.util.hexToBytes(input);
28+
} else if (ECKey.isBase64Format(input)) {
29+
bytes = Crypto.util.base64ToBytes(input);
30+
}
31+
32+
if (bytes == null || bytes.length != 32) {
33+
this.priv = null;
2134
} else {
2235
// Prepend zero byte to prevent interpretation as negative integer
23-
this.priv = BigInteger.fromByteArrayUnsigned(Crypto.util.base64ToBytes(input));
36+
this.priv = BigInteger.fromByteArrayUnsigned(bytes);
2437
}
2538
}
26-
this.compressed = !!ECKey.compressByDefault;
39+
40+
this.compressed = (this.compressed == undefined) ? !!ECKey.compressByDefault : this.compressed;
2741
};
2842

43+
ECKey.privateKeyPrefix = 0x80; // mainnet 0x80 testnet 0xEF
44+
2945
/**
3046
* Whether public keys should be returned compressed by default.
3147
*/
@@ -36,22 +52,45 @@ Bitcoin.ECKey = (function () {
3652
*/
3753
ECKey.prototype.setCompressed = function (v) {
3854
this.compressed = !!v;
55+
if (this.pubPoint) this.pubPoint.compressed = this.compressed;
56+
return this;
3957
};
4058

4159
/**
42-
* Return public key in DER encoding.
60+
* Return public key as a byte array in DER encoding.
4361
*/
4462
ECKey.prototype.getPub = function () {
45-
return this.getPubPoint().getEncoded(this.compressed);
63+
if (this.compressed) {
64+
if (this.pubComp) return this.pubComp;
65+
return this.pubComp = this.getPubPoint().getEncoded(1);
66+
} else {
67+
if (this.pubUncomp) return this.pubUncomp;
68+
return this.pubUncomp = this.getPubPoint().getEncoded(0);
69+
}
4670
};
4771

4872
/**
4973
* Return public point as ECPoint object.
5074
*/
5175
ECKey.prototype.getPubPoint = function () {
52-
if (!this.pub) this.pub = ecparams.getG().multiply(this.priv);
76+
if (!this.pubPoint) {
77+
this.pubPoint = ecparams.getG().multiply(this.priv);
78+
this.pubPoint.compressed = this.compressed;
79+
}
80+
return this.pubPoint;
81+
};
5382

54-
return this.pub;
83+
/**
84+
* Return public key as hexadecimal string.
85+
*/
86+
ECKey.prototype.getPubKeyHex = function () {
87+
if (this.compressed) {
88+
if (this.pubKeyHexComp) return this.pubKeyHexComp;
89+
return this.pubKeyHexComp = Crypto.util.bytesToHex(this.getPub()).toString().toUpperCase();
90+
} else {
91+
if (this.pubKeyHexUncomp) return this.pubKeyHexUncomp;
92+
return this.pubKeyHexUncomp = Crypto.util.bytesToHex(this.getPub()).toString().toUpperCase();
93+
}
5594
};
5695

5796
/**
@@ -61,9 +100,13 @@ Bitcoin.ECKey = (function () {
61100
* a byte array.
62101
*/
63102
ECKey.prototype.getPubKeyHash = function () {
64-
if (this.pubKeyHash) return this.pubKeyHash;
65-
66-
return this.pubKeyHash = Bitcoin.Util.sha256ripe160(this.getPub());
103+
if (this.compressed) {
104+
if (this.pubKeyHashComp) return this.pubKeyHashComp;
105+
return this.pubKeyHashComp = Bitcoin.Util.sha256ripe160(this.getPub());
106+
} else {
107+
if (this.pubKeyHashUncomp) return this.pubKeyHashUncomp;
108+
return this.pubKeyHashUncomp = Bitcoin.Util.sha256ripe160(this.getPub());
109+
}
67110
};
68111

69112
ECKey.prototype.getBitcoinAddress = function () {
@@ -72,24 +115,66 @@ Bitcoin.ECKey = (function () {
72115
return addr;
73116
};
74117

75-
ECKey.prototype.getExportedPrivateKey = function () {
76-
var hash = this.priv.toByteArrayUnsigned();
77-
while (hash.length < 32) hash.unshift(0);
78-
hash.unshift(0x80);
79-
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {asBytes: true}), {asBytes: true});
80-
var bytes = hash.concat(checksum.slice(0,4));
81-
return Bitcoin.Base58.encode(bytes);
118+
/**
119+
* Takes a public point as a hex string or byte array
120+
*/
121+
ECKey.prototype.setPub = function (pub) {
122+
// byte array
123+
if (Bitcoin.Util.isArray(pub)) {
124+
pub = Crypto.util.bytesToHex(pub).toString().toUpperCase();
125+
}
126+
var ecPoint = ecparams.getCurve().decodePointHex(pub);
127+
this.setCompressed(ecPoint.compressed);
128+
this.pubPoint = ecPoint;
129+
return this;
82130
};
83131

84-
ECKey.prototype.setPub = function (pub) {
85-
this.pub = ECPointFp.decodeFrom(ecparams.getCurve(), pub);
132+
/**
133+
* Private key encoded as standard Wallet Import Format (WIF)
134+
*/
135+
ECKey.prototype.getBitcoinWalletImportFormat = function () {
136+
var bytes = this.getBitcoinPrivateKeyByteArray();
137+
bytes.unshift(ECKey.privateKeyPrefix); // prepend 0x80 byte
138+
if (this.compressed) bytes.push(0x01); // append 0x01 byte for compressed format
139+
var checksum = Crypto.SHA256(Crypto.SHA256(bytes, { asBytes: true }), { asBytes: true });
140+
bytes = bytes.concat(checksum.slice(0, 4));
141+
var privWif = Bitcoin.Base58.encode(bytes);
142+
return privWif;
143+
};
144+
145+
/**
146+
* Private key encoded as hexadecimal string.
147+
*/
148+
ECKey.prototype.getBitcoinHexFormat = function () {
149+
return Crypto.util.bytesToHex(this.getBitcoinPrivateKeyByteArray()).toString().toUpperCase();
150+
};
151+
152+
/**
153+
* Private key encoded as Base64 string.
154+
*/
155+
ECKey.prototype.getBitcoinBase64Format = function () {
156+
return Crypto.util.bytesToBase64(this.getBitcoinPrivateKeyByteArray());
157+
};
158+
159+
/**
160+
* Private key encoded as raw byte array.
161+
*/
162+
ECKey.prototype.getBitcoinPrivateKeyByteArray = function () {
163+
// Get a copy of private key as a byte array
164+
var bytes = this.priv.toByteArrayUnsigned();
165+
// zero pad if private key is less than 32 bytes
166+
while (bytes.length < 32) bytes.unshift(0x00);
167+
return bytes;
86168
};
87169

88170
ECKey.prototype.toString = function (format) {
89-
if (format === "base64") {
90-
return Crypto.util.bytesToBase64(this.priv.toByteArrayUnsigned());
171+
format = format || "";
172+
if (format.toString().toLowerCase() == "base64" || format.toString().toLowerCase() == "b64") {
173+
return this.getBitcoinBase64Format(); // Base 64
174+
} else if (format.toString().toLowerCase() == "wif") {
175+
return this.getBitcoinWalletImportFormat(); // Wallet Import Format
91176
} else {
92-
return Crypto.util.bytesToHex(this.priv.toByteArrayUnsigned());
177+
return this.getBitcoinHexFormat(); // Hex
93178
}
94179
};
95180

@@ -102,10 +187,10 @@ Bitcoin.ECKey = (function () {
102187
};
103188

104189
/**
105-
* Parse an exported private key contained in a string.
190+
* Parse a wallet import format private key contained in a string.
106191
*/
107-
ECKey.decodeString = function (string) {
108-
var bytes = Bitcoin.Base58.decode(string);
192+
ECKey.decodeWalletImportFormat = function (privStr) {
193+
var bytes = Bitcoin.Base58.decode(privStr);
109194

110195
var hash = bytes.slice(0, 33);
111196

@@ -120,12 +205,82 @@ Bitcoin.ECKey = (function () {
120205

121206
var version = hash.shift();
122207

123-
if (version != 0x80) {
208+
if (version != ECKey.privateKeyPrefix) {
124209
throw "Version "+version+" not supported!";
125210
}
126211

127212
return hash;
128213
};
129214

215+
/**
216+
* Parse a compressed wallet import format private key contained in a string.
217+
*/
218+
ECKey.decodeCompressedWalletImportFormat = function (privStr) {
219+
var bytes = Bitcoin.Base58.decode(privStr);
220+
var hash = bytes.slice(0, 34);
221+
var checksum = Crypto.SHA256(Crypto.SHA256(hash, { asBytes: true }), { asBytes: true });
222+
if (checksum[0] != bytes[34] ||
223+
checksum[1] != bytes[35] ||
224+
checksum[2] != bytes[36] ||
225+
checksum[3] != bytes[37]) {
226+
throw "Checksum validation failed!";
227+
}
228+
var version = hash.shift();
229+
if (version != ECKey.privateKeyPrefix) {
230+
throw "Version " + version + " not supported!";
231+
}
232+
hash.pop();
233+
return hash;
234+
};
235+
236+
/**
237+
* Detects keys in hex format (64 characters [0-9A-F]).
238+
*/
239+
ECKey.isHexFormat = function (key) {
240+
key = key.toString();
241+
return /^[A-Fa-f0-9]{64}$/.test(key);
242+
};
243+
244+
/**
245+
* Detects keys in base58 format (51 characters base58, always starts with a '5')
246+
*/
247+
ECKey.isWalletImportFormat = function (key) {
248+
key = key.toString();
249+
return (ECKey.privateKeyPrefix == ECKey.privateKeyPrefix) ?
250+
(/^5[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{50}$/.test(key)) :
251+
(/^9[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{50}$/.test(key));
252+
};
253+
254+
/**
255+
* Detects keys in standard Wallet Import Format (52 characters base58)
256+
*/
257+
ECKey.isCompressedWalletImportFormat = function (key) {
258+
key = key.toString();
259+
return (ECKey.privateKeyPrefix == ECKey.privateKeyPrefix) ?
260+
(/^[LK][123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{51}$/.test(key)) :
261+
(/^c[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{51}$/.test(key));
262+
};
263+
264+
/**
265+
* Detects keys in base64 format (44 characters)
266+
*/
267+
ECKey.isBase64Format = function (key) {
268+
key = key.toString();
269+
return (/^[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789=+\/]{44}$/.test(key));
270+
};
271+
272+
/**
273+
* Detects keys in 'mini' format (22, 26 or 30 characters, always starts with an 'S')
274+
*/
275+
ECKey.isMiniFormat = function (key) {
276+
key = key.toString();
277+
var validChars22 = /^S[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{21}$/.test(key);
278+
var validChars26 = /^S[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{25}$/.test(key);
279+
var validChars30 = /^S[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{29}$/.test(key);
280+
var testBytes = Crypto.SHA256(key + "?", { asBytes: true });
281+
282+
return ((testBytes[0] === 0x00 || testBytes[0] === 0x01) && (validChars22 || validChars26 || validChars30));
283+
};
284+
130285
return ECKey;
131286
})();

src/util.js

+9
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,15 @@ Bitcoin.Util = {
218218
*/
219219
sha256ripe160: function (data) {
220220
return Crypto.RIPEMD160(Crypto.SHA256(data, {asBytes: true}), {asBytes: true});
221+
},
222+
/**
223+
* Calculate SHA256(SHA256(data)).
224+
*
225+
* Takes an arbitrary byte array as inputs and returns the doubly hashed
226+
* data as a byte array.
227+
*/
228+
dsha256: function (data) {
229+
return Crypto.SHA256(Crypto.SHA256(data, { asBytes: true }), { asBytes: true });
221230
}
222231
};
223232

0 commit comments

Comments
 (0)