1
2
3
4
5
6
7
8
9
10
11
12
13
14 package ocb
15
16 import (
17 "bytes"
18 "crypto/cipher"
19 "crypto/subtle"
20 "errors"
21 "github.com/ProtonMail/go-crypto/internal/byteutil"
22 "math/bits"
23 )
24
25 type ocb struct {
26 block cipher.Block
27 tagSize int
28 nonceSize int
29 mask mask
30
31
32
33
34
35
36
37 reusableKtop reusableKtop
38 }
39
40 type mask struct {
41
42 lAst []byte
43 lDol []byte
44 L [][]byte
45 }
46
47 type reusableKtop struct {
48 noncePrefix []byte
49 Ktop []byte
50 }
51
52 const (
53 defaultTagSize = 16
54 defaultNonceSize = 15
55 )
56
57 const (
58 enc = iota
59 dec
60 )
61
62 func (o *ocb) NonceSize() int {
63 return o.nonceSize
64 }
65
66 func (o *ocb) Overhead() int {
67 return o.tagSize
68 }
69
70
71
72 func NewOCB(block cipher.Block) (cipher.AEAD, error) {
73 return NewOCBWithNonceAndTagSize(block, defaultNonceSize, defaultTagSize)
74 }
75
76
77
78
79
80
81 func NewOCBWithNonceAndTagSize(
82 block cipher.Block, nonceSize, tagSize int) (cipher.AEAD, error) {
83 if block.BlockSize() != 16 {
84 return nil, ocbError("Block cipher must have 128-bit blocks")
85 }
86 if nonceSize < 1 {
87 return nil, ocbError("Incorrect nonce length")
88 }
89 if nonceSize >= block.BlockSize() {
90 return nil, ocbError("Nonce length exceeds blocksize - 1")
91 }
92 if tagSize > block.BlockSize() {
93 return nil, ocbError("Custom tag length exceeds blocksize")
94 }
95 return &ocb{
96 block: block,
97 tagSize: tagSize,
98 nonceSize: nonceSize,
99 mask: initializeMaskTable(block),
100 reusableKtop: reusableKtop{
101 noncePrefix: nil,
102 Ktop: nil,
103 },
104 }, nil
105 }
106
107 func (o *ocb) Seal(dst, nonce, plaintext, adata []byte) []byte {
108 if len(nonce) > o.nonceSize {
109 panic("crypto/ocb: Incorrect nonce length given to OCB")
110 }
111 ret, out := byteutil.SliceForAppend(dst, len(plaintext)+o.tagSize)
112 o.crypt(enc, out, nonce, adata, plaintext)
113 return ret
114 }
115
116 func (o *ocb) Open(dst, nonce, ciphertext, adata []byte) ([]byte, error) {
117 if len(nonce) > o.nonceSize {
118 panic("Nonce too long for this instance")
119 }
120 if len(ciphertext) < o.tagSize {
121 return nil, ocbError("Ciphertext shorter than tag length")
122 }
123 sep := len(ciphertext) - o.tagSize
124 ret, out := byteutil.SliceForAppend(dst, len(ciphertext))
125 ciphertextData := ciphertext[:sep]
126 tag := ciphertext[sep:]
127 o.crypt(dec, out, nonce, adata, ciphertextData)
128 if subtle.ConstantTimeCompare(ret[sep:], tag) == 1 {
129 ret = ret[:sep]
130 return ret, nil
131 }
132 for i := range out {
133 out[i] = 0
134 }
135 return nil, ocbError("Tag authentication failed")
136 }
137
138
139
140 func (o *ocb) crypt(instruction int, Y, nonce, adata, X []byte) []byte {
141
142
143
144
145
146 blockSize := o.block.BlockSize()
147
148
149
150
151
152
153 truncatedNonce := make([]byte, len(nonce))
154 copy(truncatedNonce, nonce)
155 truncatedNonce[len(truncatedNonce)-1] &= 192
156 Ktop := make([]byte, blockSize)
157 if bytes.Equal(truncatedNonce, o.reusableKtop.noncePrefix) {
158 Ktop = o.reusableKtop.Ktop
159 } else {
160
161 paddedNonce := append(make([]byte, blockSize-1-len(nonce)), 1)
162 paddedNonce = append(paddedNonce, truncatedNonce...)
163 paddedNonce[0] |= byte(((8 * o.tagSize) % (8 * blockSize)) << 1)
164
165 paddedNonce[blockSize-1] &= 192
166 Ktop = paddedNonce
167 o.block.Encrypt(Ktop, Ktop)
168 o.reusableKtop.noncePrefix = truncatedNonce
169 o.reusableKtop.Ktop = Ktop
170 }
171
172
173 xorHalves := make([]byte, blockSize/2)
174 byteutil.XorBytes(xorHalves, Ktop[:blockSize/2], Ktop[1:1+blockSize/2])
175 stretch := append(Ktop, xorHalves...)
176 bottom := int(nonce[len(nonce)-1] & 63)
177 offset := make([]byte, len(stretch))
178 byteutil.ShiftNBytesLeft(offset, stretch, bottom)
179 offset = offset[:blockSize]
180
181
182
183
184
185
186 checksum := make([]byte, blockSize)
187 m := len(X) / blockSize
188 for i := 0; i < m; i++ {
189 index := bits.TrailingZeros(uint(i + 1))
190 if len(o.mask.L)-1 < index {
191 o.mask.extendTable(index)
192 }
193 byteutil.XorBytesMut(offset, o.mask.L[bits.TrailingZeros(uint(i+1))])
194 blockX := X[i*blockSize : (i+1)*blockSize]
195 blockY := Y[i*blockSize : (i+1)*blockSize]
196 byteutil.XorBytes(blockY, blockX, offset)
197 switch instruction {
198 case enc:
199 o.block.Encrypt(blockY, blockY)
200 byteutil.XorBytesMut(blockY, offset)
201 byteutil.XorBytesMut(checksum, blockX)
202 case dec:
203 o.block.Decrypt(blockY, blockY)
204 byteutil.XorBytesMut(blockY, offset)
205 byteutil.XorBytesMut(checksum, blockY)
206 }
207 }
208
209
210
211 tag := make([]byte, blockSize)
212 if len(X)%blockSize != 0 {
213 byteutil.XorBytesMut(offset, o.mask.lAst)
214 pad := make([]byte, blockSize)
215 o.block.Encrypt(pad, offset)
216 chunkX := X[blockSize*m:]
217 chunkY := Y[blockSize*m : len(X)]
218 byteutil.XorBytes(chunkY, chunkX, pad[:len(chunkX)])
219
220 switch instruction {
221 case enc:
222 paddedY := append(chunkX, byte(128))
223 paddedY = append(paddedY, make([]byte, blockSize-len(chunkX)-1)...)
224 byteutil.XorBytesMut(checksum, paddedY)
225 case dec:
226 paddedX := append(chunkY, byte(128))
227 paddedX = append(paddedX, make([]byte, blockSize-len(chunkY)-1)...)
228 byteutil.XorBytesMut(checksum, paddedX)
229 }
230 byteutil.XorBytes(tag, checksum, offset)
231 byteutil.XorBytesMut(tag, o.mask.lDol)
232 o.block.Encrypt(tag, tag)
233 byteutil.XorBytesMut(tag, o.hash(adata))
234 copy(Y[blockSize*m+len(chunkY):], tag[:o.tagSize])
235 } else {
236 byteutil.XorBytes(tag, checksum, offset)
237 byteutil.XorBytesMut(tag, o.mask.lDol)
238 o.block.Encrypt(tag, tag)
239 byteutil.XorBytesMut(tag, o.hash(adata))
240 copy(Y[blockSize*m:], tag[:o.tagSize])
241 }
242 return Y
243 }
244
245
246
247
248 func (o *ocb) hash(adata []byte) []byte {
249
250
251
252 A := make([]byte, len(adata))
253 copy(A, adata)
254 blockSize := o.block.BlockSize()
255
256
257
258
259 sum := make([]byte, blockSize)
260 offset := make([]byte, blockSize)
261 m := len(A) / blockSize
262 for i := 0; i < m; i++ {
263 chunk := A[blockSize*i : blockSize*(i+1)]
264 index := bits.TrailingZeros(uint(i + 1))
265
266 if len(o.mask.L)-1 < index {
267 o.mask.extendTable(index)
268 }
269 byteutil.XorBytesMut(offset, o.mask.L[index])
270 byteutil.XorBytesMut(chunk, offset)
271 o.block.Encrypt(chunk, chunk)
272 byteutil.XorBytesMut(sum, chunk)
273 }
274
275
276
277
278 if len(A)%blockSize != 0 {
279 byteutil.XorBytesMut(offset, o.mask.lAst)
280
281 ending := make([]byte, blockSize-len(A)%blockSize)
282 ending[0] = 0x80
283 encrypted := append(A[blockSize*m:], ending...)
284 byteutil.XorBytesMut(encrypted, offset)
285 o.block.Encrypt(encrypted, encrypted)
286 byteutil.XorBytesMut(sum, encrypted)
287 }
288 return sum
289 }
290
291 func initializeMaskTable(block cipher.Block) mask {
292
293
294
295 lAst := make([]byte, block.BlockSize())
296 block.Encrypt(lAst, lAst)
297 lDol := byteutil.GfnDouble(lAst)
298 L := make([][]byte, 1)
299 L[0] = byteutil.GfnDouble(lDol)
300
301 return mask{
302 lAst: lAst,
303 lDol: lDol,
304 L: L,
305 }
306 }
307
308
309 func (m *mask) extendTable(limit int) {
310 for i := len(m.L); i <= limit; i++ {
311 m.L = append(m.L, byteutil.GfnDouble(m.L[i-1]))
312 }
313 }
314
315 func ocbError(err string) error {
316 return errors.New("crypto/ocb: " + err)
317 }
318
View as plain text