-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathset3helpers.go
130 lines (119 loc) · 3.9 KB
/
set3helpers.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
package cryptopals
import (
"bytes"
"crypto/aes"
"crypto/cipher"
)
func decryptCBCPaddingOracle(cipherText, iv []byte, decryptAndCheckPadding func([]byte) bool) []byte {
var (
plainText []byte
cipherTextToChange []byte
)
for len(cipherText) > 0 {
thisBlock := make([]byte, aes.BlockSize)
for i := 1; i <= len(thisBlock); i++ {
// XOR with the appropriate padding bytes.
xorTwoByteStringsInPlace(thisBlock[len(thisBlock)-i:], bytes.Repeat([]byte{byte(i)}, i))
// If we're decrypting the last block, then we need to change the IV.
if len(cipherText) <= aes.BlockSize {
cipherTextToChange = iv[:]
} else {
// Otherwise, we get the appropriate chunk of cipher text.
cipherTextToChange = cipherText[len(cipherText)-2*aes.BlockSize : len(cipherText)-aes.BlockSize]
}
for j := 255; j >= 0; j-- {
// Check this byte.
thisBlock[len(thisBlock)-i] ^= byte(j)
// XOR, check padding, and XOR back.
xorTwoByteStringsInPlace(cipherTextToChange, thisBlock)
validPadding := decryptAndCheckPadding(cipherText)
xorTwoByteStringsInPlace(cipherTextToChange, thisBlock)
// If we have the right padding, then we have the right byte.
if validPadding {
break
}
// Undo the XOR to try another.
thisBlock[len(thisBlock)-i] ^= byte(j)
}
// Undo the XOR padding because we are done with this byte.
xorTwoByteStringsInPlace(thisBlock[len(thisBlock)-i:], bytes.Repeat([]byte{byte(i)}, i))
}
// We're done with this check of cipher text so we append the plain text to it.
// We're doing this from last chunk to first, so we append backwards.
plainText = append(thisBlock, plainText...)
// Shorten the cipher text so we can check padding easily.
cipherText = cipherText[:len(cipherText)-aes.BlockSize]
}
return removePadding(plainText)
}
// decryptAndCheckPadding decrypts and consumes the cipher text and returns whether the padding is valid.
func (c aescbc) decryptAndCheckPadding(cipherText []byte) bool {
decryptedText, err := c.decrypt(cipherText)
if err != nil {
return false
}
_, err = isValidPadding(decryptedText, c.block.BlockSize())
return err == nil
}
// ctr implements ctr stream cipher mode.
type ctr struct {
counter []byte
nonce []byte
block cipher.Block
key []byte
iv []byte
}
// newCTR is a helper function from creating ctr mode decrypter and encrypter.
func newCTR(nonce, counter, key []byte) ctr {
if key == nil {
key = randomBytes(aes.BlockSize)
}
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
if nonce == nil {
nonce = make([]byte, block.BlockSize()/2)
}
if counter == nil {
counter = make([]byte, block.BlockSize()/2)
}
return ctr{counter: counter, nonce: nonce, iv: make([]byte, block.BlockSize()), key: key, block: block}
}
// keystream generates the next block of keystream.
func (c ctr) keystream(nonceCounter []byte) []byte {
mode := cipher.NewCBCEncrypter(c.block, c.iv)
keystream := make([]byte, c.block.BlockSize())
mode.CryptBlocks(keystream, nonceCounter)
return keystream
}
// encrypt encrypts using stream cipher mode.
func (c ctr) encrypt(plainText []byte) []byte {
counter := make([]byte, len(c.counter))
copy(counter, c.counter)
cipherText := make([]byte, 0)
numBlocks := 0
for len(plainText) > len(cipherText) {
// Get the next keystream.
keystream := c.keystream(append(c.nonce, counter...))
// XOR and append to the ciphertext
cipherText = append(cipherText, xorTwoByteStrings(keystream, plainText[numBlocks*c.block.BlockSize():])...)
counter[0]++
numBlocks++
}
return cipherText
}
// decrypt decrypts using stream cipher mode.
func (c ctr) decrypt(cipherText []byte) []byte {
return c.encrypt(cipherText)
}
// xorTwoByteStringsInPlace does exactly as it sounds, but modifies the first argument.
func xorTwoByteStringsInPlace(s1, s2 []byte) {
length := len(s1)
if length > len(s2) {
length = len(s2)
}
for i := 0; i < length; i++ {
s1[i] = s1[i] ^ s2[i]
}
}