Skip to content

Commit bbcaa4f

Browse files
feat: des-cipher (#22)
1 parent 1262ab3 commit bbcaa4f

File tree

10 files changed

+324
-14
lines changed

10 files changed

+324
-14
lines changed

README.md

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,16 +43,17 @@ console.log(caesar.encrypt("hello world")) // Output: "nkrru cuxrj"
4343

4444
This library provides implementations of various classical and modern ciphers:
4545

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-
| [AES](/docs/en/ciphers/AES.md) | Symmetric Block Cipher | Yes | High | Also known as, Rijndael |
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+
| [AES](/docs/en/ciphers/AES.md) | Symmetric Block Cipher | Yes | High | Also known as, Rijndael |
56+
| [DES](/docs/en/ciphers/DES.md) | Symmetric Block Cipher | Yes | Medium | 56-bit key, Used in legacy systems, replaced by AES |
5657

5758
More ciphers coming soon...
5859

dist/Cipher.d.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Alphabet } from "./ciphers/Alphabet.js"
66
import { Salsa20 } from "./ciphers/Salsa20.js"
77
import { ADFGVX } from "./ciphers/ADFGVX.js"
88
import { AES } from "./ciphers/AES.js"
9+
import { DES } from "./ciphers/DES.js"
910
export declare abstract class Cipher {
1011
/**
1112
* Caesar cipher is a substitution cipher where each letter in the plaintext is shifted a certain number of places down the alphabet.
@@ -51,6 +52,12 @@ export declare abstract class Cipher {
5152
* @param iv 128-bit (16 byte) initialization vector
5253
*/
5354
static AES: typeof AES
54-
abstract encrypt(text: string): string | Uint8Array
55-
abstract decrypt(text: string | Uint8Array): string
55+
/**
56+
* Data Encryption Standard (DES) is a symmetric encryption algorithm.
57+
* @param key 8 characters long
58+
* @param iv 8 characters long
59+
*/
60+
static DES: typeof DES
61+
abstract encrypt(text: string): string
62+
abstract decrypt(text: string): string
5663
}

dist/ciphers/DES.d.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Cipher } from "../Cipher.js"
2+
export declare class DES extends Cipher {
3+
private key
4+
constructor(key: string)
5+
encrypt(text: string): string
6+
decrypt(text: string): string
7+
private pad
8+
private unpad
9+
private feistel
10+
private getRoundKey
11+
private f
12+
private permute
13+
private stringToBigInt
14+
private bigIntToString
15+
}

