UNPKG

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
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 };