stencyption
Version:
Military-grade JavaScript encryption with AES-256-GCM, polymorphic obfuscation, and anti-debugging protection. Each file gets unique encryption keys embedded in heavily obfuscated code.
252 lines (213 loc) • 10.1 kB
JavaScript
const crypto = require('crypto');
const JavaScriptObfuscator = require('javascript-obfuscator');
const ENCRYPTION_ALGORITHM = 'aes-256-gcm';
const PBKDF2_ITERATIONS = 10000;
const SALT_LENGTH = 64;
const KEY_LENGTH = 32;
class Encryptor {
static generateSalt() {
return crypto.randomBytes(SALT_LENGTH);
}
static deriveKey(password, salt) {
return crypto.pbkdf2Sync(
password,
salt,
PBKDF2_ITERATIONS,
KEY_LENGTH,
'sha512'
);
}
static generateRandomVarName(length = 12) {
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
let name = chars[Math.floor(Math.random() * chars.length)];
for (let i = 0; i < length - 1; i++) {
const idx = Math.floor(Math.random() * chars.length);
name += chars[idx];
if (Math.random() > 0.6) {
name += Math.floor(Math.random() * 10);
}
}
return name;
}
static encryptAESGCM(plaintext, key) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(ENCRYPTION_ALGORITHM, key, iv);
let encrypted = cipher.update(plaintext, 'utf8');
encrypted = Buffer.concat([encrypted, cipher.final()]);
const authTag = cipher.getAuthTag();
return {
iv: iv.toString('hex'),
encrypted: encrypted.toString('hex'),
authTag: authTag.toString('hex')
};
}
static multiLayerEncrypt(sourceCode, password) {
const salt1 = this.generateSalt();
const key1 = this.deriveKey(password, salt1);
const layer1 = this.encryptAESGCM(sourceCode, key1);
const combined1 = JSON.stringify(layer1);
const salt2 = this.generateSalt();
const key2 = this.deriveKey(password + salt1.toString('hex'), salt2);
const layer2 = this.encryptAESGCM(combined1, key2);
const combined2 = JSON.stringify(layer2);
const salt3 = this.generateSalt();
const key3 = this.deriveKey(password + salt2.toString('hex'), salt3);
const layer3 = this.encryptAESGCM(combined2, key3);
return {
salt1: salt1.toString('hex'),
salt2: salt2.toString('hex'),
salt3: salt3.toString('hex'),
data: layer3
};
}
static splitArrayIntoChunks(arr, chunkSize) {
const chunks = [];
for (let i = 0; i < arr.length; i += chunkSize) {
chunks.push(arr.slice(i, i + chunkSize));
}
return chunks;
}
static generatePolymorphicVars() {
const base = this.generateRandomVarName(8);
const vars = {};
const prefixes = ['_A', '_B', '_C', '_D', '_E', '_F', '_G', '_H', '_I', '_J', '_K', '_L', '_M', '_N', '_O', '_P', '_Q', '_R', '_S', '_T', '_U', '_V', '_W', '_X', '_Y', '_Z'];
prefixes.forEach((prefix, idx) => {
vars[`var${idx}`] = `${prefix}${base}${this.generateRandomVarName(4)}`;
});
vars.crypto = vars.var0;
vars.pbkdf2 = vars.var1;
vars.decipher = vars.var2;
vars.key1 = vars.var3;
vars.key2 = vars.var4;
vars.key3 = vars.var5;
vars.salt1 = vars.var6;
vars.salt2 = vars.var7;
vars.salt3 = vars.var8;
vars.iv = vars.var9;
vars.encrypted = vars.var10;
vars.authTag = vars.var11;
vars.decrypted = vars.var12;
vars.layer1 = vars.var13;
vars.layer2 = vars.var14;
vars.layer3 = vars.var15;
vars.password = vars.var16;
return vars;
}
static createHardenedWrapper(encryptedLayers, password) {
const vars = this.generatePolymorphicVars();
const salt1Arr = Array.from(Buffer.from(encryptedLayers.salt1, 'hex'));
const salt2Arr = Array.from(Buffer.from(encryptedLayers.salt2, 'hex'));
const salt3Arr = Array.from(Buffer.from(encryptedLayers.salt3, 'hex'));
const ivArr = Array.from(Buffer.from(encryptedLayers.data.iv, 'hex'));
const encArr = Array.from(Buffer.from(encryptedLayers.data.encrypted, 'hex'));
const authArr = Array.from(Buffer.from(encryptedLayers.data.authTag, 'hex'));
const salt1Chunks = this.splitArrayIntoChunks(salt1Arr, 16);
const salt2Chunks = this.splitArrayIntoChunks(salt2Arr, 16);
const salt3Chunks = this.splitArrayIntoChunks(salt3Arr, 16);
const ivChunks = this.splitArrayIntoChunks(ivArr, 4);
const encChunks = this.splitArrayIntoChunks(encArr, 32);
const authChunks = this.splitArrayIntoChunks(authArr, 4);
const passwordChunks = [];
for (let i = 0; i < password.length; i += 2) {
passwordChunks.push(password.substring(i, i + 2));
}
const pwdVars = passwordChunks.map((_, i) => this.generateRandomVarName(10));
const pwdDeclarations = passwordChunks.map((chunk, i) =>
`const ${pwdVars[i]}=String.fromCharCode(${chunk.charCodeAt(0)})${chunk.length > 1 ? `+String.fromCharCode(${chunk.charCodeAt(1)})` : ''};`
).join('');
const pwdRecon = pwdVars.join('+');
const salt1VarChunks = salt1Chunks.map(() => this.generateRandomVarName(8));
const salt2VarChunks = salt2Chunks.map(() => this.generateRandomVarName(8));
const salt3VarChunks = salt3Chunks.map(() => this.generateRandomVarName(8));
const ivVarChunks = ivChunks.map(() => this.generateRandomVarName(8));
const encVarChunks = encChunks.map(() => this.generateRandomVarName(8));
const authVarChunks = authChunks.map(() => this.generateRandomVarName(8));
const runtimeCode = `
!function(){
'use strict';
${pwdDeclarations}
const ${vars.crypto}=require('crypto');
const ${vars.password}=${pwdRecon};
${salt3VarChunks.map((v, i) => `const ${v}=[${salt3Chunks[i].join(',')}];`).join('')}
${salt2VarChunks.map((v, i) => `const ${v}=[${salt2Chunks[i].join(',')}];`).join('')}
${salt1VarChunks.map((v, i) => `const ${v}=[${salt1Chunks[i].join(',')}];`).join('')}
${ivVarChunks.map((v, i) => `const ${v}=[${ivChunks[i].join(',')}];`).join('')}
${encVarChunks.map((v, i) => `const ${v}=[${encChunks[i].join(',')}];`).join('')}
${authVarChunks.map((v, i) => `const ${v}=[${authChunks[i].join(',')}];`).join('')}
const ${vars.salt3}=Buffer.from([${salt3VarChunks.map(v => `...${v}`).join(',')}]);
const ${vars.salt2}=Buffer.from([${salt2VarChunks.map(v => `...${v}`).join(',')}]);
const ${vars.salt1}=Buffer.from([${salt1VarChunks.map(v => `...${v}`).join(',')}]);
const ${vars.iv}=Buffer.from([${ivVarChunks.map(v => `...${v}`).join(',')}]);
const ${vars.encrypted}=Buffer.from([${encVarChunks.map(v => `...${v}`).join(',')}]);
const ${vars.authTag}=Buffer.from([${authVarChunks.map(v => `...${v}`).join(',')}]);
const ${vars.key3}=${vars.crypto}.pbkdf2Sync(${vars.password}+${vars.salt2}.toString('hex'),${vars.salt3},${PBKDF2_ITERATIONS},${KEY_LENGTH},'sha512');
const ${vars.decipher}=${vars.crypto}.createDecipheriv('${ENCRYPTION_ALGORITHM}',${vars.key3},${vars.iv});
${vars.decipher}.setAuthTag(${vars.authTag});
let ${vars.layer3}=${vars.decipher}.update(${vars.encrypted});
${vars.layer3}=Buffer.concat([${vars.layer3},${vars.decipher}.final()]);
const ${vars.layer2}=JSON.parse(${vars.layer3}.toString('utf8'));
const ${vars.key2}=${vars.crypto}.pbkdf2Sync(${vars.password}+${vars.salt1}.toString('hex'),${vars.salt2},${PBKDF2_ITERATIONS},${KEY_LENGTH},'sha512');
const ${vars.decipher}2=${vars.crypto}.createDecipheriv('${ENCRYPTION_ALGORITHM}',${vars.key2},Buffer.from(${vars.layer2}.iv,'hex'));
${vars.decipher}2.setAuthTag(Buffer.from(${vars.layer2}.authTag,'hex'));
let ${vars.layer2}Dec=${vars.decipher}2.update(Buffer.from(${vars.layer2}.encrypted,'hex'));
${vars.layer2}Dec=Buffer.concat([${vars.layer2}Dec,${vars.decipher}2.final()]);
const ${vars.layer1}=JSON.parse(${vars.layer2}Dec.toString('utf8'));
const ${vars.key1}=${vars.crypto}.pbkdf2Sync(${vars.password},${vars.salt1},${PBKDF2_ITERATIONS},${KEY_LENGTH},'sha512');
const ${vars.decipher}3=${vars.crypto}.createDecipheriv('${ENCRYPTION_ALGORITHM}',${vars.key1},Buffer.from(${vars.layer1}.iv,'hex'));
${vars.decipher}3.setAuthTag(Buffer.from(${vars.layer1}.authTag,'hex'));
let ${vars.decrypted}=${vars.decipher}3.update(Buffer.from(${vars.layer1}.encrypted,'hex'));
${vars.decrypted}=Buffer.concat([${vars.decrypted},${vars.decipher}3.final()]);
const ${vars.layer3}Code=${vars.decrypted}.toString('utf8');
if(typeof module!=='undefined'&&module._compile){
module._compile(${vars.layer3}Code,__filename||'encrypted.js');
}else{
const tmpPath=require('path').join(require('os').tmpdir(),'stenc_'+Date.now()+'_'+Math.random().toString(36)+'.js');
require('fs').writeFileSync(tmpPath,${vars.layer3}Code);
try{require(tmpPath);}finally{try{require('fs').unlinkSync(tmpPath);}catch(e){}}
}
}();`;
const obfuscationOptions = {
compact: true,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 0.5,
deadCodeInjection: true,
deadCodeInjectionThreshold: 0.3,
debugProtection: false,
debugProtectionInterval: 0,
disableConsoleOutput: false,
identifierNamesGenerator: 'hexadecimal',
log: false,
numbersToExpressions: false,
renameGlobals: false,
selfDefending: false,
simplify: true,
splitStrings: true,
splitStringsChunkLength: 5,
stringArray: true,
stringArrayCallsTransform: false,
stringArrayCallsTransformThreshold: 0.5,
stringArrayEncoding: ['base64'],
stringArrayIndexShift: true,
stringArrayRotate: true,
stringArrayShuffle: true,
stringArrayWrappersCount: 1,
stringArrayWrappersChainedCalls: false,
stringArrayWrappersParametersMaxCount: 2,
stringArrayWrappersType: 'function',
stringArrayThreshold: 0.8,
transformObjectKeys: false,
unicodeEscapeSequence: false
};
const obfuscated = JavaScriptObfuscator.obfuscate(runtimeCode, obfuscationOptions);
return obfuscated.getObfuscatedCode();
}
static encrypt(sourceCode, password) {
if (!password || password.length < 8) {
throw new Error('Password must be at least 8 characters long');
}
const encryptedLayers = this.multiLayerEncrypt(sourceCode, password);
const wrapper = this.createHardenedWrapper(encryptedLayers, password);
return wrapper;
}
}
module.exports = { Encryptor };