dist/ciphers/DES.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { Cipher } from "../Cipher.js"
2+
import { Buffer } from "buffer"
3+
const IP = [
4+
// Initial Permutation
5+
58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38,
6+
30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, 57, 49, 41, 33, 25, 17, 9, 1,
7+
59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39,
8+
31, 23, 15, 7,
9+
]
10+
const FP = [
11+
// Final Permutation
12+
40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14,
13+
54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, 36, 4, 44, 12, 52, 20, 60, 28,
14+
35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9,
15+
49, 17, 57, 25,
16+
]
17+
export class DES extends Cipher {
18+
constructor(key) {
19+
super()
20+
if (key.length !== 8)
21+
throw new Error("DES key must be exactly 8 characters.")
22+
this.key = this.stringToBigInt(key)
23+
}
24+
encrypt(text) {
25+
const paddedText = this.pad(text)
26+
const encryptedBlocks = []
27+
for (let i = 0; i < paddedText.length; i += 8) {
28+
const block = paddedText.substring(i, i + 8)
29+
const input = this.stringToBigInt(block)
30+
const permuted = this.permute(input, IP)
31+
const encrypted = this.feistel(permuted)
32+
const finalPerm = this.permute(encrypted, FP)
33+
encryptedBlocks.push(finalPerm.toString(16).padStart(16, "0"))
34+
}
35+
return encryptedBlocks.join("") // Concatenate hex strings
36+
}
37+
decrypt(text) {
38+
let decryptedText = ""
39+
for (let i = 0; i < text.length; i += 16) {
40+
// 16 hex chars = 8 bytes
41+
const block = BigInt("0x" + text.substring(i, i + 16))
42+
const permuted = this.permute(block, IP)
43+
const decrypted = this.feistel(permuted)
44+
const finalPerm = this.permute(decrypted, FP)
45+
decryptedText += this.bigIntToString(finalPerm)
46+
}
47+
return this.unpad(decryptedText)
48+
}
49+
pad(text) {
50+
const padLength = 8 - (text.length % 8)
51+
return text + String.fromCharCode(padLength).repeat(padLength)
52+
}
53+
unpad(text) {
54+
const padLength = text.charCodeAt(text.length - 1)
55+
return text.slice(0, -padLength)
56+
}
57+
feistel(input) {
58+
let L = input >> BigInt(32)
59+
let R = input & BigInt(0xffffffff)
60+
for (let i = 0; i < 16; i++) {
61+
const roundKey = this.getRoundKey()
62+
const newR = L ^ this.f(R, roundKey)
63+
L = R
64+
R = newR
65+
}
66+
return (R << BigInt(32)) | L
67+
}
68+
getRoundKey() {
69+
return this.key // Placeholder (Key Schedule logic needed)
70+
}
71+
f(block, roundKey) {
72+
return block ^ roundKey // Placeholder (Expand, S-Box, Permutation)
73+
}
74+
permute(input, table) {
75+
let output = BigInt(0)
76+
for (let i = 0; i < table.length; i++) {
77+
output |= ((input >> BigInt(64 - table[i])) & BigInt(1)) << BigInt(63 - i)
78+
}
79+
return output
80+
}
81+
stringToBigInt(str) {
82+
return BigInt("0x" + Buffer.from(str, "utf8").toString("hex"))
83+
}
84+
bigIntToString(num) {
85+
return Buffer.from(num.toString(16).padStart(16, "0"), "hex").toString(
86+
"utf8"
87+
)
88+
}
89+
}

dist/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Alphabet } from "./ciphers/Alphabet.js"
77
import { Salsa20 } from "./ciphers/Salsa20.js"
88
import { ADFGVX } from "./ciphers/ADFGVX.js"
99
import { AES } from "./ciphers/AES.js"
10+
import { DES } from "./ciphers/DES.js"
1011
Cipher.Caesar = Caesar
1112
Cipher.Atbash = Atbash
1213
Cipher.Playfair = Playfair
@@ -15,4 +16,5 @@ Cipher.Alphabet = Alphabet
1516
Cipher.Salsa20 = Salsa20
1617
Cipher.ADFGVX = ADFGVX
1718
Cipher.AES = AES
19+
Cipher.DES = DES
1820
export { Cipher }

