github.com/fawick/restic@v0.1.1-0.20171126184616-c02923fbfc79/internal/crypto/crypto_test.go (about) 1 package crypto_test 2 3 import ( 4 "bytes" 5 "crypto/rand" 6 "io" 7 "testing" 8 9 "github.com/restic/restic/internal/crypto" 10 rtest "github.com/restic/restic/internal/test" 11 12 "github.com/restic/chunker" 13 ) 14 15 const testLargeCrypto = false 16 17 func TestEncryptDecrypt(t *testing.T) { 18 k := crypto.NewRandomKey() 19 20 tests := []int{5, 23, 2<<18 + 23, 1 << 20} 21 if testLargeCrypto { 22 tests = append(tests, 7<<20+123) 23 } 24 25 for _, size := range tests { 26 data := rtest.Random(42, size) 27 buf := make([]byte, 0, size+crypto.Extension) 28 29 nonce := crypto.NewRandomNonce() 30 ciphertext := k.Seal(buf[:0], nonce, data, nil) 31 rtest.Assert(t, len(ciphertext) == len(data)+k.Overhead(), 32 "ciphertext length does not match: want %d, got %d", 33 len(data)+crypto.Extension, len(ciphertext)) 34 35 plaintext := make([]byte, 0, len(ciphertext)) 36 plaintext, err := k.Open(plaintext[:0], nonce, ciphertext, nil) 37 rtest.OK(t, err) 38 rtest.Assert(t, len(plaintext) == len(data), 39 "plaintext length does not match: want %d, got %d", 40 len(data), len(plaintext)) 41 42 rtest.Equals(t, plaintext, data) 43 } 44 } 45 46 func TestSmallBuffer(t *testing.T) { 47 k := crypto.NewRandomKey() 48 49 size := 600 50 data := make([]byte, size) 51 _, err := io.ReadFull(rand.Reader, data) 52 rtest.OK(t, err) 53 54 ciphertext := make([]byte, 0, size/2) 55 nonce := crypto.NewRandomNonce() 56 ciphertext = k.Seal(ciphertext[:0], nonce, data, nil) 57 // this must extend the slice 58 rtest.Assert(t, cap(ciphertext) > size/2, 59 "expected extended slice, but capacity is only %d bytes", 60 cap(ciphertext)) 61 62 // check for the correct plaintext 63 plaintext := make([]byte, len(ciphertext)) 64 plaintext, err = k.Open(plaintext[:0], nonce, ciphertext, nil) 65 rtest.OK(t, err) 66 rtest.Assert(t, bytes.Equal(plaintext, data), 67 "wrong plaintext returned") 68 } 69 70 func TestSameBuffer(t *testing.T) { 71 k := crypto.NewRandomKey() 72 73 size := 600 74 data := make([]byte, size) 75 _, err := io.ReadFull(rand.Reader, data) 76 rtest.OK(t, err) 77 78 ciphertext := make([]byte, 0, size+crypto.Extension) 79 80 nonce := crypto.NewRandomNonce() 81 ciphertext = k.Seal(ciphertext, nonce, data, nil) 82 83 // use the same buffer for decryption 84 ciphertext, err = k.Open(ciphertext[:0], nonce, ciphertext, nil) 85 rtest.OK(t, err) 86 rtest.Assert(t, bytes.Equal(ciphertext, data), 87 "wrong plaintext returned") 88 } 89 90 func encrypt(t testing.TB, k *crypto.Key, data, ciphertext, nonce []byte) []byte { 91 prefixlen := len(ciphertext) 92 ciphertext = k.Seal(ciphertext, nonce, data, nil) 93 if len(ciphertext) != len(data)+k.Overhead()+prefixlen { 94 t.Fatalf("destination slice has wrong length, want %d, got %d", 95 len(data)+k.Overhead(), len(ciphertext)) 96 } 97 98 return ciphertext 99 } 100 101 func decryptNewSliceAndCompare(t testing.TB, k *crypto.Key, data, ciphertext, nonce []byte) { 102 plaintext := make([]byte, 0, len(ciphertext)) 103 decryptAndCompare(t, k, data, ciphertext, nonce, plaintext) 104 } 105 106 func decryptAndCompare(t testing.TB, k *crypto.Key, data, ciphertext, nonce, dst []byte) { 107 prefix := make([]byte, len(dst)) 108 copy(prefix, dst) 109 110 plaintext, err := k.Open(dst, nonce, ciphertext, nil) 111 if err != nil { 112 t.Fatalf("unable to decrypt ciphertext: %v", err) 113 } 114 115 if len(data)+len(prefix) != len(plaintext) { 116 t.Fatalf("wrong plaintext returned, want %d bytes, got %d", len(data)+len(prefix), len(plaintext)) 117 } 118 119 if !bytes.Equal(plaintext[:len(prefix)], prefix) { 120 t.Fatal("prefix is wrong") 121 } 122 123 if !bytes.Equal(plaintext[len(prefix):], data) { 124 t.Fatal("wrong plaintext returned") 125 } 126 } 127 128 func TestAppendOpen(t *testing.T) { 129 k := crypto.NewRandomKey() 130 nonce := crypto.NewRandomNonce() 131 132 data := make([]byte, 600) 133 _, err := io.ReadFull(rand.Reader, data) 134 rtest.OK(t, err) 135 ciphertext := encrypt(t, k, data, nil, nonce) 136 137 // we need to test several different cases: 138 // * destination slice is nil 139 // * destination slice is empty and has enough capacity 140 // * destination slice is empty and does not have enough capacity 141 // * destination slice contains data and has enough capacity 142 // * destination slice contains data and does not have enough capacity 143 144 // destination slice is nil 145 t.Run("nil", func(t *testing.T) { 146 var plaintext []byte 147 decryptAndCompare(t, k, data, ciphertext, nonce, plaintext) 148 }) 149 150 // destination slice is empty and has enough capacity 151 t.Run("empty-large", func(t *testing.T) { 152 plaintext := make([]byte, 0, len(data)+100) 153 decryptAndCompare(t, k, data, ciphertext, nonce, plaintext) 154 }) 155 156 // destination slice is empty and does not have enough capacity 157 t.Run("empty-small", func(t *testing.T) { 158 plaintext := make([]byte, 0, len(data)/2) 159 decryptAndCompare(t, k, data, ciphertext, nonce, plaintext) 160 }) 161 162 // destination slice contains data and has enough capacity 163 t.Run("prefix-large", func(t *testing.T) { 164 plaintext := make([]byte, 0, len(data)+100) 165 plaintext = append(plaintext, []byte("foobar")...) 166 decryptAndCompare(t, k, data, ciphertext, nonce, plaintext) 167 }) 168 169 // destination slice contains data and does not have enough capacity 170 t.Run("prefix-small", func(t *testing.T) { 171 plaintext := make([]byte, 0, len(data)/2) 172 plaintext = append(plaintext, []byte("foobar")...) 173 decryptAndCompare(t, k, data, ciphertext, nonce, plaintext) 174 }) 175 } 176 177 func TestAppendSeal(t *testing.T) { 178 k := crypto.NewRandomKey() 179 180 data := make([]byte, 600) 181 _, err := io.ReadFull(rand.Reader, data) 182 rtest.OK(t, err) 183 184 // we need to test several different cases: 185 // * destination slice is nil 186 // * destination slice is empty and has enough capacity 187 // * destination slice is empty and does not have enough capacity 188 // * destination slice contains data and has enough capacity 189 // * destination slice contains data and does not have enough capacity 190 191 // destination slice is nil 192 t.Run("nil", func(t *testing.T) { 193 nonce := crypto.NewRandomNonce() 194 var ciphertext []byte 195 196 ciphertext = encrypt(t, k, data, ciphertext, nonce) 197 decryptNewSliceAndCompare(t, k, data, ciphertext, nonce) 198 }) 199 200 // destination slice is empty and has enough capacity 201 t.Run("empty-large", func(t *testing.T) { 202 nonce := crypto.NewRandomNonce() 203 ciphertext := make([]byte, 0, len(data)+100) 204 205 ciphertext = encrypt(t, k, data, ciphertext, nonce) 206 decryptNewSliceAndCompare(t, k, data, ciphertext, nonce) 207 }) 208 209 // destination slice is empty and does not have enough capacity 210 t.Run("empty-small", func(t *testing.T) { 211 nonce := crypto.NewRandomNonce() 212 ciphertext := make([]byte, 0, len(data)/2) 213 214 ciphertext = encrypt(t, k, data, ciphertext, nonce) 215 decryptNewSliceAndCompare(t, k, data, ciphertext, nonce) 216 }) 217 218 // destination slice contains data and has enough capacity 219 t.Run("prefix-large", func(t *testing.T) { 220 nonce := crypto.NewRandomNonce() 221 ciphertext := make([]byte, 0, len(data)+100) 222 ciphertext = append(ciphertext, []byte("foobar")...) 223 224 ciphertext = encrypt(t, k, data, ciphertext, nonce) 225 if string(ciphertext[:6]) != "foobar" { 226 t.Errorf("prefix is missing") 227 } 228 decryptNewSliceAndCompare(t, k, data, ciphertext[6:], nonce) 229 }) 230 231 // destination slice contains data and does not have enough capacity 232 t.Run("prefix-small", func(t *testing.T) { 233 nonce := crypto.NewRandomNonce() 234 ciphertext := make([]byte, 0, len(data)/2) 235 ciphertext = append(ciphertext, []byte("foobar")...) 236 237 ciphertext = encrypt(t, k, data, ciphertext, nonce) 238 if string(ciphertext[:6]) != "foobar" { 239 t.Errorf("prefix is missing") 240 } 241 decryptNewSliceAndCompare(t, k, data, ciphertext[6:], nonce) 242 }) 243 } 244 245 func TestLargeEncrypt(t *testing.T) { 246 if !testLargeCrypto { 247 t.SkipNow() 248 } 249 250 k := crypto.NewRandomKey() 251 252 for _, size := range []int{chunker.MaxSize, chunker.MaxSize + 1, chunker.MaxSize + 1<<20} { 253 data := make([]byte, size) 254 _, err := io.ReadFull(rand.Reader, data) 255 rtest.OK(t, err) 256 257 nonce := crypto.NewRandomNonce() 258 ciphertext := k.Seal(make([]byte, size+k.Overhead()), nonce, data, nil) 259 plaintext, err := k.Open([]byte{}, nonce, ciphertext, nil) 260 rtest.OK(t, err) 261 262 rtest.Equals(t, plaintext, data) 263 } 264 } 265 266 func BenchmarkEncrypt(b *testing.B) { 267 size := 8 << 20 // 8MiB 268 data := make([]byte, size) 269 270 k := crypto.NewRandomKey() 271 buf := make([]byte, len(data)+crypto.Extension) 272 nonce := crypto.NewRandomNonce() 273 274 b.ResetTimer() 275 b.SetBytes(int64(size)) 276 277 for i := 0; i < b.N; i++ { 278 _ = k.Seal(buf, nonce, data, nil) 279 } 280 } 281 282 func BenchmarkDecrypt(b *testing.B) { 283 size := 8 << 20 // 8MiB 284 data := make([]byte, size) 285 286 k := crypto.NewRandomKey() 287 288 plaintext := make([]byte, 0, size) 289 ciphertext := make([]byte, 0, size+crypto.Extension) 290 nonce := crypto.NewRandomNonce() 291 ciphertext = k.Seal(ciphertext, nonce, data, nil) 292 293 var err error 294 295 b.ResetTimer() 296 b.SetBytes(int64(size)) 297 298 for i := 0; i < b.N; i++ { 299 _, err = k.Open(plaintext, nonce, ciphertext, nil) 300 rtest.OK(b, err) 301 } 302 }