diff --git a/app/lib.js b/app/lib.js index 9bc2630..015c4c2 100644 --- a/app/lib.js +++ b/app/lib.js @@ -4,6 +4,9 @@ const passworder = require('@metamask/browser-passworder') function dedupe (arr) { const result = [] arr?.forEach(x => { + if (x == null) { + return + } if (!result.find(y => Object.keys(x).length === Object.keys(y).length && Object.entries(x).every(([k,ex]) => y[k] === ex ))) { result.push(x) } @@ -116,27 +119,55 @@ function extractVaultFromFile (data) { } } - // attempt 6: chromium 000005.ldb on windows - const matchRegex = /Keyring[0-9][^\}]*(\{[^\{\}]*\\"\})/gu - const captureRegex = /Keyring[0-9][^\}]*(\{[^\{\}]*\\"\})/u - const ivRegex = /\\"iv.{1,4}[^A-Za-z0-9+\/]{1,10}([A-Za-z0-9+\/]{10,40}=*)/u - const dataRegex = /\\"[^":,is]*\\":\\"([A-Za-z0-9+\/]*=*)/u - const saltRegex = /,\\"salt.{1,4}[^A-Za-z0-9+\/]{1,10}([A-Za-z0-9+\/]{10,100}=*)/u - const vaults = dedupe(data.match(matchRegex)?.map(m => m.match(captureRegex)[1]) - .map(s => [dataRegex, ivRegex, saltRegex].map(r => s.match(r))) - .filter(([d,i,s]) => d&&d.length>1 && i&&i.length>1 && s&&s.length>1) - .map(([d,i,s]) => ({ - data: d[1], - iv: i[1], - salt: s[1], - }))) - if (!vaults.length) { - return null + { + // attempt 6: chromium 000005.ldb on windows + const matchRegex = /Keyring[0-9][^\}]*(\{[^\{\}]*\\"\})/gu + const captureRegex = /Keyring[0-9][^\}]*(\{[^\{\}]*\\"\})/u + const ivRegex = /\\"iv.{1,4}[^A-Za-z0-9+\/]{1,10}([A-Za-z0-9+\/]{10,40}=*)/u + const dataRegex = /\\"[^":,is]*\\":\\"([A-Za-z0-9+\/]*=*)/u + const saltRegex = /,\\"salt.{1,4}[^A-Za-z0-9+\/]{1,10}([A-Za-z0-9+\/]{10,100}=*)/u + const vaults = dedupe(data.match(matchRegex)?.map(m => m.match(captureRegex)[1]) + .map(s => [dataRegex, ivRegex, saltRegex].map(r => s.match(r))) + .filter(([d,i,s]) => d&&d.length>1 && i&&i.length>1 && s&&s.length>1) + .map(([d,i,s]) => ({ + data: d[1], + iv: i[1], + salt: s[1], + }))) + if (vaults.length) { + /* istanbul ignore next */ + if (vaults.length > 1) { + console.log('Found multiple vaults!', vaults) + } + return vaults[0] + } } - if (vaults.length > 1) { - console.log('Found multiple vaults!', vaults) + { + // attempt 7: log file using split state format, chromium 000004.log on windows-2 + const vaultRegex = /KeyringController[\s\S]*?"vault":"((?:[^"\\]|\\.)*)"/g + const vaults = [] + let match + + while ((match = vaultRegex.exec(data)) !== null) { + try { + const vaultString = JSON.parse(`"${match[1]}"`) + const json = JSON.parse(vaultString) + vaults.push(json) + } catch (err) { + // Not valid JSON: continue + } + } + + const dedupedVaults = dedupe(vaults) + if (dedupedVaults.length) { + /* istanbul ignore next */ + if (dedupedVaults.length > 1) { + console.log('Found multiple vaults!', dedupedVaults) + } + return dedupedVaults[0] + } } - return vaults[0] + return null } @@ -176,5 +207,3 @@ module.exports = { extractVaultFromFile, isVaultValid, } - - diff --git a/app/lib.test.js b/app/lib.test.js index 4ef47e2..b3dcaf3 100644 --- a/app/lib.test.js +++ b/app/lib.test.js @@ -50,6 +50,11 @@ const FIXTURES = [ 'live pupil slab senior boy release lyrics shaft lazy renew potato simple', passphrase: 'correct horse battery staple', }, + { + path: 'chrome-windows-2/000004.log', + mnemonic: 'please injury labor shift much city general enemy vivid bench pottery picture', + passphrase: 'correct horse battery staple', + }, ] const VAULTS = [ @@ -57,13 +62,13 @@ const VAULTS = [ variant: 'vault with no key metadata', vaultData: '{"data":"s6TpYjlUNsn7ifhEFTkuDGBUM1GyOlPrim7JSjtfIxgTt8/6MiXgiR/CtFfR4dWW2xhq85/NGIBYEeWrZThGdKGarBzeIqBfLFhw9n509jprzJ0zc2Rf+9HVFGLw+xxC4xPxgCS0IIWeAJQ+XtGcHmn0UZXriXm8Ja4kdlow6SWinB7sr/WM3R0+frYs4WgllkwggDf2/Tv6VHygvLnhtzp6hIJFyTjh+l/KnyJTyZW1TkZhDaNDzX3SCOHT","iv":"FbeHDAW5afeWNORfNJBR0Q==","salt":"TxZ+WbCW6891C9LK/hbMAoUsSEW1E8pyGLVBU6x5KR8="}', mnemonic: 'spread raise short crane omit tent fringe mandate neglect detail suspect cradle', - passphrase: 'correct horse battery staple', + passphrase: 'correct horse battery staple', }, { variant: 'vault with key metadata and 600_000 iterations', vaultData: '{"data":"WHaP1FrrtV4zUonudIppDifsLHF39g6oPkVksAIdWAHBRzax1uy1asfAJprR7u72t4/HuYz5yPIFQrnNnv+hwQu9GRuty88VKMnvMy+sq8MNtoXI+C54bZpWa8r4iUQfa0Mj/cfJbpFpzOdF1ZYXahTfTcU5WsrHwvJew842CiJR4B2jmCHHXfm/DxLK3WazsVQwXJGx/U71UelGoOOrT8NI28EKrAwgPn+7Xmv0j92gmhau30N7Bo2fr6Zv","iv":"LfD8/tY1EjXzxuemSmDVdA==","keyMetadata":{"algorithm":"PBKDF2","params":{"iterations":600000}},"salt":"nk4xdpmMR+1s5BYe4Vnk++XAQwrISI2bCtbMg7V1wUA="}', mnemonic: 'spread raise short crane omit tent fringe mandate neglect detail suspect cradle', - passphrase: 'correct horse battery staple', + passphrase: 'correct horse battery staple', }, ] @@ -88,7 +93,7 @@ describe('decryptVault', () => { VAULTS.forEach((vault) => { it(`decrypts ${vault.variant}`, async () => { const decrypted = await decryptVault( - vault.passphrase, + vault.passphrase, JSON.parse(vault.vaultData) ); diff --git a/bundle.js b/bundle.js index dde8269..b3601a7 100644 --- a/bundle.js +++ b/bundle.js @@ -15,6 +15,9 @@ var passworder = require('@metamask/browser-passworder'); function dedupe(arr) { var result = []; arr === null || arr === void 0 ? void 0 : arr.forEach(function (x) { + if (x == null) { + return; + } if (!result.find(function (y) { return Object.keys(x).length === Object.keys(y).length && Object.entries(x).every(function (_ref) { var _ref2 = _slicedToArray(_ref, 2), @@ -36,7 +39,6 @@ function decodeMnemonic(mnemonic) { } } function extractVaultFromFile(data) { - var _data$match; var vaultBody; try { // attempt 1: raw json @@ -75,11 +77,11 @@ function extractVaultFromFile(data) { if (_matches2 && _matches2.length) { try { var keyringControllerStateFragment = _matches2[1]; - var _dataRegex = /\\"data\\":\\"([\+\/-9A-Za-z]*=*)/; - var _ivRegex = /,\\"iv\\":\\"([\+\/-9A-Za-z]{10,40}=*)/; - var _saltRegex = /,\\"salt\\":\\"([A-Za-z0-9+\/]{10,100}=*)\\"/; + var dataRegex = /\\"data\\":\\"([\+\/-9A-Za-z]*=*)/; + var ivRegex = /,\\"iv\\":\\"([\+\/-9A-Za-z]{10,40}=*)/; + var saltRegex = /,\\"salt\\":\\"([A-Za-z0-9+\/]{10,100}=*)\\"/; var keyMetaRegex = /,\\"keyMetadata\\":(.*}})/; - var vaultParts = [_dataRegex, _ivRegex, _saltRegex, keyMetaRegex].map(function (reg) { + var vaultParts = [dataRegex, ivRegex, saltRegex, keyMetaRegex].map(function (reg) { return keyringControllerStateFragment.match(reg); }).map(function (match) { return match[1]; @@ -102,11 +104,11 @@ function extractVaultFromFile(data) { if (_matches3 && _matches3.length) { try { var _keyringControllerStateFragment = _matches3[1]; - var _dataRegex2 = /\\"data\\":\\"([\+\/-9A-Za-z]*=*)/; - var _ivRegex2 = /,\\"iv\\":\\"([\+\/-9A-Za-z]{10,40}=*)/; - var _saltRegex2 = /,\\"salt\\":\\"([A-Za-z0-9+\/]{10,100}=*)\\"/; + var _dataRegex = /\\"data\\":\\"([\+\/-9A-Za-z]*=*)/; + var _ivRegex = /,\\"iv\\":\\"([\+\/-9A-Za-z]{10,40}=*)/; + var _saltRegex = /,\\"salt\\":\\"([A-Za-z0-9+\/]{10,100}=*)\\"/; var _keyMetaRegex = /,\\"keyMetadata\\":(.*}})/; - var _vaultParts = [_dataRegex2, _ivRegex2, _saltRegex2, _keyMetaRegex].map(function (reg) { + var _vaultParts = [_dataRegex, _ivRegex, _saltRegex, _keyMetaRegex].map(function (reg) { return _keyringControllerStateFragment.match(reg); }).map(function (match) { return match[1]; @@ -122,43 +124,69 @@ function extractVaultFromFile(data) { } } } - - // attempt 6: chromium 000005.ldb on windows - var matchRegex = /Keyring[0-9](?:[\0-\|~-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*(\{(?:[\0-z\|~-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*\\"\})/g; - var captureRegex = /Keyring[0-9](?:[\0-\|~-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*(\{(?:[\0-z\|~-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*\\"\})/; - var ivRegex = /\\"iv(?:[\0-\t\x0B\f\x0E-\u2027\u202A-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){1,4}(?:[\0-\*,-\.:-@\[-`\{-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){1,10}([\+\/-9A-Za-z]{10,40}=*)/; - var dataRegex = /\\"(?:[\0-!#-\+\x2D-9;-hj-rt-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*\\":\\"([\+\/-9A-Za-z]*=*)/; - var saltRegex = /,\\"salt(?:[\0-\t\x0B\f\x0E-\u2027\u202A-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){1,4}(?:[\0-\*,-\.:-@\[-`\{-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){1,10}([\+\/-9A-Za-z]{10,100}=*)/; - var vaults = dedupe((_data$match = data.match(matchRegex)) === null || _data$match === void 0 ? void 0 : _data$match.map(function (m) { - return m.match(captureRegex)[1]; - }).map(function (s) { - return [dataRegex, ivRegex, saltRegex].map(function (r) { - return s.match(r); - }); - }).filter(function (_ref3) { - var _ref4 = _slicedToArray(_ref3, 3), - d = _ref4[0], - i = _ref4[1], - s = _ref4[2]; - return d && d.length > 1 && i && i.length > 1 && s && s.length > 1; - }).map(function (_ref5) { - var _ref6 = _slicedToArray(_ref5, 3), - d = _ref6[0], - i = _ref6[1], - s = _ref6[2]; - return { - data: d[1], - iv: i[1], - salt: s[1] - }; - })); - if (!vaults.length) { - return null; + { + var _data$match; + // attempt 6: chromium 000005.ldb on windows + var matchRegex = /Keyring[0-9](?:[\0-\|~-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*(\{(?:[\0-z\|~-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*\\"\})/g; + var captureRegex = /Keyring[0-9](?:[\0-\|~-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*(\{(?:[\0-z\|~-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*\\"\})/; + var _ivRegex2 = /\\"iv(?:[\0-\t\x0B\f\x0E-\u2027\u202A-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){1,4}(?:[\0-\*,-\.:-@\[-`\{-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){1,10}([\+\/-9A-Za-z]{10,40}=*)/; + var _dataRegex2 = /\\"(?:[\0-!#-\+\x2D-9;-hj-rt-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*\\":\\"([\+\/-9A-Za-z]*=*)/; + var _saltRegex2 = /,\\"salt(?:[\0-\t\x0B\f\x0E-\u2027\u202A-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){1,4}(?:[\0-\*,-\.:-@\[-`\{-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){1,10}([\+\/-9A-Za-z]{10,100}=*)/; + var vaults = dedupe((_data$match = data.match(matchRegex)) === null || _data$match === void 0 ? void 0 : _data$match.map(function (m) { + return m.match(captureRegex)[1]; + }).map(function (s) { + return [_dataRegex2, _ivRegex2, _saltRegex2].map(function (r) { + return s.match(r); + }); + }).filter(function (_ref3) { + var _ref4 = _slicedToArray(_ref3, 3), + d = _ref4[0], + i = _ref4[1], + s = _ref4[2]; + return d && d.length > 1 && i && i.length > 1 && s && s.length > 1; + }).map(function (_ref5) { + var _ref6 = _slicedToArray(_ref5, 3), + d = _ref6[0], + i = _ref6[1], + s = _ref6[2]; + return { + data: d[1], + iv: i[1], + salt: s[1] + }; + })); + if (vaults.length) { + /* istanbul ignore next */ + if (vaults.length > 1) { + console.log('Found multiple vaults!', vaults); + } + return vaults[0]; + } } - if (vaults.length > 1) { - console.log('Found multiple vaults!', vaults); + { + // attempt 7: log file using split state format, chromium 000004.log on windows-2 + var vaultRegex = /KeyringController[\s\S]*?"vault":"((?:[^"\\]|\\.)*)"/g; + var _vaults = []; + var match; + while ((match = vaultRegex.exec(data)) !== null) { + try { + var vaultString = JSON.parse("\"".concat(match[1], "\"")); + var json = JSON.parse(vaultString); + _vaults.push(json); + } catch (err) { + // Not valid JSON: continue + } + } + var dedupedVaults = dedupe(_vaults); + if (dedupedVaults.length) { + /* istanbul ignore next */ + if (dedupedVaults.length > 1) { + console.log('Found multiple vaults!', dedupedVaults); + } + return dedupedVaults[0]; + } } - return vaults[0]; + return null; } function isVaultValid(vault) { return _typeof(vault) === 'object' && ['data', 'iv', 'salt'].every(function (e) { diff --git a/jest.config.js b/jest.config.js index 0bb81cf..694ee40 100644 --- a/jest.config.js +++ b/jest.config.js @@ -41,10 +41,10 @@ module.exports = { // An object that configures minimum threshold enforcement for coverage results coverageThreshold: { global: { - branches: 95.23, + branches: 95.45, functions: 100, - lines: 98.57, - statements: 98.64, + lines: 98.75, + statements: 98.8, }, }, diff --git a/test/fixtures/chrome-windows-2/000004.log b/test/fixtures/chrome-windows-2/000004.log new file mode 100644 index 0000000..fc22620 Binary files /dev/null and b/test/fixtures/chrome-windows-2/000004.log differ