Skip to content

Commit 1262ab3

Browse files
feat: AES cipher (#21)
1 parent 0a6af15 commit 1262ab3

File tree

10 files changed

+125
-0
lines changed

10 files changed

+125
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ This library provides implementations of various classical and modern ciphers:
5252
| [The Alphabet Cipher](/docs/en/ciphers/ALPHABET.md) | Polyalphabetic | Yes | Medium | Inspired by Vigenere, Cryptography Puzzles |
5353
| [Salsa20](/docs/en/ciphers/SALSA20.md) | Stream Cipher | Yes | High | Modern Cryptography, Secure Communications |
5454
| [ADFGVX](/docs/en/ciphers/ADFGVX.md) | Polybius Square + Columnar Transposition | Yes | Medium | Used in WWI, Known for 6x6 polybius square |
55+
| [AES](/docs/en/ciphers/AES.md) | Symmetric Block Cipher | Yes | High | Also known as, Rijndael |
5556

5657
More ciphers coming soon...
5758

dist/Cipher.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Vigenere } from "./ciphers/Vigenere.js"
55
import { Alphabet } from "./ciphers/Alphabet.js"
66
import { Salsa20 } from "./ciphers/Salsa20.js"
77
import { ADFGVX } from "./ciphers/ADFGVX.js"
8+
import { AES } from "./ciphers/AES.js"
89
export declare abstract class Cipher {
910
/**
1011
* Caesar cipher is a substitution cipher where each letter in the plaintext is shifted a certain number of places down the alphabet.
@@ -44,6 +45,12 @@ export declare abstract class Cipher {
4445
* @param codeword - 36 characters long
4546
*/
4647
static ADFGVX: typeof ADFGVX
48+
/**
49+
* Advanced Encryption Standard (AES) is a symmetric encryption algorithm.
50+
* @param key 256-bit (32 byte) key
51+
* @param iv 128-bit (16 byte) initialization vector
52+
*/
53+
static AES: typeof AES
4754
abstract encrypt(text: string): string | Uint8Array
4855
abstract decrypt(text: string | Uint8Array): string
4956
}

dist/ciphers/AES.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { Cipher } from "../Cipher.js"
2+
export declare class AES extends Cipher {
3+
private key
4+
private iv
5+
constructor(key: string, iv: string)
6+
encrypt(plaintext: string): string
7+
decrypt(ciphertext: string): string
8+
}

dist/ciphers/AES.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import crypto from "crypto"
2+
import { Cipher } from "../Cipher.js"
3+
import { Buffer } from "buffer"
4+
export class AES extends Cipher {
5+
constructor(key, iv) {
6+
super()
7+
this.key = Buffer.from(key, "hex")
8+
this.iv = Buffer.from(iv, "hex")
9+
if (this.key.length !== 32) {
10+
throw new Error("Invalid key length: AES-256 requires a 32-byte key")
11+
}
12+
if (this.iv.length !== 16) {
13+
throw new Error("Invalid IV length: AES requires a 16-byte IV")
14+
}
15+
}
16+
encrypt(plaintext) {
17+
const cipher = crypto.createCipheriv("aes-256-cbc", this.key, this.iv)
18+
let encrypted = cipher.update(plaintext, "utf8", "hex")
19+
encrypted += cipher.final("hex")
20+
return encrypted
21+
}
22+
decrypt(ciphertext) {
23+
const decipher = crypto.createDecipheriv("aes-256-cbc", this.key, this.iv)
24+
let decrypted = decipher.update(ciphertext, "hex", "utf8")
25+
decrypted += decipher.final("utf8")
26+
return decrypted
27+
}
28+
}

dist/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ import { Vigenere } from "./ciphers/Vigenere.js"
66
import { Alphabet } from "./ciphers/Alphabet.js"
77
import { Salsa20 } from "./ciphers/Salsa20.js"
88
import { ADFGVX } from "./ciphers/ADFGVX.js"
9+
import { AES } from "./ciphers/AES.js"
910
Cipher.Caesar = Caesar
1011
Cipher.Atbash = Atbash
1112
Cipher.Playfair = Playfair
1213
Cipher.Vigenere = Vigenere
1314
Cipher.Alphabet = Alphabet
1415
Cipher.Salsa20 = Salsa20
1516
Cipher.ADFGVX = ADFGVX
17+
Cipher.AES = AES
1618
export { Cipher }