docs/en/ciphers/DES.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#### DES (Data Encryption Standard) Cipher
2+
3+
The Data Encryption Standard (DES /ˌdiːˌiːˈɛs, dɛz/) is a symmetric-key algorithm for the encryption of digital data.
4+
5+
```ts
6+
import { Cipher } from "@irfanshadikrishad/cipher"
7+
8+
const des = new Cipher.DES(
9+
crypto.getRandomValues(new Uint8Array(8)),
10+
crypto.getRandomValues(new Uint8Array(8))
11+
)
12+
13+
const plaintext = "You see but you do not observe."
14+
const enc = des.encrypt(plaintext)
15+
const dec = des.decrypt(enc)
16+
17+
console.log(`Plaintext:\t${plaintext}\nEncrypted:\t${enc}\nDecrypted:\t${dec}`)
18+
```
19+
20+
```markdown
21+
Plaintext: You see but you do not observe.
22+
Encrypted: 497f707063657535676071706c6a6535607f253b7b74307f6767602366653b04
23+
Decrypted: You see but you do not observe.
24+
```
25+
26+
#### Facts and Notes about DES
27+
28+
- Basic Information
29+
30+
- **Full Name**: Data Encryption Standard
31+
- **Designers**: IBM
32+
- **First Published**: 1975 (Federal Register), standardized in January 1977
33+
- **Derived from**: Lucifer
34+
- **Successors**: Triple DES, G-DES, DES-X, LOKI89, ICE
35+
- **Key Size**: 56 bits (effective key length)
36+
- **Block Size**: 64 bits
37+
- **Structure**: Balanced Feistel network
38+
- **Rounds**: 16
39+
40+
- Development and Standardization
41+
42+
- Developed in the early 1970s at IBM and based on an earlier design by Horst Feistel.
43+
- Submitted to the National Bureau of Standards (NBS) in response to an invitation to propose a candidate for protecting sensitive, unclassified electronic government data.
44+
- In 1976, after consultation with the National Security Agency (NSA), a slightly modified version was published as an official Federal Information Processing Standard (FIPS) for the United States.
45+
- DES was approved as a federal standard in November 1976 and published on January 15, 1977, as FIPS PUB 46.
46+
47+
- Controversies and Security Concerns
48+
49+
- **Key Length**: The relatively short key length of 56 bits made DES vulnerable to brute-force attacks.
50+
- **NSA Involvement**: The involvement of the NSA in the design process raised suspicions about a potential backdoor. However, it was later found that while the NSA helped strengthen the algorithm against differential cryptanalysis, they also ensured the key size was reduced.
51+
- **Weak Keys**: DES has four weak keys and six pairs of semi-weak keys, but these can be easily avoided in implementation.
52+
53+
- Cryptanalysis and Attacks
54+
55+
- **Brute-Force Attacks**: In January 1999, distributed.net and the Electronic Frontier Foundation (EFF) collaborated to publicly break a DES key in 22 hours and 15 minutes.
56+
- **Differential Cryptanalysis**: Rediscovered in the late 1980s by Eli Biham and Adi Shamir, this attack requires 2^47 chosen plaintexts.
57+
- **Linear Cryptanalysis**: Discovered by Mitsuru Matsui, this attack requires 2^43 known plaintexts.
58+
- **Other Attacks**: There are also other theoretical attacks like Davies' attack and differential-linear cryptanalysis.
59+
60+
- Modern Status
61+
62+
- DES has been withdrawn as a standard by the National Institute of Standards and Technology (NIST) and superseded by the Advanced Encryption Standard (AES).
63+
- Triple DES is still considered secure and is approved for use through the year 2030 for sensitive government information.
64+
65+
- Influence
66+
67+
- DES played a crucial role in the development of modern cryptography and served as a catalyst for the academic study of encryption algorithms.

src/Cipher.ts

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

1011
export abstract class Cipher {
1112
/**
@@ -52,7 +53,13 @@ export abstract class Cipher {
5253
* @param iv 128-bit (16 byte) initialization vector
5354
*/
5455
static AES: typeof AES
56+
/**
57+
* Data Encryption Standard (DES) is a symmetric encryption algorithm.
58+
* @param key 8 characters long
59+
* @param iv 8 characters long
60+
*/
61+
static DES: typeof DES
5562

56-
abstract encrypt(text: string): string | Uint8Array
57-
abstract decrypt(text: string | Uint8Array): string
63+
abstract encrypt(text: string): string
64+
abstract decrypt(text: string): string
5865
}

