Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
jvdsn
GitHub Repository: jvdsn/crypto-attacks
Path: blob/master/test/test_otp.py
2587 views
1
import os
2
import sys
3
from math import log10
4
from unittest import TestCase
5
6
path = os.path.dirname(os.path.dirname(os.path.realpath(os.path.abspath(__file__))))
7
if sys.path[1] != path:
8
sys.path.insert(1, path)
9
10
from attacks.otp import key_reuse
11
12
13
class TestOTP(TestCase):
14
def test_key_reuse(self):
15
# Source: http://pi.math.cornell.edu/~mec/2003-2004/cryptography/subs/frequencies.html
16
char_frequencies = {"a": 8.12, "b": 1.49, "c": 2.71, "d": 4.32, "e": 12.02, "f": 2.30, "g": 2.03, "h": 5.92, "i": 7.31, "j": 0.10, "k": 0.69, "l": 3.98, "m": 2.61, "n": 6.95, "o": 7.68, "p": 1.82, "q": 0.11, "r": 6.02, "s": 6.28, "t": 9.10, "u": 2.88, "v": 1.11, "w": 2.09, "x": 0.17, "y": 2.11, "z": 0.07}
17
char_floor = log10(0.01 / 182303)
18
19
key = bytes.fromhex("4d4e2acd3248a5b5ecb106cff94cf10623979966f49a7fd020e395f31c58f0151b9fc78b24b5cf205efee937a8")
20
lines = [
21
b"I used to rule the world\n",
22
b"Seas would rise when I gave the word\n",
23
b"Now in the morning, I sleep alone\n",
24
b"Sweep the streets I used to own\n",
25
b"I used to roll the dice\n",
26
b"Feel the fear in my enemy's eyes\n",
27
b"Listened as the crowd would sing\n",
28
b"Now the old king is dead, long live the king\n",
29
b"One minute I held the key\n",
30
b"Next, the walls were closed on me\n",
31
b"And I discovered that my castles stand\n",
32
b"Upon pillars of salt and pillars of sand\n",
33
b"I hear Jerusalem bells a-ringing\n",
34
b"Roman cavalry choirs are singing\n",
35
b"Be my mirror, my sword and shield\n",
36
b"My missionaries in a foreign field\n",
37
b"For some reason, I can't explain\n",
38
b"Once you'd gone, there was never\n",
39
b"Never an honest word\n",
40
b"And that was when I ruled the world\n",
41
b"It was a wicked and wild wind\n",
42
b"Blew down the doors to let me in\n",
43
b"Shattered windows and the sound of drums\n",
44
b"People couldn't believe what I'd become\n",
45
b"Revolutionaries wait\n",
46
b"For my head on a silver plate\n",
47
b"Just a puppet on a lonely string (Mmm, mmm)\n",
48
b"Oh, who would ever want to be king?\n",
49
b"I hear Jerusalem bells a-ringing\n",
50
b"Roman cavalry choirs are singing\n",
51
b"Be my mirror, my sword and shield\n",
52
b"My missionaries in a foreign field\n",
53
b"For some reason, I can't explain\n",
54
b"I know Saint Peter won't call my name\n",
55
b"Never an honest word\n",
56
b"But that was when I ruled the world\n",
57
]
58
59
test_known = {
60
1: {1: 0, 2: 0, 4: 1, 5: 3},
61
3: {1: 0, 2: 0, 4: 1, 6: 1, 7: 0, 12: 4},
62
6: {1: 0, 2: 0, 4: 0, 6: 0, 7: 0, 12: 0, 16: 3},
63
36: {1: 0, 2: 0, 4: 0, 6: 0, 7: 0, 12: 0, 16: 0, 24: 0, 30: 0, 36: 3, 45: 12},
64
}
65
66
for c_size, key_sizes in test_known.items():
67
for key_size, diff in key_sizes.items():
68
c = [bytes([b ^ key[i % key_size] for i, b in enumerate(line)]) for line in lines[:c_size]]
69
key_ = key_reuse.attack(c, char_frequencies, char_floor, key_size=key_size)
70
self.assertEqual(key_size, len(key_))
71
self.assertEqual(diff, sum(x != y for x, y in zip(key, key_)))
72
73
test_unknown = {
74
1: {5: 3},
75
3: {7: 0, 12: 4},
76
6: {7: 0, 12: 0, 16: 3},
77
36: {4: 0, 6: 0, 7: 0, 12: 0, 16: 0, 24: 0, 30: 0, 36: 3},
78
}
79
80
for c_size, key_sizes in test_unknown.items():
81
for key_size, diff in key_sizes.items():
82
c = [bytes([b ^ key[i % key_size] for i, b in enumerate(line)]) for line in lines[:c_size]]
83
key_ = key_reuse.attack(c, char_frequencies, char_floor)
84
self.assertEqual(key_size, len(key_))
85
self.assertEqual(diff, sum(x != y for x, y in zip(key, key_)))
86
87