@@ -125,7 +125,8 @@ Bitcoin.BIP38 = (function () {
125
125
passfactor = Bitcoin . Util . dsha256 ( prefactorB ) ;
126
126
}
127
127
var kp = new Bitcoin . ECKey ( passfactor ) ;
128
- var passpoint = kp . getPubCompressed ( ) ;
128
+ kp . compressed = true ;
129
+ var passpoint = kp . getPub ( ) ;
129
130
130
131
var encryptedPart2 = hex . slice ( 23 , 23 + 16 ) ;
131
132
@@ -151,6 +152,120 @@ Bitcoin.BIP38 = (function () {
151
152
}
152
153
}
153
154
155
+ /**
156
+ * Generates an intermediate point based on a password which can later be used
157
+ * to directly generate new BIP38-encrypted private keys without actually knowing
158
+ * the password.
159
+ * @author Zeilap
160
+ */
161
+ BIP38 . generateIntermediate = function ( passphrase , lotNum , sequenceNum ) {
162
+ var noNumbers = lotNum == null || sequenceNum == null ;
163
+ var ownerEntropy , ownerSalt ;
164
+
165
+ if ( noNumbers ) {
166
+ ownerSalt = ownerEntropy = new Array ( 8 ) ;
167
+ rng . nextBytes ( ownerEntropy ) ;
168
+ } else {
169
+ // 1) generate 4 random bytes
170
+ var ownerSalt = Array ( 4 ) ;
171
+
172
+ rng . nextBytes ( ownerSalt ) ;
173
+
174
+ // 2) Encode the lot and sequence numbers as a 4 byte quantity (big-endian):
175
+ // lotnumber * 4096 + sequencenumber. Call these four bytes lotsequence.
176
+ var lotSequence = nbv ( 4096 * lotNum + sequenceNum ) . toByteArrayUnsigned ( ) ;
177
+
178
+ // 3) Concatenate ownersalt + lotsequence and call this ownerentropy.
179
+ var ownerEntropy = ownerSalt . concat ( lotSequence ) ;
180
+ }
181
+
182
+ // 4) Derive a key from the passphrase using scrypt
183
+ var prefactor = scrypt ( passphrase , ownerSalt , BIP38 . scryptParams . N , BIP38 . scryptParams . r , BIP38 . scryptParams . p , 32 ) ;
184
+
185
+ // Take SHA256(SHA256(prefactor + ownerentropy)) and call this passfactor
186
+ var passfactorBytes = noNumbers ? prefactor : Bitcoin . Util . dsha256 ( prefactor . concat ( ownerEntropy ) ) ;
187
+ var passfactor = BigInteger . fromByteArrayUnsigned ( passfactorBytes ) ;
188
+
189
+ // 5) Compute the elliptic curve point G * passfactor, and convert the result to compressed notation (33 bytes)
190
+ var passpoint = ecparams . getG ( ) . multiply ( passfactor ) . getEncoded ( 1 ) ;
191
+
192
+ // 6) Convey ownersalt and passpoint to the party generating the keys, along with a checksum to ensure integrity.
193
+ // magic bytes "2C E9 B3 E1 FF 39 E2 51" followed by ownerentropy, and then passpoint
194
+ var magicBytes = [ 0x2C , 0xE9 , 0xB3 , 0xE1 , 0xFF , 0x39 , 0xE2 , 0x51 ] ;
195
+ if ( noNumbers ) magicBytes [ 7 ] = 0x53 ;
196
+
197
+ var intermediate = magicBytes . concat ( ownerEntropy ) . concat ( passpoint ) ;
198
+
199
+ // base58check encode
200
+ intermediate = intermediate . concat ( Bitcoin . Util . dsha256 ( intermediate ) . slice ( 0 , 4 ) ) ;
201
+ return Bitcoin . Base58 . encode ( intermediate ) ;
202
+ } ;
203
+
204
+ /**
205
+ * Creates new private key using an intermediate EC point.
206
+ */
207
+ BIP38 . newAddressFromIntermediate = function ( intermediate , compressed ) {
208
+ var result = { } ;
209
+
210
+ // decode IPS
211
+ var x = Bitcoin . Base58 . decode ( intermediate ) ;
212
+ //TODO if(x.slice(49, 4) !== Bitcoin.Util.dsha256(x.slice(0,49)).slice(0,4)) {
213
+ // throw new Error("Invalid intermediate passphrase string");
214
+ //}
215
+ var noNumbers = ( x [ 7 ] === 0x53 ) ;
216
+ var ownerEntropy = x . slice ( 8 , 8 + 8 ) ;
217
+ var passpoint = x . slice ( 16 , 16 + 33 ) ;
218
+
219
+ // 1) Set flagbyte.
220
+ // set bit 0x20 for compressed key
221
+ // set bit 0x04 if ownerentropy contains a value for lotsequence
222
+ var flagByte = ( compressed ? 0x20 : 0x00 ) | ( noNumbers ? 0x00 : 0x04 ) ;
223
+
224
+ // 2) Generate 24 random bytes, call this seedb.
225
+ var seedB = new Array ( 24 ) ;
226
+ rng . nextBytes ( seedB ) ;
227
+
228
+ // Take SHA256(SHA256(seedb)) to yield 32 bytes, call this factorb.
229
+ var factorB = Bitcoin . Util . dsha256 ( seedB ) ;
230
+
231
+ // 3) ECMultiply passpoint by factorb. Use the resulting EC point as a public key and hash it into a Bitcoin
232
+ // address using either compressed or uncompressed public key methodology (specify which methodology is used
233
+ // inside flagbyte). This is the generated Bitcoin address, call it generatedAddress.
234
+ var ec = ecparams . getCurve ( ) ;
235
+ var generatedPoint = ec . decodePointHex ( Crypto . util . bytesToHex ( passpoint ) ) ;
236
+ var generatedBytes = generatedPoint . multiply ( BigInteger . fromByteArrayUnsigned ( factorB ) ) . getEncoded ( compressed ) ;
237
+ var generatedAddress = new Bitcoin . Address ( Bitcoin . Util . sha256ripe160 ( generatedBytes ) ) ;
238
+
239
+ // 4) Take the first four bytes of SHA256(SHA256(generatedaddress)) and call it addresshash.
240
+ var addressHash = Bitcoin . Util . dsha256 ( generatedAddress . toString ( ) ) . slice ( 0 , 4 ) ;
241
+
242
+ // 5) Now we will encrypt seedb. Derive a second key from passpoint using scrypt
243
+ var derivedBytes = scrypt ( passpoint , addressHash . concat ( ownerEntropy ) , 1024 , 1 , 1 , 64 ) ;
244
+
245
+ // 6) Do AES256Encrypt(seedb[0...15]] xor derivedhalf1[0...15], derivedhalf2), call the 16-byte result encryptedpart1
246
+ for ( var i = 0 ; i < 16 ; ++ i ) {
247
+ seedB [ i ] ^= derivedBytes [ i ] ;
248
+ }
249
+ var encryptedPart1 = Crypto . AES . encrypt ( seedB . slice ( 0 , 16 ) , derivedBytes . slice ( 32 ) , AES_opts ) ;
250
+
251
+ // 7) Do AES256Encrypt((encryptedpart1[8...15] + seedb[16...23]) xor derivedhalf1[16...31], derivedhalf2), call the 16-byte result encryptedseedb.
252
+ var message2 = encryptedPart1 . slice ( 8 , 8 + 8 ) . concat ( seedB . slice ( 16 , 16 + 8 ) ) ;
253
+ for ( var i = 0 ; i < 16 ; ++ i ) {
254
+ message2 [ i ] ^= derivedBytes [ i + 16 ] ;
255
+ }
256
+ var encryptedSeedB = Crypto . AES . encrypt ( message2 , derivedBytes . slice ( 32 ) , AES_opts ) ;
257
+
258
+ // 0x01 0x43 + flagbyte + addresshash + ownerentropy + encryptedpart1[0...7] + encryptedPart2
259
+ var encryptedKey = [ 0x01 , 0x43 , flagByte ] . concat ( addressHash ) . concat ( ownerEntropy ) . concat ( encryptedPart1 . slice ( 0 , 8 ) ) . concat ( encryptedSeedB ) ;
260
+
261
+ // base58check encode
262
+ encryptedKey = encryptedKey . concat ( Bitcoin . Util . dsha256 ( encryptedKey ) . slice ( 0 , 4 ) ) ;
263
+
264
+ result . address = generatedAddress ;
265
+ result . bip38PrivateKey = Bitcoin . Base58 . encode ( encryptedKey ) ;
266
+ return result ;
267
+ } ;
268
+
154
269
/**
155
270
* Detects keys encrypted according to BIP-38 (58 base58 characters starting with 6P)
156
271
*/
0 commit comments