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 }