@@ -279,11 +279,125 @@ Bitcoin.BIP38 = (function () {
279
279
// base58check encode
280
280
encryptedKey = encryptedKey . concat ( Bitcoin . Util . dsha256 ( encryptedKey ) . slice ( 0 , 4 ) ) ;
281
281
282
- result . address = generatedAddress ;
283
- result . bip38PrivateKey = Bitcoin . Base58 . encode ( encryptedKey ) ;
284
- return result ;
282
+ // Generate confirmation code for the new address
283
+ var confirmation = newAddressConfirmation ( addressHash , factorB , derivedBytes , flagByte , ownerEntropy ) ;
284
+
285
+ return { address : generatedAddress ,
286
+ bip38PrivateKey : Bitcoin . Base58 . encode ( encryptedKey ) ,
287
+ confirmation : confirmation } ;
288
+ } ;
289
+
290
+ /**
291
+ * Generates a confirmation code for a key/address generated using an intermediate
292
+ * ec point (see BIP38.newAddressFromIntermediate). This certifies that the address
293
+ * truly corresponds to the password from which the intermediate ec point was derived
294
+ * (see BIP38.verifyNewAddressConfirmation).
295
+ */
296
+ var newAddressConfirmation = function ( addressHash , factorB , derivedBytes , flagByte , ownerEntropy ) {
297
+ // 1) ECMultiply factorb by G, call the result pointb. The result is 33 bytes.
298
+ var pointb = ecparams . getG ( ) . multiply ( BigInteger . fromByteArrayUnsigned ( factorB ) ) . getEncoded ( 1 ) ;
299
+
300
+ // 2) he first byte is 0x02 or 0x03. XOR it by (derivedhalf2[31] & 0x01), call the resulting byte pointbprefix.
301
+ var pointbprefix = pointb [ 0 ] ^ ( derivedBytes [ 63 ] & 0x01 ) ;
302
+
303
+ // 3) Do AES256Encrypt(pointb[1...16] xor derivedhalf1[0...15], derivedhalf2) and call the result pointbx1.
304
+ for ( var i = 0 ; i < 16 ; ++ i ) {
305
+ pointb [ i + 1 ] ^= derivedBytes [ i ] ;
306
+ }
307
+ var pointbx1 = Crypto . AES . encrypt ( pointb . slice ( 1 , 17 ) , derivedBytes . slice ( 32 ) , AES_opts ) ;
308
+
309
+ // 4) Do AES256Encrypt(pointb[17...32] xor derivedhalf1[16...31], derivedhalf2) and call the result pointbx2.
310
+ for ( var i = 16 ; i < 32 ; ++ i ) {
311
+ pointb [ i + 1 ] ^= derivedBytes [ i ] ;
312
+ }
313
+ var pointbx2 = Crypto . AES . encrypt ( pointb . slice ( 17 , 33 ) , derivedBytes . slice ( 32 ) , AES_opts ) ;
314
+
315
+ var encryptedpointb = [ pointbprefix ] . concat ( pointbx1 ) . concat ( pointbx2 ) ;
316
+
317
+ var confirmationPreChecksum =
318
+ [ 0x64 , 0x3B , 0xF6 , 0xA8 , 0x9A , flagByte ]
319
+ . concat ( addressHash )
320
+ . concat ( ownerEntropy )
321
+ . concat ( encryptedpointb ) ;
322
+ var confirmationBytes = confirmationPreChecksum . concat ( Bitcoin . Util . dsha256 ( confirmationPreChecksum ) . slice ( 0 , 4 ) ) ;
323
+ var confirmation = Bitcoin . Base58 . encode ( confirmationBytes ) ;
324
+
325
+ return confirmation ;
326
+ } ;
327
+
328
+ /**
329
+ * Certifies that the given address was generated using an intermediate ec point derived
330
+ * from the given password (see BIP38.newAddressFromIntermediate).
331
+ */
332
+ BIP38 . verifyNewAddressConfirmation = function ( expectedAddressStr , confirmation , passphrase ) {
333
+ var confirmationResults = BIP38 . verifyConfirmation ( confirmation , passphrase ) ;
334
+ return ( confirmationResults . address == expectedAddressStr ) ;
285
335
} ;
286
336
337
+ /**
338
+ * Certifies that the given BIP38 confirmation code matches the password and
339
+ * returns the address the confirmation corresponds to (see BIP38.newAddressFromIntermediate).
340
+ */
341
+ BIP38 . verifyConfirmation = function ( confirmation , passphrase ) {
342
+ var bytes = Bitcoin . Base58 . decode ( confirmation ) ;
343
+
344
+ // Get the flag byte (tells us whether address compression is used and whether lot/sequence values are present).
345
+ var flagByte = bytes [ 5 ] ;
346
+
347
+ // Get the address hash.
348
+ var addressHash = bytes . slice ( 6 , 10 ) ;
349
+
350
+ // Get the owner entropy (tells us the lot/sequence values when applicable).
351
+ var ownerEntropy = bytes . slice ( 10 , 18 ) ;
352
+
353
+ // Get encryptedpointb
354
+ var encryptedpointb = bytes . slice ( 18 , 51 ) ;
355
+
356
+ var compressed = ( flagByte & 0x20 ) == 0x20 ;
357
+ var lotSequencePresent = ( flagByte & 0x04 ) == 0x04 ;
358
+ var ownerSalt = ownerEntropy . slice ( 0 , lotSequencePresent ? 4 : 8 )
359
+
360
+ var prefactor = scrypt ( passphrase , ownerSalt , scryptParams . passphrase . N , scryptParams . passphrase . r , scryptParams . passphrase . p , 32 ) ;
361
+
362
+ // Take SHA256(SHA256(prefactor + ownerentropy)) and call this passfactor
363
+ var passfactorBytes = ! lotSequencePresent ? prefactor : Bitcoin . Util . dsha256 ( prefactor . concat ( ownerEntropy ) ) ;
364
+ var passfactor = BigInteger . fromByteArrayUnsigned ( passfactorBytes ) ;
365
+
366
+ var passpoint = ecparams . getG ( ) . multiply ( passfactor ) . getEncoded ( 1 ) ;
367
+
368
+ var addresshashplusownerentropy = addressHash . concat ( ownerEntropy ) ;
369
+
370
+ var derivedBytes = scrypt ( passpoint , addresshashplusownerentropy , scryptParams . passpoint . N , scryptParams . passpoint . r , scryptParams . passpoint . p , 64 ) ;
371
+
372
+ // recover the 0x02 or 0x03 prefix
373
+ var unencryptedpubkey = [ ] ;
374
+ unencryptedpubkey [ 0 ] = encryptedpointb [ 0 ] ^ ( derivedBytes [ 63 ] & 0x01 ) ;
375
+
376
+ decrypted1 = Crypto . AES . decrypt ( encryptedpointb . slice ( 1 , 17 ) , derivedBytes . slice ( 32 ) , AES_opts ) ;
377
+ decrypted2 = Crypto . AES . decrypt ( encryptedpointb . slice ( 17 , 33 ) , derivedBytes . slice ( 32 ) , AES_opts ) ;
378
+ decrypted = unencryptedpubkey . concat ( decrypted1 ) . concat ( decrypted2 ) ;
379
+
380
+ for ( var x = 0 ; x < 32 ; x ++ ) {
381
+ decrypted [ x + 1 ] ^= derivedBytes [ x ] ;
382
+ }
383
+
384
+ var ec = ecparams . getCurve ( ) ;
385
+ var generatedPoint = ec . decodePointHex ( Crypto . util . bytesToHex ( decrypted ) . toString ( ) . toUpperCase ( ) ) ;
386
+ var generatedBytes = generatedPoint . multiply ( BigInteger . fromByteArrayUnsigned ( passfactor ) ) . getEncoded ( compressed ) ;
387
+ var generatedAddress = ( new Bitcoin . Address ( Bitcoin . Util . sha256ripe160 ( generatedBytes ) ) ) . toString ( ) ;
388
+
389
+ var generatedAddressHash = Bitcoin . Util . dsha256 ( generatedAddress ) . slice ( 0 , 4 ) ;
390
+
391
+ var valid = true ;
392
+ for ( var i = 0 ; i < 4 ; i ++ ) {
393
+ if ( addressHash [ i ] != generatedAddressHash [ i ] ) {
394
+ valid = false ;
395
+ }
396
+ }
397
+
398
+ return { valid : valid , address : generatedAddress } ;
399
+ }
400
+
287
401
/**
288
402
* Detects keys encrypted according to BIP-38 (58 base58 characters starting with 6P)
289
403
*/
0 commit comments