diff --git a/env/env_encryption.cc b/env/env_encryption.cc index 7b2a531c4..80b9793be 100644 --- a/env/env_encryption.cc +++ b/env/env_encryption.cc @@ -1123,70 +1123,65 @@ Status CTREncryptionProvider::CreateCipherStreamFromPrefix( return Status::OK(); } -namespace { -static void RegisterEncryptionBuiltins() { - static std::once_flag once; - std::call_once(once, [&]() { - auto lib = ObjectRegistry::Default()->AddLibrary("encryption"); - // Match "CTR" or "CTR://test" - lib->AddFactory( - ObjectLibrary::PatternEntry(CTREncryptionProvider::kClassName(), true) - .AddSuffix("://test"), - [](const std::string& uri, std::unique_ptr* guard, - std::string* /*errmsg*/) { - if (EndsWith(uri, "://test")) { - std::shared_ptr cipher = - std::make_shared(32); - guard->reset(new CTREncryptionProvider(cipher)); - } else { - guard->reset(new CTREncryptionProvider()); - } - return guard->get(); - }); - - lib->AddFactory( - "1://test", [](const std::string& /*uri*/, - std::unique_ptr* guard, - std::string* /*errmsg*/) { - std::shared_ptr cipher = - std::make_shared(32); - guard->reset(new CTREncryptionProvider(cipher)); - return guard->get(); - }); - - // Match "ROT13" or "ROT13:[0-9]+" - lib->AddFactory( - ObjectLibrary::PatternEntry(ROT13BlockCipher::kClassName(), true) - .AddNumber(":"), - [](const std::string& uri, std::unique_ptr* guard, - std::string* /* errmsg */) { - size_t colon = uri.find(':'); - if (colon != std::string::npos) { - size_t block_size = ParseSizeT(uri.substr(colon + 1)); - guard->reset(new ROT13BlockCipher(block_size)); - } else { - guard->reset(new ROT13BlockCipher(32)); - } - - return guard->get(); - }); - }); -} -} // namespace +// namespace { +// static void RegisterEncryptionBuiltins() { +// static std::once_flag once; +// std::call_once(once, [&]() { +// auto lib = ObjectRegistry::Default()->AddLibrary("encryption"); +// // Match "CTR" or "CTR://test" +// lib->AddFactory( +// ObjectLibrary::PatternEntry(CTREncryptionProvider::kClassName(), true) +// .AddSuffix("://test"), +// [](const std::string& uri, std::unique_ptr* guard, +// std::string* /*errmsg*/) { +// if (EndsWith(uri, "://test")) { +// std::shared_ptr cipher = +// std::make_shared(32); +// guard->reset(new CTREncryptionProvider(cipher)); +// } else { +// guard->reset(new CTREncryptionProvider()); +// } +// return guard->get(); +// }); + +// lib->AddFactory( +// "1://test", [](const std::string& /*uri*/, +// std::unique_ptr* guard, +// std::string* /*errmsg*/) { +// std::shared_ptr cipher = +// std::make_shared(32); +// guard->reset(new CTREncryptionProvider(cipher)); +// return guard->get(); +// }); + +// // Match "ROT13" or "ROT13:[0-9]+" +// lib->AddFactory( +// ObjectLibrary::PatternEntry(ROT13BlockCipher::kClassName(), true) +// .AddNumber(":"), +// [](const std::string& uri, std::unique_ptr* guard, +// std::string* /* errmsg */) { +// size_t colon = uri.find(':'); +// if (colon != std::string::npos) { +// size_t block_size = ParseSizeT(uri.substr(colon + 1)); +// guard->reset(new ROT13BlockCipher(block_size)); +// } else { +// guard->reset(new ROT13BlockCipher(32)); +// } + +// return guard->get(); +// }); +// }); +// } +// } // namespace Status BlockCipher::CreateFromString(const ConfigOptions& config_options, const std::string& value, std::shared_ptr* result) { - RegisterEncryptionBuiltins(); + //RegisterEncryptionBuiltins(); return LoadSharedObject(config_options, value, result); } -Status EncryptionProvider::CreateFromString( - const ConfigOptions& config_options, const std::string& value, - std::shared_ptr* result) { - RegisterEncryptionBuiltins(); - return LoadSharedObject(config_options, value, result); -} + } // namespace ROCKSDB_NAMESPACE diff --git a/plugin/ippcp/CODE_OF_CONDUCT.md b/plugin/ippcp/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..58dba18db --- /dev/null +++ b/plugin/ippcp/CODE_OF_CONDUCT.md @@ -0,0 +1,131 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +CommunityCodeOfConduct AT intel DOT com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq diff --git a/plugin/ippcp/CONTRIBUTING.md b/plugin/ippcp/CONTRIBUTING.md new file mode 100644 index 000000000..4fbf8d187 --- /dev/null +++ b/plugin/ippcp/CONTRIBUTING.md @@ -0,0 +1,57 @@ +# Contributing + +### License + +Intel® Integrated Performance Primitives Cryptography Plugin for RocksDB* Storage Engine is licensed under the terms in [Apache License](LICENSE). By contributing to the project, you agree to the license and copyright terms therein and release your contribution under these terms. + +### Sign your work + +Please use the sign-off line at the end of the patch. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch. The rules are pretty simple: if you can certify +the below (from [developercertificate.org](http://developercertificate.org/)): + +``` +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +Then you just add a line to every git commit message: + + Signed-off-by: Joe Smith + +Use your real name (sorry, no pseudonyms or anonymous contributions.) + +If you set your `user.name` and `user.email` git configs, you can sign your +commit automatically with `git commit -s`. diff --git a/plugin/ippcp/LICENSE b/plugin/ippcp/LICENSE new file mode 100644 index 000000000..9c8f3ea08 --- /dev/null +++ b/plugin/ippcp/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/plugin/ippcp/README.md b/plugin/ippcp/README.md new file mode 100644 index 000000000..6c6a35dc7 --- /dev/null +++ b/plugin/ippcp/README.md @@ -0,0 +1,62 @@ +# Intel® Integrated Performance Primitives Cryptography Plugin for RocksDB* Storage Engine + +`ippcp` is an encryption provider for RocksDB that is based on Intel's Integrated Performance Primitives for Cryptography (IPPCP). IPPCP is a lightweight cryptography library that is highly optimized for various Intel CPUs. It's used here to provide AES-128/192/256 encryption, with a CTR mode of operation, for RocksDB. + +## Prerequisite + +There is a dependency on ipp cryptograhy library (ippcp) which needs to be installed. Please refer below link for installtion. +https://www.intel.com/content/www/us/en/develop/documentation/get-started-with-ipp-crypto-for-oneapi-linux/top.html + +Once Installed source /opt/intel/oneapi/ippcp/latest/env/var.sh + + +## Build + +The code first needs to be linked under RocksDB's "plugin/" directory. In your RocksDB directory, run: + +``` +$ pushd ./plugin/ +$ git clone https://github.com/intel/ippcp-plugin-rocksdb.git ippcp +``` + +Next, we can build and install RocksDB with this plugin as follows: + +``` +$ popd +$ make clean && ROCKSDB_PLUGINS=ippcp make -j48 release +``` + +## Testing + +* Install ipp cryptograhy library (ippcp) as described in the previous section. +* Install https://github.com/google/googletest +* Build RocksDB as a shared library + +``` +LIB_MODE=shared make -j release + +``` + +* Go to the tests directory of ippcp plugin and build as mentioned below: + +``` +cd plugin/ippcp/tests/ +mkdir build +cd build +cmake -DROCKSDB_PATH= -DIPPCRYPTOROOT= .. +make run + +``` +## Tool usage + +For RocksDB binaries (such as the `db_bench` we built above), the plugin can be enabled through configuration. `db_bench` in particular takes a `--fs_uri` where we can specify "dedupfs" , which is the name registered by this plugin. Example usage: + +``` +$ ./db_bench --benchmarks=fillseq --env_uri=ippcp_db_bench_env --compression_type=none +``` + +## Application usage + +The plugin's interface is also exposed to applications, which can enable it either through configuration or through code. Example available under the "examples/" directory. + +``` \ No newline at end of file diff --git a/plugin/ippcp/examples/Makefile b/plugin/ippcp/examples/Makefile new file mode 100644 index 000000000..ce9a1dbec --- /dev/null +++ b/plugin/ippcp/examples/Makefile @@ -0,0 +1,12 @@ +include ../../../make_config.mk + +PLATFORM_LDFLAGS += -lrocksdb -lippcp -L../../.. -L../../../../ipp-crypto/_build/.build/RELEASE/lib +.PHONY: clean + +all: ippcp_example + +ippcp_example: ippcp_example.cc + $(CXX) $(CXXFLAGS) $@.cc -o$@ -I../../../include -O2 $(PLATFORM_LDFLAGS) $(PLATFORM_CXXFLAGS) -I../../../../ipp-crypto/_build/.build/RELEASE/include + +clean: + rm -rf ./ippcp_example diff --git a/plugin/ippcp/examples/ippcp_example b/plugin/ippcp/examples/ippcp_example new file mode 100755 index 000000000..d830b46ac Binary files /dev/null and b/plugin/ippcp/examples/ippcp_example differ diff --git a/plugin/ippcp/examples/ippcp_example.cc b/plugin/ippcp/examples/ippcp_example.cc new file mode 100644 index 000000000..f7b66d9a1 --- /dev/null +++ b/plugin/ippcp/examples/ippcp_example.cc @@ -0,0 +1,68 @@ +// 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 +#include +#include +#include +#include +#include + +#include + +#include "../ippcp_provider.h" + +using namespace ROCKSDB_NAMESPACE; + +std::string kDBPath = "/tmp/ipp_aes_example"; + +int main() { + DB* db; + Options options; + options.create_if_missing = true; + + std::shared_ptr provider; + Status status = EncryptionProvider::CreateFromString( + ConfigOptions(), IppcpProvider::kName(), &provider); + assert(status.ok()); + + status = + provider->AddCipher("", "a6d2ae2816157e2b3c4fcf098815f7xb", 32, false); + assert(status.ok()); + + options.env = NewEncryptedEnv(Env::Default(), provider); + + status = DB::Open(options, kDBPath, &db); + assert(status.ok()); + + setbuf(stdout, NULL); + printf("writing 1M records..."); + WriteOptions w_opts; + for (int i = 0; i < 1000000; ++i) { + status = db->Put(w_opts, std::to_string(i), std::to_string(i * i)); + assert(status.ok()); + } + db->Flush(FlushOptions()); + printf("done.\n"); + + printf("reading 1M records..."); + std::string value; + ReadOptions r_opts; + for (int i = 0; i < 1000000; ++i) { + status = db->Get(r_opts, std::to_string(i), &value); + assert(status.ok()); + assert(value == std::to_string(i * i)); + } + printf("done.\n"); + + // Close database + status = db->Close(); + assert(status.ok()); + //status = DestroyDB(kDBPath, options); + //assert(status.ok()); + + return 0; +} diff --git a/plugin/ippcp/examples2/Makefile b/plugin/ippcp/examples2/Makefile new file mode 100644 index 000000000..ce9a1dbec --- /dev/null +++ b/plugin/ippcp/examples2/Makefile @@ -0,0 +1,12 @@ +include ../../../make_config.mk + +PLATFORM_LDFLAGS += -lrocksdb -lippcp -L../../.. -L../../../../ipp-crypto/_build/.build/RELEASE/lib +.PHONY: clean + +all: ippcp_example + +ippcp_example: ippcp_example.cc + $(CXX) $(CXXFLAGS) $@.cc -o$@ -I../../../include -O2 $(PLATFORM_LDFLAGS) $(PLATFORM_CXXFLAGS) -I../../../../ipp-crypto/_build/.build/RELEASE/include + +clean: + rm -rf ./ippcp_example diff --git a/plugin/ippcp/examples2/ippcp_example b/plugin/ippcp/examples2/ippcp_example new file mode 100644 index 000000000..6f49211c5 Binary files /dev/null and b/plugin/ippcp/examples2/ippcp_example differ diff --git a/plugin/ippcp/examples2/ippcp_example.cc b/plugin/ippcp/examples2/ippcp_example.cc new file mode 100644 index 000000000..75b2e10ce --- /dev/null +++ b/plugin/ippcp/examples2/ippcp_example.cc @@ -0,0 +1,66 @@ +// 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 +#include +#include +#include +#include +#include + +#include + +#include "../ippcp_provider.h" + +using namespace ROCKSDB_NAMESPACE; + +std::string kDBPath = "/tmp/ipp_aes_example2"; + +int main() { + DB* db; + Options options; + options.create_if_missing = true; + + // std::shared_ptr provider; + // Status status = EncryptionProvider::CreateFromString( + // ConfigOptions(), IppcpProvider::kName(), &provider); + // assert(status.ok()); + + // status = + // provider->AddCipher("", "a6d2ae2816157e2b3c4fcf098815f7xb", 32, false); + // assert(status.ok()); + + Status status = DB::Open(options, kDBPath, &db); + assert(status.ok()); + + setbuf(stdout, NULL); + printf("writing 1M records..."); + WriteOptions w_opts; + for (int i = 0; i < 1000000; ++i) { + status = db->Put(w_opts, std::to_string(i), std::to_string(i * i)); + assert(status.ok()); + } + db->Flush(FlushOptions()); + printf("done.\n"); + + printf("reading 1M records..."); + std::string value; + ReadOptions r_opts; + for (int i = 0; i < 1000000; ++i) { + status = db->Get(r_opts, std::to_string(i), &value); + assert(status.ok()); + assert(value == std::to_string(i * i)); + } + printf("done.\n"); + + // Close database + status = db->Close(); + assert(status.ok()); + //status = DestroyDB(kDBPath, options); + //assert(status.ok()); + + return 0; +} diff --git a/plugin/ippcp/ippcp.mk b/plugin/ippcp/ippcp.mk new file mode 100644 index 000000000..ff38c32dc --- /dev/null +++ b/plugin/ippcp/ippcp.mk @@ -0,0 +1,4 @@ +ippcp_SOURCES = ippcp_provider.cc +ippcp_HEADERS = ippcp_provider.h +ippcp_LDFLAGS = -lippcp +ippcp_CXXFLAGS = -I../ipp-crypto/_build/.build/RELEASE/include \ No newline at end of file diff --git a/plugin/ippcp/ippcp_db_bench_env.cc b/plugin/ippcp/ippcp_db_bench_env.cc new file mode 100644 index 000000000..5a7e5af18 --- /dev/null +++ b/plugin/ippcp/ippcp_db_bench_env.cc @@ -0,0 +1,32 @@ +// Copyright (c) 2021-present, Facebook, Inc. All rights reserved. +// 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 + +#include "ippcp_provider.h" + +namespace ROCKSDB_NAMESPACE { + +#ifndef ROCKSDB_LITE + +extern "C" FactoryFunc ippcp_db_bench_env; + +// Registers a sample ippcp encrypted environment that can be used in db_bench +// by passing --env_uri=ippcp_db_bench_env parameter. + +FactoryFunc ippcp_db_bench_env = ObjectLibrary::Default()->AddFactory( + "ippcp_db_bench_env", + [](const std::string& /* uri */, std::unique_ptr* f, + std::string* /* errmsg */) { + auto provider = + std::shared_ptr(IppcpProvider::CreateProvider()); + provider->AddCipher("", "a6d2ae2816157e2b3c4fcf098815f7xb", 32, false); + *f = std::unique_ptr(NewEncryptedEnv(Env::Default(), provider)); + return f->get(); + }); + +#endif // ROCKSDB_LITE + +} // namespace ROCKSDB_NAMESPACE diff --git a/plugin/ippcp/ippcp_provider.cc b/plugin/ippcp/ippcp_provider.cc new file mode 100644 index 000000000..e819a915f --- /dev/null +++ b/plugin/ippcp/ippcp_provider.cc @@ -0,0 +1,250 @@ +// 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). + +#ifndef ROCKSDB_LITE + +#include "ippcp_provider.h" + +#include +#include +#include +#include "rocksdb/utilities/customizable_util.h" +#include + +#endif + +namespace ROCKSDB_NAMESPACE { + +#ifndef ROCKSDB_LITE + + +static void RegisterEncryptionAES() { + static std::once_flag once; + std::call_once(once, [&]() { + + ObjectLibrary::Default()->AddFactory( + IppcpProvider::kName(), + [](const std::string& /* uri */, std::unique_ptr* f, + std::string* /* errmsg */) { + *f = IppcpProvider::CreateProvider(); + return f->get(); + }); + + }); +} + +Status EncryptionProvider::CreateFromString( + const ConfigOptions& config_options, const std::string& value, + std::shared_ptr* result) { + RegisterEncryptionAES(); + return LoadSharedObject(config_options, value, result); +} + +// extern "C" FactoryFunc ippcp_reg; + +// FactoryFunc ippcp_reg = +// ObjectLibrary::Default()->AddFactory( +// IppcpProvider::kName(), +// [](const std::string& /* uri */, std::unique_ptr* f, +// std::string* /* errmsg */) { +// *f = IppcpProvider::CreateProvider(); +// return f->get(); +// }); + +// IppcpCipherStream implements BlockAccessCipherStream using AES block +// cipher and a CTR mode of operation. +// +// Since ipp-crypto can handle block sizes larger than kBlockSize (16 bytes for +// AES) by chopping them internally into KBlockSize bytes, there is no need to +// support the EncryptBlock and DecryptBlock member functions (and they will +// never be called). +// +// See https://github.com/intel/ipp-crypto#documentation +class IppcpCipherStream : public BlockAccessCipherStream { + public: + static constexpr size_t kBlockSize = 16; // in bytes + static constexpr size_t kCounterLen = 64; // in bits + + IppcpCipherStream(IppsAESSpec* aes_ctx, const char* init_vector); + + virtual Status Encrypt(uint64_t fileOffset, char* data, + size_t dataSize) override; + virtual Status Decrypt(uint64_t fileOffset, char* data, + size_t dataSize) override; + virtual size_t BlockSize() override { return kBlockSize; } + + protected: + // These functions are not needed and will never be called! + virtual void AllocateScratch(std::string&) override {} + virtual Status EncryptBlock(uint64_t, char*, char*) override { + return Status::NotSupported("Operation not supported."); + } + virtual Status DecryptBlock(uint64_t, char*, char*) override { + return Status::NotSupported("Operation not supported."); + } + + private: + IppsAESSpec* aes_ctx_; + __m128i init_vector_; +}; + +IppcpCipherStream::IppcpCipherStream(IppsAESSpec* aes_ctx, + const char* init_vector) + : aes_ctx_(aes_ctx) { + init_vector_ = _mm_loadu_si128((__m128i*)init_vector); +} + +Status IppcpCipherStream::Encrypt(uint64_t fileOffset, char* data, + size_t dataSize) { + if (dataSize == 0) return Status::OK(); + + size_t index = fileOffset / kBlockSize; + size_t offset = fileOffset % kBlockSize; + + Ipp8u ctr_block[kBlockSize]; + + // evaluate the counter block from the block index + __m128i counter = _mm_add_epi64(init_vector_, _mm_cvtsi64_si128(index)); + Ipp8u* ptr_counter = (Ipp8u*)&counter; + for (size_t i = 0; i < kBlockSize; ++i) + ctr_block[i] = ptr_counter[kBlockSize - 1 - i]; + + IppStatus ipp_status = ippStsNoErr; + + //- If offset is != 0, that means we would have first encrypt a partial block at the + //beginning of the offset. That requires us to take the block index at that position and + //manually do the xor operation – first we encrypt a block (called zero_block), and then + //xor it starting at the offset. + //Once that block is encrypted, we may exit (if the dataSize is less than kBlockSize) or + //let ippcrypto start encrypting beginning a kBlockSize aligned offset + //kCounterLen is 64 bits same as index size so that 64 bits are incremented + // and counter stream generated by ipp and above match + // https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf + + if (offset == 0) { + ipp_status = ippsAESEncryptCTR((Ipp8u*)(data), (Ipp8u*)data, static_cast(dataSize), + aes_ctx_, ctr_block, kCounterLen); + } else { + Ipp8u zero_block[kBlockSize]{0}; + ipp_status = ippsAESEncryptCTR(zero_block, zero_block, kBlockSize, aes_ctx_, + ctr_block, kCounterLen); + if (ipp_status != ippStsNoErr) + return Status::Aborted(ippcpGetStatusString(ipp_status)); + + size_t n = std::min(kBlockSize - offset, dataSize); + for (size_t i = 0; i < n; ++i) data[i] ^= zero_block[offset + i]; + memset(zero_block, 0, kBlockSize); + + n = kBlockSize - offset; + if (dataSize > n) { + Ipp8u* ptr = (Ipp8u*)(data + n); + ipp_status = ippsAESEncryptCTR(ptr, ptr, static_cast(dataSize - n), aes_ctx_, + ctr_block, kCounterLen); + } + } + if (ipp_status == ippStsNoErr) return Status::OK(); + + return Status::Aborted(ippcpGetStatusString(ipp_status)); +} + +Status IppcpCipherStream::Decrypt(uint64_t fileOffset, char* data, + size_t dataSize) { + // Decryption is implemented as encryption in CTR mode of operation + return Encrypt(fileOffset, data, dataSize); +} + +std::unique_ptr IppcpProvider::CreateProvider() { + return std::unique_ptr(new IppcpProvider); +} + +Status IppcpProvider::AddCipher(const std::string& /*descriptor*/, + const char* cipher, size_t len, + bool /*for_write*/) { + // We currently don't support more than one encryption key + if (aes_ctx_ != nullptr) { + return Status::InvalidArgument("Multiple encryption keys not supported."); + } + + // AES supports key sizes of only 16, 24, or 32 bytes + if (len != 16 && len != 24 && len != 32) { + return Status::InvalidArgument("Invalid key size in provider."); + } + + // len is in bytes + switch (len) { + case 16: + key_size_ = KeySize::AES_128; + break; + case 24: + key_size_ = KeySize::AES_192; + break; + case 32: + key_size_ = KeySize::AES_256; + break; + } + + // get size for context + IppStatus ipp_status = ippsAESGetSize(&ctx_size_); + if (ipp_status != ippStsNoErr) { + return Status::Aborted("Failed to create provider."); + } + + // allocate memory for context + aes_ctx_ = (IppsAESSpec*)(new Ipp8u[ctx_size_]); + assert(aes_ctx_ != nullptr); + + // initialize context + const Ipp8u* key = (const Ipp8u*)(cipher); + ipp_status = + ippsAESInit(key, static_cast(key_size_), aes_ctx_, ctx_size_); + + if (ipp_status != ippStsNoErr) { + // clean up context and abort! + ippsAESInit(0, static_cast(key_size_), aes_ctx_, ctx_size_); + delete[](Ipp8u*) aes_ctx_; + return Status::Aborted("Failed to create provider."); + } + return Status::OK(); +} + +Status IppcpProvider::CreateNewPrefix(const std::string& /*fname*/, + char* prefix, size_t prefixLength) const { + IppStatus ipp_status; + Ipp32u rnd; + const size_t rnd_size = sizeof(Ipp32u); + assert(prefixLength % rnd_size == 0); + for (size_t i = 0; i < prefixLength; i += rnd_size) { + // generate a cryptographically secured random number + ipp_status = ippsPRNGenRDRAND(&rnd, rnd_size << 3, nullptr); + if (ipp_status != ippStsNoErr) + return Status::Aborted(ippcpGetStatusString(ipp_status)); + memcpy(prefix + i, &rnd, rnd_size); + } + IppcpCipherStream cs(aes_ctx_, prefix); + return cs.Encrypt(0, prefix + IppcpCipherStream::kBlockSize, + prefixLength - IppcpCipherStream::kBlockSize); +} + +Status IppcpProvider::CreateCipherStream( + const std::string& /*fname*/, const EnvOptions& /*options*/, Slice& prefix, + std::unique_ptr* result) { + assert(result != nullptr); + assert(prefix.size() >= IppcpCipherStream::kBlockSize); + result->reset(new IppcpCipherStream(aes_ctx_, prefix.data())); + Status ipp_status = (*result)->Decrypt( + 0, (char*)prefix.data() + IppcpCipherStream::kBlockSize, + prefix.size() - IppcpCipherStream::kBlockSize); + return ipp_status; +} + +IppcpProvider::~IppcpProvider() { + ippsAESInit(0, static_cast(key_size_), aes_ctx_, ctx_size_); + delete[](Ipp8u*) aes_ctx_; +} + +#endif // ROCKSDB_LITE + +} // namespace ROCKSDB_NAMESPACE diff --git a/plugin/ippcp/ippcp_provider.h b/plugin/ippcp/ippcp_provider.h new file mode 100644 index 000000000..939a4cd56 --- /dev/null +++ b/plugin/ippcp/ippcp_provider.h @@ -0,0 +1,73 @@ +// 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). + +#pragma once + +#if !defined(ROCKSDB_LITE) + +// Includes Intel's Integrated Performance Primitives for Cryptography (IPPCP). +// IPPCP is lightweight cryptography library that is highly-optimized for +// various Intel CPUs. +// +// We use it here to provide an AES-128/192/256 encryption with a CTR mode of +// operation. +// +// Download URL: https://github.com/intel/ipp-crypto. +// + +#include +#include + +#include + +namespace ROCKSDB_NAMESPACE { + +// AES-128, AES-192, and AES-256 encryptions are all supported. +enum struct KeySize { AES_128 = 16, AES_192 = 24, AES_256 = 32 }; + +// This encryption provider uses AES block cipher and a CTR mode of operation +// with a cryptographically secure IV that is randomly generated. +// +// Note: a prefix size of 4096 (4K) is chosen for optimal performance. +// +class IppcpProvider : public EncryptionProvider { + public: + static constexpr size_t kPrefixSize = 4096; + + static std::unique_ptr CreateProvider(); + + static const char* kName() { return "ippcp"; } + + virtual const char* Name() const override { return kName(); } + + virtual size_t GetPrefixLength() const override { return kPrefixSize; } + + virtual Status AddCipher(const std::string& /*descriptor*/, + const char* /*cipher*/, size_t /*len*/, + bool /*for_write*/) override; + + virtual Status CreateNewPrefix(const std::string& fname, char* prefix, + size_t prefixLength) const override; + + virtual Status CreateCipherStream( + const std::string& fname, const EnvOptions& options, Slice& prefix, + std::unique_ptr* result) override; + + virtual ~IppcpProvider(); + + private: + int ctx_size_; + KeySize key_size_; + IppsAESSpec* aes_ctx_; + IppcpProvider() + : ctx_size_(0), key_size_(KeySize::AES_256), aes_ctx_(nullptr) {} + IppcpProvider(const IppcpProvider&) = delete; + IppcpProvider& operator=(const IppcpProvider&) = delete; +}; + +} // namespace ROCKSDB_NAMESPACE + +#endif // !defined(ROCKSDB_LITE) diff --git a/plugin/ippcp/security.md b/plugin/ippcp/security.md new file mode 100644 index 000000000..cb59eb893 --- /dev/null +++ b/plugin/ippcp/security.md @@ -0,0 +1,5 @@ +# Security Policy +Intel is committed to rapidly addressing security vulnerabilities affecting our customers and providing clear guidance on the solution, impact, severity and mitigation. + +## Reporting a Vulnerability +Please report any security vulnerabilities in this project utilizing the guidelines [here](https://www.intel.com/content/www/us/en/security-center/vulnerability-handling-guidelines.html). diff --git a/plugin/ippcp/tests/CMakeLists.txt b/plugin/ippcp/tests/CMakeLists.txt new file mode 100644 index 000000000..a573a58e7 --- /dev/null +++ b/plugin/ippcp/tests/CMakeLists.txt @@ -0,0 +1,61 @@ +# Copyright (C) 2022 Intel Corporation + +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.15) + +project(ippcp_encryptor_test VERSION 0.0.1) + +option(COVERAGE "Enable test coverage report" ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +set(ippcp_encryptor_test_CMAKE_EXE_LINKER_FLAGS "-u ippcp_reg") +add_executable(ippcp_encryptor_test ../ippcp_provider.cc ippcp_encryptor_test.cc) + + +if(NOT DEFINED IPPCRYPTOROOT) + find_package(ippcp REQUIRED) + if(ippcp_FOUND) + message(STATUS "Found ippcp: ${ippcp_DIR}") + target_link_libraries(ippcp_encryptor_test ippcp::ippcp) + endif() +else() + message(STATUS "Using IPPCRYPTOROOT: ${IPPCRYPTOROOT}") + include_directories(${IPPCRYPTOROOT}/include) + target_link_libraries(ippcp_encryptor_test ippcp) +endif() + +if(NOT DEFINED ROCKSDB_PATH) + find_package(RocksDB REQUIRED) + if(RocksDB_FOUND) + message(STATUS "Found RocksDB: ${RocksDB_DIR}") + target_link_libraries(ippcp_encryptor_test rocksdb) + endif() +elseif(DEFINED ROCKSDB_PATH) + message(STATUS "Using ROCKSDB_PATH: ${ROCKSDB_PATH}") + include_directories(${ROCKSDB_PATH} ${ROCKSDB_PATH}/include) + target_link_directories(ippcp_encryptor_test PUBLIC ${ROCKSDB_PATH}) + target_link_libraries(ippcp_encryptor_test rocksdb) +endif() + +find_package(GTest REQUIRED) +target_link_libraries(ippcp_encryptor_test gtest) + +add_compile_definitions(ROCKSDB_PLATFORM_POSIX) +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-rtti") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") +if(COVERAGE) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") +endif() + +add_custom_target(run + COMMAND ./ippcp_encryptor_test + DEPENDS ippcp_encryptor_test +) + +add_custom_target(coverage + COMMAND lcov --directory . --capture --output-file ippcp_encryptor_test.info && genhtml -o html ippcp_encryptor_test.info +) \ No newline at end of file diff --git a/plugin/ippcp/tests/ippcp_encryptor_test.cc b/plugin/ippcp/tests/ippcp_encryptor_test.cc new file mode 100644 index 000000000..3686ea48a --- /dev/null +++ b/plugin/ippcp/tests/ippcp_encryptor_test.cc @@ -0,0 +1,430 @@ +// 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../ippcp_provider.h" +#include + +namespace ROCKSDB_NAMESPACE +{ + + TEST(IppcpBasicTests, LoadIppcpProvider) + { + std::string IPPCP = IppcpProvider::kName(); + std::shared_ptr 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 = IppcpProvider::kName(); + std::shared_ptr 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 = IppcpProvider::kName(); + std::shared_ptr 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 = IppcpProvider::kName(); + std::shared_ptr 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 = IppcpProvider::kName(); + std::shared_ptr 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 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 = IppcpProvider::kName(); + std::shared_ptr 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 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 = IppcpProvider::kName(); + std::shared_ptr 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 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 = IppcpProvider::kName(); + std::shared_ptr 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 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 = IppcpProvider::kName(); + std::shared_ptr 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 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 IppcpProviderTest : public testing::TestWithParam> + { + public: + static void SetUpTestSuite() + { + ObjectLibrary::Default()->AddFactory( + IppcpProvider::kName(), + [](const std::string & /* uri */, std::unique_ptr *f, + std::string * /* errmsg */) + { + *f = IppcpProvider::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, IppcpProvider::kName(), &provider); + } + std::shared_ptr provider; + const EnvOptions soptions_; + }; + + TEST_P(IppcpProviderTest, 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 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(IppcpProviderTestInstance, + IppcpProviderTest, + 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(); +}