-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathset1.go
356 lines (288 loc) · 7.85 KB
/
set1.go
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
// Package matasano does some stuff
package matasano
import (
"crypto/aes"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"io/ioutil"
"log"
"unicode/utf8"
)
func Toggle(in []byte) []byte {
r := make([]byte, len(in))
for i := 0; i < len(in); i++ {
r[i] = ^in[i]
}
return r
}
// Set the bit in pos position to the v value
func setBit(n byte, pos uint, v bool) byte {
if v == true {
return n | (1 << pos)
} else {
return n &^ (1 << pos)
}
}
func hasBit(n byte, pos uint) bool {
return (n & (1 << pos)) > 0
}
// challenge 1
func HexToBase64(s string) string {
r, err := hex.DecodeString(s)
if err != nil {
log.Fatal(err)
}
log.Printf("%s", r)
return base64.StdEncoding.EncodeToString(r)
}
// Function Xor performs bitwise xor between two byte arrays.
//
// This function is used in Set1/Challenge 2
func Xor(s1, s2 []byte) ([]byte, error) {
if len(s1) != len(s2) {
log.Printf("s1: %v, len s1: %d", s1, len(s1))
log.Printf("s2: %v, len s2: %d", s2, len(s2))
log.Fatal("Different length buffers\n")
}
res := make([]byte, len(s1))
for i := 0; i < len(s1); i++ {
res[i] = s1[i] ^ s2[i]
}
return res, nil
}
// challenge 3
func SingleByteXor(in []byte, key byte) ([]byte, error) {
var r []byte = make([]byte, len(in))
for i := 0; i < len(r); i++ {
r[i] = in[i] ^ key
}
return r, nil
}
/* the computer cant really tell when a word is an english one, so
* we have to sort of train it to do so by taking real world examples
* (books will do it) and give him a metric by which it can tell a word from
* a non word
*/
func AnalyzeCorpus(text string) map[rune]float64 {
var freq map[rune]float64 = make(map[rune]float64)
total := utf8.RuneCountInString(text)
for _, c := range text {
freq[c] += 1
}
/* normalize frequencies */
for c := range freq {
freq[c] = freq[c] / float64(total)
}
return freq
}
// Count the frequency of each letter and calculate the score
// as a weighted sum. naturally, non english words will score lower due to
// rare characters being more frequent (lower weigth)
func ScoreEnglish(text string, freq map[rune]float64) float64 {
var score float64
var t map[rune]float64 = make(map[rune]float64)
for _, c := range text {
t[c] += 1
}
for c, _ := range t {
score += t[c] * freq[c]
}
return score
}
// LoadCorpus is a helper function that opens a text file
// and returns its content
func LoadCorpus(filename string) (string, error) {
text, err := ioutil.ReadFile(filename)
if err != nil {
return "", err
}
return string(text), nil
}
// initCorpus is a helper function that returns Alice in Wonderland
// and the frequency of english letters, obtained analyzing Alice in Wonderland
func initCorpus() (string, map[rune]float64) {
data, err := LoadCorpus("_testdata/aliceinwonderland.txt")
if err != nil {
log.Fatal(err)
}
return data, AnalyzeCorpus(data)
}
// challenge 4
func DetectSingleByteXor(in string, freq map[rune]float64) (byte, string, float64) {
var key byte
var plain string
var best_score float64
b, err := hex.DecodeString(in)
if err != nil {
// bytes may note exist UTF8 (common when dealing with encrypted text)
b = []byte(in)
// log.Fatal(err)
}
for i := 1; i < 256; i++ {
enc, err := SingleByteXor(b, byte(i))
if err != nil {
log.Fatal(err)
}
// log.Printf("%s", enc)
curr_score := ScoreEnglish(string(enc), freq)
if curr_score > best_score {
key = byte(i)
plain = string(enc)
best_score = curr_score
}
}
return key, plain, best_score
}
// challenge 5
func RepeatingKeyXor(in, key []byte) []byte {
var r []byte = make([]byte, len(in))
l := len(key)
for i := 0; i < len(in); i++ {
r[i] = in[i] ^ byte(key[i%l])
}
return r
}
// challenge 6
func ComputeHammingDistance(s1, s2 []byte) int {
var distance int
mask := byte(01)
xor, err := Xor(s1, s2)
if err != nil {
log.Fatal(err)
}
for i := 0; i < len(xor); i++ {
b := xor[i]
for j := 0; j < 8; j++ {
if mask&b != byte(0) {
distance++
}
b = b >> 1
}
}
return distance
}
// Split splits a buffer @buf in N blocks of @size size
func Split(buf []byte, size int) [][]byte {
var chunk []byte
chunks := make([][]byte, 0, len(buf)/size+1)
for len(buf) >= size {
chunk, buf = buf[:size], buf[size:]
chunks = append(chunks, chunk)
}
if len(buf) > 0 {
chunks = append(chunks, buf[:len(buf)])
}
return chunks
}
// TransposeBlocks will transpose a N x M matrix
func TransposeBlocks(in [][]byte) [][]byte {
// Create a matrix with N=col and M=1
transposed_blocks := make([][]byte, len(in[0]))
for i := 0; i < len(in); i++ {
for j := 0; j < len(in[i]); j++ {
// log.Printf("i: %d, j: %d", i, j)
transposed_blocks[j] = append(transposed_blocks[j], in[i][j])
}
}
return transposed_blocks
}
// BreakRepeatingKeyXor recovers the plain text and the key
// from an encrypted byte array @in
func BreakRepeatingKeyXor(in []byte) ([]byte, string) {
var key []byte
var keys []int
var partial_key []byte
var plain string
var plaintext string
var score float64
var best_score float64
// Storing all keys with their respective distances may be interesting...
distances := make(map[int]float64, 41)
for i := 2; i < len(in) && i < 41; i++ {
s := float64(ComputeHammingDistance(in[:i], in[i:i*2]))
// t := float64(ComputeHammingDistance(in[:i*3], in[i:i*4]))
// d := (s + t) / (2 * float64(i))
d := s / float64(i)
distances[i] = d
}
// Extract keysizes and sort them in decreasing order
for k, _ := range distances {
keys = append(keys, k)
}
// Make the cypher text go through every key
// and see which one breaks it. The one that does is the one
// that generates what mostly likely is english
for _, keysize := range keys {
partial_key = nil
plain = ""
score = 0
// Now that you probably know the KEYSIZE: break the ciphertext into blocks of KEYSIZE length.
blocks := Split(in, keysize)
// Now transpose the blocks: make a block that is the first byte of
// every block, and a block that is the second byte of every block,
// and so on.
tblocks := TransposeBlocks(blocks)
// tblocks := blocks
// Compute english's letters frequency
_, freq := initCorpus()
// Transposing blocks is only useful for the purpose of solving
// them with SingleByteXor() but not as much anything else.
// The plain text is not that interesting either as we would have
// to transpose the text again...
// Since this is a symmetric cypher encrypting the input with
// the know known key will suffice
// Solve each block as if it was single-byte XOR
for i := 0; i < len(tblocks); i++ {
k, p, s := DetectSingleByteXor(string(tblocks[i]), freq)
partial_key = append(partial_key, k)
plain += p
score += s
}
// Since multiple keys are being tested, the one that scores the best
// is the one used for the encryption
if best_score < score {
key = partial_key
plaintext = plain
best_score = score
}
}
plaintext = string(RepeatingKeyXor(in, key))
return key, plaintext
}
func AESDecryptECB(data, key []byte) ([]byte, error) {
plaintext := make([]byte, len(data))
keySize := len(key)
if keySize != 8 && keySize != 16 && keySize != 32 {
return nil, errors.New(fmt.Sprintf("invalid key size, use 8, 16 or 32 bytes (64, 128, 256 bits)"))
}
blocks, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
for i := 0; i <= len(plaintext)-blocks.BlockSize(); i += blocks.BlockSize() {
blocks.Decrypt(plaintext[i:i+blocks.BlockSize()], data[i:i+blocks.BlockSize()])
}
return plaintext, nil
}
// challenge 8
func DetectAESECB(in string) int {
var freq map[string]int = make(map[string]int)
var max int
size := 16
enc := []byte(in)
// Count frequencies for each block
for i := 0; i < len(enc)-size; i += size {
freq[in[i:i+size]] += 1
}
// Find the highest number of repeated blocks
// The higher the number of occurences, the
// higher the chance it was ECB encrypted
for _, v := range freq {
if v > max {
max = v
}
}
return max
}