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/binary"
    21  	"encoding/hex"
    22  	"fmt"
    23  	"math/rand"
    24  	"sort"
    25  	"strings"
    26  	"testing"
    27  )
    28  
    29  var commonCounter = []byte{0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff}
    30  
    31  var ctrAESTests = []struct {
    32  	name string
    33  	key  []byte
    34  	iv   []byte
    35  	in   []byte
    36  	out  []byte
    37  }{
    38  	// NIST SP 800-38A pp 55-58
    39  	{
    40  		"CTR-AES128",
    41  		commonKey128,
    42  		commonCounter,
    43  		commonInput,
    44  		[]byte{
    45  			0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, 0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
    46  			0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff, 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
    47  			0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e, 0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
    48  			0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1, 0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee,
    49  		},
    50  	},
    51  	{
    52  		"CTR-AES192",
    53  		commonKey192,
    54  		commonCounter,
    55  		commonInput,
    56  		[]byte{
    57  			0x1a, 0xbc, 0x93, 0x24, 0x17, 0x52, 0x1c, 0xa2, 0x4f, 0x2b, 0x04, 0x59, 0xfe, 0x7e, 0x6e, 0x0b,
    58  			0x09, 0x03, 0x39, 0xec, 0x0a, 0xa6, 0xfa, 0xef, 0xd5, 0xcc, 0xc2, 0xc6, 0xf4, 0xce, 0x8e, 0x94,
    59  			0x1e, 0x36, 0xb2, 0x6b, 0xd1, 0xeb, 0xc6, 0x70, 0xd1, 0xbd, 0x1d, 0x66, 0x56, 0x20, 0xab, 0xf7,
    60  			0x4f, 0x78, 0xa7, 0xf6, 0xd2, 0x98, 0x09, 0x58, 0x5a, 0x97, 0xda, 0xec, 0x58, 0xc6, 0xb0, 0x50,
    61  		},
    62  	},
    63  	{
    64  		"CTR-AES256",
    65  		commonKey256,
    66  		commonCounter,
    67  		commonInput,
    68  		[]byte{
    69  			0x60, 0x1e, 0xc3, 0x13, 0x77, 0x57, 0x89, 0xa5, 0xb7, 0xa7, 0xf5, 0x04, 0xbb, 0xf3, 0xd2, 0x28,
    70  			0xf4, 0x43, 0xe3, 0xca, 0x4d, 0x62, 0xb5, 0x9a, 0xca, 0x84, 0xe9, 0x90, 0xca, 0xca, 0xf5, 0xc5,
    71  			0x2b, 0x09, 0x30, 0xda, 0xa2, 0x3d, 0xe9, 0x4c, 0xe8, 0x70, 0x17, 0xba, 0x2d, 0x84, 0x98, 0x8d,
    72  			0xdf, 0xc9, 0xc5, 0x8d, 0xb6, 0x7a, 0xad, 0xa6, 0x13, 0xc2, 0xdd, 0x08, 0x45, 0x79, 0x41, 0xa6,
    73  		},
    74  	},
    75  }
    76  
    77  func TestCTR_AES(t *testing.T) {
    78  	cryptotest.TestAllImplementations(t, "aes", testCTR_AES)
    79  }
    80  
    81  func testCTR_AES(t *testing.T) {
    82  	for _, tt := range ctrAESTests {
    83  		test := tt.name
    84  
    85  		c, err := aes.NewCipher(tt.key)
    86  		if err != nil {
    87  			t.Errorf("%s: NewCipher(%d bytes) = %s", test, len(tt.key), err)
    88  			continue
    89  		}
    90  
    91  		for j := 0; j <= 5; j += 5 {
    92  			in := tt.in[0 : len(tt.in)-j]
    93  			ctr := cipher.NewCTR(c, tt.iv)
    94  			encrypted := make([]byte, len(in))
    95  			ctr.XORKeyStream(encrypted, in)
    96  			if out := tt.out[:len(in)]; !bytes.Equal(out, encrypted) {
    97  				t.Errorf("%s/%d: CTR\ninpt %x\nhave %x\nwant %x", test, len(in), in, encrypted, out)
    98  			}
    99  		}
   100  
   101  		for j := 0; j <= 7; j += 7 {
   102  			in := tt.out[0 : len(tt.out)-j]
   103  			ctr := cipher.NewCTR(c, tt.iv)
   104  			plain := make([]byte, len(in))
   105  			ctr.XORKeyStream(plain, in)
   106  			if out := tt.in[:len(in)]; !bytes.Equal(out, plain) {
   107  				t.Errorf("%s/%d: CTRReader\nhave %x\nwant %x", test, len(out), plain, out)
   108  			}
   109  		}
   110  
   111  		if t.Failed() {
   112  			break
   113  		}
   114  	}
   115  }
   116  
   117  func makeTestingCiphers(aesBlock cipher.Block, iv []byte) (genericCtr, multiblockCtr cipher.Stream) {
   118  	return cipher.NewCTR(wrap(aesBlock), iv), cipher.NewCTR(aesBlock, iv)
   119  }
   120  
   121  // TestCTR_AES_blocks8FastPathMatchesGeneric ensures the overlow aware branch
   122  // produces identical keystreams to the generic counter walker across
   123  // representative IVs, including near-overflow cases.
   124  func TestCTR_AES_blocks8FastPathMatchesGeneric(t *testing.T) {
   125  	key := make([]byte, aes.BlockSize)
   126  	block, err := aes.NewCipher(key)
   127  	if err != nil {
   128  		t.Fatal(err)
   129  	}
   130  	if _, ok := block.(*fipsaes.Block); !ok {
   131  		t.Skip("requires crypto/internal/fips140/aes")
   132  	}
   133  
   134  	keystream := make([]byte, 8*aes.BlockSize)
   135  
   136  	testCases := []struct {
   137  		name string
   138  		hi   uint64
   139  		lo   uint64
   140  	}{
   141  		{"Zero", 0, 0},
   142  		{"NearOverflowMinus7", 1, ^uint64(0) - 7},
   143  		{"NearOverflowMinus6", 2, ^uint64(0) - 6},
   144  		{"Overflow", 0, ^uint64(0)},
   145  	}
   146  
   147  	for _, tc := range testCases {
   148  		t.Run(tc.name, func(t *testing.T) {
   149  			var iv [aes.BlockSize]byte
   150  			binary.BigEndian.PutUint64(iv[0:8], tc.hi)
   151  			binary.BigEndian.PutUint64(iv[8:], tc.lo)
   152  
   153  			generic, multiblock := makeTestingCiphers(block, iv[:])
   154  
   155  			genericOut := make([]byte, len(keystream))
   156  			multiblockOut := make([]byte, len(keystream))
   157  
   158  			generic.XORKeyStream(genericOut, keystream)
   159  			multiblock.XORKeyStream(multiblockOut, keystream)
   160  
   161  			if !bytes.Equal(multiblockOut, genericOut) {
   162  				t.Fatalf("mismatch for iv %#x:%#x\n"+
   163  					"asm keystream: %x\n"+
   164  					"gen keystream: %x\n"+
   165  					"asm counters: %x\n"+
   166  					"gen counters: %x",
   167  					tc.hi, tc.lo, multiblockOut, genericOut,
   168  					extractCounters(block, multiblockOut),
   169  					extractCounters(block, genericOut))
   170  			}
   171  		})
   172  	}
   173  }
   174  
   175  func randBytes(t *testing.T, r *rand.Rand, count int) []byte {
   176  	t.Helper()
   177  	buf := make([]byte, count)
   178  	n, err := r.Read(buf)
   179  	if err != nil {
   180  		t.Fatal(err)
   181  	}
   182  	if n != count {
   183  		t.Fatal("short read from Rand")
   184  	}
   185  	return buf
   186  }
   187  
   188  const aesBlockSize = 16
   189  
   190  type ctrAble interface {
   191  	NewCTR(iv []byte) cipher.Stream
   192  }
   193  
   194  // Verify that multiblock AES CTR (src/crypto/aes/ctr_*.s)
   195  // produces the same results as generic single-block implementation.
   196  // This test runs checks on random IV.
   197  func TestCTR_AES_multiblock_random_IV(t *testing.T) {
   198  	r := rand.New(rand.NewSource(54321))
   199  	iv := randBytes(t, r, aesBlockSize)
   200  	const Size = 100
   201  
   202  	for _, keySize := range []int{16, 24, 32} {
   203  		t.Run(fmt.Sprintf("keySize=%d", keySize), func(t *testing.T) {
   204  			key := randBytes(t, r, keySize)
   205  			aesBlock, err := aes.NewCipher(key)
   206  			if err != nil {
   207  				t.Fatal(err)
   208  			}
   209  			genericCtr, _ := makeTestingCiphers(aesBlock, iv)
   210  
   211  			plaintext := randBytes(t, r, Size)
   212  
   213  			// Generate reference ciphertext.
   214  			genericCiphertext := make([]byte, len(plaintext))
   215  			genericCtr.XORKeyStream(genericCiphertext, plaintext)
   216  
   217  			// Split the text in 3 parts in all possible ways and encrypt them
   218  			// individually using multiblock implementation to catch edge cases.
   219  
   220  			for part1 := 0; part1 <= Size; part1++ {
   221  				t.Run(fmt.Sprintf("part1=%d", part1), func(t *testing.T) {
   222  					for part2 := 0; part2 <= Size-part1; part2++ {
   223  						t.Run(fmt.Sprintf("part2=%d", part2), func(t *testing.T) {
   224  							_, multiblockCtr := makeTestingCiphers(aesBlock, iv)
   225  							multiblockCiphertext := make([]byte, len(plaintext))
   226  							multiblockCtr.XORKeyStream(multiblockCiphertext[:part1], plaintext[:part1])
   227  							multiblockCtr.XORKeyStream(multiblockCiphertext[part1:part1+part2], plaintext[part1:part1+part2])
   228  							multiblockCtr.XORKeyStream(multiblockCiphertext[part1+part2:], plaintext[part1+part2:])
   229  							if !bytes.Equal(genericCiphertext, multiblockCiphertext) {
   230  								t.Fatal("multiblock CTR's output does not match generic CTR's output")
   231  							}
   232  						})
   233  					}
   234  				})
   235  			}
   236  		})
   237  	}
   238  }
   239  
   240  func parseHex(str string) []byte {
   241  	b, err := hex.DecodeString(strings.ReplaceAll(str, " ", ""))
   242  	if err != nil {
   243  		panic(err)
   244  	}
   245  	return b
   246  }
   247  
   248  // Verify that multiblock AES CTR (src/crypto/aes/ctr_*.s)
   249  // produces the same results as generic single-block implementation.
   250  // This test runs checks on edge cases (IV overflows).
   251  func TestCTR_AES_multiblock_overflow_IV(t *testing.T) {
   252  	r := rand.New(rand.NewSource(987654))
   253  
   254  	const Size = 4096
   255  	plaintext := randBytes(t, r, Size)
   256  
   257  	ivs := [][]byte{
   258  		parseHex("00 00 00 00 00 00 00 00   FF FF FF FF FF FF FF FF"),
   259  		parseHex("FF FF FF FF FF FF FF FF   FF FF FF FF FF FF FF FF"),
   260  		parseHex("FF FF FF FF FF FF FF FF   00 00 00 00 00 00 00 00"),
   261  		parseHex("FF FF FF FF FF FF FF FF   FF FF FF FF FF FF FF fe"),
   262  		parseHex("00 00 00 00 00 00 00 00   FF FF FF FF FF FF FF fe"),
   263  		parseHex("FF FF FF FF FF FF FF FF   FF FF FF FF FF FF FF 00"),
   264  		parseHex("00 00 00 00 00 00 00 01   FF FF FF FF FF FF FF 00"),
   265  		parseHex("00 00 00 00 00 00 00 01   FF FF FF FF FF FF FF FF"),
   266  		parseHex("00 00 00 00 00 00 00 01   FF FF FF FF FF FF FF fe"),
   267  		parseHex("00 00 00 00 00 00 00 01   FF FF FF FF FF FF FF 00"),
   268  	}
   269  
   270  	for _, keySize := range []int{16, 24, 32} {
   271  		t.Run(fmt.Sprintf("keySize=%d", keySize), func(t *testing.T) {
   272  			for _, iv := range ivs {
   273  				key := randBytes(t, r, keySize)
   274  				aesBlock, err := aes.NewCipher(key)
   275  				if err != nil {
   276  					t.Fatal(err)
   277  				}
   278  
   279  				t.Run(fmt.Sprintf("iv=%s", hex.EncodeToString(iv)), func(t *testing.T) {
   280  					for _, offset := range []int{0, 1, 16, 1024} {
   281  						t.Run(fmt.Sprintf("offset=%d", offset), func(t *testing.T) {
   282  							genericCtr, multiblockCtr := makeTestingCiphers(aesBlock, iv)
   283  
   284  							// Generate reference ciphertext.
   285  							genericCiphertext := make([]byte, Size)
   286  							genericCtr.XORKeyStream(genericCiphertext, plaintext)
   287  
   288  							multiblockCiphertext := make([]byte, Size)
   289  							multiblockCtr.XORKeyStream(multiblockCiphertext, plaintext[:offset])
   290  							multiblockCtr.XORKeyStream(multiblockCiphertext[offset:], plaintext[offset:])
   291  							if !bytes.Equal(genericCiphertext, multiblockCiphertext) {
   292  								t.Fatal("multiblock CTR's output does not match generic CTR's output")
   293  							}
   294  						})
   295  					}
   296  				})
   297  			}
   298  		})
   299  	}
   300  }
   301  
   302  // Check that method XORKeyStreamAt works correctly.
   303  func TestCTR_AES_multiblock_XORKeyStreamAt(t *testing.T) {
   304  	if boring.Enabled {
   305  		t.Skip("XORKeyStreamAt is not available in boring mode")
   306  	}
   307  
   308  	r := rand.New(rand.NewSource(12345))
   309  	const Size = 32 * 1024 * 1024
   310  	plaintext := randBytes(t, r, Size)
   311  
   312  	for _, keySize := range []int{16, 24, 32} {
   313  		t.Run(fmt.Sprintf("keySize=%d", keySize), func(t *testing.T) {
   314  			key := randBytes(t, r, keySize)
   315  			iv := randBytes(t, r, aesBlockSize)
   316  
   317  			aesBlock, err := aes.NewCipher(key)
   318  			if err != nil {
   319  				t.Fatal(err)
   320  			}
   321  			genericCtr, _ := makeTestingCiphers(aesBlock, iv)
   322  			ctrAt := fipsaes.NewCTR(aesBlock.(*fipsaes.Block), iv)
   323  
   324  			// Generate reference ciphertext.
   325  			genericCiphertext := make([]byte, Size)
   326  			genericCtr.XORKeyStream(genericCiphertext, plaintext)
   327  
   328  			multiblockCiphertext := make([]byte, Size)
   329  			// Split the range to random slices.
   330  			const N = 1000
   331  			boundaries := make([]int, 0, N+2)
   332  			for i := 0; i < N; i++ {
   333  				boundaries = append(boundaries, r.Intn(Size))
   334  			}
   335  			boundaries = append(boundaries, 0)
   336  			boundaries = append(boundaries, Size)
   337  			sort.Ints(boundaries)
   338  
   339  			for _, i := range r.Perm(N + 1) {
   340  				begin := boundaries[i]
   341  				end := boundaries[i+1]
   342  				ctrAt.XORKeyStreamAt(
   343  					multiblockCiphertext[begin:end],
   344  					plaintext[begin:end],
   345  					uint64(begin),
   346  				)
   347  			}
   348  
   349  			if !bytes.Equal(genericCiphertext, multiblockCiphertext) {
   350  				t.Fatal("multiblock CTR's output does not match generic CTR's output")
   351  			}
   352  		})
   353  	}
   354  }
   355  
   356  func extractCounters(block cipher.Block, keystream []byte) []byte {
   357  	blockSize := block.BlockSize()
   358  	res := make([]byte, len(keystream))
   359  	for i := 0; i < len(keystream); i += blockSize {
   360  		block.Decrypt(res[i:i+blockSize], keystream[i:i+blockSize])
   361  	}
   362  	return res
   363  }
   364  

View as plain text