aescts.go
6.1 KB
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
// Package aescts provides AES CBC CipherText Stealing encryption and decryption methods
package aescts
import (
"crypto/aes"
"crypto/cipher"
"errors"
"fmt"
)
// Encrypt the message with the key and the initial vector.
// Returns: next iv, ciphertext bytes, error
func Encrypt(key, iv, plaintext []byte) ([]byte, []byte, error) {
l := len(plaintext)
block, err := aes.NewCipher(key)
if err != nil {
return []byte{}, []byte{}, fmt.Errorf("Error creating cipher: %v", err)
}
mode := cipher.NewCBCEncrypter(block, iv)
m := make([]byte, len(plaintext))
copy(m, plaintext)
/*For consistency, ciphertext stealing is always used for the last two
blocks of the data to be encrypted, as in [RC5]. If the data length
is a multiple of the block size, this is equivalent to plain CBC mode
with the last two ciphertext blocks swapped.*/
/*The initial vector carried out from one encryption for use in a
subsequent encryption is the next-to-last block of the encryption
output; this is the encrypted form of the last plaintext block.*/
if l <= aes.BlockSize {
m, _ = zeroPad(m, aes.BlockSize)
mode.CryptBlocks(m, m)
return m, m, nil
}
if l%aes.BlockSize == 0 {
mode.CryptBlocks(m, m)
iv = m[len(m)-aes.BlockSize:]
rb, _ := swapLastTwoBlocks(m, aes.BlockSize)
return iv, rb, nil
}
m, _ = zeroPad(m, aes.BlockSize)
rb, pb, lb, err := tailBlocks(m, aes.BlockSize)
if err != nil {
return []byte{}, []byte{}, fmt.Errorf("Error tailing blocks: %v", err)
}
var ct []byte
if rb != nil {
// Encrpt all but the lats 2 blocks and update the rolling iv
mode.CryptBlocks(rb, rb)
iv = rb[len(rb)-aes.BlockSize:]
mode = cipher.NewCBCEncrypter(block, iv)
ct = append(ct, rb...)
}
mode.CryptBlocks(pb, pb)
mode = cipher.NewCBCEncrypter(block, pb)
mode.CryptBlocks(lb, lb)
// Cipher Text Stealing (CTS) - Ref: https://en.wikipedia.org/wiki/Ciphertext_stealing#CBC_ciphertext_stealing
// Swap the last two cipher blocks
// Truncate the ciphertext to the length of the original plaintext
ct = append(ct, lb...)
ct = append(ct, pb...)
return lb, ct[:l], nil
}
// Decrypt the ciphertext with the key and the initial vector.
func Decrypt(key, iv, ciphertext []byte) ([]byte, error) {
// Copy the cipher text as golang slices even when passed by value to this method can result in the backing arrays of the calling code value being updated.
ct := make([]byte, len(ciphertext))
copy(ct, ciphertext)
if len(ct) < aes.BlockSize {
return []byte{}, fmt.Errorf("Ciphertext is not large enough. It is less that one block size. Blocksize:%v; Ciphertext:%v", aes.BlockSize, len(ct))
}
// Configure the CBC
block, err := aes.NewCipher(key)
if err != nil {
return nil, fmt.Errorf("Error creating cipher: %v", err)
}
var mode cipher.BlockMode
//If ciphertext is multiple of blocksize we just need to swap back the last two blocks and then do CBC
//If the ciphertext is just one block we can't swap so we just decrypt
if len(ct)%aes.BlockSize == 0 {
if len(ct) > aes.BlockSize {
ct, _ = swapLastTwoBlocks(ct, aes.BlockSize)
}
mode = cipher.NewCBCDecrypter(block, iv)
message := make([]byte, len(ct))
mode.CryptBlocks(message, ct)
return message[:len(ct)], nil
}
// Cipher Text Stealing (CTS) using CBC interface. Ref: https://en.wikipedia.org/wiki/Ciphertext_stealing#CBC_ciphertext_stealing
// Get ciphertext of the 2nd to last (penultimate) block (cpb), the last block (clb) and the rest (crb)
crb, cpb, clb, _ := tailBlocks(ct, aes.BlockSize)
v := make([]byte, len(iv), len(iv))
copy(v, iv)
var message []byte
if crb != nil {
//If there is more than just the last and the penultimate block we decrypt it and the last bloc of this becomes the iv for later
rb := make([]byte, len(crb))
mode = cipher.NewCBCDecrypter(block, v)
v = crb[len(crb)-aes.BlockSize:]
mode.CryptBlocks(rb, crb)
message = append(message, rb...)
}
// We need to modify the cipher text
// Decryt the 2nd to last (penultimate) block with a the original iv
pb := make([]byte, aes.BlockSize)
mode = cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(pb, cpb)
// number of byte needed to pad
npb := aes.BlockSize - len(ct)%aes.BlockSize
//pad last block using the number of bytes needed from the tail of the plaintext 2nd to last (penultimate) block
clb = append(clb, pb[len(pb)-npb:]...)
// Now decrypt the last block in the penultimate position (iv will be from the crb, if the is no crb it's zeros)
// iv for the penultimate block decrypted in the last position becomes the modified last block
lb := make([]byte, aes.BlockSize)
mode = cipher.NewCBCDecrypter(block, v)
v = clb
mode.CryptBlocks(lb, clb)
message = append(message, lb...)
// Now decrypt the penultimate block in the last position (iv will be from the modified last block)
mode = cipher.NewCBCDecrypter(block, v)
mode.CryptBlocks(cpb, cpb)
message = append(message, cpb...)
// Truncate to the size of the original cipher text
return message[:len(ct)], nil
}
func tailBlocks(b []byte, c int) ([]byte, []byte, []byte, error) {
if len(b) <= c {
return []byte{}, []byte{}, []byte{}, errors.New("bytes slice is not larger than one block so cannot tail")
}
// Get size of last block
var lbs int
if l := len(b) % aes.BlockSize; l == 0 {
lbs = aes.BlockSize
} else {
lbs = l
}
// Get last block
lb := b[len(b)-lbs:]
// Get 2nd to last (penultimate) block
pb := b[len(b)-lbs-c : len(b)-lbs]
if len(b) > 2*c {
rb := b[:len(b)-lbs-c]
return rb, pb, lb, nil
}
return nil, pb, lb, nil
}
func swapLastTwoBlocks(b []byte, c int) ([]byte, error) {
rb, pb, lb, err := tailBlocks(b, c)
if err != nil {
return nil, err
}
var out []byte
if rb != nil {
out = append(out, rb...)
}
out = append(out, lb...)
out = append(out, pb...)
return out, nil
}
// zeroPad pads bytes with zeros to nearest multiple of message size m.
func zeroPad(b []byte, m int) ([]byte, error) {
if m <= 0 {
return nil, errors.New("Invalid message block size when padding")
}
if b == nil || len(b) == 0 {
return nil, errors.New("Data not valid to pad: Zero size")
}
if l := len(b) % m; l != 0 {
n := m - l
z := make([]byte, n)
b = append(b, z...)
}
return b, nil
}