• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1package 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 {
63	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
119	noSeal     bool
120	fails      bool
121}
122
123// options adds additional options for a test.
124type options struct {
125	// extraPadding causes an extra block of padding to be added.
126	extraPadding bool
127	// maximalPadding causes 256 bytes of padding to be added.
128	maximalPadding bool
129	// wrongPadding causes one of the padding bytes to be wrong.
130	wrongPadding bool
131	// wrongPaddingOffset specifies the byte offset of the incorrect padding
132	// byte.
133	wrongPaddingOffset int
134	// noPadding causes padding is to be omitted. The plaintext + MAC must
135	// be a multiple of the block size.
136	noPadding bool
137	// omitMAC causes the MAC to be omitted.
138	omitMAC bool
139}
140
141func makeTestCase(length int, options options) (*testCase, error) {
142	rand, err := newRc4Stream("input stream")
143	if err != nil {
144		return nil, err
145	}
146
147	input := make([]byte, length)
148	rand.fillBytes(input)
149
150	var adFull []byte
151	if *ssl3 {
152		adFull = make([]byte, 11)
153	} else {
154		adFull = make([]byte, 13)
155	}
156	ad := adFull[:len(adFull)-2]
157	rand.fillBytes(ad)
158	adFull[len(adFull)-2] = uint8(length >> 8)
159	adFull[len(adFull)-1] = uint8(length & 0xff)
160
161	hash, ok := getHash(*mac)
162	if !ok {
163		return nil, fmt.Errorf("unknown hash function '%s'", *mac)
164	}
165
166	macKey := make([]byte, hash.Size())
167	rand.fillBytes(macKey)
168
169	var digest []byte
170	if *ssl3 {
171		if hash != crypto.SHA1 && hash != crypto.MD5 {
172			return nil, fmt.Errorf("invalid hash for SSLv3: '%s'", *mac)
173		}
174		digest = ssl30MAC(hash, macKey, input, adFull)
175	} else {
176		h := hmac.New(hash.New, macKey)
177		h.Write(adFull)
178		h.Write(input)
179		digest = h.Sum(nil)
180	}
181
182	size := getKeySize(*bulkCipher)
183	if size == 0 {
184		return nil, fmt.Errorf("unknown cipher '%s'", *bulkCipher)
185	}
186	encKey := make([]byte, size)
187	rand.fillBytes(encKey)
188
189	var fixedIV []byte
190	var nonce []byte
191	var sealed []byte
192	var noSeal, fails bool
193	block, err := newBlockCipher(*bulkCipher, encKey)
194	if err != nil {
195		return nil, err
196	}
197
198	iv := make([]byte, block.BlockSize())
199	rand.fillBytes(iv)
200	if *implicitIV || *ssl3 {
201		fixedIV = iv
202	} else {
203		nonce = iv
204	}
205
206	cbc := cipher.NewCBCEncrypter(block, iv)
207
208	sealed = make([]byte, 0, len(input)+len(digest)+cbc.BlockSize())
209	sealed = append(sealed, input...)
210	if options.omitMAC {
211		noSeal = true
212		fails = true
213	} else {
214		sealed = append(sealed, digest...)
215	}
216	paddingLen := cbc.BlockSize() - (len(sealed) % cbc.BlockSize())
217	if options.noPadding {
218		if paddingLen != cbc.BlockSize() {
219			return nil, fmt.Errorf("invalid length for noPadding")
220		}
221		noSeal = true
222		fails = true
223	} else {
224		if options.extraPadding || options.maximalPadding {
225			if options.extraPadding {
226				paddingLen += cbc.BlockSize()
227			} else {
228				if paddingLen != cbc.BlockSize() {
229					return nil, fmt.Errorf("invalid length for maximalPadding")
230				}
231				paddingLen = 256
232			}
233			noSeal = true
234			if *ssl3 {
235				// SSLv3 padding must be minimal.
236				fails = true
237			}
238		}
239		if *ssl3 {
240			sealed = append(sealed, make([]byte, paddingLen-1)...)
241			sealed = append(sealed, byte(paddingLen-1))
242		} else {
243			pad := make([]byte, paddingLen)
244			for i := range pad {
245				pad[i] = byte(paddingLen - 1)
246			}
247			sealed = append(sealed, pad...)
248		}
249		if options.wrongPadding {
250			if options.wrongPaddingOffset >= paddingLen {
251				return nil, fmt.Errorf("invalid wrongPaddingOffset")
252			}
253			sealed[len(sealed)-paddingLen+options.wrongPaddingOffset]++
254			noSeal = true
255			if !*ssl3 {
256				// TLS specifies the all the padding bytes.
257				fails = true
258			}
259		}
260	}
261	cbc.CryptBlocks(sealed, sealed)
262
263	key := make([]byte, 0, len(macKey)+len(encKey)+len(fixedIV))
264	key = append(key, macKey...)
265	key = append(key, encKey...)
266	key = append(key, fixedIV...)
267	t := &testCase{
268		digest:     digest,
269		key:        key,
270		nonce:      nonce,
271		input:      input,
272		ad:         ad,
273		ciphertext: sealed[:len(sealed)-hash.Size()],
274		tag:        sealed[len(sealed)-hash.Size():],
275		noSeal:     noSeal,
276		fails:      fails,
277	}
278	return t, nil
279}
280
281func printTestCase(t *testCase) {
282	fmt.Printf("# DIGEST: %s\n", hex.EncodeToString(t.digest))
283	fmt.Printf("KEY: %s\n", hex.EncodeToString(t.key))
284	fmt.Printf("NONCE: %s\n", hex.EncodeToString(t.nonce))
285	fmt.Printf("IN: %s\n", hex.EncodeToString(t.input))
286	fmt.Printf("AD: %s\n", hex.EncodeToString(t.ad))
287	fmt.Printf("CT: %s\n", hex.EncodeToString(t.ciphertext))
288	fmt.Printf("TAG: %s\n", hex.EncodeToString(t.tag))
289	if t.noSeal {
290		fmt.Printf("NO_SEAL: 01\n")
291	}
292	if t.fails {
293		fmt.Printf("FAILS: 01\n")
294	}
295}
296
297func addTestCase(length int, options options) {
298	t, err := makeTestCase(length, options)
299	if err != nil {
300		fmt.Fprintf(os.Stderr, "%s\n", err)
301		os.Exit(1)
302	}
303	printTestCase(t)
304	fmt.Printf("\n")
305}
306
307func main() {
308	flag.Parse()
309
310	commandLine := fmt.Sprintf("go run make_legacy_aead_tests.go -cipher %s -mac %s", *bulkCipher, *mac)
311	if *implicitIV {
312		commandLine += " -implicit-iv"
313	}
314	if *ssl3 {
315		commandLine += " -ssl3"
316	}
317	fmt.Printf("# Generated by\n")
318	fmt.Printf("#   %s\n", commandLine)
319	fmt.Printf("#\n")
320	fmt.Printf("# Note: aead_test's input format splits the ciphertext and tag positions of the sealed\n")
321	fmt.Printf("# input. But these legacy AEADs are MAC-then-encrypt and may include padding, so this\n")
322	fmt.Printf("# split isn't meaningful. The unencrypted MAC is included in the 'DIGEST' tag above\n")
323	fmt.Printf("# each test case.\n")
324	fmt.Printf("\n")
325
326	// For CBC-mode ciphers, emit tests for padding flexibility.
327	fmt.Printf("# Test with non-minimal padding.\n")
328	addTestCase(5, options{extraPadding: true})
329
330	fmt.Printf("# Test with bad padding values.\n")
331	addTestCase(5, options{wrongPadding: true})
332
333	hash, ok := getHash(*mac)
334	if !ok {
335		panic("unknown hash")
336	}
337
338	fmt.Printf("# Test with no padding.\n")
339	addTestCase(64-hash.Size(), options{noPadding: true})
340
341	fmt.Printf("# Test with maximal padding.\n")
342	addTestCase(64-hash.Size(), options{maximalPadding: true})
343
344	fmt.Printf("# Test if the unpadded input is too short for a MAC, but not publicly so.\n")
345	addTestCase(0, options{omitMAC: true, maximalPadding: true})
346
347	fmt.Printf("# Test that each byte of incorrect padding is noticed.\n")
348	for i := 0; i < 256; i++ {
349		addTestCase(64-hash.Size(), options{
350			maximalPadding:     true,
351			wrongPadding:       true,
352			wrongPaddingOffset: i,
353		})
354	}
355
356	// Generate long enough of input to cover a non-zero num_starting_blocks
357	// value in the constant-time CBC logic.
358	for l := 0; l < 500; l += 5 {
359		addTestCase(l, options{})
360	}
361}
362