forked from ctz/cryptopals
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mcp56.c
executable file
·136 lines (107 loc) · 3.09 KB
/
mcp56.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include "util-1.h"
/* assume we know this (we can find it out with oracle('') if not...) */
#define SECRET_LEN 30
/* 2^24 seems enough for reliable recovery */
#define ROUNDS 0x1000000
/* weighting for big and slight biases */
#define FULL_WEIGHT 4
#define HALF_WEIGHT 1
/* --- quick and dirty rc4 --- */
static void swap8(uint8_t *left, uint8_t *right)
{
uint8_t tmp = *left;
*left = *right;
*right = tmp;
}
static void rc4(const uint8_t *key, size_t keylen, uint8_t *xorbuf, size_t xorbuflen)
{
uint8_t S[256];
for (unsigned i = 0; i < 256; i++)
S[i] = i;
for (unsigned i = 0, j = 0; i < 256; i++)
{
j = (j + S[i] + key[i % keylen]) & 0xff;
swap8(S + i, S + j);
}
for (unsigned i = 0, j = 0, n = 0; n < xorbuflen; n++)
{
i = (i + 1) & 0xff;
j = (j + S[i]) & 0xff;
swap8(S + i, S + j);
xorbuf[n] ^= S[(S[i] + S[j]) & 0xff];
}
}
static void oracle(const byteblock *prefix, const byteblock *secret, byteblock *out)
{
uint8_t key[16];
random_fill(key, sizeof key);
out->len = prefix->len + secret->len;
memcpy(out->buf, prefix->buf, prefix->len);
memcpy(out->buf + prefix->len, secret->buf, secret->len);
rc4(key, sizeof key, out->buf, out->len);
}
static uint8_t winner(unsigned byte[256])
{
uint8_t win = 0;
unsigned best_score = 0;
for (size_t i = 0; i < 256; i++)
{
if (byte[i] > best_score)
{
best_score = byte[i];
win = i;
}
}
return win;
}
static byteblock recover(pool *p, unsigned counts[SECRET_LEN][256])
{
byteblock r = { p->alloc(p, SECRET_LEN), SECRET_LEN };
for (size_t i = 0; i < SECRET_LEN; i++)
{
r.buf[i] = winner(counts[i]);
}
return r;
}
int main(int argc, char **argv)
{
pool p[1] = { pool_create() };
random_init();
/* biases at 16 and 32, so we prefix our 30 byte secret with
* two bytes to start with, up to 17 */
byteblock prefix = { (uint8_t *) "AAAAAAAAAAAAAAAAAA", 2 };
assert(argc == 2);
byteblock secret = from_base64(p, argv[1]);
assert(secret.len == SECRET_LEN);
unsigned counts[SECRET_LEN][256];
memset(counts, 0, sizeof(counts));
while (prefix.len < 18)
{
uint8_t buf[64];
byteblock bb = { buf, 0 };
for (size_t i = 0; i < ROUNDS; i++)
{
oracle(&prefix, &secret, &bb);
if (prefix.len <= 15)
{
/* bias at 16 towards 240 (full) 0 (half) 16 (half) */
uint8_t b16 = buf[15];
counts[15 - prefix.len][b16 ^ 240] += FULL_WEIGHT;
counts[15 - prefix.len][b16 ^ 0] += HALF_WEIGHT;
counts[15 - prefix.len][b16 ^ 16] += HALF_WEIGHT;
}
/* bias at 32 towards 224 (full) 0 (half) 32 (half) */
uint8_t b32 = buf[31];
counts[31 - prefix.len][b32 ^ 224] += FULL_WEIGHT;
counts[31 - prefix.len][b32 ^ 0] += HALF_WEIGHT;
counts[31 - prefix.len][b32 ^ 32] += HALF_WEIGHT;
}
prefix.len++;
byteblock plaintext = recover(p, counts);
printf("guess: %s\n", to_ascii(p, &plaintext));
}
byteblock recovered = recover(p, counts);
printf("message: %s\n", to_ascii(p, &recovered));
p->finish(p);
return 0;
}