YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/encryption/ctr_cipher_stream-test.cc
Line
Count
Source
1
// Copyright (c) YugaByte, Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4
// in compliance with the License.  You may obtain a copy of the License at
5
//
6
// http://www.apache.org/licenses/LICENSE-2.0
7
//
8
// Unless required by applicable law or agreed to in writing, software distributed under the License
9
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
10
// or implied.  See the License for the specific language governing permissions and limitations
11
// under the License.
12
//
13
14
#include <string>
15
#include <thread>
16
17
#include <glog/logging.h>
18
#include <gtest/gtest.h>
19
20
#include "yb/encryption/cipher_stream.h"
21
#include "yb/encryption/encryption_util.h"
22
#include "yb/encryption/header_manager.h"
23
24
#include "yb/util/random_util.h"
25
#include "yb/util/slice.h"
26
#include "yb/util/status.h"
27
#include "yb/util/test_macros.h"
28
#include "yb/util/test_util.h"
29
30
DECLARE_bool(TEST_encryption_use_openssl_compatible_counter_overflow);
31
32
namespace yb {
33
namespace encryption {
34
35
constexpr int kDataSize = 1024;
36
constexpr int kNumRuns = 1000;
37
38
class TestCipherStream : public YBTest {
39
 public:
40
2
  Status TestOverFlowWithKeyType(bool use_openssl_compatible_counter_overflow) {
41
    // Create a cipher stream on an iv about to overflow.
42
2
    FLAGS_TEST_encryption_use_openssl_compatible_counter_overflow =
43
2
        use_openssl_compatible_counter_overflow;
44
2
    auto params = EncryptionParams::NewEncryptionParams();
45
    // Initialize the nonce to be about to overflow for each position.
46
2
    uint8_t nonce[12];
47
26
    for (size_t i = 0; i < sizeof(nonce); i++) {
48
24
      nonce[i] = 0xFF;
49
24
    }
50
2
    params->counter = 0xFFFFFFF0;
51
2
    memcpy(params->nonce, nonce, 12);
52
2
    auto cipher_stream  = VERIFY_RESULT(BlockAccessCipherStream::FromEncryptionParams(
53
2
        std::move(params)));
54
55
    // Encrypt data such that part of the message is before the overflow and part is after.
56
2
    auto plaintext_bytes = RandomBytes(kDataSize);
57
2
    uint8_t encrypted_bytes[kDataSize];
58
2
    RETURN_NOT_OK(cipher_stream->Encrypt(
59
2
        0, Slice(plaintext_bytes.data(), kDataSize), encrypted_bytes));
60
61
2
    uint8_t decrypted_bytes[kDataSize];
62
1.00k
    for (int i = 0; i < kNumRuns; i++) {
63
1.00k
      memset(decrypted_bytes, 0, kDataSize);
64
1.00k
      int start_idx = RandomUniformInt(0, kDataSize);
65
1.00k
      int size = RandomUniformInt(0, kDataSize - start_idx);
66
1.00k
      RETURN_NOT_OK(cipher_stream->Decrypt(
67
1.00k
          start_idx, Slice(encrypted_bytes + start_idx, size), decrypted_bytes));
68
1.00k
      if (Slice(decrypted_bytes, size) != Slice(plaintext_bytes.data() + start_idx, size)) {
69
1
        return STATUS(Corruption, Format("Corrupted bytes starting at $0 with size $1",
70
1
                                         start_idx, size));
71
1
      }
72
1.00k
    }
73
1
    return Status::OK();
74
2
  }
75
};
76
77
1
TEST_F(TestCipherStream, ConcurrentEncryption) {
78
1
  InitOpenSSL();
79
80
1
  constexpr int kBufSize = 10000;
81
1
  constexpr int kNumRuns = 10000;
82
1
  auto plaintext_bytes = RandomBytes(kBufSize);
83
1
  uint8_t encrypted_bytes[kBufSize];
84
1
  auto encryption_params = EncryptionParams::NewEncryptionParams();
85
1
  auto cipher_stream  = ASSERT_RESULT(BlockAccessCipherStream::FromEncryptionParams(
86
1
      std::move(encryption_params)));
87
1
  ASSERT_OK(cipher_stream->Encrypt(0, Slice(plaintext_bytes.data(), kBufSize), encrypted_bytes));
88
89
399k
  std::function<void(bool)> f = [&](bool encrypt) {
90
399k
    uint8_t result[kBufSize];
91
399k
    int start = RandomUniformInt(0, kBufSize);
92
399k
    int size = RandomUniformInt(0, kBufSize - start);
93
94
199k
    uint8_t* start_data = encrypt ? plaintext_bytes.data() : encrypted_bytes;
95
199k
    uint8_t* verify_data = encrypt ? encrypted_bytes : plaintext_bytes.data();
96
97
98
399k
    ASSERT_OK(cipher_stream->Encrypt(start, Slice(start_data + start, size), result));
99
399k
    ASSERT_EQ(Slice(verify_data + start, size), Slice(result, result + size));
100
399k
  };
101
102
1
  std::vector<std::thread> threads;
103
  // Create 20 encrypt threads
104
21
  for (int i = 0; i < 20; i++) {
105
20
    threads.emplace_back([&]() {
106
200k
      for (int i = 0; i < kNumRuns; i++) {
107
        // Do this to trigger use of openssl RAND_bytes
108
199k
        EncryptionParams::NewEncryptionParams();
109
199k
        ASSERT_NO_FATALS(f(true /* encrypt */));
110
199k
      }
111
20
    });
112
20
  }
113
114
  // Create 20 decrypt threads
115
21
  for (int i = 0; i < 20; i++) {
116
20
    threads.emplace_back([&]() {
117
200k
      for (int i = 0; i < kNumRuns; i++) {
118
199k
        EncryptionParams::NewEncryptionParams();
119
199k
        ASSERT_NO_FATALS(f(false /* encrypt */));
120
199k
      }
121
20
    });
122
20
  }
123
124
40
  for (auto& thread : threads) {
125
40
    thread.join();
126
40
  }
127
1
}
128
129
1
TEST_F(TestCipherStream, Overflow) {
130
  // Create a cipher stream on a iv about to overflow.
131
1
  ASSERT_OK(TestOverFlowWithKeyType(true /* use_openssl_compatible_counter_overflow */ ));
132
1
  Status s = TestOverFlowWithKeyType(false /* use_openssl_compatible_counter_overflow */);
133
1
  ASSERT_NOK(s);
134
1
  ASSERT_EQ(s.CodeAsString(), "Corruption");
135
1
  ASSERT_STR_CONTAINS(s.message().ToBuffer(), "Corrupted bytes starting");
136
1
}
137
138
} // namespace encryption
139
} // namespace yb