PL
JavaScript - szyfr z kluczem jednorazowym przy użyciu XOR
8 points
W tym krótkim artykule, chcielibyśmy przedstawić prostą, bazującą na XOR, implementację szyfru z kluczem jednorazowym w języku JavaScript.
Wykorzystywana formuła XOR:
xxxxxxxxxx
1
Szyfrowanie:
2
3
00110100 bajt wejściowy
4
XOR 11000111 sekretny klucz
5
--------
6
11110011 zaszyfrowany bajt
7
8
Deszyfrowanie:
9
10
11110010 zaszyfrowany bajt
11
XOR 11000111 sekretny klucz
12
--------
13
11110011 Deszyfrowany bajt == bajt wejściowy
Szybkie rozwiązanie:
1. szyfrowanie pojedyńczego bajtu
xxxxxxxxxx
1
const inputByte = 97;
2
const secretKey = 238; // losowy bajt uzyty jako sekretny klucz
3
4
const encryptedByte = inputByte ^ secretKey; // Szyfrowanie
5
const decryptedByte = encryptedByte ^ secretKey; // Deszyfrowanie
6
7
console.log(inputByte); // 97
8
console.log(encryptedByte); // 143
9
console.log(decryptedByte); // 97
2. szyfrowanie wielu bajtów
xxxxxxxxxx
1
// Logika:
2
3
const encryptBytes = (secretKeys, inputBytes) => {
4
const encryptedBytes = Array(inputBytes.length);
5
for (let i = 0; i < inputBytes.length; ++i) {
6
encryptedBytes[i] = inputBytes[i] ^ secretKeys[i];
7
}
8
return encryptedBytes;
9
};
10
11
const decryptBytes = (secretKeys, encryptedBytes) => {
12
const decryptedBytes = Array(encryptedBytes.length);
13
for (let i = 0; i < encryptedBytes.length; ++i) {
14
decryptedBytes[i] = encryptedBytes[i] ^ secretKeys[i];
15
}
16
return decryptedBytes;
17
};
18
19
// Dane:
20
21
const inputBytes = [97, 98, 99]; // reprezentuje ciąg 'abc', gdy używamy kodowania UTF-8
22
const secretKeys = [238, 203, 230]; // 3 losowe bajty uzyte jako sekretny klucz
23
24
// Szyfrowanie / Deszyfrowanie:
25
26
const encryptedBytes = encryptBytes(secretKeys, inputBytes);
27
const decryptedBytes = decryptBytes(secretKeys, encryptedBytes);
28
29
// Wynik:
30
31
console.log(inputBytes); // [97, 98, 99]
32
console.log(encryptedBytes); // [143, 169, 133]
33
console.log(decryptedBytes); // [97, 98, 99]
Algorym ten jest tak silny jak perfekcyjne są liczby losowe użyte jako klucze prywatne.
Koncepcja szyfru z kluczem jednorazowym opiera się na:
- użyciu formuły XOR na elementarnych porcjach danych (w naszym przypadku są to bajty),
Wskazówka: sprawdź znajdującą się na końcu artykułu referencję do Wikipedi, aby poznać inne możliwe formuły szyfrowania danych po za XOR.
- przechowywaniu sekretnych kluczy na urządzeniu nadawcy i odbiorcy, składających się z perfekcyjnych liczb losowych (liczy losowe wygenerowane przez popularne algorytmy mogą być przewidywalne, więc warto użyć innych metod bazujących np. na naturze),
- nieudostępnianiu nikomu liczb losowych po za nadawcą i odbiorcą,
- używaniu zawsze tylko części sekretnych kluczy do szyforwania i deszyfrowania przesyłanych wiadomości (jeden bajt szyfrowany przez jeden sekretny klucz),
- nie używaniu nigdy skeretnego klucz więcej niż raz.
Poniższy program przechowuje informację o ostatnim sekretnym kluczu, który został użyty do szyfrowania i deszyfrowania, chroniąc przd złamaniem zasady 5 (nie używaniu nigdy skeretnego klucz więcej niż raz).
xxxxxxxxxx
1
// Szyfr z kluczem jednorazowym przy uzyciu XOR
2
3
const Cipher = (secretKeys) => {
4
const createScope = () => {
5
let secretOffset = 0;
6
const execute = (inputBytes) => {
7
if (secretOffset + inputBytes.length > secretKeys.length) {
8
throw new Error('There is not enought secret keys to encode / decode bytes.');
9
}
10
const outputBytes = Array(inputBytes.length);
11
for (let i = 0; i < inputBytes.length; i += 1, secretOffset += 1) {
12
outputBytes[i] = secretKeys[secretOffset] ^ inputBytes[i];
13
}
14
return outputBytes;
15
};
16
return execute;
17
};
18
return {
19
encrypt: createScope(),
20
decrypt: createScope() // do deszyfrowania uzywamy tej również funkcji szyfrującej
21
};
22
};
23
24
25
// Przykład uzycia:
26
27
// wspólna część - informacje, które znają się u nadawcy i odbiorcy
28
29
// dla poniższego przykładu używamy zwykłych liczb losowych - pamiętaj, aby były one z zakresu od 0 do 255 (włącznie)
30
const secretKeys = [238, 203, 230, 91, 223, 161, 59, 10, 105, 200, 45, 145, 64, 145, 210]; // sekwencja liczb losowych użyta jako sekretny klucz
31
32
33
// nadawca
34
35
const senderCipher = Cipher(secretKeys);
36
37
const encryptedBytes1 = senderCipher.encrypt([97, 98, 99]); // reprezentuje ciąg 'abc', gdy używamy kodowania UTF-8
38
const encryptedBytes2 = senderCipher.encrypt([49, 50, 51]); // reprezentuje ciąg '123', gdy używamy kodowania UTF-8
39
const encryptedBytes3 = senderCipher.encrypt([97, 98, 99]); // reprezentuje ciąg 'abc', gdy używamy kodowania UTF-8
40
41
console.log(`Szyfrowanie 1: ${encryptedBytes1}`); // Szyfrowanie 1: [143, 169, 133]
42
console.log(`Szyfrowanie 2: ${encryptedBytes2}`); // Szyfrowanie 2: [106, 237, 146]
43
console.log(`Szyfrowanie 3: ${encryptedBytes3}`); // Szyfrowanie 3: [90, 104, 10]
44
45
46
// odbiorca
47
48
const recipientCipher = Cipher(secretKeys);
49
50
const decryptedBytes1 = recipientCipher.decrypt(encryptedBytes1);
51
const decryptedBytes2 = recipientCipher.decrypt(encryptedBytes2);
52
const decryptedBytes3 = recipientCipher.decrypt(encryptedBytes3);
53
54
console.log(`Deszyfrowanie 1: ${decryptedBytes1}`); // Deszyfrowanie 1: [97, 98, 99]
55
console.log(`Deszyfrowanie 2: ${decryptedBytes2}`); // Deszyfrowanie 2: [49, 50, 51]
56
console.log(`Deszyfrowanie 3: ${decryptedBytes3}`); // Deszyfrowanie 3: [97, 98, 99]
Wnioski:
[97, 98, 99]
zakodowany 2 razy zwrócił 2 różne wyniki:[143, 169, 133]
oraz[90, 104, 10]
- jest to zagwarantowane poprzez jednorazowe użycie każdego klucza, który jest liczbą losową.
xxxxxxxxxx
1
const maskBytes = (offset, key, bytes) => {
2
const result = Array(bytes.length);
3
for (let i = 0; i < bytes.length; ++i) {
4
result[i] = key[offset + i] ^ bytes[i];
5
}
6
return result;
7
};
8
9
const Cipher = (secret) => {
10
let offset = 0;
11
const execute = (bytes) => {
12
if (offset + bytes.length > secretSequence.length) {
13
throw new Error('There is not enought numbers in the secret sequence to encode / decode bytes.');
14
}
15
const result = maskBytes(offset, secret, bytes);
16
offset += result.length;
17
return result;
18
};
19
return {
20
encrypt: execute,
21
decrypt: execute
22
};
23
};