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  }