github.com/emmansun/gmsm@v0.29.1/internal/cryptotest/blockmode.go (about) 1 // Copyright 2024 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 package cryptotest 6 7 import ( 8 "bytes" 9 "crypto/cipher" 10 "testing" 11 ) 12 13 // MakeBlockMode returns a cipher.BlockMode instance. 14 // It expects len(iv) == b.BlockSize(). 15 type MakeBlockMode func(b cipher.Block, iv []byte) cipher.BlockMode 16 17 // TestBlockMode performs a set of tests on cipher.BlockMode implementations, 18 // checking the documented requirements of CryptBlocks. 19 func TestBlockMode(t *testing.T, block cipher.Block, makeEncrypter, makeDecrypter MakeBlockMode) { 20 rng := newRandReader(t) 21 iv := make([]byte, block.BlockSize()) 22 rng.Read(iv) 23 24 testBlockModePair(t, block, makeEncrypter, makeDecrypter, iv) 25 } 26 27 func testBlockModePair(t *testing.T, b cipher.Block, enc, dec MakeBlockMode, iv []byte) { 28 t.Run("Encryption", func(t *testing.T) { 29 testBlockMode(t, enc, b, iv) 30 }) 31 32 t.Run("Decryption", func(t *testing.T) { 33 testBlockMode(t, dec, b, iv) 34 }) 35 36 t.Run("Roundtrip", func(t *testing.T) { 37 rng := newRandReader(t) 38 39 blockSize := enc(b, iv).BlockSize() 40 if decBlockSize := dec(b, iv).BlockSize(); decBlockSize != blockSize { 41 t.Errorf("decryption blocksize different than encryption's; got %d, want %d", decBlockSize, blockSize) 42 } 43 44 before, dst, after := make([]byte, blockSize*2), make([]byte, blockSize*2), make([]byte, blockSize*2) 45 rng.Read(before) 46 47 enc(b, iv).CryptBlocks(dst, before) 48 dec(b, iv).CryptBlocks(after, dst) 49 if !bytes.Equal(after, before) { 50 t.Errorf("plaintext is different after an encrypt/decrypt cycle; got %x, want %x", after, before) 51 } 52 }) 53 } 54 55 func testBlockMode(t *testing.T, bm MakeBlockMode, b cipher.Block, iv []byte) { 56 blockSize := bm(b, iv).BlockSize() 57 58 t.Run("WrongIVLen", func(t *testing.T) { 59 iv := make([]byte, b.BlockSize()+1) 60 mustPanic(t, "IV length must equal block size", func() { bm(b, iv) }) 61 }) 62 63 t.Run("AlterInput", func(t *testing.T) { 64 rng := newRandReader(t) 65 66 src, dst, before := make([]byte, blockSize*2), make([]byte, blockSize*2), make([]byte, blockSize*2) 67 68 for _, length := range []int{0, blockSize, blockSize * 2} { 69 rng.Read(src) 70 copy(before, src) 71 72 bm(b, iv).CryptBlocks(dst[:length], src[:length]) 73 if !bytes.Equal(src, before) { 74 t.Errorf("CryptBlocks modified src; got %x, want %x", src, before) 75 } 76 } 77 }) 78 79 t.Run("Aliasing", func(t *testing.T) { 80 rng := newRandReader(t) 81 82 buff, expectedOutput := make([]byte, blockSize*2), make([]byte, blockSize*2) 83 84 for _, length := range []int{0, blockSize, blockSize * 2} { 85 // Record what output is when src and dst are different 86 rng.Read(buff) 87 bm(b, iv).CryptBlocks(expectedOutput[:length], buff[:length]) 88 89 // Check that the same output is generated when src=dst alias to the same 90 // memory 91 bm(b, iv).CryptBlocks(buff[:length], buff[:length]) 92 if !bytes.Equal(buff[:length], expectedOutput[:length]) { 93 t.Errorf("block cipher produced different output when dst = src; got %x, want %x", buff[:length], expectedOutput[:length]) 94 } 95 } 96 }) 97 98 t.Run("OutOfBoundsWrite", func(t *testing.T) { // Issue 21104 99 rng := newRandReader(t) 100 101 src := make([]byte, blockSize) 102 rng.Read(src) 103 104 // Make a buffer with dst in the middle and data on either end 105 buff := make([]byte, blockSize*3) 106 endOfPrefix, startOfSuffix := blockSize, blockSize*2 107 rng.Read(buff[:endOfPrefix]) 108 rng.Read(buff[startOfSuffix:]) 109 dst := buff[endOfPrefix:startOfSuffix] 110 111 // Record the prefix and suffix data to make sure they aren't written to 112 initPrefix, initSuffix := make([]byte, blockSize), make([]byte, blockSize) 113 copy(initPrefix, buff[:endOfPrefix]) 114 copy(initSuffix, buff[startOfSuffix:]) 115 116 // Write to dst (the middle of the buffer) and make sure it doesn't write 117 // beyond the dst slice on a valid CryptBlocks call 118 bm(b, iv).CryptBlocks(dst, src) 119 if !bytes.Equal(buff[startOfSuffix:], initSuffix) { 120 t.Errorf("block cipher did out of bounds write after end of dst slice; got %x, want %x", buff[startOfSuffix:], initSuffix) 121 } 122 if !bytes.Equal(buff[:endOfPrefix], initPrefix) { 123 t.Errorf("block cipher did out of bounds write before beginning of dst slice; got %x, want %x", buff[:endOfPrefix], initPrefix) 124 } 125 126 // Check that dst isn't written to beyond len(src) even if there is room in 127 // the slice 128 dst = buff[endOfPrefix:] // Extend dst to include suffix 129 bm(b, iv).CryptBlocks(dst, src) 130 if !bytes.Equal(buff[startOfSuffix:], initSuffix) { 131 t.Errorf("CryptBlocks modified dst past len(src); got %x, want %x", buff[startOfSuffix:], initSuffix) 132 } 133 134 // Issue 21104: Shouldn't write to anything outside of dst even if src is bigger 135 src = make([]byte, blockSize*3) 136 rng.Read(src) 137 138 mustPanic(t, "output smaller than input", func() { 139 bm(b, iv).CryptBlocks(dst, src) 140 }) 141 142 if !bytes.Equal(buff[startOfSuffix:], initSuffix) { 143 t.Errorf("block cipher did out of bounds write after end of dst slice; got %x, want %x", buff[startOfSuffix:], initSuffix) 144 } 145 if !bytes.Equal(buff[:endOfPrefix], initPrefix) { 146 t.Errorf("block cipher did out of bounds write before beginning of dst slice; got %x, want %x", buff[:endOfPrefix], initPrefix) 147 } 148 }) 149 150 // Check that output of cipher isn't affected by adjacent data beyond input 151 // slice scope 152 t.Run("OutOfBoundsRead", func(t *testing.T) { 153 rng := newRandReader(t) 154 155 src := make([]byte, blockSize) 156 rng.Read(src) 157 expectedDst := make([]byte, blockSize) 158 bm(b, iv).CryptBlocks(expectedDst, src) 159 160 // Make a buffer with src in the middle and data on either end 161 buff := make([]byte, blockSize*3) 162 endOfPrefix, startOfSuffix := blockSize, blockSize*2 163 164 copy(buff[endOfPrefix:startOfSuffix], src) 165 rng.Read(buff[:endOfPrefix]) 166 rng.Read(buff[startOfSuffix:]) 167 168 testDst := make([]byte, blockSize) 169 bm(b, iv).CryptBlocks(testDst, buff[endOfPrefix:startOfSuffix]) 170 171 if !bytes.Equal(testDst, expectedDst) { 172 t.Errorf("CryptBlocks affected by data outside of src slice bounds; got %x, want %x", testDst, expectedDst) 173 } 174 }) 175 176 t.Run("BufferOverlap", func(t *testing.T) { 177 rng := newRandReader(t) 178 179 buff := make([]byte, blockSize*2) 180 rng.Read(buff) 181 182 // Make src and dst slices point to same array with inexact overlap 183 src := buff[:blockSize] 184 dst := buff[1 : blockSize+1] 185 mustPanic(t, "invalid buffer overlap", func() { bm(b, iv).CryptBlocks(dst, src) }) 186 187 // Only overlap on one byte 188 src = buff[:blockSize] 189 dst = buff[blockSize-1 : 2*blockSize-1] 190 mustPanic(t, "invalid buffer overlap", func() { bm(b, iv).CryptBlocks(dst, src) }) 191 192 // src comes after dst with one byte overlap 193 src = buff[blockSize-1 : 2*blockSize-1] 194 dst = buff[:blockSize] 195 mustPanic(t, "invalid buffer overlap", func() { bm(b, iv).CryptBlocks(dst, src) }) 196 }) 197 198 // Input to CryptBlocks should be a multiple of BlockSize 199 t.Run("PartialBlocks", func(t *testing.T) { 200 // Check a few cases of not being a multiple of BlockSize 201 for _, srcSize := range []int{blockSize - 1, blockSize + 1, 2*blockSize - 1, 2*blockSize + 1} { 202 src := make([]byte, srcSize) 203 dst := make([]byte, 3*blockSize) // Make a dst large enough for all src 204 mustPanic(t, "input not full blocks", func() { bm(b, iv).CryptBlocks(dst, src) }) 205 } 206 }) 207 208 t.Run("KeepState", func(t *testing.T) { 209 rng := newRandReader(t) 210 211 src, serialDst, compositeDst := make([]byte, blockSize*4), make([]byte, blockSize*4), make([]byte, blockSize*4) 212 rng.Read(src) 213 214 length, block := 2*blockSize, bm(b, iv) 215 block.CryptBlocks(serialDst, src[:length]) 216 block.CryptBlocks(serialDst[length:], src[length:]) 217 218 bm(b, iv).CryptBlocks(compositeDst, src) 219 220 if !bytes.Equal(serialDst, compositeDst) { 221 t.Errorf("two successive CryptBlocks calls returned a different result than a single one; got %x, want %x", serialDst, compositeDst) 222 } 223 }) 224 }