akamai-v3-sensor-data-helper
Version:
A Node.js module for decrypting Akamai v3 sensor data. Check out our web app at https://akamai-v3-tools.vercel.app and read the detailed guide at https://medium.com/@glizzykingdreko/akamai-v3-sensor-data-deep-dive-into-encryption-decryption-and-bypass-too
151 lines (138 loc) • 5.64 kB
JavaScript
const {extractFileHash} = require('./extract_hash/index.js');
/**
* The set of characters allowed in the encrypted data
* This matches Akamai's v3 character set for sensor data
*/
const ALLOWED_CHARS = ' !#$%&()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~';
/**
* Performs element swapping operation used in Akamai v3 encryption
* @param {string} inputString - The string containing elements to be swapped
* @param {number} seedValue - The seed value used to generate swap pairs
* @returns {string} The string with elements swapped according to the encryption pattern
* @private
*/
function elementSwapping(inputString, seedValue) {
var parts = inputString;
var currentSeed = seedValue;
var swapPairs = [];
var delimiter = ':';
var partsArray = parts.split(delimiter);
// Generate the same swap pairs used in encryption
for (var i = 0; i < partsArray.length; i++) {
var firstIndex = ((currentSeed >> 8) & 65535) % partsArray.length;
currentSeed *= 65793;
currentSeed &= 4294967295;
currentSeed += 4282663;
currentSeed &= 8388607;
var secondIndex = ((currentSeed >> 8) & 65535) % partsArray.length;
currentSeed *= 65793;
currentSeed &= 4294967295;
currentSeed += 4282663;
currentSeed &= 8388607;
swapPairs.push([firstIndex, secondIndex]);
}
// Apply the swap operations in order
for (var i = 0; i < swapPairs.length; i++) {
var firstIndex = swapPairs[i][0];
var secondIndex = swapPairs[i][1];
// Swap the elements
var temp = partsArray[firstIndex];
partsArray[firstIndex] = partsArray[secondIndex];
partsArray[secondIndex] = temp;
}
return partsArray.join(delimiter);
}
/**
* Performs character substitution operation used in Akamai v3 encryption
* @param {string} inputString - The string containing characters to be substituted
* @param {number} seedValue - The seed value used for character substitution
* @returns {string} The string with characters substituted according to the encryption pattern
* @private
*/
function characterSubstitution(inputString, seedValue) {
var result = '';
// Process each character in order, using the same seedValue updates
for (var i = 0; i < inputString.length; i++) {
var currentChar = inputString.charAt(i);
// Compute the pseudo-random value
var randomValue = (seedValue >> 8) & 65535;
// Update seedValue exactly as in encryption
seedValue *= 65793;
seedValue &= 4294967295;
seedValue += 4282663;
seedValue &= 8388607;
// Check if the current character is from the allowed set
var charPosition = ALLOWED_CHARS.indexOf(currentChar);
if (charPosition >= 0) {
// Get the offset to add
var offset = randomValue % ALLOWED_CHARS.length;
// Add the offset and wrap around modulo ALLOWED_CHARS.length
var newIndex = (charPosition + offset) % ALLOWED_CHARS.length;
// The substituted character (from the allowed set) is then:
currentChar = ALLOWED_CHARS.charAt(newIndex);
}
result += currentChar;
}
return result;
}
/**
* Encrypts data in Akamai v3 format by applying the encryption process
* @param {Object|string} payload - The data to encrypt
* @param {number} cookieHash - The cookie hash from Akamai
* @param {string} [fileContent] - The content of the input.js file used for hash extraction
* @param {number} [fileHash] - Optional pre-extracted file hash to avoid re-extraction
* @returns {Object} An object containing the encryption result
* @property {boolean} success - Whether the encryption was successful
* @property {Object} [data] - The encrypted data object (if success is true)
* @property {string} [encryptedData] - The raw encrypted string (if success is true)
* @property {number[]} [seeds] - The seeds used in encryption [cookieHash, fileHash]
* @property {string} [message] - Error message (if success is false)
*/
function encryptSensorData(
payload,
cookieHash,
fileContent = null,
fileHash = null
) {
if (!fileContent && !fileHash) {
return {
success: false,
message: 'File content or file hash is required'
};
}
if (!fileHash) {
try {
fileHash = extractFileHash(fileContent);
} catch (error) {
return {
success: false,
message: 'Unable to load file hash'
};
}
}
try {
// Convert payload to string if it's an object
const payloadString = typeof payload === 'object' ? JSON.stringify(payload) : payload;
// Apply encryption in reverse order of decryption
let encryptedData = elementSwapping(payloadString, fileHash);
encryptedData = characterSubstitution(encryptedData, cookieHash);
// Construct the full sensor_data string
const sensorData = `3;0;1;0;${cookieHash};wS5KmeE4vP5vBcKRIM2pPQlq4qZivf0B53dgMqmUH4E=;141659;${encryptedData}`;
return {
success: true,
encryptedData,
seeds: [cookieHash, fileHash],
data: {
sensor_data: '{"sensor_data":'.concat(JSON.stringify(sensorData), '}')
}
};
} catch (error) {
return {
success: false,
message: `Encryption failed: ${error.message}`
};
}
}
module.exports = {
encryptSensorData
};