Home | History | Annotate | Download | only in test
      1 package main
      2 
      3 import (
      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 
     20 var bulkCipher *string = flag.String("cipher", "", "The bulk cipher to use")
     21 var mac *string = flag.String("mac", "", "The hash function to use in the MAC")
     22 var implicitIV *bool = flag.Bool("implicit-iv", false, "If true, generate tests for a cipher using a pre-TLS-1.0 implicit IV")
     23 var 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.
     27 type rc4Stream struct {
     28 	cipher *rc4.Cipher
     29 }
     30 
     31 func 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 
     39 func (rs *rc4Stream) fillBytes(p []byte) {
     40 	for i := range p {
     41 		p[i] = 0
     42 	}
     43 	rs.cipher.XORKeyStream(p, p)
     44 }
     45 
     46 func 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 
     61 func 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 
     74 func 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 
     87 var 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 
     89 var 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 
     91 func 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 
    111 type testCase struct {
    112 	digest     []byte
    113 	key        []byte
    114 	nonce      []byte
    115 	input      []byte
    116 	ad         []byte
    117 	ciphertext []byte
    118 	tag        []byte
    119 	tag_len    int
    120 	noSeal     bool
    121 	fails      bool
    122 }
    123 
    124 // options adds additional options for a test.
    125 type options struct {
    126 	// extraPadding causes an extra block of padding to be added.
    127 	extraPadding bool
    128 	// maximalPadding causes 256 bytes of padding to be added.
    129 	maximalPadding bool
    130 	// wrongPadding causes one of the padding bytes to be wrong.
    131 	wrongPadding bool
    132 	// wrongPaddingOffset specifies the byte offset of the incorrect padding
    133 	// byte.
    134 	wrongPaddingOffset int
    135 	// noPadding causes padding is to be omitted. The plaintext + MAC must
    136 	// be a multiple of the block size.
    137 	noPadding bool
    138 	// omitMAC causes the MAC to be omitted.
    139 	omitMAC bool
    140 }
    141 
    142 func 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
    194 	block, err := newBlockCipher(*bulkCipher, encKey)
    195 	if err != nil {
    196 		return nil, err
    197 	}
    198 
    199 	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 
    264 	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,
    274 		ciphertext: sealed[:len(input)],
    275 		tag:        sealed[len(input):],
    276 		tag_len:    hash.Size(),
    277 		noSeal:     noSeal,
    278 		fails:      fails,
    279 	}
    280 	return t, nil
    281 }
    282 
    283 func 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))
    291 	fmt.Printf("TAG_LEN: %d\n", t.tag_len)
    292 	if t.noSeal {
    293 		fmt.Printf("NO_SEAL: 01\n")
    294 	}
    295 	if t.fails {
    296 		fmt.Printf("FAILS: 01\n")
    297 	}
    298 }
    299 
    300 func 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 
    310 func 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")
    323 	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")
    327 	fmt.Printf("# each test case.\n")
    328 	fmt.Printf("\n")
    329 
    330 	// For CBC-mode ciphers, emit tests for padding flexibility.
    331 	fmt.Printf("# Test with non-minimal padding.\n")
    332 	addTestCase(5, options{extraPadding: true})
    333 
    334 	fmt.Printf("# Test with bad padding values.\n")
    335 	addTestCase(5, options{wrongPadding: true})
    336 
    337 	hash, ok := getHash(*mac)
    338 	if !ok {
    339 		panic("unknown hash")
    340 	}
    341 
    342 	fmt.Printf("# Test with no padding.\n")
    343 	addTestCase(64-hash.Size(), options{noPadding: true})
    344 
    345 	fmt.Printf("# Test with maximal padding.\n")
    346 	addTestCase(64-hash.Size(), options{maximalPadding: true})
    347 
    348 	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})
    350 
    351 	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 		})
    358 	}
    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 {
    363 		addTestCase(l, options{})
    364 	}
    365 }
    366