@irfanshadikrishad/cipher 1.0.3 → 1.0.5
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 +50 -14
- package/dist/Cipher.d.ts +37 -2
- package/dist/ciphers/ADFGVX.d.ts +13 -0
- package/dist/ciphers/ADFGVX.js +89 -0
- package/dist/ciphers/Salsa20.d.ts +16 -0
- package/dist/ciphers/Salsa20.js +94 -0
- package/dist/index.js +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,29 +2,65 @@
|
|
|
2
2
|
|
|
3
3
|
A versatile and secure cryptographic library for implementing various cipher algorithms in Node.js applications.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
#### 🚀 Installation
|
|
10
|
+
|
|
11
|
+
Install the package via npm:
|
|
6
12
|
|
|
7
13
|
```bash
|
|
8
|
-
npm
|
|
14
|
+
npm install @irfanshadikrishad/cipher
|
|
9
15
|
```
|
|
10
16
|
|
|
11
|
-
|
|
17
|
+
yarn:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
yarn add @irfanshadikrishad/cipher
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
bun:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
bun add @irfanshadikrishad/cipher
|
|
27
|
+
```
|
|
12
28
|
|
|
13
|
-
|
|
29
|
+
#### 📖 Usage
|
|
30
|
+
|
|
31
|
+
Import the library and use a cipher algorithm:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
14
34
|
import { Cipher } from "@irfanshadikrishad/cipher"
|
|
15
35
|
|
|
16
|
-
|
|
36
|
+
// Create a Caesar Cipher instance with a shift of 6
|
|
37
|
+
const caesar = new Cipher.Caesar(6)
|
|
17
38
|
|
|
18
|
-
console.log(
|
|
39
|
+
console.log(caesar.encrypt("hello world")) // Output: "nkrru cuxrj"
|
|
19
40
|
```
|
|
20
41
|
|
|
21
|
-
####
|
|
42
|
+
#### 🔐 Supported Ciphers
|
|
43
|
+
|
|
44
|
+
This library provides implementations of various classical and modern ciphers:
|
|
45
|
+
|
|
46
|
+
| Cipher | Type | Key required? | Strength | Used In/Notes |
|
|
47
|
+
| --------------------------------------------------- | ---------------------------------------- | ------------- | -------- | ------------------------------------------ |
|
|
48
|
+
| [Caesar Cipher](/docs/en/ciphers/CAESAR.md) | Substitution | No | Low | Ancient Rome, Simple Obsfuscation |
|
|
49
|
+
| [Atbash Cipher](/docs/en/ciphers/ATBASH.md) | Substitution | No | Low | Hebrew Cipher, Basic Encryption |
|
|
50
|
+
| [Playfair Cipher](/docs/en/ciphers/PLAYFAIR.md) | Diagraph-based | Yes | Medium | Used in WWI & WWII |
|
|
51
|
+
| [Vigenère Cipher](/docs/en/ciphers/VIGENERE.md) | Polyalphabetic | Yes | Medium | Used in Historical Documents |
|
|
52
|
+
| [The Alphabet Cipher](/docs/en/ciphers/ALPHABET.md) | Polyalphabetic | Yes | Medium | Inspired by Vigenere, Cryptography Puzzles |
|
|
53
|
+
| [Salsa20](/docs/en/ciphers/SALSA20.md) | Stream Cipher | Yes | High | Modern Cryptography, Secure Communications |
|
|
54
|
+
| [ADFGVX](/docs/en/ciphers/ADFGVX.md) | Polybius Square + Columnar Transposition | Yes | Medium | Used in WWI, Known for 6x6 polybius square |
|
|
55
|
+
|
|
56
|
+
More ciphers coming soon...
|
|
57
|
+
|
|
58
|
+
#### 🍀 Contribution
|
|
59
|
+
|
|
60
|
+
To contribute on the codebase, follow [contribution guideline](/docs/en/CONTRIBUTING.md).
|
|
61
|
+
|
|
62
|
+
#### ❤️ Support
|
|
22
63
|
|
|
23
|
-
|
|
24
|
-
- Atbash Cipher
|
|
25
|
-
- Playfair Cipher
|
|
26
|
-
- Vigenere Cipher
|
|
27
|
-
- The Alphabet Cipher
|
|
28
|
-
- & more coming soon ...
|
|
64
|
+
If you find this library useful, consider giving it a ⭐ on GitHub!
|
|
29
65
|
|
|
30
|
-
|
|
66
|
+
Thanks for visiting! (>'-'<)
|
package/dist/Cipher.d.ts
CHANGED
|
@@ -3,12 +3,47 @@ import { Atbash } from "./ciphers/Atbash.js";
|
|
|
3
3
|
import { Playfair } from "./ciphers/Playfair.js";
|
|
4
4
|
import { Vigenere } from "./ciphers/Vigenere.js";
|
|
5
5
|
import { Alphabet } from "./ciphers/Alphabet.js";
|
|
6
|
+
import { Salsa20 } from "./ciphers/Salsa20.js";
|
|
7
|
+
import { ADFGVX } from "./ciphers/ADFGVX.js";
|
|
6
8
|
export declare abstract class Cipher {
|
|
9
|
+
/**
|
|
10
|
+
* Caesar cipher is a substitution cipher where each letter in the plaintext is shifted a certain number of places down the alphabet.
|
|
11
|
+
* @param shift Number of places to shift the alphabet
|
|
12
|
+
*/
|
|
7
13
|
static Caesar: typeof Caesar;
|
|
14
|
+
/**
|
|
15
|
+
* Atbash cipher is a substitution cipher where the alphabet is reversed.
|
|
16
|
+
* A -> Z, B -> Y, C -> X, ..., Z -> A
|
|
17
|
+
*/
|
|
8
18
|
static Atbash: typeof Atbash;
|
|
19
|
+
/**
|
|
20
|
+
* Playfair cipher is a digraph substitution cipher that encrypts pairs of letters.
|
|
21
|
+
* @param key 5x5 grid of letters
|
|
22
|
+
*/
|
|
9
23
|
static Playfair: typeof Playfair;
|
|
24
|
+
/**
|
|
25
|
+
* Vigenère cipher is a polyalphabetic substitution cipher that encrypts the plaintext with a keyword.
|
|
26
|
+
* @param key Keyword to encrypt the plaintext
|
|
27
|
+
*/
|
|
10
28
|
static Vigenere: typeof Vigenere;
|
|
29
|
+
/**
|
|
30
|
+
* "The Alphabet Cipher" was a brief study published by Lewis Carroll in 1868, describing how to use the alphabet to send encrypted codes.
|
|
31
|
+
*/
|
|
11
32
|
static Alphabet: typeof Alphabet;
|
|
12
|
-
|
|
13
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Salsa20 is a stream cipher developed by Daniel J. Bernstein.
|
|
35
|
+
* It is widely used in the industry and is considered secure.
|
|
36
|
+
* @param key 256-bit key
|
|
37
|
+
* @param nonce 64-bit nonce
|
|
38
|
+
* @param counter 64-bit counter
|
|
39
|
+
*/
|
|
40
|
+
static Salsa20: typeof Salsa20;
|
|
41
|
+
/**
|
|
42
|
+
* The ADFGVX cipher was a manually applied field cipher used by the Imperial German Army during World War I.
|
|
43
|
+
* @param key - more than 1 characters long (more length, more secure)
|
|
44
|
+
* @param codeword - 36 characters long
|
|
45
|
+
*/
|
|
46
|
+
static ADFGVX: typeof ADFGVX;
|
|
47
|
+
abstract encrypt(text: string): string | Uint8Array;
|
|
48
|
+
abstract decrypt(text: string | Uint8Array): string;
|
|
14
49
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Cipher } from "../Cipher.js";
|
|
2
|
+
export declare class ADFGVX extends Cipher {
|
|
3
|
+
private key;
|
|
4
|
+
private codeword;
|
|
5
|
+
private polybiusSquare;
|
|
6
|
+
private reverseSquare;
|
|
7
|
+
private adfgvx;
|
|
8
|
+
constructor(key?: string | null, codeword?: string | null);
|
|
9
|
+
private createPolybiusSquare;
|
|
10
|
+
private columnarTranspose;
|
|
11
|
+
encrypt(text: string): string;
|
|
12
|
+
decrypt(text: string): string;
|
|
13
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Cipher } from "../Cipher.js";
|
|
2
|
+
export class ADFGVX extends Cipher {
|
|
3
|
+
constructor(key, codeword) {
|
|
4
|
+
super();
|
|
5
|
+
this.key = "CIPHER";
|
|
6
|
+
this.codeword = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
7
|
+
this.polybiusSquare = new Map();
|
|
8
|
+
this.reverseSquare = new Map();
|
|
9
|
+
this.adfgvx = ["A", "D", "F", "G", "V", "X"];
|
|
10
|
+
if (key)
|
|
11
|
+
this.key = key.toUpperCase();
|
|
12
|
+
if (codeword)
|
|
13
|
+
this.codeword = codeword.toUpperCase();
|
|
14
|
+
this.createPolybiusSquare(this.codeword);
|
|
15
|
+
}
|
|
16
|
+
createPolybiusSquare(codeword) {
|
|
17
|
+
const uniqueLetters = [];
|
|
18
|
+
for (const char of codeword.toUpperCase()) {
|
|
19
|
+
if (!uniqueLetters.includes(char)) {
|
|
20
|
+
uniqueLetters.push(char);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const remainingChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
|
24
|
+
.split("")
|
|
25
|
+
.filter((char) => !uniqueLetters.includes(char));
|
|
26
|
+
const alphabet = [...uniqueLetters, ...remainingChars];
|
|
27
|
+
let index = 0;
|
|
28
|
+
for (const row of this.adfgvx) {
|
|
29
|
+
for (const col of this.adfgvx) {
|
|
30
|
+
const letter = alphabet[index];
|
|
31
|
+
const code = row + col;
|
|
32
|
+
this.polybiusSquare.set(letter, code);
|
|
33
|
+
this.reverseSquare.set(code, letter);
|
|
34
|
+
index++;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
columnarTranspose(text, decrypt = false) {
|
|
39
|
+
const keyOrder = [...this.key]
|
|
40
|
+
.map((char, index) => ({ char, index }))
|
|
41
|
+
.sort((a, b) => a.char.localeCompare(b.char))
|
|
42
|
+
.map((item) => item.index);
|
|
43
|
+
const numCols = this.key.length;
|
|
44
|
+
const numRows = Math.ceil(text.length / numCols);
|
|
45
|
+
const grid = Array.from({ length: numRows }, () => new Array(numCols).fill(""));
|
|
46
|
+
if (!decrypt) {
|
|
47
|
+
let charIndex = 0;
|
|
48
|
+
for (let row = 0; row < numRows; row++) {
|
|
49
|
+
for (let col = 0; col < numCols; col++) {
|
|
50
|
+
if (charIndex < text.length) {
|
|
51
|
+
grid[row][col] = text[charIndex++];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return keyOrder
|
|
56
|
+
.map((col) => grid.map((row) => row[col]).join(""))
|
|
57
|
+
.join("");
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
const sortedKeyOrder = [...keyOrder].sort((a, b) => this.key[a].localeCompare(this.key[b]));
|
|
61
|
+
let charIndex = 0;
|
|
62
|
+
for (const sortedCol of sortedKeyOrder) {
|
|
63
|
+
for (let row = 0; row < numRows; row++) {
|
|
64
|
+
if (charIndex < text.length) {
|
|
65
|
+
grid[row][sortedCol] = text[charIndex++];
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return grid.flat().join("");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
encrypt(text) {
|
|
73
|
+
const plaintext = text.replace(/[^A-Za-z0-9]/g, "").toUpperCase();
|
|
74
|
+
const polybiusText = plaintext
|
|
75
|
+
.split("")
|
|
76
|
+
.map((char) => this.polybiusSquare.get(char) || "")
|
|
77
|
+
.join("");
|
|
78
|
+
return this.columnarTranspose(polybiusText);
|
|
79
|
+
}
|
|
80
|
+
decrypt(text) {
|
|
81
|
+
var _a;
|
|
82
|
+
const encrypted = text.replace(/[^A-Za-z0-9]/g, "").toUpperCase();
|
|
83
|
+
const intermediate = this.columnarTranspose(encrypted, true);
|
|
84
|
+
const polybiusText = ((_a = intermediate.match(/.{1,2}/g)) === null || _a === void 0 ? void 0 : _a.map((char) => {
|
|
85
|
+
return this.reverseSquare.get(char) || "";
|
|
86
|
+
})) || [];
|
|
87
|
+
return polybiusText.join("");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Cipher } from "../Cipher.js";
|
|
2
|
+
export declare class Salsa20 extends Cipher {
|
|
3
|
+
private key;
|
|
4
|
+
private nonce;
|
|
5
|
+
private counter;
|
|
6
|
+
constructor(key: Uint8Array | string, nonce: Uint8Array | string, counter?: number);
|
|
7
|
+
private static rotateLeft;
|
|
8
|
+
private static quarterRound;
|
|
9
|
+
private generateKeystreamBlock;
|
|
10
|
+
encrypt(plaintext: string): string;
|
|
11
|
+
decrypt(ciphertext: string): string;
|
|
12
|
+
static generateKey(): string;
|
|
13
|
+
static generateNonce(): string;
|
|
14
|
+
static encodeBase64(uint8array: Uint8Array): string;
|
|
15
|
+
static decodeBase64(base64: string): Uint8Array;
|
|
16
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Cipher } from "../Cipher.js";
|
|
2
|
+
import crypto from "crypto";
|
|
3
|
+
import { Buffer } from "buffer";
|
|
4
|
+
export class Salsa20 extends Cipher {
|
|
5
|
+
constructor(key, nonce, counter = 0) {
|
|
6
|
+
super();
|
|
7
|
+
if (typeof key === "string")
|
|
8
|
+
key = Buffer.from(key, "base64");
|
|
9
|
+
if (typeof nonce === "string")
|
|
10
|
+
nonce = Buffer.from(nonce, "base64");
|
|
11
|
+
if (key.length !== 32)
|
|
12
|
+
throw new Error("Key must be 32 bytes (256-bit).");
|
|
13
|
+
if (nonce.length !== 8)
|
|
14
|
+
throw new Error("Nonce must be 8 bytes (64-bit).");
|
|
15
|
+
this.key = new Uint32Array(new Uint8Array(key).buffer);
|
|
16
|
+
this.nonce = new Uint32Array(new Uint8Array(nonce).buffer);
|
|
17
|
+
this.counter = counter;
|
|
18
|
+
}
|
|
19
|
+
static rotateLeft(x, n) {
|
|
20
|
+
return (x << n) | (x >>> (32 - n));
|
|
21
|
+
}
|
|
22
|
+
static quarterRound(state, a, b, c, d) {
|
|
23
|
+
state[b] ^= Salsa20.rotateLeft(state[a] + state[d], 7);
|
|
24
|
+
state[c] ^= Salsa20.rotateLeft(state[b] + state[a], 9);
|
|
25
|
+
state[d] ^= Salsa20.rotateLeft(state[c] + state[b], 13);
|
|
26
|
+
state[a] ^= Salsa20.rotateLeft(state[d] + state[c], 18);
|
|
27
|
+
}
|
|
28
|
+
generateKeystreamBlock() {
|
|
29
|
+
const constants = new Uint32Array([
|
|
30
|
+
0x61707865, 0x3320646e, 0x79622d32, 0x6b206574,
|
|
31
|
+
]); // "expand 32-byte k"
|
|
32
|
+
const state = new Uint32Array(16);
|
|
33
|
+
state.set(constants, 0);
|
|
34
|
+
state.set(this.key.slice(0, 4), 4);
|
|
35
|
+
state.set(this.nonce, 8);
|
|
36
|
+
state.set([this.counter, 0], 10);
|
|
37
|
+
state.set(this.key.slice(4, 8), 12);
|
|
38
|
+
const workingState = new Uint32Array(state);
|
|
39
|
+
for (let i = 0; i < 10; i++) {
|
|
40
|
+
Salsa20.quarterRound(workingState, 0, 4, 8, 12);
|
|
41
|
+
Salsa20.quarterRound(workingState, 1, 5, 9, 13);
|
|
42
|
+
Salsa20.quarterRound(workingState, 2, 6, 10, 14);
|
|
43
|
+
Salsa20.quarterRound(workingState, 3, 7, 11, 15);
|
|
44
|
+
Salsa20.quarterRound(workingState, 0, 5, 10, 15);
|
|
45
|
+
Salsa20.quarterRound(workingState, 1, 6, 11, 12);
|
|
46
|
+
Salsa20.quarterRound(workingState, 2, 7, 8, 13);
|
|
47
|
+
Salsa20.quarterRound(workingState, 3, 4, 9, 14);
|
|
48
|
+
}
|
|
49
|
+
for (let i = 0; i < 16; i++) {
|
|
50
|
+
workingState[i] += state[i];
|
|
51
|
+
}
|
|
52
|
+
this.counter++; // Increment counter for next block
|
|
53
|
+
return new Uint8Array(workingState.buffer);
|
|
54
|
+
}
|
|
55
|
+
encrypt(plaintext) {
|
|
56
|
+
const textBytes = new TextEncoder().encode(plaintext);
|
|
57
|
+
const ciphertext = new Uint8Array(textBytes.length);
|
|
58
|
+
for (let i = 0; i < textBytes.length; i += 64) {
|
|
59
|
+
const keystream = this.generateKeystreamBlock();
|
|
60
|
+
const blockLength = Math.min(64, textBytes.length - i);
|
|
61
|
+
for (let j = 0; j < blockLength; j++) {
|
|
62
|
+
ciphertext[i + j] = textBytes[i + j] ^ keystream[j];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return Salsa20.encodeBase64(ciphertext);
|
|
66
|
+
}
|
|
67
|
+
decrypt(ciphertext) {
|
|
68
|
+
const originalCounter = this.counter;
|
|
69
|
+
this.counter = 0;
|
|
70
|
+
const cipherUnit8Array = Salsa20.decodeBase64(ciphertext);
|
|
71
|
+
const decryptedBytes = new Uint8Array(cipherUnit8Array.length);
|
|
72
|
+
for (let i = 0; i < cipherUnit8Array.length; i += 64) {
|
|
73
|
+
const keystream = this.generateKeystreamBlock();
|
|
74
|
+
const blockLength = Math.min(64, cipherUnit8Array.length - i);
|
|
75
|
+
for (let j = 0; j < blockLength; j++) {
|
|
76
|
+
decryptedBytes[i + j] = cipherUnit8Array[i + j] ^ keystream[j];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
this.counter = originalCounter;
|
|
80
|
+
return new TextDecoder().decode(decryptedBytes);
|
|
81
|
+
}
|
|
82
|
+
static generateKey() {
|
|
83
|
+
return Salsa20.encodeBase64(crypto.getRandomValues(new Uint8Array(32)));
|
|
84
|
+
}
|
|
85
|
+
static generateNonce() {
|
|
86
|
+
return Salsa20.encodeBase64(crypto.getRandomValues(new Uint8Array(8)));
|
|
87
|
+
}
|
|
88
|
+
static encodeBase64(uint8array) {
|
|
89
|
+
return Buffer.from(uint8array).toString("base64");
|
|
90
|
+
}
|
|
91
|
+
static decodeBase64(base64) {
|
|
92
|
+
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
93
|
+
}
|
|
94
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -4,9 +4,13 @@ import { Atbash } from "./ciphers/Atbash.js";
|
|
|
4
4
|
import { Playfair } from "./ciphers/Playfair.js";
|
|
5
5
|
import { Vigenere } from "./ciphers/Vigenere.js";
|
|
6
6
|
import { Alphabet } from "./ciphers/Alphabet.js";
|
|
7
|
+
import { Salsa20 } from "./ciphers/Salsa20.js";
|
|
8
|
+
import { ADFGVX } from "./ciphers/ADFGVX.js";
|
|
7
9
|
Cipher.Caesar = Caesar;
|
|
8
10
|
Cipher.Atbash = Atbash;
|
|
9
11
|
Cipher.Playfair = Playfair;
|
|
10
12
|
Cipher.Vigenere = Vigenere;
|
|
11
13
|
Cipher.Alphabet = Alphabet;
|
|
14
|
+
Cipher.Salsa20 = Salsa20;
|
|
15
|
+
Cipher.ADFGVX = ADFGVX;
|
|
12
16
|
export { Cipher };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@irfanshadikrishad/cipher",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "A versatile and secure cryptographic library for implementing various cipher algorithms in Node.js applications.",
|
|
5
5
|
"homepage": "https://github.com/irfanshadikrishad/cipher#readme",
|
|
6
6
|
"bugs": {
|