@@ -15,17 +15,33 @@ Bitcoin.ECKey = (function () {
15
15
// Prepend zero byte to prevent interpretation as negative integer
16
16
this . priv = BigInteger . fromByteArrayUnsigned ( input ) ;
17
17
} 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 ;
21
34
} else {
22
35
// Prepend zero byte to prevent interpretation as negative integer
23
- this . priv = BigInteger . fromByteArrayUnsigned ( Crypto . util . base64ToBytes ( input ) ) ;
36
+ this . priv = BigInteger . fromByteArrayUnsigned ( bytes ) ;
24
37
}
25
38
}
26
- this . compressed = ! ! ECKey . compressByDefault ;
39
+
40
+ this . compressed = ( this . compressed == undefined ) ? ! ! ECKey . compressByDefault : this . compressed ;
27
41
} ;
28
42
43
+ ECKey . privateKeyPrefix = 0x80 ; // mainnet 0x80 testnet 0xEF
44
+
29
45
/**
30
46
* Whether public keys should be returned compressed by default.
31
47
*/
@@ -36,22 +52,45 @@ Bitcoin.ECKey = (function () {
36
52
*/
37
53
ECKey . prototype . setCompressed = function ( v ) {
38
54
this . compressed = ! ! v ;
55
+ if ( this . pubPoint ) this . pubPoint . compressed = this . compressed ;
56
+ return this ;
39
57
} ;
40
58
41
59
/**
42
- * Return public key in DER encoding.
60
+ * Return public key as a byte array in DER encoding.
43
61
*/
44
62
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
+ }
46
70
} ;
47
71
48
72
/**
49
73
* Return public point as ECPoint object.
50
74
*/
51
75
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
+ } ;
53
82
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
+ }
55
94
} ;
56
95
57
96
/**
@@ -61,9 +100,13 @@ Bitcoin.ECKey = (function () {
61
100
* a byte array.
62
101
*/
63
102
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
+ }
67
110
} ;
68
111
69
112
ECKey . prototype . getBitcoinAddress = function ( ) {
@@ -72,24 +115,66 @@ Bitcoin.ECKey = (function () {
72
115
return addr ;
73
116
} ;
74
117
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 ;
82
130
} ;
83
131
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 ;
86
168
} ;
87
169
88
170
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
91
176
} else {
92
- return Crypto . util . bytesToHex ( this . priv . toByteArrayUnsigned ( ) ) ;
177
+ return this . getBitcoinHexFormat ( ) ; // Hex
93
178
}
94
179
} ;
95
180
@@ -102,10 +187,10 @@ Bitcoin.ECKey = (function () {
102
187
} ;
103
188
104
189
/**
105
- * Parse an exported private key contained in a string.
190
+ * Parse a wallet import format private key contained in a string.
106
191
*/
107
- ECKey . decodeString = function ( string ) {
108
- var bytes = Bitcoin . Base58 . decode ( string ) ;
192
+ ECKey . decodeWalletImportFormat = function ( privStr ) {
193
+ var bytes = Bitcoin . Base58 . decode ( privStr ) ;
109
194
110
195
var hash = bytes . slice ( 0 , 33 ) ;
111
196
@@ -120,12 +205,82 @@ Bitcoin.ECKey = (function () {
120
205
121
206
var version = hash . shift ( ) ;
122
207
123
- if ( version != 0x80 ) {
208
+ if ( version != ECKey . privateKeyPrefix ) {
124
209
throw "Version " + version + " not supported!" ;
125
210
}
126
211
127
212
return hash ;
128
213
} ;
129
214
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 - F a - f 0 - 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 [ 1 2 3 4 5 6 7 8 9 A B C D E F G H J K L M N P Q R S T U V W X Y Z a b c d e f g h i j k m n o p q r s t u v w x y z ] { 50 } $ / . test ( key ) ) :
251
+ ( / ^ 9 [ 1 2 3 4 5 6 7 8 9 A B C D E F G H J K L M N P Q R S T U V W X Y Z a b c d e f g h i j k m n o p q r s t u v w x y z ] { 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
+ ( / ^ [ L K ] [ 1 2 3 4 5 6 7 8 9 A B C D E F G H J K L M N P Q R S T U V W X Y Z a b c d e f g h i j k m n o p q r s t u v w x y z ] { 51 } $ / . test ( key ) ) :
261
+ ( / ^ c [ 1 2 3 4 5 6 7 8 9 A B C D E F G H J K L M N P Q R S T U V W X Y Z a b c d e f g h i j k m n o p q r s t u v w x y z ] { 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 ( / ^ [ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 = + \/ ] { 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 [ 1 2 3 4 5 6 7 8 9 A B C D E F G H J K L M N P Q R S T U V W X Y Z a b c d e f g h i j k m n o p q r s t u v w x y z ] { 21 } $ / . test ( key ) ;
278
+ var validChars26 = / ^ S [ 1 2 3 4 5 6 7 8 9 A B C D E F G H J K L M N P Q R S T U V W X Y Z a b c d e f g h i j k m n o p q r s t u v w x y z ] { 25 } $ / . test ( key ) ;
279
+ var validChars30 = / ^ S [ 1 2 3 4 5 6 7 8 9 A B C D E F G H J K L M N P Q R S T U V W X Y Z a b c d e f g h i j k m n o p q r s t u v w x y z ] { 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
+
130
285
return ECKey ;
131
286
} ) ( ) ;
0 commit comments