summaryrefslogtreecommitdiffstats
path: root/src/crypto/cipher/test/make_legacy_aead_tests.go
diff options
context:
space:
mode:
authorAdam Langley <agl@google.com>2015-01-22 14:27:53 -0800
committerAdam Langley <agl@google.com>2015-01-30 16:52:14 -0800
commitd9e397b599b13d642138480a28c14db7a136bf05 (patch)
tree34bab61dc4ce323b123ad4614dbc07e86ea2f9ef /src/crypto/cipher/test/make_legacy_aead_tests.go
downloadexternal_boringssl-d9e397b599b13d642138480a28c14db7a136bf05.zip
external_boringssl-d9e397b599b13d642138480a28c14db7a136bf05.tar.gz
external_boringssl-d9e397b599b13d642138480a28c14db7a136bf05.tar.bz2
Initial commit of BoringSSL for Android.
Diffstat (limited to 'src/crypto/cipher/test/make_legacy_aead_tests.go')
-rw-r--r--src/crypto/cipher/test/make_legacy_aead_tests.go357
1 files changed, 357 insertions, 0 deletions
diff --git a/src/crypto/cipher/test/make_legacy_aead_tests.go b/src/crypto/cipher/test/make_legacy_aead_tests.go
new file mode 100644
index 0000000..40b8a01
--- /dev/null
+++ b/src/crypto/cipher/test/make_legacy_aead_tests.go
@@ -0,0 +1,357 @@
+package main
+
+import (
+ "crypto"
+ "crypto/aes"
+ "crypto/cipher"
+ "crypto/des"
+ "crypto/hmac"
+ _ "crypto/md5"
+ "crypto/rc4"
+ _ "crypto/sha1"
+ _ "crypto/sha256"
+ _ "crypto/sha512"
+ "encoding/hex"
+ "flag"
+ "fmt"
+ "os"
+)
+
+var bulkCipher *string = flag.String("cipher", "", "The bulk cipher to use")
+var mac *string = flag.String("mac", "", "The hash function to use in the MAC")
+var implicitIV *bool = flag.Bool("implicit-iv", false, "If true, generate tests for a cipher using a pre-TLS-1.0 implicit IV")
+var ssl3 *bool = flag.Bool("ssl3", false, "If true, use the SSLv3 MAC and padding rather than TLS")
+
+// rc4Stream produces a deterministic stream of pseudorandom bytes. This is to
+// make this script idempotent.
+type rc4Stream struct {
+ cipher *rc4.Cipher
+}
+
+func newRc4Stream(seed string) (*rc4Stream, error) {
+ cipher, err := rc4.NewCipher([]byte(seed))
+ if err != nil {
+ return nil, err
+ }
+ return &rc4Stream{cipher}, nil
+}
+
+func (rs *rc4Stream) fillBytes(p []byte) {
+ for i := range p {
+ p[i] = 0
+ }
+ rs.cipher.XORKeyStream(p, p)
+}
+
+func getHash(name string) (crypto.Hash, bool) {
+ switch name {
+ case "md5":
+ return crypto.MD5, true
+ case "sha1":
+ return crypto.SHA1, true
+ case "sha256":
+ return crypto.SHA256, true
+ case "sha384":
+ return crypto.SHA384, true
+ default:
+ return 0, false
+ }
+}
+
+func getKeySize(name string) int {
+ switch name {
+ case "rc4":
+ return 16
+ case "aes128":
+ return 16
+ case "aes256":
+ return 32
+ case "3des":
+ return 24
+ default:
+ return 0
+ }
+}
+
+func newBlockCipher(name string, key []byte) (cipher.Block, error) {
+ switch name {
+ case "aes128":
+ return aes.NewCipher(key)
+ case "aes256":
+ return aes.NewCipher(key)
+ case "3des":
+ return des.NewTripleDESCipher(key)
+ default:
+ return nil, fmt.Errorf("unknown cipher '%s'", name)
+ }
+}
+
+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}
+
+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}
+
+func ssl30MAC(hash crypto.Hash, key, input, ad []byte) []byte {
+ padLength := 48
+ if hash.Size() == 20 {
+ padLength = 40
+ }
+
+ h := hash.New()
+ h.Write(key)
+ h.Write(ssl30Pad1[:padLength])
+ h.Write(ad)
+ h.Write(input)
+ digestBuf := h.Sum(nil)
+
+ h.Reset()
+ h.Write(key)
+ h.Write(ssl30Pad2[:padLength])
+ h.Write(digestBuf)
+ return h.Sum(digestBuf[:0])
+}
+
+type testCase struct {
+ digest []byte
+ key []byte
+ nonce []byte
+ input []byte
+ ad []byte
+ ciphertext []byte
+ tag []byte
+ noSeal bool
+ fails bool
+}
+
+// options adds additional options for a test.
+type options struct {
+ // extraPadding causes an extra block of padding to be added.
+ extraPadding bool
+ // wrongPadding causes one of the padding bytes to be wrong.
+ wrongPadding bool
+ // noPadding causes padding is to be omitted. The plaintext + MAC must
+ // be a multiple of the block size.
+ noPadding bool
+}
+
+func makeTestCase(length int, options options) (*testCase, error) {
+ rand, err := newRc4Stream("input stream")
+ if err != nil {
+ return nil, err
+ }
+
+ input := make([]byte, length)
+ rand.fillBytes(input)
+
+ var adFull []byte
+ if *ssl3 {
+ adFull = make([]byte, 11)
+ } else {
+ adFull = make([]byte, 13)
+ }
+ ad := adFull[:len(adFull)-2]
+ rand.fillBytes(ad)
+ adFull[len(adFull)-2] = uint8(length >> 8)
+ adFull[len(adFull)-1] = uint8(length & 0xff)
+
+ hash, ok := getHash(*mac)
+ if !ok {
+ return nil, fmt.Errorf("unknown hash function '%s'", *mac)
+ }
+
+ macKey := make([]byte, hash.Size())
+ rand.fillBytes(macKey)
+
+ var digest []byte
+ if *ssl3 {
+ if hash != crypto.SHA1 && hash != crypto.MD5 {
+ return nil, fmt.Errorf("invalid hash for SSLv3: '%s'", *mac)
+ }
+ digest = ssl30MAC(hash, macKey, input, adFull)
+ } else {
+ h := hmac.New(hash.New, macKey)
+ h.Write(adFull)
+ h.Write(input)
+ digest = h.Sum(nil)
+ }
+
+ size := getKeySize(*bulkCipher)
+ if size == 0 {
+ return nil, fmt.Errorf("unknown cipher '%s'", *bulkCipher)
+ }
+ encKey := make([]byte, size)
+ rand.fillBytes(encKey)
+
+ var fixedIV []byte
+ var nonce []byte
+ var sealed []byte
+ var noSeal, fails bool
+ if *bulkCipher == "rc4" {
+ if *implicitIV {
+ return nil, fmt.Errorf("implicit IV enabled on a stream cipher")
+ }
+
+ stream, err := rc4.NewCipher(encKey)
+ if err != nil {
+ return nil, err
+ }
+
+ sealed = make([]byte, 0, len(input)+len(digest))
+ sealed = append(sealed, input...)
+ sealed = append(sealed, digest...)
+ stream.XORKeyStream(sealed, sealed)
+ } else {
+ block, err := newBlockCipher(*bulkCipher, encKey)
+ if err != nil {
+ return nil, err
+ }
+
+ iv := make([]byte, block.BlockSize())
+ rand.fillBytes(iv)
+ if *implicitIV || *ssl3 {
+ fixedIV = iv
+ } else {
+ nonce = iv
+ }
+
+ cbc := cipher.NewCBCEncrypter(block, iv)
+
+ sealed = make([]byte, 0, len(input)+len(digest)+cbc.BlockSize())
+ sealed = append(sealed, input...)
+ sealed = append(sealed, digest...)
+ paddingLen := cbc.BlockSize() - (len(sealed) % cbc.BlockSize())
+ if options.noPadding {
+ if paddingLen != cbc.BlockSize() {
+ return nil, fmt.Errorf("invalid length for noPadding")
+ }
+ noSeal = true
+ fails = true
+ } else {
+ if options.extraPadding {
+ paddingLen += cbc.BlockSize()
+ noSeal = true
+ if *ssl3 {
+ // SSLv3 padding must be minimal.
+ fails = true
+ }
+ }
+ if *ssl3 {
+ sealed = append(sealed, make([]byte, paddingLen-1)...)
+ sealed = append(sealed, byte(paddingLen-1))
+ } else {
+ pad := make([]byte, paddingLen)
+ for i := range pad {
+ pad[i] = byte(paddingLen - 1)
+ }
+ sealed = append(sealed, pad...)
+ }
+ if options.wrongPadding && paddingLen > 1 {
+ sealed[len(sealed)-2]++
+ noSeal = true
+ if !*ssl3 {
+ // TLS specifies the all the padding bytes.
+ fails = true
+ }
+ }
+ }
+ cbc.CryptBlocks(sealed, sealed)
+ }
+
+ key := make([]byte, 0, len(macKey)+len(encKey)+len(fixedIV))
+ key = append(key, macKey...)
+ key = append(key, encKey...)
+ key = append(key, fixedIV...)
+ t := &testCase{
+ digest: digest,
+ key: key,
+ nonce: nonce,
+ input: input,
+ ad: ad,
+ ciphertext: sealed[:len(sealed)-hash.Size()],
+ tag: sealed[len(sealed)-hash.Size():],
+ noSeal: noSeal,
+ fails: fails,
+ }
+ return t, nil
+}
+
+func printTestCase(t *testCase) {
+ fmt.Printf("# DIGEST: %s\n", hex.EncodeToString(t.digest))
+ fmt.Printf("KEY: %s\n", hex.EncodeToString(t.key))
+ fmt.Printf("NONCE: %s\n", hex.EncodeToString(t.nonce))
+ fmt.Printf("IN: %s\n", hex.EncodeToString(t.input))
+ fmt.Printf("AD: %s\n", hex.EncodeToString(t.ad))
+ fmt.Printf("CT: %s\n", hex.EncodeToString(t.ciphertext))
+ fmt.Printf("TAG: %s\n", hex.EncodeToString(t.tag))
+ if t.noSeal {
+ fmt.Printf("NO_SEAL: 01\n")
+ }
+ if t.fails {
+ fmt.Printf("FAILS: 01\n")
+ }
+}
+
+func main() {
+ flag.Parse()
+
+ commandLine := fmt.Sprintf("go run make_legacy_aead_tests.go -cipher %s -mac %s", *bulkCipher, *mac)
+ if *implicitIV {
+ commandLine += " -implicit-iv"
+ }
+ if *ssl3 {
+ commandLine += " -ssl3"
+ }
+ fmt.Printf("# Generated by\n")
+ fmt.Printf("# %s\n", commandLine)
+ fmt.Printf("#\n")
+ fmt.Printf("# Note: aead_test's input format splits the ciphertext and tag positions of the sealed\n")
+ fmt.Printf("# input. But these legacy AEADs are MAC-then-encrypt and may include padding, so this\n")
+ fmt.Printf("# split isn't meaningful. The unencrypted MAC is included in the 'DIGEST' tag above\n")
+ fmt.Printf("# each test case.\n")
+ fmt.Printf("\n")
+
+ // For CBC-mode ciphers, emit tests for padding flexibility.
+ if *bulkCipher != "rc4" {
+ fmt.Printf("# Test with non-minimal padding.\n")
+ t, err := makeTestCase(5, options{extraPadding: true})
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s\n", err)
+ os.Exit(1)
+ }
+ printTestCase(t)
+ fmt.Printf("\n")
+
+ fmt.Printf("# Test with bad padding values.\n")
+ t, err = makeTestCase(5, options{wrongPadding: true})
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s\n", err)
+ os.Exit(1)
+ }
+ printTestCase(t)
+ fmt.Printf("\n")
+
+ fmt.Printf("# Test with no padding.\n")
+ hash, ok := getHash(*mac)
+ if !ok {
+ panic("unknown hash")
+ }
+ t, err = makeTestCase(64-hash.Size(), options{noPadding: true})
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s\n", err)
+ os.Exit(1)
+ }
+ printTestCase(t)
+ fmt.Printf("\n")
+ }
+
+ // Generate long enough of input to cover a non-zero num_starting_blocks
+ // value in the constant-time CBC logic.
+ for l := 0; l < 500; l += 5 {
+ t, err := makeTestCase(l, options{})
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s\n", err)
+ os.Exit(1)
+ }
+ printTestCase(t)
+ fmt.Printf("\n")
+ }
+}