fix: broken key parser
Browse files- providers/src/bsmart/crypto.js +47 -10
providers/src/bsmart/crypto.js
CHANGED
|
@@ -2,6 +2,34 @@ import fetch from 'node-fetch';
|
|
| 2 |
import msgpack from 'msgpack-lite';
|
| 3 |
import aesjs from 'aes-js';
|
| 4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
/**
|
| 6 |
* Fetches the encryption key from the bSmart website
|
| 7 |
* @returns {Promise<Buffer>} The encryption key
|
|
@@ -9,15 +37,24 @@ import aesjs from 'aes-js';
|
|
| 9 |
export async function fetchEncryptionKey() {
|
| 10 |
const page = await fetch('https://my.bsmart.it/');
|
| 11 |
const text = await page.text();
|
| 12 |
-
|
| 13 |
-
const
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
}
|
| 22 |
|
| 23 |
/**
|
|
@@ -52,4 +89,4 @@ export async function decryptFile(file, key) {
|
|
| 52 |
reject({ e, file });
|
| 53 |
}
|
| 54 |
});
|
| 55 |
-
}
|
|
|
|
| 2 |
import msgpack from 'msgpack-lite';
|
| 3 |
import aesjs from 'aes-js';
|
| 4 |
|
| 5 |
+
function extractBase64Key(snippet) {
|
| 6 |
+
const keyMatch = snippet.match(/['"]((?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?)['"]/);
|
| 7 |
+
return keyMatch ? Buffer.from(keyMatch[1], 'base64') : null;
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
function extractConstructorKey(scriptText) {
|
| 11 |
+
const constructorMatch = scriptText.match(
|
| 12 |
+
/var\s+([A-Za-z_$][\w$]*)=String\.fromCharCode\(([^)]*)\),([A-Za-z_$][\w$]*)=["']constructor["'];\3\[\3\]\[\3\]\((.*?)\)\(\)/s
|
| 13 |
+
);
|
| 14 |
+
if (!constructorMatch) {
|
| 15 |
+
return null;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
const [, charVar, charCodes, , expression] = constructorMatch;
|
| 19 |
+
const sourceCharacters = charCodes
|
| 20 |
+
.split(',')
|
| 21 |
+
.map(e => parseInt(e.trim(), 10))
|
| 22 |
+
.map(e => String.fromCharCode(e));
|
| 23 |
+
const indexPattern = new RegExp(`${charVar}\\[(\\d+)\\]`, 'g');
|
| 24 |
+
const indexes = [...expression.matchAll(indexPattern)].map(match => parseInt(match[1], 10));
|
| 25 |
+
if (indexes.length === 0) {
|
| 26 |
+
return null;
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
const snippet = indexes.map(index => sourceCharacters[index]).join('');
|
| 30 |
+
return extractBase64Key(snippet);
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
/**
|
| 34 |
* Fetches the encryption key from the bSmart website
|
| 35 |
* @returns {Promise<Buffer>} The encryption key
|
|
|
|
| 37 |
export async function fetchEncryptionKey() {
|
| 38 |
const page = await fetch('https://my.bsmart.it/');
|
| 39 |
const text = await page.text();
|
| 40 |
+
|
| 41 |
+
const scripts = [...text.matchAll(/<script[^>]+src="([^"]+\.js[^"]*)"[^>]*>/g)]
|
| 42 |
+
.map(match => match[1])
|
| 43 |
+
.filter(src => src.startsWith('/'));
|
| 44 |
+
|
| 45 |
+
if (scripts.length === 0) {
|
| 46 |
+
throw new Error('No JavaScript bundles found on https://my.bsmart.it/');
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
for (const script of scripts) {
|
| 50 |
+
const scriptText = await fetch('https://my.bsmart.it' + script).then(res => res.text());
|
| 51 |
+
const key = extractConstructorKey(scriptText);
|
| 52 |
+
if (key) {
|
| 53 |
+
return key;
|
| 54 |
+
}
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
throw new Error('Unable to extract the bSmart encryption key from the current website bundles.');
|
| 58 |
}
|
| 59 |
|
| 60 |
/**
|
|
|
|
| 89 |
reject({ e, file });
|
| 90 |
}
|
| 91 |
});
|
| 92 |
+
}
|