summaryrefslogtreecommitdiffstats
path: root/challenge/c14.go
blob: adfadc834af802f7caa3df6db769c46fb4087993 (plain) (blame)
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
// Copyright © 2021 rsiddharth <s@ricketyspace.net>
// SPDX-License-Identifier: ISC

package challenge

import (
	"fmt"

	"ricketyspace.net/cryptopals/lib"
)

func C14() {
	sheep := byte(65)
	unknown := `Um9sbGluJyBpbiBteSA1LjAKV2l0aCBteSByYWctdG9wIGRvd24gc28gbXkg
aGFpciBjYW4gYmxvdwpUaGUgZ2lybGllcyBvbiBzdGFuZGJ5IHdhdmluZyBq
dXN0IHRvIHNheSBoaQpEaWQgeW91IHN0b3A/IE5vLCBJIGp1c3QgZHJvdmUg
YnkK`
	key, err := lib.RandomBytes(16)
	if err != nil {
		fmt.Printf("key generatation error: %v", err)
	}
	oracleRandom, err := lib.RandomBytes(int(lib.RandomInt(1, 4096)))
	if err != nil {
		fmt.Printf("oracle random generation error: %v", err)
	}
	encrypt := func(in []byte) []byte {
		in = append(oracleRandom, in...)
		in = append(in, lib.Base64ToBytes(unknown)...)
		return lib.AESEncryptECB(in, key)
	}
	freshSheepBytes := func(n int) []byte {
		in := make([]byte, n)
		for i := 0; i < n; i++ {
			in[i] = sheep
		}
		return in
	}
	findBlockSize := func() int {
		in := make([]byte, 0)

		in = append(in, sheep)
		is := len(encrypt(in)) // initial size
		bs := 0
		for {
			in = append(in, sheep)
			bs = len(encrypt(in))
			if bs != is {
				return (bs - is)
			}
		}
	}
	findPrefixLen := func(blocksize int) (int, int, int) {
		// Make two sheep blocks.
		tsb := append(freshSheepBytes(blocksize), freshSheepBytes(blocksize)...)

		v := []byte{}
		index := 0
		found := false
		for {
			in := append(v, tsb...)
			c := encrypt(in)
			index, found = lib.HasConsecutiveMatchingBlocks(c, blocksize)
			if found {
				break
			}
			// Add another sheep
			v = append(v, sheep)
		}
		l := index - len(v)
		nrpb := l / blocksize // number of randbox prefix blocks
		rpo := 0              // random prefix offset
		if l%blocksize != 0 {
			nrpb += 1
			rpo = blocksize - (l % blocksize)
		}
		return l, nrpb, rpo
	}
	// Finds the cipher block size and the length of the unknown string
	// (target-bytes).
	findUnknownStringNumBlocksLength := func(rpl, blocksize int) (int, int) {
		padding := blocksize - (rpl % blocksize)
		in := make([]byte, padding)
		c_sz := len(encrypt(in)) // Cipher size

		nblocks := c_sz / blocksize            // total number of blocks
		rblocks := (rpl + padding) / blocksize // number of blocks of random prefix
		ublocks := nblocks - rblocks           // number of blocks of unknown string
		// Figure out size of unknown string.
		for {
			in = append(in, sheep)
			bs := len(encrypt(in))
			if bs != c_sz {
				return ublocks, (c_sz - len(in) - rpl)
			}
		}
	}
	// `nrpb` number of random prefix blocks
	// `rpo` random prefix offset
	// `blocksize` is the size of a block
	// `block` is the nth block that is being deciphered
	// `n` is the nth byte of the block `block` that is going to be deciphered.
	// `in` (n-1)th block that is known
	// `ds` deciphered unknown string
	decipherOneByte := func(nrpb, rpo, blocksize, block, n int, in, ds []byte) ([]byte, []byte) {
		oo := encrypt(in[0:(len(in) - n)])

		s := (nrpb * blocksize) + 16*(block-1)
		e := s + 16
		nbl := oo[s:e] // nth block of the cipher

		// Shift `in` to the left by one place.
		for i := rpo; i < len(in)-1; i++ {
			in[i] = in[i+1]
		}

		// Try all combinations.
		for i := 0; i < 256; i++ {
			in[len(in)-1] = byte(i)
			oo = encrypt(in)

			if lib.BlocksEqual(nbl,
				oo[(nrpb*blocksize):(nrpb*blocksize)+16]) {
				ds = append(ds, in[len(in)-1])
				return in, ds
			}
		}
		panic("not found!")
	}
	blocksize := findBlockSize()
	rpl, nrpb, rpo := findPrefixLen(blocksize)
	nbl, us_sz := findUnknownStringNumBlocksLength(
		rpl, blocksize)
	in := append(freshSheepBytes(rpo), // random prefix offset
		freshSheepBytes(blocksize)...)
	ds := make([]byte, 0) // deciphered unknown string in bytes
	for i := 0; i < nbl; i++ {
		nby := blocksize
		if i == nbl-1 {
			nby = us_sz % blocksize
		}
		for j := 0; j < nby; j++ {
			in, ds = decipherOneByte(nrpb, rpo, blocksize,
				i+1, j+1, in, ds)
		}
		s := 16 * i
		e := s + 16
		in = append(freshSheepBytes(rpo), ds[s:e]...)
	}
	fmt.Printf("Unknown String:\n%v", lib.BytesToStr(ds))
}

// Output:
// Unknown String:
// Rollin' in my 5.0
// With my rag-top down so my hair can blow
// The girlies on standby waving just to say hi
// Did you stop? No, I just drove by