mojic 1.1.0 → 1.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin/mojic.js +23 -74
- package/lib/CipherEngine.js +14 -24
- package/package.json +6 -2
package/README.md
CHANGED
package/bin/mojic.js
CHANGED
|
@@ -12,7 +12,7 @@ import { CipherEngine } from '../lib/CipherEngine.js';
|
|
|
12
12
|
program
|
|
13
13
|
.name('mojic')
|
|
14
14
|
.description('Obfuscate C source code into emojis')
|
|
15
|
-
.version('1.
|
|
15
|
+
.version('1.2.4')
|
|
16
16
|
.addHelpCommand('help [command]', 'Display help for command')
|
|
17
17
|
.showHelpAfterError();
|
|
18
18
|
|
|
@@ -78,7 +78,6 @@ const createHeaderSkipper = () => {
|
|
|
78
78
|
});
|
|
79
79
|
};
|
|
80
80
|
|
|
81
|
-
// --- FLATTENING (Minifier) ---
|
|
82
81
|
const createMinifier = () => {
|
|
83
82
|
return new Transform({
|
|
84
83
|
transform(chunk, encoding, cb) {
|
|
@@ -91,11 +90,8 @@ const createMinifier = () => {
|
|
|
91
90
|
});
|
|
92
91
|
};
|
|
93
92
|
|
|
94
|
-
// --- Recursive Logic ---
|
|
95
|
-
|
|
96
93
|
async function traverseDirectory(currentPath, extension, callback) {
|
|
97
94
|
const entries = fs.readdirSync(currentPath, { withFileTypes: true });
|
|
98
|
-
|
|
99
95
|
for (const entry of entries) {
|
|
100
96
|
const fullPath = path.join(currentPath, entry.name);
|
|
101
97
|
if (entry.isDirectory()) {
|
|
@@ -123,7 +119,7 @@ program
|
|
|
123
119
|
throw new Error(`'${targetPath}' is a directory. Use -r to process recursively.`);
|
|
124
120
|
}
|
|
125
121
|
|
|
126
|
-
console.log(chalk.blue('Initiating Mojic Encryption v1.
|
|
122
|
+
console.log(chalk.blue('Initiating Mojic Encryption v1.2...'));
|
|
127
123
|
if (options.flat) console.log(chalk.yellow(' -> Structural Flattening Enabled'));
|
|
128
124
|
|
|
129
125
|
const password = await promptPassword('Create password for file(s):');
|
|
@@ -142,10 +138,7 @@ program
|
|
|
142
138
|
|
|
143
139
|
await new Promise((resolve, reject) => {
|
|
144
140
|
let pipeline = readStream;
|
|
145
|
-
|
|
146
|
-
if (options.flat) {
|
|
147
|
-
pipeline = pipeline.pipe(createMinifier());
|
|
148
|
-
}
|
|
141
|
+
if (options.flat) pipeline = pipeline.pipe(createMinifier());
|
|
149
142
|
|
|
150
143
|
pipeline
|
|
151
144
|
.pipe(engine.getEncryptStream())
|
|
@@ -193,10 +186,7 @@ program
|
|
|
193
186
|
|
|
194
187
|
const headerStr = await getStreamHeader(filePath);
|
|
195
188
|
const metadata = CipherEngine.decodeHeader(headerStr);
|
|
196
|
-
|
|
197
189
|
const engine = new CipherEngine(password);
|
|
198
|
-
|
|
199
|
-
// Pass the Auth Check Hex (if available in new format)
|
|
200
190
|
await engine.init(metadata.saltHex, metadata.authCheckHex);
|
|
201
191
|
|
|
202
192
|
const readStream = fs.createReadStream(filePath);
|
|
@@ -204,8 +194,9 @@ program
|
|
|
204
194
|
|
|
205
195
|
await new Promise((resolve, reject) => {
|
|
206
196
|
const decryptStream = engine.getDecryptStream();
|
|
207
|
-
|
|
208
197
|
decryptStream.on('error', (err) => {
|
|
198
|
+
readStream.destroy(); // Stop reading
|
|
199
|
+
writeStream.destroy(); // Stop writing
|
|
209
200
|
reject(err);
|
|
210
201
|
});
|
|
211
202
|
|
|
@@ -216,19 +207,12 @@ program
|
|
|
216
207
|
.on('finish', resolve)
|
|
217
208
|
.on('error', reject);
|
|
218
209
|
});
|
|
219
|
-
|
|
220
|
-
return true; // Success
|
|
210
|
+
return true;
|
|
221
211
|
} catch (e) {
|
|
222
212
|
const outputName = filePath.replace(/\.mojic$/, '') + '.restored.c';
|
|
213
|
+
// Wait for stream handles to release
|
|
214
|
+
setTimeout(() => { if (fs.existsSync(outputName)) try { fs.unlinkSync(outputName); } catch(ign){} }, 100);
|
|
223
215
|
|
|
224
|
-
// Cleanup output file on error
|
|
225
|
-
setTimeout(() => {
|
|
226
|
-
if (fs.existsSync(outputName)) {
|
|
227
|
-
try { fs.unlinkSync(outputName); } catch(ign){}
|
|
228
|
-
}
|
|
229
|
-
}, 100);
|
|
230
|
-
|
|
231
|
-
// Specific Error Messaging (No Emojis)
|
|
232
216
|
if (e.message === "WRONG_PASSWORD") {
|
|
233
217
|
console.log(chalk.red(` Error: Incorrect Password`));
|
|
234
218
|
} else if (e.message === "FILE_TAMPERED") {
|
|
@@ -236,8 +220,7 @@ program
|
|
|
236
220
|
} else {
|
|
237
221
|
console.log(chalk.red(` Error: ${e.message}`));
|
|
238
222
|
}
|
|
239
|
-
|
|
240
|
-
return false; // Failure
|
|
223
|
+
return false;
|
|
241
224
|
}
|
|
242
225
|
};
|
|
243
226
|
|
|
@@ -247,9 +230,7 @@ program
|
|
|
247
230
|
console.log(chalk.green('Batch decryption complete.'));
|
|
248
231
|
} else {
|
|
249
232
|
const success = await processFile(targetPath);
|
|
250
|
-
if (success)
|
|
251
|
-
console.log(chalk.green(`Restored.`));
|
|
252
|
-
}
|
|
233
|
+
if (success) console.log(chalk.green(`Restored.`));
|
|
253
234
|
}
|
|
254
235
|
|
|
255
236
|
} catch (err) {
|
|
@@ -258,7 +239,6 @@ program
|
|
|
258
239
|
});
|
|
259
240
|
|
|
260
241
|
// --- SRT ---
|
|
261
|
-
|
|
262
242
|
const rotatePassword = async (file) => {
|
|
263
243
|
try {
|
|
264
244
|
if (!fs.existsSync(file)) throw new Error('File not found');
|
|
@@ -283,16 +263,8 @@ const rotatePassword = async (file) => {
|
|
|
283
263
|
|
|
284
264
|
await new Promise((resolve, reject) => {
|
|
285
265
|
const decryptStream = oldEngine.getDecryptStream();
|
|
286
|
-
|
|
287
|
-
decryptStream.on('
|
|
288
|
-
|
|
289
|
-
readStream
|
|
290
|
-
.pipe(createHeaderSkipper())
|
|
291
|
-
.pipe(decryptStream)
|
|
292
|
-
.pipe(newEngine.getEncryptStream())
|
|
293
|
-
.pipe(writeStream)
|
|
294
|
-
.on('finish', resolve)
|
|
295
|
-
.on('error', reject);
|
|
266
|
+
decryptStream.on('error', reject);
|
|
267
|
+
readStream.pipe(createHeaderSkipper()).pipe(decryptStream).pipe(newEngine.getEncryptStream()).pipe(writeStream).on('finish', resolve).on('error', reject);
|
|
296
268
|
});
|
|
297
269
|
|
|
298
270
|
fs.renameSync(tempFile, file);
|
|
@@ -300,11 +272,8 @@ const rotatePassword = async (file) => {
|
|
|
300
272
|
|
|
301
273
|
} catch (err) {
|
|
302
274
|
if (fs.existsSync(file + '.tmp')) fs.unlinkSync(file + '.tmp');
|
|
303
|
-
if (err.message === "WRONG_PASSWORD")
|
|
304
|
-
|
|
305
|
-
} else {
|
|
306
|
-
console.error(chalk.red('Error:'), err.message);
|
|
307
|
-
}
|
|
275
|
+
if (err.message === "WRONG_PASSWORD") console.error(chalk.red('Error: Incorrect Current Password'));
|
|
276
|
+
else console.error(chalk.red('Error:'), err.message);
|
|
308
277
|
}
|
|
309
278
|
};
|
|
310
279
|
|
|
@@ -331,15 +300,8 @@ const reEncrypt = async (file) => {
|
|
|
331
300
|
|
|
332
301
|
await new Promise((resolve, reject) => {
|
|
333
302
|
const decryptStream = oldEngine.getDecryptStream();
|
|
334
|
-
decryptStream.on('error',
|
|
335
|
-
|
|
336
|
-
readStream
|
|
337
|
-
.pipe(createHeaderSkipper())
|
|
338
|
-
.pipe(decryptStream)
|
|
339
|
-
.pipe(newEngine.getEncryptStream())
|
|
340
|
-
.pipe(writeStream)
|
|
341
|
-
.on('finish', resolve)
|
|
342
|
-
.on('error', reject);
|
|
303
|
+
decryptStream.on('error', reject);
|
|
304
|
+
readStream.pipe(createHeaderSkipper()).pipe(decryptStream).pipe(newEngine.getEncryptStream()).pipe(writeStream).on('finish', resolve).on('error', reject);
|
|
343
305
|
});
|
|
344
306
|
|
|
345
307
|
fs.renameSync(tempFile, file);
|
|
@@ -347,28 +309,15 @@ const reEncrypt = async (file) => {
|
|
|
347
309
|
|
|
348
310
|
} catch (err) {
|
|
349
311
|
if (fs.existsSync(file + '.tmp')) fs.unlinkSync(file + '.tmp');
|
|
350
|
-
if (err.message === "WRONG_PASSWORD")
|
|
351
|
-
|
|
352
|
-
} else {
|
|
353
|
-
console.error(chalk.red('Error:'), err.message);
|
|
354
|
-
}
|
|
312
|
+
if (err.message === "WRONG_PASSWORD") console.error(chalk.red('Error: Incorrect Password'));
|
|
313
|
+
else console.error(chalk.red('Error:'), err.message);
|
|
355
314
|
}
|
|
356
315
|
};
|
|
357
316
|
|
|
358
|
-
program
|
|
359
|
-
.
|
|
360
|
-
.
|
|
361
|
-
.
|
|
362
|
-
|
|
363
|
-
.action(async (options) => {
|
|
364
|
-
if (options.pass) {
|
|
365
|
-
await rotatePassword(options.pass);
|
|
366
|
-
} else if (options.re) {
|
|
367
|
-
await reEncrypt(options.re);
|
|
368
|
-
} else {
|
|
369
|
-
console.log(chalk.yellow('Please specify an option: --pass <file> or --re <file>'));
|
|
370
|
-
program.commands.find(c => c.name() === 'srt').help();
|
|
371
|
-
}
|
|
372
|
-
});
|
|
317
|
+
program.command('srt').description('Security and Rotation Tools').option('--pass <file>', 'Update password').option('--re <file>', 'Re-encrypt').action(async (options) => {
|
|
318
|
+
if (options.pass) await rotatePassword(options.pass);
|
|
319
|
+
else if (options.re) await reEncrypt(options.re);
|
|
320
|
+
else { console.log(chalk.yellow('Please specify an option: --pass <file> or --re <file>')); program.commands.find(c => c.name() === 'srt').help(); }
|
|
321
|
+
});
|
|
373
322
|
|
|
374
323
|
program.parse(process.argv);
|
package/lib/CipherEngine.js
CHANGED
|
@@ -3,15 +3,11 @@ import { Transform } from 'stream';
|
|
|
3
3
|
import { StringDecoder } from 'string_decoder';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* MOJIC v1.
|
|
6
|
+
* MOJIC v1.2.4 CIPHER ENGINE
|
|
7
7
|
* "Operation Polymorphic Chaos"
|
|
8
|
-
* *
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
11
|
-
* - Polymorphic Keyword Encryption (Rolling Cipher)
|
|
12
|
-
* - HMAC-SHA256 Integrity Footer
|
|
13
|
-
* - Line Wrapping (New: Prevents lag in editors)
|
|
14
|
-
* - Instant Password Verification (New: Header Auth Check)
|
|
8
|
+
* * Fixes:
|
|
9
|
+
* - Word Boundaries: Prevents splitting variables like 'secretCode'
|
|
10
|
+
* - Regex: correctly handles #directives vs keywords
|
|
15
11
|
*/
|
|
16
12
|
|
|
17
13
|
// --- EMOJI UNIVERSE GENERATION ---
|
|
@@ -158,7 +154,7 @@ export class CipherEngine {
|
|
|
158
154
|
|
|
159
155
|
_encodeHeader() {
|
|
160
156
|
const saltHex = this.salt.toString('hex');
|
|
161
|
-
const authCheck = this.authKey.subarray(0, 4).toString('hex');
|
|
157
|
+
const authCheck = this.authKey.subarray(0, 4).toString('hex');
|
|
162
158
|
|
|
163
159
|
let headerStr = '';
|
|
164
160
|
for (const char of (saltHex + authCheck)) {
|
|
@@ -196,9 +192,14 @@ export class CipherEngine {
|
|
|
196
192
|
transform(chunk, encoding, callback) {
|
|
197
193
|
const str = chunk.toString('utf8');
|
|
198
194
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
const
|
|
195
|
+
// --- FIXED REGEX LOGIC ---
|
|
196
|
+
// Separate alpha keywords (int, void) from symbols (#include)
|
|
197
|
+
const alphaKeywords = C_KEYWORDS.filter(k => /^\w+$/.test(k)).sort((a,b)=>b.length-a.length).join('|');
|
|
198
|
+
const symKeywords = C_KEYWORDS.filter(k => !/^\w+$/.test(k)).map(k => k.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|');
|
|
199
|
+
|
|
200
|
+
// Match: \b(int|void)\b OR (#include)
|
|
201
|
+
// This ensures 'secretCode' isn't matched as 'Code' containing 'do' or 'int'
|
|
202
|
+
const regex = new RegExp(`(\\b(?:${alphaKeywords})\\b|(?:${symKeywords}))`, 'g');
|
|
202
203
|
|
|
203
204
|
const parts = str.split(regex);
|
|
204
205
|
|
|
@@ -218,8 +219,6 @@ export class CipherEngine {
|
|
|
218
219
|
|
|
219
220
|
const outBuf = Buffer.from(polyEmoji);
|
|
220
221
|
engine.hmac.update(outBuf);
|
|
221
|
-
|
|
222
|
-
// Emit wrapped
|
|
223
222
|
this.push(engine._wrapOutput(outBuf));
|
|
224
223
|
|
|
225
224
|
} else {
|
|
@@ -256,7 +255,6 @@ export class CipherEngine {
|
|
|
256
255
|
footerStr += HEADER_ALPHABET[val];
|
|
257
256
|
}
|
|
258
257
|
}
|
|
259
|
-
// Push footer (always starts on a new line or wrapped)
|
|
260
258
|
this.push(Buffer.from('\n' + footerStr));
|
|
261
259
|
callback();
|
|
262
260
|
}
|
|
@@ -289,7 +287,6 @@ export class CipherEngine {
|
|
|
289
287
|
|
|
290
288
|
_flushDataBuffer(buf) {
|
|
291
289
|
if (buf.length === 0) return Buffer.alloc(0);
|
|
292
|
-
|
|
293
290
|
const padded = Buffer.alloc(5);
|
|
294
291
|
buf.copy(padded);
|
|
295
292
|
const enc = this._encodeBase1024(padded);
|
|
@@ -313,8 +310,7 @@ export class CipherEngine {
|
|
|
313
310
|
const segments = [...segmenter.segment(str)];
|
|
314
311
|
|
|
315
312
|
for (const { segment } of segments) {
|
|
316
|
-
|
|
317
|
-
if (segment.match(/\s/)) continue;
|
|
313
|
+
if (segment.match(/\s/)) continue; // Skip wraps
|
|
318
314
|
|
|
319
315
|
emojiBuffer.push(segment);
|
|
320
316
|
|
|
@@ -349,13 +345,9 @@ export class CipherEngine {
|
|
|
349
345
|
}
|
|
350
346
|
|
|
351
347
|
if (footerHex !== calcDigest) {
|
|
352
|
-
// Critical security failure
|
|
353
348
|
this.emit('error', new Error("FILE_TAMPERED"));
|
|
354
349
|
return;
|
|
355
350
|
}
|
|
356
|
-
|
|
357
|
-
if (engine.decodeDataBuf.length > 0) {
|
|
358
|
-
}
|
|
359
351
|
callback();
|
|
360
352
|
}
|
|
361
353
|
});
|
|
@@ -388,8 +380,6 @@ export class CipherEngine {
|
|
|
388
380
|
const cleanChunk = chunk.filter(b => b !== 0x00);
|
|
389
381
|
stream.push(cleanChunk);
|
|
390
382
|
}
|
|
391
|
-
} else {
|
|
392
|
-
// Unknown emojis are ignored now
|
|
393
383
|
}
|
|
394
384
|
}
|
|
395
385
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mojic",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"description": "Obfuscate C source code into encrypted, password-seeded emoji streams.",
|
|
5
5
|
"main": "bin/mojic.js",
|
|
6
6
|
"bin": {
|
|
@@ -15,6 +15,10 @@
|
|
|
15
15
|
"type": "git",
|
|
16
16
|
"url": "git+https://github.com/notamitgamer/mojic.git"
|
|
17
17
|
},
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"registry": "https://registry.npmjs.org/",
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
18
22
|
"dependencies": {
|
|
19
23
|
"chalk": "^5.3.0",
|
|
20
24
|
"commander": "^11.1.0",
|
|
@@ -50,4 +54,4 @@
|
|
|
50
54
|
],
|
|
51
55
|
"outputPath": "dist"
|
|
52
56
|
}
|
|
53
|
-
}
|
|
57
|
+
}
|