docs/en/ciphers/AES.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#### AES (Advanced Encryption Standard) CIPHER
2+
3+
The Advanced Encryption Standard, also known by its original name Rijndael, is a specification for the encryption of electronic data established by the U.S. National Institute of Standards and Technology in 2001.
4+
5+
```ts
6+
import { Cipher } from "@irfanshadikrishad/cipher"
7+
8+
const aes = new Cipher.AES("32bytekey", "16byteinitializebector")
9+
const plaintext = "hello, aes!"
10+
const encrypt = aes.encrypt(plaintext)
11+
const decrypt = aes.decrypt(encrypt)
12+
13+
console.log(
14+
`Plaintext:\t${plaintext}\nEncrypted:\t${encrypt}\nDecrypted:\t${decrypt}`
15+
)
16+
```
17+
18+
#### Notes
19+
20+
- This one uses `CBC (Cipher Block Chaining)`, It’s more secure than ECB and ensures each block is dependent on the previous one.
21+
- Using `IV (Identical Vector)`, It prevents identical plaintexts from encrypting to the same ciphertext.

src/Cipher.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Vigenere } from "./ciphers/Vigenere.js"
55
import { Alphabet } from "./ciphers/Alphabet.js"
66
import { Salsa20 } from "./ciphers/Salsa20.js"
77
import { ADFGVX } from "./ciphers/ADFGVX.js"
8+
import { AES } from "./ciphers/AES.js"
89

910
export abstract class Cipher {
1011
/**
@@ -45,6 +46,12 @@ export abstract class Cipher {
4546
* @param codeword - 36 characters long
4647
*/
4748
static ADFGVX: typeof ADFGVX
49+
/**
50+
* Advanced Encryption Standard (AES) is a symmetric encryption algorithm.
51+
* @param key 256-bit (32 byte) key
52+
* @param iv 128-bit (16 byte) initialization vector
53+
*/
54+
static AES: typeof AES
4855

4956
abstract encrypt(text: string): string | Uint8Array
5057
abstract decrypt(text: string | Uint8Array): string

src/ciphers/AES.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import crypto from "crypto"
2+
import { Cipher } from "../Cipher.js"
3+
import { Buffer } from "buffer"
4+
5+
export class AES extends Cipher {
6+
private key: Buffer
7+
private iv: Buffer
8+
9+
constructor(key: string, iv: string) {
10+
super()
11+
this.key = Buffer.from(key, "hex")
12+
this.iv = Buffer.from(iv, "hex")
13+
14+
if (this.key.length !== 32) {
15+
throw new Error("Invalid key length: AES-256 requires a 32-byte key")
16+
}
17+
if (this.iv.length !== 16) {
18+
throw new Error("Invalid IV length: AES requires a 16-byte IV")
19+
}
20+
}
21+
22+
encrypt(plaintext: string): string {
23+
const cipher = crypto.createCipheriv("aes-256-cbc", this.key, this.iv)
24+
let encrypted = cipher.update(plaintext, "utf8", "hex")
25+
encrypted += cipher.final("hex")
26+
return encrypted
27+
}
28+
29+
decrypt(ciphertext: string): string {
30+
const decipher = crypto.createDecipheriv("aes-256-cbc", this.key, this.iv)
31+
let decrypted = decipher.update(ciphertext, "hex", "utf8")
32+
decrypted += decipher.final("utf8")
33+
return decrypted
34+
}
35+
}

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Vigenere } from "./ciphers/Vigenere.js"
66
import { Alphabet } from "./ciphers/Alphabet.js"
77
import { Salsa20 } from "./ciphers/Salsa20.js"
88
import { ADFGVX } from "./ciphers/ADFGVX.js"
9+
import { AES } from "./ciphers/AES.js"
910

1011
Cipher.Caesar = Caesar
1112
Cipher.Atbash = Atbash
@@ -14,5 +15,6 @@ Cipher.Vigenere = Vigenere
1415
Cipher.Alphabet = Alphabet
1516
Cipher.Salsa20 = Salsa20
1617
Cipher.ADFGVX = ADFGVX
18+
Cipher.AES = AES
1719

1820
export { Cipher }

tests/aes.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import crypto from "crypto"
2+
import { Cipher } from "../src/index"
3+
4+
test("AES", () => {
5+
const key = crypto.randomBytes(32).toString("hex")
6+
const iv = crypto.randomBytes(16).toString("hex")
7+
const aes = new Cipher.AES(key, iv)
8+
9+
const plaintext = "Hello, World!"
10+
const ciphertext = aes.encrypt(plaintext)
11+
const decrypted = aes.decrypt(ciphertext)
12+
13+
expect(decrypted).toBe(plaintext)
14+
})

0 commit comments

Comments
 (0)