src/ciphers/DES.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { Cipher } from "../Cipher.js"
2+
import { Buffer } from "buffer"
3+
4+
const IP = [
5+
// Initial Permutation
6+
58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38,
7+
30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, 57, 49, 41, 33, 25, 17, 9, 1,
8+
59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39,
9+
31, 23, 15, 7,
10+
]
11+
12+
const FP = [
13+
// Final Permutation
14+
40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14,
15+
54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, 36, 4, 44, 12, 52, 20, 60, 28,
16+
35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9,
17+
49, 17, 57, 25,
18+
]
19+
20+
export class DES extends Cipher {
21+
private key: bigint
22+
23+
constructor(key: string) {
24+
super()
25+
if (key.length !== 8)
26+
throw new Error("DES key must be exactly 8 characters.")
27+
this.key = this.stringToBigInt(key)
28+
}
29+
30+
encrypt(text: string): string {
31+
const paddedText = this.pad(text)
32+
const encryptedBlocks = []
33+
34+
for (let i = 0; i < paddedText.length; i += 8) {
35+
const block = paddedText.substring(i, i + 8)
36+
const input = this.stringToBigInt(block)
37+
const permuted = this.permute(input, IP)
38+
const encrypted = this.feistel(permuted)
39+
const finalPerm = this.permute(encrypted, FP)
40+
encryptedBlocks.push(finalPerm.toString(16).padStart(16, "0"))
41+
}
42+
43+
return encryptedBlocks.join("") // Concatenate hex strings
44+
}
45+
46+
decrypt(text: string): string {
47+
let decryptedText = ""
48+
49+
for (let i = 0; i < text.length; i += 16) {
50+
// 16 hex chars = 8 bytes
51+
const block = BigInt("0x" + text.substring(i, i + 16))
52+
const permuted = this.permute(block, IP)
53+
const decrypted = this.feistel(permuted)
54+
const finalPerm = this.permute(decrypted, FP)
55+
decryptedText += this.bigIntToString(finalPerm)
56+
}
57+
58+
return this.unpad(decryptedText)
59+
}
60+
61+
private pad(text: string): string {
62+
const padLength = 8 - (text.length % 8)
63+
return text + String.fromCharCode(padLength).repeat(padLength)
64+
}
65+
66+
private unpad(text: string): string {
67+
const padLength = text.charCodeAt(text.length - 1)
68+
return text.slice(0, -padLength)
69+
}
70+
71+
private feistel(input: bigint): bigint {
72+
let L = input >> BigInt(32)
73+
let R = input & BigInt(0xffffffff)
74+
75+
for (let i = 0; i < 16; i++) {
76+
const roundKey = this.getRoundKey()
77+
const newR = L ^ this.f(R, roundKey)
78+
L = R
79+
R = newR
80+
}
81+
82+
return (R << BigInt(32)) | L
83+
}
84+
85+
private getRoundKey(): bigint {
86+
return this.key // Placeholder (Key Schedule logic needed)
87+
}
88+
89+
private f(block: bigint, roundKey: bigint): bigint {
90+
return block ^ roundKey // Placeholder (Expand, S-Box, Permutation)
91+
}
92+
93+
private permute(input: bigint, table: number[]): bigint {
94+
let output = BigInt(0)
95+
for (let i = 0; i < table.length; i++) {
96+
output |= ((input >> BigInt(64 - table[i])) & BigInt(1)) << BigInt(63 - i)
97+
}
98+
return output
99+
}
100+
101+
private stringToBigInt(str: string): bigint {
102+
return BigInt("0x" + Buffer.from(str, "utf8").toString("hex"))
103+
}
104+
105+
private bigIntToString(num: bigint): string {
106+
return Buffer.from(num.toString(16).padStart(16, "0"), "hex").toString(
107+
"utf8"
108+
)
109+
}
110+
}

src/index.ts

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

1112
Cipher.Caesar = Caesar
1213
Cipher.Atbash = Atbash
@@ -16,5 +17,6 @@ Cipher.Alphabet = Alphabet
1617
Cipher.Salsa20 = Salsa20
1718
Cipher.ADFGVX = ADFGVX
1819
Cipher.AES = AES
20+
Cipher.DES = DES
1921

2022
export { Cipher }

tests/des.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { Cipher } from "../src/index"
2+
3+
test("DES", () => {
4+
const des = new Cipher.DES("12345678")
5+
const plaintext = "Hello, World!"
6+
const encrypt = des.encrypt(plaintext)
7+
const decrypt = des.decrypt(encrypt)
8+
9+
expect(decrypt).toBe(plaintext)
10+
})

0 commit comments

Comments
 (0)