@@ -3,6 +3,7 @@ var crypto = require('crypto')
33var scryptsy = require ( 'scrypt.js' )
44var uuid = require ( 'uuid' )
55var utf8 = require ( 'utf8' )
6+ var aesjs = require ( 'aes-js' )
67
78function assert ( val , msg ) {
89 if ( ! val ) {
@@ -334,7 +335,36 @@ Wallet.fromEtherCamp = function (passphrase) {
334335 return new Wallet ( ethUtil . sha3 ( new Buffer ( passphrase ) ) )
335336}
336337
337- Wallet . fromKryptoKit = function ( entropy ) {
338+ Wallet . fromKryptoKit = function ( entropy , password ) {
339+ function kryptoKitBrokenScryptSeed ( buf ) {
340+ // js-scrypt calls `new Buffer(String(salt), 'utf8')` on the seed even though it is a buffer
341+ //
342+ // The `buffer`` implementation used does the below transformation (doesn't matches the current version):
343+ // https://github.com/feross/buffer/blob/67c61181b938b17d10dbfc0a545f713b8bd59de8/index.js
344+
345+ function decodeUtf8Char ( str ) {
346+ try {
347+ return decodeURIComponent ( str )
348+ } catch ( err ) {
349+ return String . fromCharCode ( 0xFFFD ) // UTF 8 invalid char
350+ }
351+ }
352+
353+ var res = ''
354+ var tmp = ''
355+
356+ for ( var i = 0 ; i < buf . length ; i ++ ) {
357+ if ( buf [ i ] <= 0x7F ) {
358+ res += decodeUtf8Char ( tmp ) + String . fromCharCode ( buf [ i ] )
359+ tmp = ''
360+ } else {
361+ tmp += '%' + buf [ i ] . toString ( 16 )
362+ }
363+ }
364+
365+ return new Buffer ( res + decodeUtf8Char ( tmp ) )
366+ }
367+
338368 if ( entropy [ 0 ] === '#' ) {
339369 entropy = entropy . slice ( 1 )
340370 }
@@ -345,6 +375,42 @@ Wallet.fromKryptoKit = function (entropy) {
345375 var privKey
346376 if ( type === 'd' ) {
347377 privKey = ethUtil . sha256 ( entropy )
378+ } else if ( type === 'q' ) {
379+ if ( typeof password !== 'string' ) {
380+ throw new Error ( 'Password required' )
381+ }
382+
383+ var encryptedSeed = ethUtil . sha256 ( new Buffer ( entropy . slice ( 0 , 30 ) ) )
384+ var checksum = entropy . slice ( 30 , 46 )
385+
386+ var salt = kryptoKitBrokenScryptSeed ( encryptedSeed )
387+ var aesKey = scryptsy ( new Buffer ( password , 'utf8' ) , salt , 16384 , 8 , 1 , 32 )
388+
389+ /* FIXME: try to use `crypto` instead of `aesjs`
390+
391+ // NOTE: ECB doesn't use the IV, so it can be anything
392+ var decipher = crypto.createDecipheriv("aes-256-ecb", aesKey, new Buffer(0))
393+
394+ // FIXME: this is a clear abuse, but seems to match how ECB in aesjs works
395+ privKey = Buffer.concat([
396+ decipher.update(encryptedSeed).slice(0, 16),
397+ decipher.update(encryptedSeed).slice(0, 16),
398+ ])
399+ */
400+
401+ /* eslint-disable new-cap */
402+ var decipher = new aesjs . ModeOfOperation . ecb ( aesKey )
403+ /* eslint-enable new-cap */
404+ privKey = Buffer . concat ( [
405+ decipher . decrypt ( encryptedSeed . slice ( 0 , 16 ) ) ,
406+ decipher . decrypt ( encryptedSeed . slice ( 16 , 32 ) )
407+ ] )
408+
409+ if ( checksum . length > 0 ) {
410+ if ( checksum !== ethUtil . sha256 ( ethUtil . sha256 ( privKey ) ) . slice ( 0 , 8 ) . toString ( 'hex' ) ) {
411+ throw new Error ( 'Failed to decrypt input - possibly invalid passphrase' )
412+ }
413+ }
348414 } else {
349415 throw new Error ( 'Unsupported or invalid entropy type' )
350416 }
0 commit comments