summaryrefslogtreecommitdiffstats
path: root/challenge/c27.go
blob: 6a5051f16b77b601969651edb2de240106eedf8b (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
// Copyright © 2021 siddharth <s@ricketyspace.net>
// SPDX-License-Identifier: ISC

package challenge

import (
	"fmt"

	"ricketyspace.net/cryptopals/lib"
)

func C27() {
	// Generate random key.
	key, err := lib.RandomBytes(16)
	if err != nil {
		fmt.Printf("Error: %v", err)
		return
	}

	// Make IV same as `key`.
	iv := key

	// Encrypt `plain` with AES-CBC
	encrypt := func(plain []byte) []byte {
		return lib.AESEncryptCBC(plain, key, iv)
	}

	// Same as lib.AESDecryptCBC but ignores padding error and
	// checks if plain text has high ASCII values.
	//
	// Always returns full plain text; even when there is an
	// error.
	decrypt := func(cipher []byte) ([]byte, error) {
		iter := len(cipher) / 16

		lc := iv
		output := make([]byte, 0)
		for i := 0; i < iter; i++ {
			s := (i * 16)
			e := (i * 16) + 16
			c := cipher[s:e]
			output = append(output, lib.FixedXORBytes(
				lib.AESInvCipher(c, key), lc)...)

			lc = c
		}

		// Undo padding
		plain, err := lib.Pkcs7PaddingUndo(output)
		if err != nil {
			// If padding undo fails, just use `output`.
			plain = output
		}

		// Check if `plain` high ASCII
		for _, p := range plain {
			if p >= 128 {
				return plain, lib.CPError{"Has high ASCII values"}
			}
		}

		return plain, nil
	}

	// Encrypt atleast 3 blocks of plain text.
	c := encrypt(lib.StrToBytes(
		"As soon as you are born they make you feel small"))

	// Modify cipher.
	copy(c[32:48], c[0:16])              // C_3 <- C_1
	copy(c[16:32], lib.FillBytes(0, 16)) // C_2 <- 0

	// Try decrypting.
	p, err := decrypt(c)
	if err != nil {
		// Has high ASCII values; recover key.
		k := lib.FixedXORBytes(p[0:16], p[32:48])
		if lib.BytesEqual(k, key) {
			fmt.Printf("Recovered key: %v\n", k)
			return
		}
	}
	fmt.Printf("Could not recover key\n")
}

// Output:
// Recovered key: [62 228 155 158 92 189 217 142 58 118 162 140 165 41 152 237]