Source file src/crypto/cipher/ctr_aes_test.go

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // CTR AES test vectors.
     6  
     7  // See U.S. National Institute of Standards and Technology (NIST)
     8  // Special Publication 800-38A, ``Recommendation for Block Cipher
     9  // Modes of Operation,'' 2001 Edition, pp. 55-58.
    10  
    11  package cipher_test
    12  
    13  import (
    14  	"bytes"
    15  	"crypto/aes"
    16  	"crypto/cipher"
    17  	"crypto/internal/boring"
    18  	"crypto/internal/cryptotest"
    19  	fipsaes "crypto/internal/fips140/aes"
    20  	"encoding/hex"
    21  	"fmt"
    22  	"math/rand"
    23  	"sort"
    24  	"strings"
    25  	"testing"
    26  )
    27  
    28  var commonCounter = []byte{0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}
    29  
    30  var ctrAESTests = []struct {
    31  	name string
    32  	key  []byte
    33  	iv   []byte
    34  	in   []byte
    35  	out  []byte
    36  }{
    37  	// NIST SP 800-38A pp 55-58
    38  	{
    39  		"CTR-AES128",
    40  		commonKey128,
    41  		commonCounter,
    42  		commonInput,
    43  		[]byte{
    44  			0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
    45  			0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff, 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
    46  			0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e, 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
    47  			0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1, 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee,
    48  		},
    49  	},
    50  	{
    51  		"CTR-AES192",
    52  		commonKey192,
    53  		commonCounter,
    54  		commonInput,
    55  		[]byte{
    56  			0x1a, 0xbc, 0x93, 0x24, 0x17, 0x52, 0x1c, 0xa2, 0x4f, 0x2b, 0x04, 0x59, 0xfe, 0x7e, 0x6e, 0x0b,
    57  			0x09, 0x03, 0x39, 0xec, 0x0a, 0xa6, 0xfa, 0xef, 0xd5, 0xcc, 0xc2, 0xc6, 0xf4, 0xce, 0x8e, 0x94,
    58  			0x1e, 0x36, 0xb2, 0x6b, 0xd1, 0xeb, 0xc6, 0x70, 0xd1, 0xbd, 0x1d, 0x66, 0x56, 0x20, 0xab, 0xf7,
    59  			0x4f, 0x78, 0xa7, 0xf6, 0xd2, 0x98, 0x09, 0x58, 0x5a, 0x97, 0xda, 0xec, 0x58, 0xc6, 0xb0, 0x50,
    60  		},
    61  	},
    62  	{
    63  		"CTR-AES256",
    64  		commonKey256,
    65  		commonCounter,
    66  		commonInput,
    67  		[]byte{
    68  			0x60, 0x1e, 0xc3, 0x13, 0x77, 0x57, 0x89, 0xa5, 0xb7, 0xa7, 0xf5, 0x04, 0xbb, 0xf3, 0xd2, 0x28,
    69  			0xf4, 0x43, 0xe3, 0xca, 0x4d, 0x62, 0xb5, 0x9a, 0xca, 0x84, 0xe9, 0x90, 0xca, 0xca, 0xf5, 0xc5,
    70  			0x2b, 0x09, 0x30, 0xda, 0xa2, 0x3d, 0xe9, 0x4c, 0xe8, 0x70, 0x17, 0xba, 0x2d, 0x84, 0x98, 0x8d,
    71  			0xdf, 0xc9, 0xc5, 0x8d, 0xb6, 0x7a, 0xad, 0xa6, 0x13, 0xc2, 0xdd, 0x08, 0x45, 0x79, 0x41, 0xa6,
    72  		},
    73  	},
    74  }
    75  
    76  func TestCTR_AES(t *testing.T) {
    77  	cryptotest.TestAllImplementations(t, "aes", testCTR_AES)
    78  }
    79  
    80  func testCTR_AES(t *testing.T) {
    81  	for _, tt := range ctrAESTests {
    82  		test := tt.name
    83  
    84  		c, err := aes.NewCipher(tt.key)
    85  		if err != nil {
    86  			t.Errorf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err)
    87  			continue
    88  		}
    89  
    90  		for j := 0; j <= 5; j += 5 {
    91  			in := tt.in[0 : len(tt.in)-j]
    92  			ctr := cipher.NewCTR(c, tt.iv)
    93  			encrypted := make([]byte, len(in))
    94  			ctr.XORKeyStream(encrypted, in)
    95  			if out := tt.out[:len(in)]; !bytes.Equal(out, encrypted) {
    96  				t.Errorf("%s/%d: CTR\ninpt %x\nhave %x\nwant %x", test, len(in), in, encrypted, out)
    97  			}
    98  		}
    99  
   100  		for j := 0; j <= 7; j += 7 {
   101  			in := tt.out[0 : len(tt.out)-j]
   102  			ctr := cipher.NewCTR(c, tt.iv)
   103  			plain := make([]byte, len(in))
   104  			ctr.XORKeyStream(plain, in)
   105  			if out := tt.in[:len(in)]; !bytes.Equal(out, plain) {
   106  				t.Errorf("%s/%d: CTRReader\nhave %x\nwant %x", test, len(out), plain, out)
   107  			}
   108  		}
   109  
   110  		if t.Failed() {
   111  			break
   112  		}
   113  	}
   114  }
   115  
   116  func makeTestingCiphers(aesBlock cipher.Block, iv []byte) (genericCtr, multiblockCtr cipher.Stream) {
   117  	return cipher.NewCTR(wrap(aesBlock), iv), cipher.NewCTR(aesBlock, iv)
   118  }
   119  
   120  func randBytes(t *testing.T, r *rand.Rand, count int) []byte {
   121  	t.Helper()
   122  	buf := make([]byte, count)
   123  	n, err := r.Read(buf)
   124  	if err != nil {
   125  		t.Fatal(err)
   126  	}
   127  	if n != count {
   128  		t.Fatal("short read from Rand")
   129  	}
   130  	return buf
   131  }
   132  
   133  const aesBlockSize = 16
   134  
   135  type ctrAble interface {
   136  	NewCTR(iv []byte) cipher.Stream
   137  }
   138  
   139  // Verify that multiblock AES CTR (src/crypto/aes/ctr_*.s)
   140  // produces the same results as generic single-block implementation.
   141  // This test runs checks on random IV.
   142  func TestCTR_AES_multiblock_random_IV(t *testing.T) {
   143  	r := rand.New(rand.NewSource(54321))
   144  	iv := randBytes(t, r, aesBlockSize)
   145  	const Size = 100
   146  
   147  	for _, keySize := range []int{16, 24, 32} {
   148  		t.Run(fmt.Sprintf("keySize=%d", keySize), func(t *testing.T) {
   149  			key := randBytes(t, r, keySize)
   150  			aesBlock, err := aes.NewCipher(key)
   151  			if err != nil {
   152  				t.Fatal(err)
   153  			}
   154  			genericCtr, _ := makeTestingCiphers(aesBlock, iv)
   155  
   156  			plaintext := randBytes(t, r, Size)
   157  
   158  			// Generate reference ciphertext.
   159  			genericCiphertext := make([]byte, len(plaintext))
   160  			genericCtr.XORKeyStream(genericCiphertext, plaintext)
   161  
   162  			// Split the text in 3 parts in all possible ways and encrypt them
   163  			// individually using multiblock implementation to catch edge cases.
   164  
   165  			for part1 := 0; part1 <= Size; part1++ {
   166  				t.Run(fmt.Sprintf("part1=%d", part1), func(t *testing.T) {
   167  					for part2 := 0; part2 <= Size-part1; part2++ {
   168  						t.Run(fmt.Sprintf("part2=%d", part2), func(t *testing.T) {
   169  							_, multiblockCtr := makeTestingCiphers(aesBlock, iv)
   170  							multiblockCiphertext := make([]byte, len(plaintext))
   171  							multiblockCtr.XORKeyStream(multiblockCiphertext[:part1], plaintext[:part1])
   172  							multiblockCtr.XORKeyStream(multiblockCiphertext[part1:part1+part2], plaintext[part1:part1+part2])
   173  							multiblockCtr.XORKeyStream(multiblockCiphertext[part1+part2:], plaintext[part1+part2:])
   174  							if !bytes.Equal(genericCiphertext, multiblockCiphertext) {
   175  								t.Fatal("multiblock CTR's output does not match generic CTR's output")
   176  							}
   177  						})
   178  					}
   179  				})
   180  			}
   181  		})
   182  	}
   183  }
   184  
   185  func parseHex(str string) []byte {
   186  	b, err := hex.DecodeString(strings.ReplaceAll(str, " ", ""))
   187  	if err != nil {
   188  		panic(err)
   189  	}
   190  	return b
   191  }
   192  
   193  // Verify that multiblock AES CTR (src/crypto/aes/ctr_*.s)
   194  // produces the same results as generic single-block implementation.
   195  // This test runs checks on edge cases (IV overflows).
   196  func TestCTR_AES_multiblock_overflow_IV(t *testing.T) {
   197  	r := rand.New(rand.NewSource(987654))
   198  
   199  	const Size = 4096
   200  	plaintext := randBytes(t, r, Size)
   201  
   202  	ivs := [][]byte{
   203  		parseHex("00 00 00 00 00 00 00 00   FF FF FF FF FF FF FF FF"),
   204  		parseHex("FF FF FF FF FF FF FF FF   FF FF FF FF FF FF FF FF"),
   205  		parseHex("FF FF FF FF FF FF FF FF   00 00 00 00 00 00 00 00"),
   206  		parseHex("FF FF FF FF FF FF FF FF   FF FF FF FF FF FF FF fe"),
   207  		parseHex("00 00 00 00 00 00 00 00   FF FF FF FF FF FF FF fe"),
   208  		parseHex("FF FF FF FF FF FF FF FF   FF FF FF FF FF FF FF 00"),
   209  		parseHex("00 00 00 00 00 00 00 01   FF FF FF FF FF FF FF 00"),
   210  		parseHex("00 00 00 00 00 00 00 01   FF FF FF FF FF FF FF FF"),
   211  		parseHex("00 00 00 00 00 00 00 01   FF FF FF FF FF FF FF fe"),
   212  		parseHex("00 00 00 00 00 00 00 01   FF FF FF FF FF FF FF 00"),
   213  	}
   214  
   215  	for _, keySize := range []int{16, 24, 32} {
   216  		t.Run(fmt.Sprintf("keySize=%d", keySize), func(t *testing.T) {
   217  			for _, iv := range ivs {
   218  				key := randBytes(t, r, keySize)
   219  				aesBlock, err := aes.NewCipher(key)
   220  				if err != nil {
   221  					t.Fatal(err)
   222  				}
   223  
   224  				t.Run(fmt.Sprintf("iv=%s", hex.EncodeToString(iv)), func(t *testing.T) {
   225  					for _, offset := range []int{0, 1, 16, 1024} {
   226  						t.Run(fmt.Sprintf("offset=%d", offset), func(t *testing.T) {
   227  							genericCtr, multiblockCtr := makeTestingCiphers(aesBlock, iv)
   228  
   229  							// Generate reference ciphertext.
   230  							genericCiphertext := make([]byte, Size)
   231  							genericCtr.XORKeyStream(genericCiphertext, plaintext)
   232  
   233  							multiblockCiphertext := make([]byte, Size)
   234  							multiblockCtr.XORKeyStream(multiblockCiphertext, plaintext[:offset])
   235  							multiblockCtr.XORKeyStream(multiblockCiphertext[offset:], plaintext[offset:])
   236  							if !bytes.Equal(genericCiphertext, multiblockCiphertext) {
   237  								t.Fatal("multiblock CTR's output does not match generic CTR's output")
   238  							}
   239  						})
   240  					}
   241  				})
   242  			}
   243  		})
   244  	}
   245  }
   246  
   247  // Check that method XORKeyStreamAt works correctly.
   248  func TestCTR_AES_multiblock_XORKeyStreamAt(t *testing.T) {
   249  	if boring.Enabled {
   250  		t.Skip("XORKeyStreamAt is not available in boring mode")
   251  	}
   252  
   253  	r := rand.New(rand.NewSource(12345))
   254  	const Size = 32 * 1024 * 1024
   255  	plaintext := randBytes(t, r, Size)
   256  
   257  	for _, keySize := range []int{16, 24, 32} {
   258  		t.Run(fmt.Sprintf("keySize=%d", keySize), func(t *testing.T) {
   259  			key := randBytes(t, r, keySize)
   260  			iv := randBytes(t, r, aesBlockSize)
   261  
   262  			aesBlock, err := aes.NewCipher(key)
   263  			if err != nil {
   264  				t.Fatal(err)
   265  			}
   266  			genericCtr, _ := makeTestingCiphers(aesBlock, iv)
   267  			ctrAt := fipsaes.NewCTR(aesBlock.(*fipsaes.Block), iv)
   268  
   269  			// Generate reference ciphertext.
   270  			genericCiphertext := make([]byte, Size)
   271  			genericCtr.XORKeyStream(genericCiphertext, plaintext)
   272  
   273  			multiblockCiphertext := make([]byte, Size)
   274  			// Split the range to random slices.
   275  			const N = 1000
   276  			boundaries := make([]int, 0, N+2)
   277  			for i := 0; i < N; i++ {
   278  				boundaries = append(boundaries, r.Intn(Size))
   279  			}
   280  			boundaries = append(boundaries, 0)
   281  			boundaries = append(boundaries, Size)
   282  			sort.Ints(boundaries)
   283  
   284  			for _, i := range r.Perm(N + 1) {
   285  				begin := boundaries[i]
   286  				end := boundaries[i+1]
   287  				ctrAt.XORKeyStreamAt(
   288  					multiblockCiphertext[begin:end],
   289  					plaintext[begin:end],
   290  					uint64(begin),
   291  				)
   292  			}
   293  
   294  			if !bytes.Equal(genericCiphertext, multiblockCiphertext) {
   295  				t.Fatal("multiblock CTR's output does not match generic CTR's output")
   296  			}
   297  		})
   298  	}
   299  }
   300  

View as plain text