commit 83249bb9c45eaf36c47b80b347fcdd6431e52415
parent 591699709409ddae146547fcefd0d3e1622fab08
Author: Amit Dutta <amitdutta4255@gmail.com>
Date: Wed, 28 Jan 2026 14:30:26 +0530
Uploaded to Github Package
Diffstat:
15 files changed, 14 insertions(+), 1087 deletions(-)
diff --git a/bin/mojic.js b/bin/mojic.js
@@ -12,7 +12,7 @@ import { CipherEngine } from '../lib/CipherEngine.js';
program
.name('mojic')
.description('Obfuscate C source code into emojis')
- .version('1.0.2')
+ .version('1.0.3')
.addHelpCommand('help [command]', 'Display help for command')
.showHelpAfterError();
diff --git a/node_modules/.bin/mojic b/node_modules/.bin/mojic
@@ -1,16 +0,0 @@
-#!/bin/sh
-basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
-
-case `uname` in
- *CYGWIN*|*MINGW*|*MSYS*)
- if command -v cygpath > /dev/null 2>&1; then
- basedir=`cygpath -w "$basedir"`
- fi
- ;;
-esac
-
-if [ -x "$basedir/node" ]; then
- exec "$basedir/node" "$basedir/../mojic/bin/mojic.js" "$@"
-else
- exec node "$basedir/../mojic/bin/mojic.js" "$@"
-fi
diff --git a/node_modules/.bin/mojic.cmd b/node_modules/.bin/mojic.cmd
@@ -1,17 +0,0 @@
-@ECHO off
-GOTO start
-:find_dp0
-SET dp0=%~dp0
-EXIT /b
-:start
-SETLOCAL
-CALL :find_dp0
-
-IF EXIST "%dp0%\node.exe" (
- SET "_prog=%dp0%\node.exe"
-) ELSE (
- SET "_prog=node"
- SET PATHEXT=%PATHEXT:;.JS;=;%
-)
-
-endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\..\mojic\bin\mojic.js" %*
diff --git a/node_modules/.bin/mojic.ps1 b/node_modules/.bin/mojic.ps1
@@ -1,28 +0,0 @@
-#!/usr/bin/env pwsh
-$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
-
-$exe=""
-if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
- # Fix case when both the Windows and Linux builds of Node
- # are installed in the same directory
- $exe=".exe"
-}
-$ret=0
-if (Test-Path "$basedir/node$exe") {
- # Support pipeline input
- if ($MyInvocation.ExpectingInput) {
- $input | & "$basedir/node$exe" "$basedir/../mojic/bin/mojic.js" $args
- } else {
- & "$basedir/node$exe" "$basedir/../mojic/bin/mojic.js" $args
- }
- $ret=$LASTEXITCODE
-} else {
- # Support pipeline input
- if ($MyInvocation.ExpectingInput) {
- $input | & "node$exe" "$basedir/../mojic/bin/mojic.js" $args
- } else {
- & "node$exe" "$basedir/../mojic/bin/mojic.js" $args
- }
- $ret=$LASTEXITCODE
-}
-exit $ret
diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json
@@ -1,6 +1,6 @@
{
"name": "mojic",
- "version": "1.0.2",
+ "version": "1.0.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
@@ -378,23 +378,6 @@
"node": ">=6"
}
},
- "node_modules/mojic": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/mojic/-/mojic-1.0.2.tgz",
- "integrity": "sha512-p1J/707UtPmTRMCWNAaYwOAWjTWkuTIThLTkE2SsDebDbJd3mtbk/Yqe6odsXP09RJ192GTfnKXVqNE+YBcsLA==",
- "license": "Apache-2.0",
- "dependencies": {
- "chalk": "^5.3.0",
- "commander": "^11.1.0",
- "inquirer": "^9.2.12"
- },
- "bin": {
- "mojic": "bin/mojic.js"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
"node_modules/mute-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz",
diff --git a/node_modules/mojic/CODE_OF_CONDUCT.md b/node_modules/mojic/CODE_OF_CONDUCT.md
@@ -1,34 +0,0 @@
-# Contributor Covenant Code of Conduct
-
-## Our Pledge
-
-In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
-
-## Our Standards
-
-Examples of behavior that contributes to creating a positive environment include:
-
-* Using welcoming and inclusive language
-* Being respectful of differing viewpoints and experiences
-* Gracefully accepting constructive criticism
-* Focusing on what is best for the community
-* Showing empathy towards other community members
-
-Examples of unacceptable behavior by participants include:
-
-* The use of sexualized language or imagery and unwelcome sexual attention or advances
-* Trolling, insulting/derogatory comments, and personal or political attacks
-* Public or private harassment
-* Publishing others' private information, such as a physical or electronic address, without explicit permission
-* Other conduct which could reasonably be considered inappropriate in a professional setting
-
-## Enforcement
-
-Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at **amitdutta4255@gmail.com** or **mail@amit.is-a.dev**. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident.
-
-## Attribution
-
-This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
-
-[homepage]: http://contributor-covenant.org
-[version]: http://contributor-covenant.org/version/1/4/
diff --git a/node_modules/mojic/CONTRIBUTING.md b/node_modules/mojic/CONTRIBUTING.md
@@ -1,38 +0,0 @@
-# Contributing to Mojic
-
-First off, thanks for taking the time to contribute! ๐
-
-The following is a set of guidelines for contributing to Mojic. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
-
-## How Can I Contribute?
-
-### Reporting Bugs
-
-This section guides you through submitting a bug report for Mojic. Following these guidelines helps maintainers and the community understand your report, reproduce the behavior, and find related reports.
-
-* **Use a clear and descriptive title** for the issue to identify the problem.
-* **Describe the exact steps which reproduce the problem** in as many details as possible.
-* **Provide specific examples** to demonstrate the steps.
-
-### Suggesting Enhancements
-
-This section guides you through submitting an enhancement suggestion for Mojic, including completely new features and minor improvements to existing functionality.
-
-* **Use a clear and descriptive title** for the issue to identify the suggestion.
-* **Provide a step-by-step description of the suggested enhancement** in as many details as possible.
-* **Explain why this enhancement would be useful** to most Mojic users.
-
-## Styleguides
-
-### Git Commit Messages
-
-* Use the present tense ("Add feature" not "Added feature")
-* Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
-* Limit the first line to 72 characters or less
-
-### Mojic Code Style
-
-* **Cipher Logic:** Changes to `CipherEngine.js` must ensure backward compatibility with the header format.
-* **Streams:** Always use `StringDecoder` when handling text streams to prevent multi-byte emoji corruption.
-
-Happy Hacking! ๐
diff --git a/node_modules/mojic/LICENSE b/node_modules/mojic/LICENSE
@@ -1,201 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.-
\ No newline at end of file
diff --git a/node_modules/mojic/README.md b/node_modules/mojic/README.md
@@ -1,67 +0,0 @@
-# Mojic ๐๐ฎ
-
-> **Obfuscate C source code into a randomized stream of emojis using password-seeded encryption.**
-
-**Mojic** (Magic + Emoji + Logic) is a CLI tool that transforms readable C code into an unreadable mess of emojis. Unlike simple substitution ciphers, Mojic uses your password to seed a Pseudo-Random Number Generator (PRNG), creating a unique "Emoji Universe" for every password.
-
-## ๐ Features
-
-* **๐ Seeded Shuffling:** Your password derives a seed that shuffles the emoji mapping. Two different passwords will produce completely different emoji outputs for the same file.
-* **๐ Moon Header Protocol:** The file header (Salt + Auth Hash) is encoded using only Moon and Clock emojis to allow metadata reading before decryption.
-* **๐ Recursive Processing:** Encrypt or decrypt entire project directories with one flag.
-* **๐ Security Rotation:** Rotate passwords or re-scramble the entropy of encrypted files without seeing the plaintext.
-* **โก Stream Based:** Handles large files efficiently using Node.js streams.
-
-## ๐ฆ Installation
-
-```bash
-npm install -g mojic
-```
-
-## ๐ ๏ธ Usage
-
-### 1. Encrypting Code (`encode`)
-Turns a `.c` file into a `.mojic` file.
-
-```bash
-# Encrypt a single file
-mojic encode main.c
-
-# Encrypt an entire directory recursively
-mojic encode ./src -r
-```
-*You will be prompted to create a password. This password is required to decrypt.*
-
-### 2. Decrypting Code (`decode`)
-Restores the original C code from a `.mojic` file.
-
-```bash
-# Decrypt a single file
-mojic decode main.mojic
-
-# Decrypt an entire directory recursively
-mojic decode ./src -r
-```
-
-### 3. Security & Rotation Tools (`srt`)
-Manage encrypted files without revealing their contents.
-
-```bash
-# Change the password of an encrypted file
-mojic srt --pass secret.mojic
-
-# Re-shuffle the encryption (New Salt) with the SAME password
-# (Useful if you want to change the visual emoji pattern without changing the password)
-mojic srt --re secret.mojic
-```
-
-## ๐ง How it Works
-
-1. **Key Derivation:** Mojic uses **PBKDF2** to derive a 32-bit seed and an Auth Hash from your password and a random Salt.
-2. **The Shuffle:** It uses a custom **Mulberry32 PRNG** seeded with your key to shuffle a "Universe" of ~1500 emojis.
-3. **Tokenization:** C keywords (`int`, `return`, `void`) are tokenized and mapped to unique emojis from the shuffled universe. Remaining characters map to other emojis.
-4. **The Header:** The Salt and Auth Hash are written to the top of the file using a fixed "Header Alphabet" (๐๐๐๐...) so the tool can verify your password before attempting decryption.
-
-## ๐ License
-
-This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.
diff --git a/node_modules/mojic/SECURITY.md b/node_modules/mojic/SECURITY.md
@@ -1,13 +0,0 @@
-# Security Policy
-
-## Reporting a Vulnerability
-
-We take security seriously.
-
-If you discover a security vulnerability within Mojic (e.g., in the `CipherEngine` logic or the PRNG implementation), please send an e-mail to **amitdutta4255@gmail.com**. All security vulnerabilities will be promptly addressed.
-
-**Please do not open public issues for security vulnerabilities.**
-
-### Scope
-* **Supported:** Issues regarding key derivation, salt collisions, or stream buffer overflows.
-* **Not Supported:** Brute-forcing weak passwords (users are responsible for password strength).
diff --git a/node_modules/mojic/bin/mojic.js b/node_modules/mojic/bin/mojic.js
@@ -1,340 +0,0 @@
-#!/usr/bin/env node
-
-import { program } from 'commander';
-import inquirer from 'inquirer';
-import chalk from 'chalk';
-import fs from 'fs';
-import path from 'path';
-import { Transform } from 'stream';
-import { StringDecoder } from 'string_decoder';
-import { CipherEngine } from '../lib/CipherEngine.js';
-
-program
- .name('mojic')
- .description('Obfuscate C source code into emojis')
- .version('1.0.2')
- .addHelpCommand('help [command]', 'Display help for command')
- .showHelpAfterError();
-
-// --- Helpers ---
-
-const promptPassword = async (msg) => {
- const { password } = await inquirer.prompt([{
- type: 'password', name: 'password', message: msg, mask: '*',
- validate: (input) => input.length > 5 || 'Password must be > 5 chars.'
- }]);
- return password;
-};
-
-const getStreamHeader = async (filePath) => {
- const fileStream = fs.createReadStream(filePath, { highWaterMark: 1024 });
- const decoder = new StringDecoder('utf8');
-
- return new Promise((resolve, reject) => {
- let buffer = '';
- let found = false;
-
- fileStream.on('data', (chunk) => {
- if (found) return;
- buffer += decoder.write(chunk);
- const newlineIndex = buffer.indexOf('\n');
- if (newlineIndex !== -1) {
- found = true;
- fileStream.destroy();
- resolve(buffer.substring(0, newlineIndex));
- }
- });
-
- fileStream.on('end', () => {
- if (!found) resolve(buffer + decoder.end());
- });
- fileStream.on('error', reject);
- });
-};
-
-const createHeaderSkipper = () => {
- const decoder = new StringDecoder('utf8');
- let isHeaderSkipped = false;
- let buffer = '';
-
- return new Transform({
- transform(chunk, encoding, cb) {
- if (isHeaderSkipped) {
- this.push(chunk);
- return cb();
- }
-
- buffer += decoder.write(chunk);
- const nlIndex = buffer.indexOf('\n');
-
- if (nlIndex !== -1) {
- const remainder = buffer.substring(nlIndex + 1);
- this.push(remainder);
- isHeaderSkipped = true;
- buffer = '';
- }
- cb();
- }
- });
-};
-
-// --- Recursive Logic ---
-
-async function traverseDirectory(currentPath, extension, callback) {
- const entries = fs.readdirSync(currentPath, { withFileTypes: true });
-
- for (const entry of entries) {
- const fullPath = path.join(currentPath, entry.name);
- if (entry.isDirectory()) {
- await traverseDirectory(fullPath, extension, callback);
- } else if (entry.isFile() && entry.name.endsWith(extension)) {
- await callback(fullPath);
- }
- }
-}
-
-// --- Main Commands ---
-
-program
- .command('encode')
- .argument('<path>', 'File or Directory to encode')
- .option('-r, --recursive', 'Recursively encrypt all .c files in directory')
- .description('Encrypt a file or directory into emojis')
- .action(async (targetPath, options) => {
- try {
- if (!fs.existsSync(targetPath)) throw new Error('Path not found');
- const stats = fs.statSync(targetPath);
-
- // Validation
- if (stats.isDirectory() && !options.recursive) {
- throw new Error(`'${targetPath}' is a directory. Use -r to process recursively.`);
- }
-
- console.log(chalk.blue('๐ Initiating Mojic Encryption...'));
- const password = await promptPassword('Create password for file(s):');
-
- const processFile = async (filePath) => {
- const outputName = filePath.replace(/\.c$/, '') + '.mojic';
- console.log(chalk.dim(` Processing: ${path.basename(filePath)} -> ${path.basename(outputName)}`));
-
- const engine = new CipherEngine(password);
- await engine.init(); // Unique salt per file
-
- const readStream = fs.createReadStream(filePath);
- const writeStream = fs.createWriteStream(outputName);
-
- writeStream.write(engine._encodeHeader());
-
- await new Promise((resolve, reject) => {
- readStream
- .pipe(engine.getEncryptStream())
- .pipe(writeStream)
- .on('finish', resolve)
- .on('error', reject);
- });
- };
-
- if (stats.isDirectory()) {
- console.log(chalk.blue(`๐ Scanning directory: ${targetPath}`));
- await traverseDirectory(targetPath, '.c', processFile);
- console.log(chalk.green('โ
Batch encryption complete.'));
- } else {
- await processFile(targetPath);
- console.log(chalk.green(`โ
Encrypted.`));
- }
-
- } catch (err) {
- console.error(chalk.red('Error:'), err.message);
- }
- });
-
-program
- .command('decode')
- .argument('<path>', 'File or Directory to decode')
- .option('-r, --recursive', 'Recursively decode all .mojic files')
- .description('Restore mojic files to C source code')
- .action(async (targetPath, options) => {
- try {
- if (!fs.existsSync(targetPath)) throw new Error('Path not found');
- const stats = fs.statSync(targetPath);
-
- if (stats.isDirectory() && !options.recursive) {
- throw new Error(`'${targetPath}' is a directory. Use -r to process recursively.`);
- }
-
- console.log(chalk.blue('๐ Initiating Decryption...'));
- const password = await promptPassword('Enter password:'); // Assume same pass for dir
-
- const processFile = async (filePath) => {
- try {
- const outputName = filePath.replace(/\.mojic$/, '') + '.restored.c';
- console.log(chalk.dim(` Restoring: ${path.basename(filePath)}`));
-
- const headerStr = await getStreamHeader(filePath);
- const metadata = CipherEngine.decodeHeader(headerStr);
-
- const engine = new CipherEngine(password);
- await engine.init(metadata.saltHex);
-
- if (engine.authHash.toString('hex') !== metadata.authHex) {
- console.log(chalk.red(` โ Skipped ${path.basename(filePath)}: Incorrect Password`));
- return;
- }
-
- const readStream = fs.createReadStream(filePath);
- const writeStream = fs.createWriteStream(outputName);
-
- await new Promise((resolve, reject) => {
- readStream
- .pipe(createHeaderSkipper())
- .pipe(engine.getDecryptStream())
- .pipe(writeStream)
- .on('finish', resolve)
- .on('error', reject);
- });
- } catch (e) {
- console.log(chalk.red(` โ ๏ธ Error processing ${path.basename(filePath)}: ${e.message}`));
- }
- };
-
- if (stats.isDirectory()) {
- console.log(chalk.blue(`๐ Scanning directory: ${targetPath}`));
- await traverseDirectory(targetPath, '.mojic', processFile);
- console.log(chalk.green('โ
Batch decryption complete.'));
- } else {
- await processFile(targetPath);
- console.log(chalk.green(`โ
Restored.`));
- }
-
- } catch (err) {
- console.error(chalk.red('Error:'), err.message);
- }
- });
-
-// --- Security Rotation Tools (SRT) ---
-
-const rotatePassword = async (file) => {
- try {
- if (!fs.existsSync(file)) throw new Error('File not found');
- console.log(chalk.yellow(`๐ Rotating Password for ${path.basename(file)}...`));
-
- // 1. Authenticate Old
- const headerStr = await getStreamHeader(file);
- const metadata = CipherEngine.decodeHeader(headerStr);
- const oldPass = await promptPassword('Enter CURRENT password:');
-
- const oldEngine = new CipherEngine(oldPass);
- await oldEngine.init(metadata.saltHex);
-
- if (oldEngine.authHash.toString('hex') !== metadata.authHex) {
- console.error(chalk.red('โ Incorrect Current Password'));
- process.exit(1);
- }
-
- // 2. Init New
- const newPass = await promptPassword('Enter NEW password:');
- const newEngine = new CipherEngine(newPass);
- await newEngine.init();
-
- // 3. Process
- const tempFile = file + '.tmp';
- const readStream = fs.createReadStream(file);
- const writeStream = fs.createWriteStream(tempFile);
-
- writeStream.write(newEngine._encodeHeader());
-
- await new Promise((resolve, reject) => {
- readStream
- .pipe(createHeaderSkipper())
- .pipe(oldEngine.getDecryptStream())
- .pipe(newEngine.getEncryptStream())
- .pipe(writeStream)
- .on('finish', resolve)
- .on('error', reject);
- });
-
- fs.renameSync(tempFile, file);
- console.log(chalk.green(`โ
Password updated.`));
-
- } catch (err) {
- console.error(chalk.red('Error:'), err.message);
- }
-};
-
-const reEncrypt = async (file) => {
- try {
- if (!fs.existsSync(file)) throw new Error('File not found');
- console.log(chalk.yellow(`๐ฒ Re-shuffling Entropy for ${path.basename(file)}...`));
-
- // 1. Authenticate
- const headerStr = await getStreamHeader(file);
- const metadata = CipherEngine.decodeHeader(headerStr);
- const password = await promptPassword('Enter password:');
-
- const oldEngine = new CipherEngine(password);
- await oldEngine.init(metadata.saltHex);
-
- if (oldEngine.authHash.toString('hex') !== metadata.authHex) {
- console.error(chalk.red('โ Incorrect Password'));
- process.exit(1);
- }
-
- // 2. Init New
- const newEngine = new CipherEngine(password);
- await newEngine.init();
-
- // 3. Process
- const tempFile = file + '.tmp';
- const readStream = fs.createReadStream(file);
- const writeStream = fs.createWriteStream(tempFile);
-
- writeStream.write(newEngine._encodeHeader());
-
- await new Promise((resolve, reject) => {
- readStream
- .pipe(createHeaderSkipper())
- .pipe(oldEngine.getDecryptStream())
- .pipe(newEngine.getEncryptStream())
- .pipe(writeStream)
- .on('finish', resolve)
- .on('error', reject);
- });
-
- fs.renameSync(tempFile, file);
- console.log(chalk.green(`โ
File re-encrypted.`));
-
- } catch (err) {
- console.error(chalk.red('Error:'), err.message);
- }
-};
-
-program
- .command('srt')
- .description('Security and Rotation Tools')
- .option('--pass <file>', 'Update the password for an existing file')
- .option('--re <file>', 'Re-encrypt with new random seed (Same password)')
- .addHelpText('after', `
-Examples:
- $ mojic srt --pass secret.mojic # Change the password of an encrypted file
- $ mojic srt --re secret.mojic # Re-scramble the emojis (new salt) with same password
-`)
- .action(async (options) => {
- if (options.pass) {
- await rotatePassword(options.pass);
- } else if (options.re) {
- await reEncrypt(options.re);
- } else {
- console.log(chalk.yellow('Please specify an option: --pass <file> or --re <file>'));
- program.commands.find(c => c.name() === 'srt').help();
- }
- });
-
-program.addHelpText('after', `
-Usage Examples:
- $ mojic encode test.c # Encrypt a single C file
- $ mojic encode ./src -r # Recursively encrypt all .c files in ./src
- $ mojic decode test.mojic # Decrypt a single file
- $ mojic decode ./src -r # Recursively decrypt all .mojic files
-`);
-
-program.parse(process.argv);-
\ No newline at end of file
diff --git a/node_modules/mojic/lib/CipherEngine.js b/node_modules/mojic/lib/CipherEngine.js
@@ -1,253 +0,0 @@
-import crypto from 'crypto';
-import { Transform } from 'stream';
-import { StringDecoder } from 'string_decoder';
-
-/**
- * Emojic CipherEngine
- * Handles the logic for mapping C-code to Emojis based on a password seed.
- * * * Updates:
- * - Uses Intl.Segmenter for robust Emoji/Grapheme decoding.
- * - Implements 'tail buffering' in streams to prevent splitting multi-codepoint emojis.
- */
-
-const HEADER_ALPHABET = ['๐', '๐', '๐', '๐', '๐', '๐', '๐', '๐', '๐', '๐', '๐', '๐', '๐', '๐', '๐', '๐'];
-
-const EMOJI_UNIVERSE = [
- '๐','๐','๐','๐','๐','๐
','๐คฃ','๐','๐','๐','๐','๐','๐','๐ฅฐ','๐','๐คฉ',
- '๐','๐','โบ๏ธ','๐','๐','๐ฅฒ','๐','๐','๐','๐คช','๐','๐ค','๐ค','๐คญ','๐คซ','๐ค',
- '๐ค','๐คจ','๐','๐','๐ถ','๐','๐','๐','๐ฌ','๐คฅ','๐','๐','๐ช','๐คค','๐ด','๐ท',
- '๐ค','๐ค','๐คข','๐คฎ','๐คง','๐ฅต','๐ฅถ','๐ฅด','๐ต','๐คฏ','๐ค ','๐ฅณ','๐','๐ค','๐ง','๐',
- '๐','๐','โน๏ธ','๐ฎ','๐ฏ','๐ฒ','๐ณ','๐ฅบ','๐ฆ','๐ง','๐จ','๐ฐ','๐ฅ','๐ข','๐ญ','๐ฑ',
- '๐','๐ฃ','๐','๐','๐ฉ','๐ซ','๐ฅฑ','๐ค','๐ก','๐ ','๐คฌ','๐','๐ฟ','๐','โ ๏ธ','๐ฉ',
- '๐คก','๐น','๐บ','๐ป','๐ฝ','๐พ','๐ค','๐บ','๐ธ','๐น','๐ป','๐ผ','๐ฝ','๐','๐ฟ','๐พ',
- '๐','๐','๐','๐','๐','๐','๐','๐','๐','๐','๐','๐','๐','โฃ๏ธ','๐','โค๏ธ',
- '๐งก','๐','๐','๐','๐','๐ค','๐ค','๐ค','๐ฏ','๐ข','๐ฅ','๐ซ','๐ฆ','๐จ','๐ณ๏ธ','๐ฃ',
- '๐ฌ','๐๏ธ','๐ญ','๐ค','๐','๐ค','๐๏ธ','โ','๐','๐','๐ค','โ๏ธ','๐ค','๐ค','๐ค',
- '๐ค','๐','๐','๐','๐','๐','โ๏ธ','๐','๐','โ','๐','๐ค','๐ค','๐','๐','๐',
- '๐คฒ','๐ค','๐','โ๏ธ','๐
','๐คณ','๐ช','๐ฆพ','๐ฆฟ','๐ฆต','๐ฆถ','๐','๐ฆป','๐','๐ง ','๐ซ',
- '๐ซ','๐ฆท','๐ฆด','๐','๐
','๐','๐ถ','๐ง','๐ฆ','๐ง','๐ง','๐ฑ','๐จ','๐ง','๐จโ๐ฆฐ','๐จโ๐ฆฑ',
- '๐ฅ','๐','โ๏ธ','โ๏ธ','๐ฉ๏ธ','โ๏ธ','๐ต','๐ท','๐ฒ','๐ณ','๐ด','๐ฒ','๐','๐ฆ','๐ฆ','๐',
- '๐','๐ฆ','๐ฆ','๐','๐
','๐','๐','๐','๐','๐','๐','๐ช','๐ซ','๐ฆ','๐ฆ','๐',
- '๐ฆ','๐ฆ','๐','๐','๐น','๐ฐ','๐','๐ฟ๏ธ','๐ฆ','๐ฆ','๐ป','๐จ','๐ผ','๐ฆฅ','๐ฆฆ','๐ฆจ',
- '๐ฆ','๐ฆก','๐พ','๐ฆ','๐','๐','๐ฃ','๐ค','๐ฅ','๐ฆ','๐ง','๐๏ธ','๐ฆ
','๐ฆ','๐ฆข','๐ฆ',
- '๐','๐งจ','๐งง','๐','๐','๐๏ธ','๐๏ธ','๐ซ','๐๏ธ','๐','๐
','๐ฅ','๐ฅ','๐ฅ','โฝ','โพ',
- '๐ฅ','๐','๐','๐','๐','๐พ','๐ฅ','๐ณ','๐','๐','๐','๐ฅ','๐','๐ธ','๐ฅ','๐ฅ',
- '๐ฅ
','โณ','โธ๏ธ','๐ฃ','๐คฟ','๐ฝ','๐ฟ','๐ท','๐ฅ','๐ฏ','๐ช','๐ช','๐ฑ','๐ฎ','๐งฟ','๐ฎ'
-];
-
-const C_KEYWORDS = [
- 'auto', 'break', 'case', 'char', 'const', 'continue', 'default', 'do',
- 'double', 'else', 'enum', 'extern', 'float', 'for', 'goto', 'if',
- 'int', 'long', 'register', 'return', 'short', 'signed', 'sizeof', 'static',
- 'struct', 'switch', 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while',
- 'include', 'define', 'main', 'printf', 'NULL'
-];
-
-const ASCII_CHARS = Array.from({ length: 95 }, (_, i) => String.fromCharCode(i + 32))
- .concat(['\n', '\t', '\r']);
-
-const TOTAL_TOKENS = C_KEYWORDS.length + ASCII_CHARS.length;
-
-export class CipherEngine {
- constructor(password) {
- this.password = password;
- this.tokenMap = new Map();
- this.reverseMap = new Map();
- this.isReady = false;
-
- if (EMOJI_UNIVERSE.length < TOTAL_TOKENS) {
- throw new Error(`CRITICAL: Not enough emojis. Need ${TOTAL_TOKENS}, have ${EMOJI_UNIVERSE.length}`);
- }
- }
-
- async init(existingSaltHex = null) {
- this.salt = existingSaltHex
- ? Buffer.from(existingSaltHex, 'hex')
- : crypto.randomBytes(16);
-
- const derivedKey = await new Promise((resolve, reject) => {
- crypto.pbkdf2(this.password, this.salt, 100000, 36, 'sha256', (err, key) => {
- if (err) reject(err); else resolve(key);
- });
- });
-
- const seedBuffer = derivedKey.subarray(0, 4);
- this.authHash = derivedKey.subarray(4);
- const seedInt = seedBuffer.readUInt32BE(0);
-
- const shuffledEmojis = this._shuffleArray([...EMOJI_UNIVERSE], seedInt);
-
- let emojiIndex = 0;
- for (const keyword of C_KEYWORDS) {
- const emo = shuffledEmojis[emojiIndex++];
- this.tokenMap.set(keyword, emo);
- this.reverseMap.set(emo, keyword);
- }
-
- for (const char of ASCII_CHARS) {
- const emo = shuffledEmojis[emojiIndex++];
- this.tokenMap.set(char, emo);
- this.reverseMap.set(emo, char);
- }
-
- this.isReady = true;
- }
-
- _mulberry32(a) {
- return function() {
- var t = a += 0x6D2B79F5;
- t = Math.imul(t ^ t >>> 15, t | 1);
- t ^= t + Math.imul(t ^ t >>> 7, t | 61);
- return ((t ^ t >>> 14) >>> 0) / 4294967296;
- }
- }
-
- _shuffleArray(array, seed) {
- const rng = this._mulberry32(seed);
- for (let i = array.length - 1; i > 0; i--) {
- const j = Math.floor(rng() * (i + 1));
- [array[i], array[j]] = [array[j], array[i]];
- }
- return array;
- }
-
- _encodeHeader() {
- const saltHex = this.salt.toString('hex');
- const authHex = this.authHash.toString('hex');
- const fullHexString = saltHex + authHex;
-
- let headerStr = '';
- for (const char of fullHexString) {
- const val = parseInt(char, 16);
- headerStr += HEADER_ALPHABET[val];
- }
- return headerStr + '\n';
- }
-
- static decodeHeader(headerStr) {
- let hexString = '';
- // Use segmenter here just in case moon emojis have variation selectors
- const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
- const segments = segmenter.segment(headerStr.trim());
-
- for (const { segment } of segments) {
- const index = HEADER_ALPHABET.indexOf(segment);
- if (index === -1) throw new Error("Invalid Header format.");
- hexString += index.toString(16);
- }
- const saltHex = hexString.substring(0, 32);
- const authHex = hexString.substring(32);
- return { saltHex, authHex };
- }
-
- getEncryptStream() {
- if (!this.isReady) throw new Error("Engine not initialized");
- const engine = this;
- const decoder = new StringDecoder('utf8');
- let buffer = '';
-
- return new Transform({
- transform(chunk, encoding, callback) {
- buffer += decoder.write(chunk);
-
- const sortedKeywords = [...C_KEYWORDS].sort((a, b) => b.length - a.length);
- const keywordPattern = sortedKeywords.map(k => k.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|');
- const regex = new RegExp(`\\b(${keywordPattern})\\b|([\\s\\S])`, 'g');
-
- let match;
- let output = '';
-
- while ((match = regex.exec(buffer)) !== null) {
- const token = match[0];
- if (engine.tokenMap.has(token)) {
- output += engine.tokenMap.get(token);
- } else {
- output += token;
- }
- }
-
- buffer = '';
- this.push(output);
- callback();
- },
- flush(callback) {
- buffer += decoder.end();
- // Simple flush for MVP; in prod, we'd regex one last time.
- callback();
- }
- });
- }
-
- getDecryptStream() {
- if (!this.isReady) throw new Error("Engine not initialized");
- const engine = this;
- const decoder = new StringDecoder('utf8');
- const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
-
- let buffer = '';
-
- return new Transform({
- transform(chunk, encoding, callback) {
- // 1. Decode bytes to string (handles partial UTF8 bytes)
- buffer += decoder.write(chunk);
-
- // 2. Segment into Graphemes (Emojis)
- const segments = [...segmenter.segment(buffer)];
-
- // 3. Process all BUT the last segment
- // We keep the last segment in the buffer because it might be
- // the start of a multi-codepoint emoji that was split by the chunk boundary.
- // (e.g. Base char is here, Variation Selector is in next chunk)
-
- const processUntilIndex = segments.length > 1 ? segments.length - 1 : 0;
- let output = '';
- let processedString = '';
-
- // If we only have 1 segment, we can't be sure it's complete, wait for next chunk
- // UNLESS the buffer is getting huge, then force it.
- if (segments.length === 1 && buffer.length < 100) {
- // Wait for more data
- callback();
- return;
- }
-
- // Process safe segments
- for (let i = 0; i < processUntilIndex; i++) {
- const char = segments[i].segment;
- processedString += char;
-
- if (engine.reverseMap.has(char)) {
- output += engine.reverseMap.get(char);
- } else {
- output += char;
- }
- }
-
- // Update buffer to only contain the remaining tail
- // Note: buffer might contain bytes not yet in segments if StringDecoder held them?
- // No, decoder.write returns what is available.
- // We just need to remove the processed part from the buffer string.
- if (processUntilIndex > 0) {
- buffer = buffer.slice(processedString.length);
- this.push(output);
- }
-
- callback();
- },
- flush(callback) {
- // Process any remaining tail (buffer + decoder leftovers)
- buffer += decoder.end();
- if (buffer) {
- const segments = segmenter.segment(buffer);
- let output = '';
- for (const { segment } of segments) {
- if (engine.reverseMap.has(segment)) output += engine.reverseMap.get(segment);
- else output += segment;
- }
- this.push(output);
- }
- callback();
- }
- });
- }
-}-
\ No newline at end of file
diff --git a/node_modules/mojic/package.json b/node_modules/mojic/package.json
@@ -1,31 +0,0 @@
-{
- "name": "mojic",
- "version": "1.0.2",
- "description": "Obfuscate C source code into emojis using password-seeded mapping",
- "main": "bin/mojic.js",
- "bin": {
- "mojic": "bin/mojic.js"
- },
- "type": "module",
- "scripts": {
- "start": "node bin/mojic.js"
- },
- "dependencies": {
- "chalk": "^5.3.0",
- "commander": "^11.1.0",
- "inquirer": "^9.2.12"
- },
- "engines": {
- "node": ">=18.0.0"
- },
- "keywords": [
- "cli",
- "encryption",
- "emoji",
- "obfuscation",
- "c",
- "security"
- ],
- "author": "",
- "license": "Apache-2.0"
-}-
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
@@ -1,18 +1,17 @@
{
"name": "mojic",
- "version": "1.0.2",
+ "version": "1.0.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mojic",
- "version": "1.0.2",
+ "version": "1.0.3",
"license": "Apache-2.0",
"dependencies": {
"chalk": "^5.3.0",
"commander": "^11.1.0",
- "inquirer": "^9.2.12",
- "mojic": "^1.0.2"
+ "inquirer": "^9.2.12"
},
"bin": {
"mojic": "bin/mojic.js"
@@ -395,23 +394,6 @@
"node": ">=6"
}
},
- "node_modules/mojic": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/mojic/-/mojic-1.0.2.tgz",
- "integrity": "sha512-p1J/707UtPmTRMCWNAaYwOAWjTWkuTIThLTkE2SsDebDbJd3mtbk/Yqe6odsXP09RJ192GTfnKXVqNE+YBcsLA==",
- "license": "Apache-2.0",
- "dependencies": {
- "chalk": "^5.3.0",
- "commander": "^11.1.0",
- "inquirer": "^9.2.12"
- },
- "bin": {
- "mojic": "bin/mojic.js"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
"node_modules/mute-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz",
diff --git a/package.json b/package.json
@@ -1,6 +1,6 @@
{
"name": "mojic",
- "version": "1.0.2",
+ "version": "1.0.3",
"description": "Obfuscate C source code into emojis using password-seeded mapping",
"main": "bin/mojic.js",
"bin": {
@@ -10,11 +10,14 @@
"scripts": {
"start": "node bin/mojic.js"
},
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/notamitgamer/mojic.git"
+ },
"dependencies": {
"chalk": "^5.3.0",
"commander": "^11.1.0",
- "inquirer": "^9.2.12",
- "mojic": "^1.0.2"
+ "inquirer": "^9.2.12"
},
"engines": {
"node": ">=18.0.0"
@@ -27,6 +30,6 @@
"c",
"security"
],
- "author": "",
+ "author": "notamitgamer",
"license": "Apache-2.0"
-}
+}+
\ No newline at end of file