You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

431 lines
18 KiB

// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
// Copyright (c) 2020 Intel Corporation
// This source code is licensed under both the GPLv2 (found in the
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
#include <rocksdb/db.h>
#include <rocksdb/env_encryption.h>
#include <rocksdb/options.h>
#include <rocksdb/slice.h>
#include <rocksdb/utilities/options_util.h>
#include <rocksdb/utilities/object_registry.h>
#include <cmath>
#include <iostream>
#include <string>
#include <tuple>
#include "../openssl_provider.h"
#include <gtest/gtest.h>
namespace ROCKSDB_NAMESPACE
{
TEST(IppcpBasicTests, LoadOpensslProvider)
{
std::string IPPCP = OpensslProvider::kName();
std::shared_ptr<EncryptionProvider> provider;
Status s = EncryptionProvider::CreateFromString(ConfigOptions(), IPPCP, &provider);
ASSERT_TRUE(s.ok());
ASSERT_NE(provider, nullptr);
ASSERT_EQ(provider->Name(), IPPCP);
std::string cipher_key;
cipher_key.assign("a6d2ae2816157e2b3c4fcf098815f7x1");
s = provider->AddCipher("", cipher_key.c_str(), cipher_key.length(), false);
ASSERT_TRUE(s.ok()) << s.ToString();
;
}
TEST(IppcpBasicTests, TestAddKeys)
{
std::string IPPCP = OpensslProvider::kName();
std::shared_ptr<EncryptionProvider> provider;
Status s = EncryptionProvider::CreateFromString(ConfigOptions(), IPPCP, &provider);
ASSERT_TRUE(s.ok()) << s.ToString();
;
ASSERT_NE(provider, nullptr);
std::string cipher_key;
cipher_key.assign("a6d2ae2816157e2b3c4fcf098815f7x2");
s = provider->AddCipher("", cipher_key.c_str(), cipher_key.length(), false);
ASSERT_TRUE(s.ok()) << s.ToString();
provider.reset();
s = EncryptionProvider::CreateFromString(ConfigOptions(), IPPCP, &provider);
ASSERT_TRUE(s.ok()) << s.ToString();
;
ASSERT_NE(provider, nullptr);
cipher_key.assign("a6d2ae2816157e2beeeeeeee");
s = provider->AddCipher("", cipher_key.c_str(), cipher_key.length(), false);
ASSERT_TRUE(s.ok()) << s.ToString();
provider.reset();
cipher_key.assign("a6d2ae2816157e21");
s = EncryptionProvider::CreateFromString(ConfigOptions(), IPPCP, &provider);
ASSERT_TRUE(s.ok()) << s.ToString();
ASSERT_NE(provider, nullptr);
s = provider->AddCipher("", cipher_key.c_str(), cipher_key.length(), false);
ASSERT_TRUE(s.ok()) << s.ToString();
}
TEST(IppcpBasicTests, TestIncorrectKeyLength)
{
std::string IPPCP = OpensslProvider::kName();
std::shared_ptr<EncryptionProvider> provider;
Status s = EncryptionProvider::CreateFromString(ConfigOptions(), IPPCP, &provider);
ASSERT_TRUE(s.ok());
ASSERT_NE(provider, nullptr);
std::string cipher_key;
// empty encryption key
cipher_key.assign("");
s = provider->AddCipher("", cipher_key.c_str(), cipher_key.length(), false);
ASSERT_TRUE(s.IsInvalidArgument()) << s.ToString();
// incoorect encryption key length
cipher_key.assign("a6d2ae2");
s = provider->AddCipher("", cipher_key.c_str(), cipher_key.length(), false);
ASSERT_TRUE(s.IsInvalidArgument()) << s.ToString();
}
TEST(IppcpBasicTests, TestAddingMultipleKeys)
{
std::string IPPCP = OpensslProvider::kName();
std::shared_ptr<EncryptionProvider> provider;
Status s = EncryptionProvider::CreateFromString(ConfigOptions(), IPPCP, &provider);
ASSERT_TRUE(s.ok());
ASSERT_NE(provider, nullptr);
std::string cipher_key;
// correct encryption key
cipher_key.assign("a6d2ae2816157e21");
s = provider->AddCipher("", cipher_key.c_str(), cipher_key.length(), false);
ASSERT_TRUE(s.ok()) << s.ToString();
// adding multiple cipher/encryption keys not allowed
cipher_key.assign("a6d2ae281wwwwddd22222213");
s = provider->AddCipher("", cipher_key.c_str(), cipher_key.length(), false);
ASSERT_TRUE(s.IsInvalidArgument()) << s.ToString();
}
TEST(IppcpEncryptionTests, CounterBlkTests)
{
std::string IPPCP = OpensslProvider::kName();
std::shared_ptr<EncryptionProvider> provider;
// creating ipp provider and setting cipher key
Status s = EncryptionProvider::CreateFromString(ConfigOptions(), IPPCP, &provider);
ASSERT_TRUE(s.ok());
ASSERT_NE(provider, nullptr);
std::string cipher_key;
cipher_key.assign("a6d2ae2816157e2b3c4fcf098815f7x2");
s = provider->AddCipher("", cipher_key.c_str(), cipher_key.length(), false);
ASSERT_TRUE(s.ok()) << s.ToString();
// initilizing prefix which sets the 128 initVector data memmber
// the first 8 bytes will be used for counter
size_t prefixLen = 16; // minimum size of prefix is 16(blockSize)
uint8_t ctr[] = {0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
Slice prefix((char *)ctr, prefixLen);
std::unique_ptr<BlockAccessCipherStream> stream;
const EnvOptions options;
// creating cipher stream object to perform encryption and decryption
s = provider->CreateCipherStream("", options, prefix, &stream);
ASSERT_TRUE(s.ok()) << s.ToString();
std::string input1, input2, input3, plainTxt;
uint64_t offset = 0; // offset where from we need to perform encryption/decryption
plainTxt = "";
input1.assign("1 input for CounterBlk hellooo0 ");
input2.assign("2 input for CounterBlk hellooo0 ");
input3.assign("3 input for CounterBlk helloo0 ");
// concatenate the strings and encrypt them
plainTxt = input1 + input2 + input3;
s = stream->Encrypt(offset, (char *)plainTxt.c_str(), plainTxt.length()); // does in place encryption so plainTxt will be encrypted now
s = stream->Decrypt(offset, (char *)plainTxt.c_str(), plainTxt.length()); // in .place decryption
ASSERT_EQ(input1 + input2 + input3, plainTxt) << " both are strings are same after decryption!!";
}
/*
This test checks wraparound condition for counter.The plugin code uses 64 bit intrinsic _mm_add_epi64 for addition as index is 64bits.
plugin counter for all ff -> (ff ff ff ff ff ff ff ff 0 0 0 0 0 0 0 0) and (ff ff ff ff ff ff ff ff 0 0 0 0 0 0 0 1) so on
if the kCounterLen passed to ipp lib is 128 then it use all 128 bits for addition which means counter created
by plugin and ipp code will differ as it will rollover to all 0.
if all FF counter is passed to ipp then new counter created ->() 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 ),( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2) etc
To fix this issue the counter addition bit length needs to be same in both plugin and ipp lib code
so kCounterLen needs to be 64 bits.
This test will fail if kCounterLen is 128
*/
TEST(IppcpEncryptionTests, CounterBlkOverFlowTests)
{
std::string IPPCP = OpensslProvider::kName();
std::shared_ptr<EncryptionProvider> provider;
// creating ipp provider and setting cipher key
Status s = EncryptionProvider::CreateFromString(ConfigOptions(), IPPCP, &provider);
ASSERT_TRUE(s.ok());
ASSERT_NE(provider, nullptr);
std::string cipher_key;
cipher_key.assign("a6d2ae2816157e2b3c4fcf098815f7x2");
s = provider->AddCipher("", cipher_key.c_str(), cipher_key.length(), false);
ASSERT_TRUE(s.ok()) << s.ToString();
// creating prefix which sets the 128 initVector data memmber
size_t prefixLen = 16; // minimum size of prefix is 16
// setting prefix/counter to all ff's to check the overflow
uint8_t ctr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
Slice prefix((char *)ctr, prefixLen);
// creating cipher stream object to perform encryption and decryption
std::unique_ptr<BlockAccessCipherStream> stream;
const EnvOptions options;
s = provider->CreateCipherStream("", options, prefix, &stream);
ASSERT_TRUE(s.ok()) << s.ToString();
// creating string each of 16 byte(blocksize) for encryption
std::string str1, str2, str3;
str1.assign("1111111111111111");
str2.assign("2222222222222222");
str3.assign("3333333333333333");
std::string encryptedString = "";
encryptedString += str1;
encryptedString += str2;
encryptedString += str3;
// encrypted all the strings in one go.Here ipp lib will create counter block for 2nd and 3rd string block
s = stream->Encrypt(0, (char *)encryptedString.c_str(), encryptedString.length());
std::string cipherTxt = encryptedString.substr(str1.length());
// decrypt the encrypted string from str2 onwards i.e from block 2 onwards
s = stream->Decrypt(str1.length(), (char *)cipherTxt.c_str(), cipherTxt.length());
// the decrypted string should match the str2 + str3
ASSERT_EQ((str2 + str3), cipherTxt) << " both are strings are same after decryption!!";
ASSERT_TRUE(s.ok()) << s.ToString();
}
/*
This test encrypts the input data and then decrypts it. Decrypted data should match the input for success.
This Matches RocksDB Encryption API flow.
*/
TEST(IppcpEncryptionTests, EncryptDecryptTest)
{
std::string IPPCP = OpensslProvider::kName();
std::shared_ptr<EncryptionProvider> provider;
Status s = EncryptionProvider::CreateFromString(ConfigOptions(), IPPCP, &provider);
ASSERT_TRUE(s.ok());
ASSERT_NE(provider, nullptr);
std::string cipher_key;
cipher_key.assign("a6d2ae2816157e2b3c4fcf098815f7x2");
s = provider->AddCipher("", cipher_key.c_str(), cipher_key.length(), false);
ASSERT_TRUE(s.ok()) << s.ToString();
size_t prefixLen = provider->GetPrefixLength();
ASSERT_GT(prefixLen, 0);
char *buf = (char *)malloc(prefixLen);
ASSERT_NE(buf, nullptr);
std::unique_ptr<BlockAccessCipherStream> stream;
const EnvOptions options;
s = provider->CreateNewPrefix("", buf, prefixLen);
ASSERT_TRUE(s.ok()) << s.ToString();
Slice prefix(buf, prefixLen);
s = provider->CreateCipherStream("", options, prefix, &stream);
ASSERT_TRUE(s.ok()) << s.ToString();
std::string input, plainTxt;
uint64_t offset = prefixLen;
input.assign("test ippcp crypto");
plainTxt = input; // input becomes cipher txt in below API.
s = stream->Encrypt(offset, (char *)input.c_str(), input.length()); // does in place encryption
ASSERT_TRUE(s.ok()) << s.ToString();
s = stream->Decrypt(offset, (char *)input.c_str(), input.length());
ASSERT_EQ(plainTxt, input) << " both are strings are same after decryption!!";
free(buf);
}
/*
This test encrypts the multple input data and then decrypts it in one go.
Decrypted data should match the combined input for success.
This is to test the random decryption functionality.
*/
TEST(IppcpEncryptionTests, RandomDecryptionTests)
{
std::string IPPCP = OpensslProvider::kName();
std::shared_ptr<EncryptionProvider> provider;
Status s = EncryptionProvider::CreateFromString(ConfigOptions(), IPPCP, &provider);
ASSERT_TRUE(s.ok());
ASSERT_NE(provider, nullptr);
std::string cipher_key;
cipher_key.assign("a6d2ae2816157e2b3c4fcf098815f7x2");
s = provider->AddCipher("", cipher_key.c_str(), cipher_key.length(), false);
ASSERT_TRUE(s.ok()) << s.ToString();
size_t prefixLen = provider->GetPrefixLength();
ASSERT_GT(prefixLen, 0);
char *buf = (char *)malloc(prefixLen);
ASSERT_NE(buf, nullptr);
s = provider->CreateNewPrefix("", buf, prefixLen);
ASSERT_TRUE(s.ok()) << s.ToString();
Slice prefix(buf, prefixLen);
std::unique_ptr<BlockAccessCipherStream> stream;
const EnvOptions options;
s = provider->CreateCipherStream("", options, prefix, &stream);
ASSERT_TRUE(s.ok()) << s.ToString();
std::string input1, plainTxt, cipherTxt;
uint64_t offset = prefixLen;
input1.assign("1 input for encryption hellooo0 ");
plainTxt = input1;
s = stream->Encrypt(offset, (char *)input1.c_str(), input1.length()); // does in place encryption
ASSERT_TRUE(s.ok()) << s.ToString();
cipherTxt = input1;
offset += input1.length();
std::string input2;
input2.assign("2 input for encryption hellooo0 ");
plainTxt += input2;
s = stream->Encrypt(offset, (char *)input2.c_str(), input2.length()); // does in place encryption
ASSERT_TRUE(s.ok()) << s.ToString();
cipherTxt += input2;
offset += input2.length();
std::string input3;
input3.assign("3 input for encryption helloo0 ");
plainTxt += input3;
s = stream->Encrypt(offset, (char *)input3.c_str(), input3.length()); // does in place encryption
ASSERT_TRUE(s.ok()) << s.ToString();
cipherTxt += input3;
// decrypt the all the input string in one go.
s = stream->Decrypt(prefixLen, (char *)cipherTxt.c_str(), cipherTxt.length());
ASSERT_EQ(plainTxt, cipherTxt) << " both are strings are same after decryption!!";
free(buf);
}
TEST(IppcpEncryptionTests, EncryptDecryptWithDifferentKeys)
{
std::string IPPCP = OpensslProvider::kName();
std::shared_ptr<EncryptionProvider> provider;
Status s = EncryptionProvider::CreateFromString(ConfigOptions(), IPPCP, &provider);
ASSERT_TRUE(s.ok());
ASSERT_NE(provider, nullptr);
std::string cipher_key;
cipher_key.assign("a6d2ae2816157e2b3c4fcf098815f7x2");
s = provider->AddCipher("", cipher_key.c_str(), cipher_key.length(), false);
ASSERT_TRUE(s.ok()) << s.ToString();
size_t prefixLen = provider->GetPrefixLength();
ASSERT_GT(prefixLen, 0);
char *buf = (char *)malloc(prefixLen);
ASSERT_NE(buf, nullptr);
std::unique_ptr<BlockAccessCipherStream> stream;
const EnvOptions options;
s = provider->CreateNewPrefix("", buf, prefixLen);
ASSERT_TRUE(s.ok()) << s.ToString();
Slice prefix(buf, prefixLen);
s = provider->CreateCipherStream("", options, prefix, &stream);
ASSERT_TRUE(s.ok()) << s.ToString();
std::string input, plainTxt, cipherTxt;
uint64_t offset = prefixLen;
input.assign("test ippcp crypto");
plainTxt = input;
s = stream->Encrypt(offset, (char *)input.c_str(), input.length()); // does in place encryption
ASSERT_TRUE(s.ok()) << s.ToString();
cipherTxt = input; // encrypted txt
provider.reset();
s = EncryptionProvider::CreateFromString(ConfigOptions(), IPPCP, &provider);
ASSERT_TRUE(s.ok()) << s.ToString();
;
ASSERT_NE(provider, nullptr);
// change the key
cipher_key.assign("a6d2ae2816157e2b");
s = provider->AddCipher("", cipher_key.c_str(), cipher_key.length(), false);
ASSERT_TRUE(s.ok()) << s.ToString();
s = stream->Decrypt(offset, (char *)cipherTxt.c_str(), input.length());
ASSERT_TRUE(s.ok()) << s.ToString();
ASSERT_NE(plainTxt, cipherTxt) << " both are strings are same after decryption!!";
free(buf);
}
struct TestParam
{
TestParam(std::string _cipher_desc, std::string _cipher_key, std::string _plainTxt = "") : cipher_desc(_cipher_desc), cipher_key(_cipher_key), plainTxt(_plainTxt) {}
std::string cipher_desc;
std::string cipher_key;
std::string plainTxt;
std::string GetOpts()
{
return "cipher_desc = " + cipher_desc + "; cipher_key = " + cipher_key + "; cipher_size = " + std::to_string(cipher_key.length()) + "; plaintxt = " + plainTxt;
}
};
class OpensslProviderTest : public testing::TestWithParam<std::tuple<std::string, std::string, std::string>>
{
public:
static void SetUpTestSuite()
{
ObjectLibrary::Default()->AddFactory<EncryptionProvider>(
OpensslProvider::kName(),
[](const std::string & /* uri */, std::unique_ptr<EncryptionProvider> *f,
std::string * /* errmsg */)
{
*f = OpensslProvider::CreateProvider();
return f->get();
});
}
void SetUp() override
{
TestParam test_param(std::get<0>(GetParam()), std::get<1>(GetParam()), std::get<2>(GetParam()));
ConfigOptions config_options;
Status s = EncryptionProvider::CreateFromString(config_options, OpensslProvider::kName(), &provider);
}
std::shared_ptr<EncryptionProvider> provider;
const EnvOptions soptions_;
};
TEST_P(OpensslProviderTest, EncryptDecrypt)
{
TestParam test_param(std::get<0>(GetParam()), std::get<1>(GetParam()), std::get<2>(GetParam()));
Status s = provider->AddCipher(test_param.cipher_desc, (char *)test_param.cipher_key.c_str(), test_param.cipher_key.length(), false);
ASSERT_TRUE(s.ok()) << s.ToString();
size_t prefixLen = 16; // minimum size of prefix is 16(blockSize)
uint8_t ctr[] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
Slice prefix((char *)ctr, prefixLen);
std::unique_ptr<BlockAccessCipherStream> stream;
s = provider->CreateCipherStream("", soptions_, prefix, &stream);
ASSERT_TRUE(s.ok()) << s.ToString();
std::string input = test_param.plainTxt;
s = stream->Encrypt(0, (char *)input.c_str(), input.length());
ASSERT_TRUE(s.ok()) << s.ToString();
s = stream->Decrypt(0, (char *)input.c_str(), input.length());
ASSERT_TRUE(s.ok()) << s.ToString();
ASSERT_TRUE(test_param.plainTxt == input) << " both are strings are same after decryption!!";
}
// working but uses cartesian product
INSTANTIATE_TEST_SUITE_P(OpensslProviderTestInstance,
OpensslProviderTest,
testing::Combine(testing::Values("ippcp_test_aes"), // key description
testing::Values("a6d2ae2816157e2b3c4fcf098815f7xb", "a6d2ae2816157e2334512345", "a6d2ae2816157e23"), // encryption key // offset for encryption and decryption
testing::Values("Hello world", "Helloooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo worldddddddddddddddddddddddddddddddd 111111111111111111111111111111111111111111111111111111111111111"))); // plain text to encrypt
} // end of namespace
int main(int argc, char *argv[])
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}