Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ node_modules/
!.yarn/releases
!.yarn/sdks
!.yarn/versions
coverage/
116 changes: 95 additions & 21 deletions app/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,27 +88,103 @@ function extractVaultFromFile (data) {
}
}
}
// attempt 5: 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 5: 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 > 1) {
console.log('Found multiple vaults!', vaults)
}
if (vaults.length > 0)
return vaults[0]
}
{
// attempt 6: chrome 158063.ldb on windows - Corrupted LDB file without Keyring but with vault data
// Looking for the following pattern: :\"data_b64\",\"iv\":\"iv_b64\",\"keyMetadata\":{\"algorithm\":\"PBKDF2\",\"params\":{\"iterations\":10000}},\"salt\":\"salt_b64\"}"}
const regex = /":\\"([^"]+)\",\\"iv\\":\\"([^"]+)\",\\"keyMetadata\\":(\{[\s\S]*?\}),\\"salt\\":\\"([^"]+)\\"/;
const match = data.match(regex);

if (match) {
// match[1] => data
// match[2] => iv
// match[3] => keyMetadata
// match[4] => salt

const dataBase64 = match[1];
const iv = match[2];
const keyMetadataRaw = match[3];
const salt = match[4];

const cleanedKeyMetadata = keyMetadataRaw.replace(/\\/g, '');
let keyMetadata;
try {
keyMetadata = JSON.parse(cleanedKeyMetadata);
} catch (err) {
console.error('Error converting keyMetadata:', err);
return null;
}

const vault = {
data: dataBase64,
iv,
keyMetadata,
salt,
};

return vault
}
}
if (vaults.length > 1) {
console.log('Found multiple vaults!', vaults)
{
// attempt 7: chrome 000024.ldb on windows - Corrupted LDB file with corrupted PBKDF2 and vault data
// Looking for the following pattern: :\"BASE64DATA",\",\"iv\":\"BASE64iv\",CORRUPTED\":{\"CORRUPTED\",\"CORRUPTED...}},\"salt\":"BASE64salt"}
const regex = /":\\"([^"]+)\",\\"iv\\":\\"([^"]+)\",.*?\\"salt.*?([^"]+)\\"}/;
const match = data.match(regex);

if (match) {
// match[1] => data (may contain corrupted characters)
// match[2] => iv (may contain corrupted characters)
// match[3] => salt (may contain corrupted characters)

const clean = (input) => {
if (!input) return '';

// Remove escape characters such as \"
let cleaned = input.replace(/\\/g, '');

// Find the last valid Base64 sequence in the string (in order to avoid parsing corrupted data)
const validMatch = cleaned.match(/[A-Za-z0-9+/=]+$/);

// If a valid sequence is found, return it, otherwise return an empty string
return validMatch ? validMatch[0] : '';
};

const data = clean(match[1]);
const iv = clean(match[2]);
const salt = clean(match[3]);

const vault = {
data: data,
iv,
keyMetadata: { algorithm: 'PBKDF2', params: { iterations: 600000 } }, // Hardcoded as we cannot parse the corrupted keyMetadata, iterations are set to 600000 but could be any value.
salt,
};

return vault;
}
return null;
}
return vaults[0]
}


Expand Down Expand Up @@ -148,5 +224,3 @@ module.exports = {
extractVaultFromFile,
isVaultValid,
}


10 changes: 10 additions & 0 deletions app/lib.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ const FIXTURES = [
mnemonic: 'dolphin peanut amateur party differ tomorrow clean coconut when spatial hard trigger',
passphrase: 't0b1m4ru',
},
{
path: 'chrome-windows-2/000024.ldb',
mnemonic: 'custom route lumber fringe gasp copper need monster lesson happy swarm rib',
passphrase: '4lv4r0dh5_dummy!',
},
{
path: 'chrome-windows-3/158063.ldb',
mnemonic: 'custom route lumber fringe gasp copper need monster lesson happy swarm rib',
passphrase: '4lv4r0dh5_dummy!',
},
{
path: 'chromium-108.0_5359.98_4.10.24.2/000003.log',
mnemonic: 'harvest afraid useful nose electric swift various man boil diagram confirm ahead',
Expand Down
2 changes: 1 addition & 1 deletion app/root.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ AppRoot.prototype.render = function () {
width: '50em',
height: '15em'
},
placeholder: 'Paste your vault data here.',
placeholder: 'Paste your vault data here...\n\n{"data":"...","iv":"...","keyMetadata":{"algorithm":"PBKDF2","params":{"iterations":600000}},"salt":"..."}',
onChange: (event) => {
try {
const vaultData = JSON.parse(event.target.value)
Expand Down
152 changes: 112 additions & 40 deletions bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ function decodeMnemonic(mnemonic) {
}
}
function extractVaultFromFile(data) {
var _data$match;
var vaultBody;
try {
// attempt 1: raw json
Expand Down Expand Up @@ -75,11 +74,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];
Expand All @@ -95,42 +94,115 @@ function extractVaultFromFile(data) {
}
}
}
// attempt 5: 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 5: 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 > 1) {
console.log('Found multiple vaults!', vaults);
}
if (vaults.length > 0) return vaults[0];
}
{
// attempt 6: chrome 158063.ldb on windows - Corrupted LDB file without Keyring but with vault data
// Looking for the following pattern: :\"data_b64\",\"iv\":\"iv_b64\",\"keyMetadata\":{\"algorithm\":\"PBKDF2\",\"params\":{\"iterations\":10000}},\"salt\":\"salt_b64\"}"}
var regex = /":\\"([^"]+)\",\\"iv\\":\\"([^"]+)\",\\"keyMetadata\\":(\{[\s\S]*?\}),\\"salt\\":\\"([^"]+)\\"/;
var match = data.match(regex);
if (match) {
// match[1] => data
// match[2] => iv
// match[3] => keyMetadata
// match[4] => salt

var dataBase64 = match[1];
var iv = match[2];
var keyMetadataRaw = match[3];
var salt = match[4];
var cleanedKeyMetadata = keyMetadataRaw.replace(/\\/g, '');
var keyMetadata;
try {
keyMetadata = JSON.parse(cleanedKeyMetadata);
} catch (err) {
console.error('Error converting keyMetadata:', err);
return null;
}
var _vault = {
data: dataBase64,
iv: iv,
keyMetadata: keyMetadata,
salt: salt
};
return _vault;
}
}
if (vaults.length > 1) {
console.log('Found multiple vaults!', vaults);
{
// attempt 7: chrome 000024.ldb on windows - Corrupted LDB file with corrupted PBKDF2 and vault data
// Looking for the following pattern: :\"BASE64DATA",\",\"iv\":\"BASE64iv\",CORRUPTED\":{\"CORRUPTED\",\"CORRUPTED...}},\"salt\":"BASE64salt"}
var _regex = /":\\"([^"]+)\",\\"iv\\":\\"([^"]+)\",.*?\\"salt.*?([^"]+)\\"}/;
var _match = data.match(_regex);
if (_match) {
// match[1] => data (may contain corrupted characters)
// match[2] => iv (may contain corrupted characters)
// match[3] => salt (may contain corrupted characters)

var clean = function clean(input) {
if (!input) return '';

// Remove escape characters such as \"
var cleaned = input.replace(/\\/g, '');

// Find the last valid Base64 sequence in the string (in order to avoid parsing corrupted data)
var validMatch = cleaned.match(/[A-Za-z0-9+/=]+$/);

// If a valid sequence is found, return it, otherwise return an empty string
return validMatch ? validMatch[0] : '';
};
var _data = clean(_match[1]);
var _iv = clean(_match[2]);
var _salt = clean(_match[3]);
var _vault2 = {
data: _data,
iv: _iv,
keyMetadata: {
algorithm: 'PBKDF2',
params: {
iterations: 600000
}
},
// Hardcoded as we cannot parse the corrupted keyMetadata, iterations are set to 600000 but could be any value.
salt: _salt
};
return _vault2;
}
return null;
}
return vaults[0];
}
function isVaultValid(vault) {
return _typeof(vault) === 'object' && ['data', 'iv', 'salt'].every(function (e) {
Expand Down Expand Up @@ -329,7 +401,7 @@ AppRoot.prototype.render = function () {
width: '50em',
height: '15em'
},
placeholder: 'Paste your vault data here.',
placeholder: 'Paste your vault data here...\n\n{"data":"...","iv":"...","keyMetadata":{"algorithm":"PBKDF2","params":{"iterations":600000}},"salt":"..."}',
onChange: function onChange(event) {
try {
var vaultData = JSON.parse(event.target.value);
Expand Down
6 changes: 3 additions & 3 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ module.exports = {
// An object that configures minimum threshold enforcement for coverage results
coverageThreshold: {
global: {
branches: 94.73,
branches: 91.3,
functions: 100,
lines: 98.27,
statements: 98.38,
lines: 96.47,
statements: 95.55,
},
},

Expand Down
Binary file added test/fixtures/chrome-windows-2/000024.ldb
Binary file not shown.
Binary file added test/fixtures/chrome-windows-3/158063.ldb
Binary file not shown.