blob: 1933fcc18045999a5c223d9288226bd8eebb4b15 [file] [log] [blame]
Adam Langleyd9e397b2015-01-22 14:27:53 -08001package main
2
3import (
4 "crypto"
5 "crypto/aes"
6 "crypto/cipher"
7 "crypto/des"
8 "crypto/hmac"
9 _ "crypto/md5"
10 "crypto/rc4"
11 _ "crypto/sha1"
12 _ "crypto/sha256"
13 _ "crypto/sha512"
14 "encoding/hex"
15 "flag"
16 "fmt"
17 "os"
18)
19
20var bulkCipher *string = flag.String("cipher", "", "The bulk cipher to use")
21var mac *string = flag.String("mac", "", "The hash function to use in the MAC")
22var implicitIV *bool = flag.Bool("implicit-iv", false, "If true, generate tests for a cipher using a pre-TLS-1.0 implicit IV")
23var ssl3 *bool = flag.Bool("ssl3", false, "If true, use the SSLv3 MAC and padding rather than TLS")
24
25// rc4Stream produces a deterministic stream of pseudorandom bytes. This is to
26// make this script idempotent.
27type rc4Stream struct {
28 cipher *rc4.Cipher
29}
30
31func newRc4Stream(seed string) (*rc4Stream, error) {
32 cipher, err := rc4.NewCipher([]byte(seed))
33 if err != nil {
34 return nil, err
35 }
36 return &rc4Stream{cipher}, nil
37}
38
39func (rs *rc4Stream) fillBytes(p []byte) {
40 for i := range p {
41 p[i] = 0
42 }
43 rs.cipher.XORKeyStream(p, p)
44}
45
46func getHash(name string) (crypto.Hash, bool) {
47 switch name {
48 case "md5":
49 return crypto.MD5, true
50 case "sha1":
51 return crypto.SHA1, true
52 case "sha256":
53 return crypto.SHA256, true
54 case "sha384":
55 return crypto.SHA384, true
56 default:
57 return 0, false
58 }
59}
60
61func getKeySize(name string) int {
62 switch name {
Adam Langleyd9e397b2015-01-22 14:27:53 -080063 case "aes128":
64 return 16
65 case "aes256":
66 return 32
67 case "3des":
68 return 24
69 default:
70 return 0
71 }
72}
73
74func newBlockCipher(name string, key []byte) (cipher.Block, error) {
75 switch name {
76 case "aes128":
77 return aes.NewCipher(key)
78 case "aes256":
79 return aes.NewCipher(key)
80 case "3des":
81 return des.NewTripleDESCipher(key)
82 default:
83 return nil, fmt.Errorf("unknown cipher '%s'", name)
84 }
85}
86
87var ssl30Pad1 = [48]byte{0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36}
88
89var ssl30Pad2 = [48]byte{0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c}
90
91func ssl30MAC(hash crypto.Hash, key, input, ad []byte) []byte {
92 padLength := 48
93 if hash.Size() == 20 {
94 padLength = 40
95 }
96
97 h := hash.New()
98 h.Write(key)
99 h.Write(ssl30Pad1[:padLength])
100 h.Write(ad)
101 h.Write(input)
102 digestBuf := h.Sum(nil)
103
104 h.Reset()
105 h.Write(key)
106 h.Write(ssl30Pad2[:padLength])
107 h.Write(digestBuf)
108 return h.Sum(digestBuf[:0])
109}
110
111type testCase struct {
112 digest []byte
113 key []byte
114 nonce []byte
115 input []byte
116 ad []byte
117 ciphertext []byte
118 tag []byte
Robert Sloan8ff03552017-06-14 12:40:58 -0700119 tag_len int
Adam Langleyd9e397b2015-01-22 14:27:53 -0800120 noSeal bool
121 fails bool
122}
123
124// options adds additional options for a test.
125type options struct {
126 // extraPadding causes an extra block of padding to be added.
127 extraPadding bool
David Benjaminc895d6b2016-08-11 13:26:41 -0400128 // maximalPadding causes 256 bytes of padding to be added.
129 maximalPadding bool
Adam Langleyd9e397b2015-01-22 14:27:53 -0800130 // wrongPadding causes one of the padding bytes to be wrong.
131 wrongPadding bool
David Benjaminc895d6b2016-08-11 13:26:41 -0400132 // wrongPaddingOffset specifies the byte offset of the incorrect padding
133 // byte.
134 wrongPaddingOffset int
Adam Langleyd9e397b2015-01-22 14:27:53 -0800135 // noPadding causes padding is to be omitted. The plaintext + MAC must
136 // be a multiple of the block size.
137 noPadding bool
David Benjaminc895d6b2016-08-11 13:26:41 -0400138 // omitMAC causes the MAC to be omitted.
139 omitMAC bool
Adam Langleyd9e397b2015-01-22 14:27:53 -0800140}
141
142func makeTestCase(length int, options options) (*testCase, error) {
143 rand, err := newRc4Stream("input stream")
144 if err != nil {
145 return nil, err
146 }
147
148 input := make([]byte, length)
149 rand.fillBytes(input)
150
151 var adFull []byte
152 if *ssl3 {
153 adFull = make([]byte, 11)
154 } else {
155 adFull = make([]byte, 13)
156 }
157 ad := adFull[:len(adFull)-2]
158 rand.fillBytes(ad)
159 adFull[len(adFull)-2] = uint8(length >> 8)
160 adFull[len(adFull)-1] = uint8(length & 0xff)
161
162 hash, ok := getHash(*mac)
163 if !ok {
164 return nil, fmt.Errorf("unknown hash function '%s'", *mac)
165 }
166
167 macKey := make([]byte, hash.Size())
168 rand.fillBytes(macKey)
169
170 var digest []byte
171 if *ssl3 {
172 if hash != crypto.SHA1 && hash != crypto.MD5 {
173 return nil, fmt.Errorf("invalid hash for SSLv3: '%s'", *mac)
174 }
175 digest = ssl30MAC(hash, macKey, input, adFull)
176 } else {
177 h := hmac.New(hash.New, macKey)
178 h.Write(adFull)
179 h.Write(input)
180 digest = h.Sum(nil)
181 }
182
183 size := getKeySize(*bulkCipher)
184 if size == 0 {
185 return nil, fmt.Errorf("unknown cipher '%s'", *bulkCipher)
186 }
187 encKey := make([]byte, size)
188 rand.fillBytes(encKey)
189
190 var fixedIV []byte
191 var nonce []byte
192 var sealed []byte
193 var noSeal, fails bool
David Benjamin7c0d06c2016-08-11 13:26:41 -0400194 block, err := newBlockCipher(*bulkCipher, encKey)
195 if err != nil {
196 return nil, err
Adam Langleyd9e397b2015-01-22 14:27:53 -0800197 }
198
David Benjamin7c0d06c2016-08-11 13:26:41 -0400199 iv := make([]byte, block.BlockSize())
200 rand.fillBytes(iv)
201 if *implicitIV || *ssl3 {
202 fixedIV = iv
203 } else {
204 nonce = iv
205 }
206
207 cbc := cipher.NewCBCEncrypter(block, iv)
208
209 sealed = make([]byte, 0, len(input)+len(digest)+cbc.BlockSize())
210 sealed = append(sealed, input...)
211 if options.omitMAC {
212 noSeal = true
213 fails = true
214 } else {
215 sealed = append(sealed, digest...)
216 }
217 paddingLen := cbc.BlockSize() - (len(sealed) % cbc.BlockSize())
218 if options.noPadding {
219 if paddingLen != cbc.BlockSize() {
220 return nil, fmt.Errorf("invalid length for noPadding")
221 }
222 noSeal = true
223 fails = true
224 } else {
225 if options.extraPadding || options.maximalPadding {
226 if options.extraPadding {
227 paddingLen += cbc.BlockSize()
228 } else {
229 if paddingLen != cbc.BlockSize() {
230 return nil, fmt.Errorf("invalid length for maximalPadding")
231 }
232 paddingLen = 256
233 }
234 noSeal = true
235 if *ssl3 {
236 // SSLv3 padding must be minimal.
237 fails = true
238 }
239 }
240 if *ssl3 {
241 sealed = append(sealed, make([]byte, paddingLen-1)...)
242 sealed = append(sealed, byte(paddingLen-1))
243 } else {
244 pad := make([]byte, paddingLen)
245 for i := range pad {
246 pad[i] = byte(paddingLen - 1)
247 }
248 sealed = append(sealed, pad...)
249 }
250 if options.wrongPadding {
251 if options.wrongPaddingOffset >= paddingLen {
252 return nil, fmt.Errorf("invalid wrongPaddingOffset")
253 }
254 sealed[len(sealed)-paddingLen+options.wrongPaddingOffset]++
255 noSeal = true
256 if !*ssl3 {
257 // TLS specifies the all the padding bytes.
258 fails = true
259 }
260 }
261 }
262 cbc.CryptBlocks(sealed, sealed)
263
Adam Langleyd9e397b2015-01-22 14:27:53 -0800264 key := make([]byte, 0, len(macKey)+len(encKey)+len(fixedIV))
265 key = append(key, macKey...)
266 key = append(key, encKey...)
267 key = append(key, fixedIV...)
268 t := &testCase{
269 digest: digest,
270 key: key,
271 nonce: nonce,
272 input: input,
273 ad: ad,
Robert Sloan8ff03552017-06-14 12:40:58 -0700274 ciphertext: sealed[:len(input)],
275 tag: sealed[len(input):],
276 tag_len: hash.Size(),
Adam Langleyd9e397b2015-01-22 14:27:53 -0800277 noSeal: noSeal,
278 fails: fails,
279 }
280 return t, nil
281}
282
283func printTestCase(t *testCase) {
284 fmt.Printf("# DIGEST: %s\n", hex.EncodeToString(t.digest))
285 fmt.Printf("KEY: %s\n", hex.EncodeToString(t.key))
286 fmt.Printf("NONCE: %s\n", hex.EncodeToString(t.nonce))
287 fmt.Printf("IN: %s\n", hex.EncodeToString(t.input))
288 fmt.Printf("AD: %s\n", hex.EncodeToString(t.ad))
289 fmt.Printf("CT: %s\n", hex.EncodeToString(t.ciphertext))
290 fmt.Printf("TAG: %s\n", hex.EncodeToString(t.tag))
Robert Sloan8ff03552017-06-14 12:40:58 -0700291 fmt.Printf("TAG_LEN: %d\n", t.tag_len)
Adam Langleyd9e397b2015-01-22 14:27:53 -0800292 if t.noSeal {
293 fmt.Printf("NO_SEAL: 01\n")
294 }
295 if t.fails {
296 fmt.Printf("FAILS: 01\n")
297 }
298}
299
David Benjaminc895d6b2016-08-11 13:26:41 -0400300func addTestCase(length int, options options) {
301 t, err := makeTestCase(length, options)
302 if err != nil {
303 fmt.Fprintf(os.Stderr, "%s\n", err)
304 os.Exit(1)
305 }
306 printTestCase(t)
307 fmt.Printf("\n")
308}
309
Adam Langleyd9e397b2015-01-22 14:27:53 -0800310func main() {
311 flag.Parse()
312
313 commandLine := fmt.Sprintf("go run make_legacy_aead_tests.go -cipher %s -mac %s", *bulkCipher, *mac)
314 if *implicitIV {
315 commandLine += " -implicit-iv"
316 }
317 if *ssl3 {
318 commandLine += " -ssl3"
319 }
320 fmt.Printf("# Generated by\n")
321 fmt.Printf("# %s\n", commandLine)
322 fmt.Printf("#\n")
Robert Sloan8ff03552017-06-14 12:40:58 -0700323 fmt.Printf("# Note: aead_test's input format splits the ciphertext and tag positions of the\n")
324 fmt.Printf("# sealed input. But these legacy AEADs are MAC-then-encrypt and so the 'TAG' may\n")
325 fmt.Printf("# also include padding. We write the byte length of the MAC to 'TAG_LEN' and\n")
326 fmt.Printf("# include the unencrypted MAC in the 'DIGEST' tag above # each test case.\n")
Adam Langleyd9e397b2015-01-22 14:27:53 -0800327 fmt.Printf("# each test case.\n")
328 fmt.Printf("\n")
329
330 // For CBC-mode ciphers, emit tests for padding flexibility.
David Benjamin7c0d06c2016-08-11 13:26:41 -0400331 fmt.Printf("# Test with non-minimal padding.\n")
332 addTestCase(5, options{extraPadding: true})
Adam Langleyd9e397b2015-01-22 14:27:53 -0800333
David Benjamin7c0d06c2016-08-11 13:26:41 -0400334 fmt.Printf("# Test with bad padding values.\n")
335 addTestCase(5, options{wrongPadding: true})
Adam Langleyd9e397b2015-01-22 14:27:53 -0800336
David Benjamin7c0d06c2016-08-11 13:26:41 -0400337 hash, ok := getHash(*mac)
338 if !ok {
339 panic("unknown hash")
340 }
David Benjaminc895d6b2016-08-11 13:26:41 -0400341
David Benjamin7c0d06c2016-08-11 13:26:41 -0400342 fmt.Printf("# Test with no padding.\n")
343 addTestCase(64-hash.Size(), options{noPadding: true})
David Benjaminc895d6b2016-08-11 13:26:41 -0400344
David Benjamin7c0d06c2016-08-11 13:26:41 -0400345 fmt.Printf("# Test with maximal padding.\n")
346 addTestCase(64-hash.Size(), options{maximalPadding: true})
David Benjaminc895d6b2016-08-11 13:26:41 -0400347
David Benjamin7c0d06c2016-08-11 13:26:41 -0400348 fmt.Printf("# Test if the unpadded input is too short for a MAC, but not publicly so.\n")
349 addTestCase(0, options{omitMAC: true, maximalPadding: true})
David Benjaminc895d6b2016-08-11 13:26:41 -0400350
David Benjamin7c0d06c2016-08-11 13:26:41 -0400351 fmt.Printf("# Test that each byte of incorrect padding is noticed.\n")
352 for i := 0; i < 256; i++ {
353 addTestCase(64-hash.Size(), options{
354 maximalPadding: true,
355 wrongPadding: true,
356 wrongPaddingOffset: i,
357 })
Adam Langleyd9e397b2015-01-22 14:27:53 -0800358 }
359
360 // Generate long enough of input to cover a non-zero num_starting_blocks
361 // value in the constant-time CBC logic.
362 for l := 0; l < 500; l += 5 {
David Benjaminc895d6b2016-08-11 13:26:41 -0400363 addTestCase(l, options{})
Adam Langleyd9e397b2015-01-22 14:27:53 -0800364 }
365}