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

package challenge

import (
	"fmt"
	"ricketyspace.net/cryptopals/lib"
)

func C14() {
	blocksize := findBlockSizeForVarEncryptECB()
	rpl, nrpb, rpo := findRandPrefixLen(blocksize)
	nbl, us_sz := findUnknownStringNumBlocksLength(
		rpl, blocksize)
	in := append(freshSheepBlock(rpo), // random prefix offset
		freshSheepBlock(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 = decipherOneByteUS(nrpb, rpo, blocksize,
				i+1, j+1, in, ds)
		}
		s := 16 * i
		e := s + 16
		in = append(freshSheepBlock(rpo), ds[s:e]...)
	}
	fmt.Printf("Unknown String:\n%v", lib.BytesToStr(ds))
}

func findBlockSizeForVarEncryptECB() int {
	in := make([]byte, 0)

	in = append(in, sheep)
	is := len(lib.OracleAESVarEncryptECB(in)) // initial size
	bs := 0
	for {
		in = append(in, sheep)
		bs = len(lib.OracleAESVarEncryptECB(in))
		if bs != is {
			return (bs - is)
		}
	}
}

func findRandPrefixLen(blocksize int) (int, int, int) {
	// Make two sheep blocks.
	tsb := append(freshSheepBlock(blocksize), freshSheepBlock(blocksize)...)

	v := []byte{}
	index := 0
	found := false
	for {
		in := append(v, tsb...)
		c := lib.OracleAESVarEncryptECB(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).
func findUnknownStringNumBlocksLength(rpl, blocksize int) (int, int) {
	padding := blocksize - (rpl % blocksize)
	in := make([]byte, padding)
	c_sz := len(lib.OracleAESVarEncryptECB(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(lib.OracleAESVarEncryptECB(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
func decipherOneByteUS(nrpb, rpo, blocksize, block, n int, in, ds []byte) ([]byte, []byte) {
	oo := lib.OracleAESVarEncryptECB(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 = lib.OracleAESVarEncryptECB(in)

		if lib.BlocksEqual(nbl,
			oo[(nrpb*blocksize):(nrpb*blocksize)+16]) {
			ds = append(ds, in[len(in)-1])
			return in, ds
		}
	}
	panic("not found!")
}