github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ccl/storageccl/engineccl/ctr_stream_test.go (about)

     1  // Copyright 2019 The Cockroach Authors.
     2  //
     3  // Licensed as a CockroachDB Enterprise file under the Cockroach Community
     4  // License (the "License"); you may not use this file except in compliance with
     5  // the License. You may obtain a copy of the License at
     6  //
     7  //     https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt
     8  
     9  package engineccl
    10  
    11  import (
    12  	"context"
    13  	"crypto/rand"
    14  	"fmt"
    15  	"strings"
    16  	"testing"
    17  
    18  	"github.com/cockroachdb/cockroach/pkg/ccl/storageccl/engineccl/enginepbccl"
    19  	"github.com/cockroachdb/cockroach/pkg/storage/enginepb"
    20  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    21  	"github.com/kr/pretty"
    22  	"github.com/stretchr/testify/require"
    23  )
    24  
    25  var testData = []byte("Call me Ishmael. Some years ago—never mind how long precisely—" +
    26  	"having little or no money in my purse, and nothing particular to interest me " +
    27  	"on shore, I thought I would sail about a little and see the watery part of the world.")
    28  
    29  func generateKey(encType enginepbccl.EncryptionType) (*enginepbccl.SecretKey, error) {
    30  	key := &enginepbccl.SecretKey{}
    31  	key.Info = &enginepbccl.KeyInfo{}
    32  	key.Info.EncryptionType = encType
    33  	var keyLength int
    34  	switch encType {
    35  	case enginepbccl.EncryptionType_AES128_CTR:
    36  		keyLength = 16
    37  	case enginepbccl.EncryptionType_AES192_CTR:
    38  		keyLength = 24
    39  	case enginepbccl.EncryptionType_AES256_CTR:
    40  		keyLength = 32
    41  	}
    42  	key.Key = make([]byte, keyLength)
    43  	_, err := rand.Read(key.Key)
    44  	return key, err
    45  }
    46  
    47  func TestFileCipherStream(t *testing.T) {
    48  	defer leaktest.AfterTest(t)()
    49  
    50  	encTypes := []enginepbccl.EncryptionType{enginepbccl.EncryptionType_AES128_CTR,
    51  		enginepbccl.EncryptionType_AES192_CTR, enginepbccl.EncryptionType_AES256_CTR}
    52  	for _, encType := range encTypes {
    53  		key, err := generateKey(encType)
    54  		require.NoError(t, err)
    55  		var counter uint32 = 5
    56  		nonce := make([]byte, ctrNonceSize)
    57  		_, err = rand.Read(nonce)
    58  		require.NoError(t, err)
    59  		bcs, err := newCTRBlockCipherStream(key, nonce, counter)
    60  		require.NoError(t, err)
    61  		fcs := fileCipherStream{bcs: bcs}
    62  
    63  		var data []byte
    64  		data = append(data, testData...)
    65  
    66  		// Using some arbitrary file offsets, and for each of these offsets cycle through the
    67  		// full block size so that we have tested all partial blocks at the beginning and end
    68  		// of a sequence.
    69  		for _, fOffset := range []int64{5, 23, 435, 2000} {
    70  			for i := 0; i < ctrBlockSize; i++ {
    71  				offset := fOffset + int64(i)
    72  				fcs.Encrypt(offset, data)
    73  				if diff := pretty.Diff(data, testData); diff == nil {
    74  					t.Fatal("encryption was a noop")
    75  				}
    76  				fcs.Decrypt(offset, data)
    77  				if diff := pretty.Diff(data, testData); diff != nil {
    78  					t.Fatalf("%s\n%s", strings.Join(diff, "\n"), data)
    79  				}
    80  			}
    81  		}
    82  	}
    83  }
    84  
    85  type testKeyManager struct {
    86  	keys     map[string]*enginepbccl.SecretKey
    87  	activeID string
    88  }
    89  
    90  func (m *testKeyManager) ActiveKey(ctx context.Context) (*enginepbccl.SecretKey, error) {
    91  	key, _ := m.GetKey(m.activeID)
    92  	return key, nil
    93  }
    94  func (m *testKeyManager) GetKey(id string) (*enginepbccl.SecretKey, error) {
    95  	key, found := m.keys[id]
    96  	if !found {
    97  		return nil, fmt.Errorf("")
    98  	}
    99  	return key, nil
   100  }
   101  
   102  func TestFileCipherStreamCreator(t *testing.T) {
   103  	defer leaktest.AfterTest(t)()
   104  
   105  	// Key manager with a "foo" active key.
   106  	km := testKeyManager{}
   107  	km.activeID = "foo"
   108  	key, err := generateKey(enginepbccl.EncryptionType_AES192_CTR)
   109  	key.Info.KeyId = "foo"
   110  	require.NoError(t, err)
   111  	km.keys = make(map[string]*enginepbccl.SecretKey)
   112  	km.keys["foo"] = key
   113  	fcs := &FileCipherStreamCreator{envType: enginepb.EnvType_Data, keyManager: &km}
   114  
   115  	// Existing stream that uses "foo" key.
   116  	nonce := make([]byte, 12)
   117  	encSettings := &enginepbccl.EncryptionSettings{
   118  		EncryptionType: enginepbccl.EncryptionType_AES192_CTR, KeyId: "foo", Nonce: nonce}
   119  	fs1, err := fcs.CreateExisting(encSettings)
   120  	require.NoError(t, err)
   121  	data := append([]byte{}, testData...)
   122  	fs1.Encrypt(5, data)
   123  	encData := append([]byte{}, data...) // remember the encrypted data.
   124  
   125  	// Create another stream that uses "foo" key with the same nonce and counter (i.e., same file)
   126  	// and decrypt and compare.
   127  	fs2, err := fcs.CreateExisting(encSettings)
   128  	require.NoError(t, err)
   129  	fs2.Decrypt(5, data)
   130  	if diff := pretty.Diff(data, testData); diff != nil {
   131  		t.Fatalf("%s\n%s", strings.Join(diff, "\n"), data)
   132  	}
   133  
   134  	// Encryption/decryption is noop.
   135  	encSettings.EncryptionType = enginepbccl.EncryptionType_Plaintext
   136  	fs3, err := fcs.CreateExisting(encSettings)
   137  	require.NoError(t, err)
   138  	fs3.Encrypt(5, data)
   139  	if diff := pretty.Diff(data, testData); diff != nil {
   140  		t.Fatalf("%s\n%s", strings.Join(diff, "\n"), data)
   141  	}
   142  	fs3.Decrypt(5, data)
   143  	if diff := pretty.Diff(data, testData); diff != nil {
   144  		t.Fatalf("%s\n%s", strings.Join(diff, "\n"), data)
   145  	}
   146  
   147  	// Create a new stream that uses the "foo" key. A different IV and nonce should be chosen so the
   148  	// encrypted state will not be the same as the previous stream.
   149  	encSettings, fs4, err := fcs.CreateNew(context.Background())
   150  	require.Equal(t, "foo", encSettings.KeyId)
   151  	require.Equal(t, enginepbccl.EncryptionType_AES192_CTR, encSettings.EncryptionType)
   152  	require.NoError(t, err)
   153  	fs4.Encrypt(5, data)
   154  	if diff := pretty.Diff(data, testData); diff == nil {
   155  		t.Fatalf("encryption was a noop")
   156  	}
   157  	if diff := pretty.Diff(data, encData); diff == nil {
   158  		t.Fatalf("unexpected equality")
   159  	}
   160  	fs4.Decrypt(5, data)
   161  	if diff := pretty.Diff(data, testData); diff != nil {
   162  		t.Fatalf("%s\n%s", strings.Join(diff, "\n"), data)
   163  	}
   164  
   165  	// Make the active key = nil, so encryption/decryption is a noop.
   166  	km.activeID = "bar"
   167  	encSettings, fs5, err := fcs.CreateNew(context.Background())
   168  	require.Equal(t, "", encSettings.KeyId)
   169  	require.Equal(t, enginepbccl.EncryptionType_Plaintext, encSettings.EncryptionType)
   170  	require.NoError(t, err)
   171  	fs5.Encrypt(5, data)
   172  	if diff := pretty.Diff(data, testData); diff != nil {
   173  		t.Fatalf("%s\n%s", strings.Join(diff, "\n"), data)
   174  	}
   175  }