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  		keySize := keySize
   149  		t.Run(fmt.Sprintf("keySize=%d", keySize), func(t *testing.T) {
   150  			key := randBytes(t, r, keySize)
   151  			aesBlock, err := aes.NewCipher(key)
   152  			if err != nil {
   153  				t.Fatal(err)
   154  			}
   155  			genericCtr, _ := makeTestingCiphers(aesBlock, iv)
   156  
   157  			plaintext := randBytes(t, r, Size)
   158  
   159  			// Generate reference ciphertext.
   160  			genericCiphertext := make([]byte, len(plaintext))
   161  			genericCtr.XORKeyStream(genericCiphertext, plaintext)
   162  
   163  			// Split the text in 3 parts in all possible ways and encrypt them
   164  			// individually using multiblock implementation to catch edge cases.
   165  
   166  			for part1 := 0; part1 <= Size; part1++ {
   167  				part1 := part1
   168  				t.Run(fmt.Sprintf("part1=%d", part1), func(t *testing.T) {
   169  					for part2 := 0; part2 <= Size-part1; part2++ {
   170  						part2 := part2
   171  						t.Run(fmt.Sprintf("part2=%d", part2), func(t *testing.T) {
   172  							_, multiblockCtr := makeTestingCiphers(aesBlock, iv)
   173  							multiblockCiphertext := make([]byte, len(plaintext))
   174  							multiblockCtr.XORKeyStream(multiblockCiphertext[:part1], plaintext[:part1])
   175  							multiblockCtr.XORKeyStream(multiblockCiphertext[part1:part1+part2], plaintext[part1:part1+part2])
   176  							multiblockCtr.XORKeyStream(multiblockCiphertext[part1+part2:], plaintext[part1+part2:])
   177  							if !bytes.Equal(genericCiphertext, multiblockCiphertext) {
   178  								t.Fatal("multiblock CTR's output does not match generic CTR's output")
   179  							}
   180  						})
   181  					}
   182  				})
   183  			}
   184  		})
   185  	}
   186  }
   187  
   188  func parseHex(str string) []byte {
   189  	b, err := hex.DecodeString(strings.ReplaceAll(str, " ", ""))
   190  	if err != nil {
   191  		panic(err)
   192  	}
   193  	return b
   194  }
   195  
   196  // Verify that multiblock AES CTR (src/crypto/aes/ctr_*.s)
   197  // produces the same results as generic single-block implementation.
   198  // This test runs checks on edge cases (IV overflows).
   199  func TestCTR_AES_multiblock_overflow_IV(t *testing.T) {
   200  	r := rand.New(rand.NewSource(987654))
   201  
   202  	const Size = 4096
   203  	plaintext := randBytes(t, r, Size)
   204  
   205  	ivs := [][]byte{
   206  		parseHex("00 00 00 00 00 00 00 00   FF FF FF FF FF FF FF FF"),
   207  		parseHex("FF FF FF FF FF FF FF FF   FF FF FF FF FF FF FF FF"),
   208  		parseHex("FF FF FF FF FF FF FF FF   00 00 00 00 00 00 00 00"),
   209  		parseHex("FF FF FF FF FF FF FF FF   FF FF FF FF FF FF FF fe"),
   210  		parseHex("00 00 00 00 00 00 00 00   FF FF FF FF FF FF FF fe"),
   211  		parseHex("FF FF FF FF FF FF FF FF   FF FF FF FF FF FF FF 00"),
   212  		parseHex("00 00 00 00 00 00 00 01   FF FF FF FF FF FF FF 00"),
   213  		parseHex("00 00 00 00 00 00 00 01   FF FF FF FF FF FF FF FF"),
   214  		parseHex("00 00 00 00 00 00 00 01   FF FF FF FF FF FF FF fe"),
   215  		parseHex("00 00 00 00 00 00 00 01   FF FF FF FF FF FF FF 00"),
   216  	}
   217  
   218  	for _, keySize := range []int{16, 24, 32} {
   219  		keySize := keySize
   220  		t.Run(fmt.Sprintf("keySize=%d", keySize), func(t *testing.T) {
   221  			for _, iv := range ivs {
   222  				key := randBytes(t, r, keySize)
   223  				aesBlock, err := aes.NewCipher(key)
   224  				if err != nil {
   225  					t.Fatal(err)
   226  				}
   227  
   228  				t.Run(fmt.Sprintf("iv=%s", hex.EncodeToString(iv)), func(t *testing.T) {
   229  					for _, offset := range []int{0, 1, 16, 1024} {
   230  						offset := offset
   231  						t.Run(fmt.Sprintf("offset=%d", offset), func(t *testing.T) {
   232  							genericCtr, multiblockCtr := makeTestingCiphers(aesBlock, iv)
   233  
   234  							// Generate reference ciphertext.
   235  							genericCiphertext := make([]byte, Size)
   236  							genericCtr.XORKeyStream(genericCiphertext, plaintext)
   237  
   238  							multiblockCiphertext := make([]byte, Size)
   239  							multiblockCtr.XORKeyStream(multiblockCiphertext, plaintext[:offset])
   240  							multiblockCtr.XORKeyStream(multiblockCiphertext[offset:], plaintext[offset:])
   241  							if !bytes.Equal(genericCiphertext, multiblockCiphertext) {
   242  								t.Fatal("multiblock CTR's output does not match generic CTR's output")
   243  							}
   244  						})
   245  					}
   246  				})
   247  			}
   248  		})
   249  	}
   250  }
   251  
   252  // Check that method XORKeyStreamAt works correctly.
   253  func TestCTR_AES_multiblock_XORKeyStreamAt(t *testing.T) {
   254  	if boring.Enabled {
   255  		t.Skip("XORKeyStreamAt is not available in boring mode")
   256  	}
   257  
   258  	r := rand.New(rand.NewSource(12345))
   259  	const Size = 32 * 1024 * 1024
   260  	plaintext := randBytes(t, r, Size)
   261  
   262  	for _, keySize := range []int{16, 24, 32} {
   263  		keySize := keySize
   264  		t.Run(fmt.Sprintf("keySize=%d", keySize), func(t *testing.T) {
   265  			key := randBytes(t, r, keySize)
   266  			iv := randBytes(t, r, aesBlockSize)
   267  
   268  			aesBlock, err := aes.NewCipher(key)
   269  			if err != nil {
   270  				t.Fatal(err)
   271  			}
   272  			genericCtr, _ := makeTestingCiphers(aesBlock, iv)
   273  			ctrAt := fipsaes.NewCTR(aesBlock.(*fipsaes.Block), iv)
   274  
   275  			// Generate reference ciphertext.
   276  			genericCiphertext := make([]byte, Size)
   277  			genericCtr.XORKeyStream(genericCiphertext, plaintext)
   278  
   279  			multiblockCiphertext := make([]byte, Size)
   280  			// Split the range to random slices.
   281  			const N = 1000
   282  			boundaries := make([]int, 0, N+2)
   283  			for i := 0; i < N; i++ {
   284  				boundaries = append(boundaries, r.Intn(Size))
   285  			}
   286  			boundaries = append(boundaries, 0)
   287  			boundaries = append(boundaries, Size)
   288  			sort.Ints(boundaries)
   289  
   290  			for _, i := range r.Perm(N + 1) {
   291  				begin := boundaries[i]
   292  				end := boundaries[i+1]
   293  				ctrAt.XORKeyStreamAt(
   294  					multiblockCiphertext[begin:end],
   295  					plaintext[begin:end],
   296  					uint64(begin),
   297  				)
   298  			}
   299  
   300  			if !bytes.Equal(genericCiphertext, multiblockCiphertext) {
   301  				t.Fatal("multiblock CTR's output does not match generic CTR's output")
   302  			}
   303  		})
   304  	}
   305  }
   306  

View as plain text