Unique symmetric stream cipher with dynamic masking, cascading feedback, chunked streaming support, and internal MAC.
SymmetricMorph is a lightweight, dependency-free encryption library that provides:
- High entropy stream output
- Cascading feedback structure
- Dynamic masking against known-plaintext attacks
- Built-in MAC for integrity verification
- Chunked encryption for large data streams
- Works in Node.js, browsers, and Web Workers
- Written in TypeScript, zero external dependencies
SymmetricMorph is designed to work seamlessly across a wide range of JavaScript environments:
β
Node.js (require
and import
)
β
Browsers via <script>
(UMD)
β
Browsers via import
(ESM)
β
Web Workers (background encryption)
β
Full TypeScript type support
β
No external dependencies
β
Efficient for large file encryption
Perfect for:
- Node.js servers
- Web applications
- Mobile browsers
- Desktop Electron apps
- Progressive Web Apps (PWA)
- Background data encryption with Web Workers
- Embedded JavaScript runtimes (e.g., Deno)
npm install symmetricmorph
or
yarn add symmetricmorph
You can also load SymmetricMorph directly from a CDN without installing:
Using unpkg:
<script src="https://unpkg.com/symmetricmorph/dist/browser/symmetricmorph.umd.js"></script>
Or using jsDelivr:
<script src="https://cdn.jsdelivr.net/npm/symmetricmorph/dist/browser/symmetricmorph.umd.js"></script>
Then use it via global SymmetricMorph object in your scripts.
"Important: Always save the salt after encryption. It's required for correct decryption."
const SymmetricMorph = require('symmetricmorph').default;
// Step 1: Encrypt
const cipherEnc = SymmetricMorph.fromPassword('MyStrongPassword');
const salt = cipherEnc.getSalt();
const plain = new Uint8Array(Array.from('Hello Node!').map(c => c.charCodeAt(0)));
const encrypted = cipherEnc.encrypt(plain);
// Step 2: Decrypt using the saved salt
const cipherDec = SymmetricMorph.fromPasswordWithSalt('MyStrongPassword', salt);
const decrypted = cipherDec.decrypt(encrypted);
console.log(String.fromCharCode(...decrypted)); // Hello Node!
import SymmetricMorph from 'symmetricmorph';
// Step 1: Encrypt
const cipherEnc = SymmetricMorph.fromPassword('MyStrongPassword');
const salt = cipherEnc.getSalt();
const plain = new Uint8Array(Array.from('Hello Node ESM!').map(c => c.charCodeAt(0)));
const encrypted = cipherEnc.encrypt(plain);
// Step 2: Decrypt using the saved salt
const cipherDec = SymmetricMorph.fromPasswordWithSalt('MyStrongPassword', salt);
const decrypted = cipherDec.decrypt(encrypted);
console.log(String.fromCharCode(...decrypted)); // Hello Node ESM!
<script src="symmetricmorph.umd.js"></script>
<script>
// Step 1: Encrypt
const cipherEnc = SymmetricMorph.fromPassword('MyStrongPassword');
const salt = cipherEnc.getSalt();
const plain = new Uint8Array(Array.from('Hello Browser!').map(c => c.charCodeAt(0)));
const encrypted = cipherEnc.encrypt(plain);
// Step 2: Decrypt using the saved salt
const cipherDec = SymmetricMorph.fromPasswordWithSalt('MyStrongPassword', salt);
const decrypted = cipherDec.decrypt(encrypted);
console.log(String.fromCharCode(...decrypted)); // Hello Browser!
</script>
<script type="module">
import SymmetricMorph from './symmetricmorph.es.js';
// Step 1: Encrypt
const cipherEnc = SymmetricMorph.fromPassword('Secret123');
const salt = cipherEnc.getSalt();
const plain = new Uint8Array(Array.from('Hello Browser!').map(c => c.charCodeAt(0)));
const encrypted = cipherEnc.encrypt(plain);
// Step 2: Decrypt using the saved salt
const cipherDec = SymmetricMorph.fromPasswordWithSalt('Secret123', salt);
const decrypted = cipherDec.decrypt(encrypted);
console.log(String.fromCharCode(...decrypted)); // Hello Browser!
</script>
Encrypt large files in a Web Worker without blocking the main UI:
<script>
const worker = new Worker('./symmetricmorph-worker.js');
let savedSalt = null;
let encryptedData = null;
worker.postMessage({ type: 'init', password: 'StrongPassword123' });
worker.onmessage = (event) => {
const { type, encrypted, decrypted, salt } = event.data;
if (type === 'ready') {
console.log('Worker ready! Encrypting now...');
const plainText = 'This is a big secret message!';
const plainBytes = new Uint8Array(Array.from(plainText).map(c => c.charCodeAt(0)));
worker.postMessage({ type: 'encrypt', data: plainBytes });
}
if (type === 'encrypted') {
console.log('Encrypted data:', encrypted);
encryptedData = encrypted;
savedSalt = event.data.salt;
worker.postMessage({ type: 'decrypt', data: encryptedData, salt: savedSalt });
}
if (type === 'decrypted') {
console.log('Decrypted data:', String.fromCharCode(...decrypted));
}
if (type === 'error') {
console.error('Worker error:', event.data.message);
}
};
</script>
and in symmetricmorph-worker.js
:
importScripts('../../../dist/browser/symmetricmorph.umd.js');
let cipher = null;
let currentPassword = null;
let currentSalt = null;
self.addEventListener('message', (event) => {
const { type, password, data, salt } = event.data;
if (type === 'init') {
currentPassword = password;
cipher = SymmetricMorph.fromPassword(currentPassword);
currentSalt = cipher.getSalt();
postMessage({ type: 'ready' });
} else if (type === 'encrypt' && cipher) {
const encrypted = cipher.encrypt(data);
postMessage({ type: 'encrypted', encrypted, salt: currentSalt });
} else if (type === 'decrypt' && data && salt) {
const decryptCipher = SymmetricMorph.fromPasswordWithSalt(currentPassword, salt);
const decrypted = decryptCipher.decrypt(data);
postMessage({ type: 'decrypted', decrypted });
} else {
postMessage({ type: 'error', message: 'Invalid operation' });
}
});
Encrypt and decrypt multiple chunks of data independently:
import SymmetricMorph from 'symmetricmorph';
const cipher = SymmetricMorph.fromPassword('ChunkyPassword');
const chunks = [
Array.from('First chunk').map(c => c.charCodeAt(0)),
Array.from('Second chunk').map(c => c.charCodeAt(0)),
Array.from('Third chunk').map(c => c.charCodeAt(0))
];
const encryptedChunks = cipher.encryptChunks(chunks);
const decryptedChunks = cipher.decryptChunks(encryptedChunks);
console.log(decryptedChunks.map(chunk => String.fromCharCode(...chunk)).join(' '));
import SymmetricMorph from 'symmetricmorph';
const randomKey = SymmetricMorph.generateKey(64);
const cipher = SymmetricMorph.fromKey(randomKey);
const message = Array.from('Random key encryption!').map(c => c.charCodeAt(0));
const encrypted = cipher.encrypt(message);
const decrypted = cipher.decrypt(encrypted);
console.log(String.fromCharCode(...decrypted)); // Random key encryption!
Method | Description |
---|---|
SymmetricMorph.fromPassword(password: string, iterations?: number, keyLength?: number) |
Creates a cipher instance from a password and generates a random salt. |
SymmetricMorph.fromPasswordWithSalt(password: string, salt: Uint8Array | number[], iterations?: number, keyLength?: number) |
Creates a cipher instance from a password and a provided salt. |
SymmetricMorph.fromKey(key: Uint8Array) |
Creates a cipher instance from a raw encryption key. |
SymmetricMorph.generateKey(length?: number) |
Generates a secure random encryption key. |
cipher.getSalt() |
Returns the salt used during password-based key derivation (or undefined if using a raw key). |
cipher.encrypt(plainBytes: Uint8Array) |
Encrypts an array of bytes. |
cipher.decrypt(encryptedBytes: Uint8Array) |
Decrypts an array of bytes and verifies integrity. |
cipher.encryptChunks(chunks: Uint8Array[]) |
Encrypts multiple data chunks. |
cipher.decryptChunks(chunks: Uint8Array[]) |
Decrypts multiple data chunks. |
You can encrypt and decrypt files easily from the command line using the official SymmetricMorph CLI. β Simple usage: β Supports encryption and decryption of any files β Automatically handles salt and password securely β Lightweight and fast
Install globally:
npm install -g symmorph-cli
Usage example:
# Encrypt a file
symmorph encrypt --input myfile.txt --output encrypted.bin --password "MyStrongPassword"
# Decrypt a file
symmorph decrypt --input encrypted.bin --output decrypted.txt --password "MyStrongPassword"
β‘οΈ Learn more: https://github.com/sur-ser/symmorph-cli
MIT License
Copyright (c) 2025