blob: 1c4c44217873d7adc1f657cd99663f28a70092a8 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
#include "properties/Decryptor.h"
#include "unit/Catch.h"
#include "unit/TestBase.h"
#include "utils/StringUtils.h"
#include "properties/Configure.h"
namespace minifi = org::apache::nifi::minifi;
namespace utils = org::apache::nifi::minifi::utils;
TEST_CASE("Decryptor can decide whether a property is encrypted", "[isValidEncryptionMarker]") {
utils::crypto::Bytes encryption_key;
minifi::Decryptor decryptor{utils::crypto::EncryptionProvider{encryption_key}};
REQUIRE(minifi::Decryptor::isValidEncryptionMarker(std::nullopt) == false);
REQUIRE(minifi::Decryptor::isValidEncryptionMarker(std::optional<std::string>{""}) == false);
REQUIRE(minifi::Decryptor::isValidEncryptionMarker(std::optional<std::string>{"plaintext"}) == false);
REQUIRE(minifi::Decryptor::isValidEncryptionMarker(std::optional<std::string>{"AES256-GCM"}) == false);
REQUIRE(minifi::Decryptor::isValidEncryptionMarker(utils::crypto::EncryptionType::name()) == true);
}
TEST_CASE("Decryptor can decrypt a property", "[decrypt]") {
utils::crypto::Bytes encryption_key = utils::string::from_hex("4024b327fdc987ce3eb43dd1f690b9987e4072e0020e3edf4349ce1ad91a4e38");
minifi::Decryptor decryptor{utils::crypto::EncryptionProvider{encryption_key}};
std::string encrypted_value = "l3WY1V27knTiPa6jVX0jrq4qjmKsySOu||ErntqZpHP1M+6OkA14p5sdnqJhuNHWHDVUU5EyMloTtSytKk9a5xNKo=";
REQUIRE(decryptor.decrypt(encrypted_value) == "CorrectHorseBatteryStaple");
}
TEST_CASE("Decryptor will throw if the value is incorrect", "[decrypt]") {
utils::crypto::Bytes encryption_key = utils::string::from_hex("4024b327fdc987ce3eb43dd1f690b9987e4072e0020e3edf4349ce1ad91a4e38");
minifi::Decryptor decryptor{utils::crypto::EncryptionProvider{encryption_key}};
// correct nonce + ciphertext and mac: "l3WY1V27knTiPa6jVX0jrq4qjmKsySOu||ErntqZpHP1M+6OkA14p5sdnqJhuNHWHDVUU5EyMloTtSytKk9a5xNKo="
REQUIRE_THROWS_AS(decryptor.decrypt( // this is not even close
"some totally incorrect value"),
std::exception);
REQUIRE_THROWS_AS(decryptor.decrypt( // separator missing
"l3WY1V27knTiPa6jVX0jrq4qjmKsySOuErntqZpHP1M+6OkA14p5sdnqJhuNHWHDVUU5EyMloTtSytKk9a5xNKo="),
std::exception);
REQUIRE_THROWS_AS(decryptor.decrypt( // separator wrong
"l3WY1V27knTiPa6jVX0jrq4qjmKsySOu__ErntqZpHP1M+6OkA14p5sdnqJhuNHWHDVUU5EyMloTtSytKk9a5xNKo="),
std::exception);
REQUIRE_THROWS_AS(decryptor.decrypt( // more than one separator
"l3WY1V27knTiPa6jVX0jrq4qjmKsySOu||ErntqZpHP1M+6OkA14p5sdnqJhuNHWHDVUU5EyMloTtSytKk9a5xNKo=||extra+stuff"),
std::exception);
REQUIRE_THROWS_AS(decryptor.decrypt( // nonce is off by one char
"L3WY1V27knTiPa6jVX0jrq4qjmKsySOu||ErntqZpHP1M+6OkA14p5sdnqJhuNHWHDVUU5EyMloTtSytKk9a5xNKo="),
std::exception);
REQUIRE_THROWS_AS(decryptor.decrypt( // ciphertext is off by one char
"l3WY1V27knTiPa6jVX0jrq4qjmKsySOu||erntqZpHP1M+6OkA14p5sdnqJhuNHWHDVUU5EyMloTtSytKk9a5xNKo="),
std::exception);
REQUIRE_THROWS_AS(decryptor.decrypt( // nonce is too short
"l3WY1V27knTiPa6jVX0rq4qjmKsySOu||ErntqZpHP1M+6OkA14p5sdnqJhuNHWHDVUU5EyMloTtSytKk9a5xNKo="),
std::exception);
REQUIRE_THROWS_AS(decryptor.decrypt( // nonce is too long
"l3WY1V27knTiPa6jVX0jrq4qjmKsySOup||ErntqZpHP1M+6OkA14p5sdnqJhuNHWHDVUU5EyMloTtSytKk9a5xNKo="),
std::exception);
REQUIRE_THROWS_AS(decryptor.decrypt( // ciphertext-and-mac is too short
"l3WY1V27knTiPa6jVX0jrq4qjmKsySOu||ErntqZpHP1M+6OkA14p5sdnqJhuNHWHDVUU5EyMloTtSytk9a5xNKo="),
std::exception);
REQUIRE_THROWS_AS(decryptor.decrypt( // ciphertext-and-mac is too long
"l3WY1V27knTiPa6jVX0jrq4qjmKsySOu||ErntqZpHP1M+6OkA14p5sdnqJhuNHWHDVUUU5EyMloTtSytKk9a5xNKo="),
std::exception);
REQUIRE_THROWS_AS(decryptor.decrypt( // correct format but random value
"81hf/4bHIRVd2pYglniBW3zOUcaLe+Cw||mkN2sKHS+nepRTcBhOJ5tFW4GXvaywYLD8xzIEbCP0lgUA6Qf3jZ/oMi"),
std::exception);
}
TEST_CASE("Decryptor can decrypt a configuration file", "[decryptSensitiveProperties]") {
utils::crypto::Bytes encryption_key = utils::string::from_hex("5506c28d0fe265299e294a4c766b723a48986764953e93d38b3c627176fd10ed");
minifi::Decryptor decryptor{utils::crypto::EncryptionProvider{encryption_key}};
minifi::ConfigureImpl configuration{decryptor};
std::filesystem::path resources_dir{TEST_RESOURCES};
configuration.loadConfigureFile(resources_dir / "encrypted.minifi.properties");
REQUIRE_FALSE(configuration.getConfiguredKeys().empty());
const auto passphrase = configuration.get(minifi::Configure::nifi_security_client_pass_phrase);
REQUIRE(passphrase);
REQUIRE(*passphrase == "SpeakFriendAndEnter");
const auto password = configuration.get(minifi::Configure::nifi_rest_api_password);
REQUIRE(password);
REQUIRE(*password == "OpenSesame");
std::string agent_identifier;
REQUIRE(configuration.get(minifi::Configuration::nifi_c2_agent_identifier, "c2.agent.identifier", agent_identifier));
REQUIRE(agent_identifier == "TailFileTester-001");
const auto unencrypted_property = configuration.get(minifi::Configure::nifi_bored_yield_duration);
REQUIRE(unencrypted_property);
REQUIRE(*unencrypted_property == "10 millis");
const auto nonexistent_property = configuration.get("this.property.does.not.exist");
REQUIRE_FALSE(nonexistent_property);
}
TEST_CASE("Decryptor can be created from a bootstrap file", "[create]") {
std::filesystem::path resources_dir{TEST_RESOURCES};
const auto valid_decryptor = minifi::Decryptor::create(resources_dir);
REQUIRE(valid_decryptor);
REQUIRE(valid_decryptor->decrypt("HvbPejGT3ur9/00gXQK/dJCYwaNqhopf||CiXKiNaljSN7VkLXP5zfJnb4+4UcKIG3ddwuVfSPpkRRfT4=") == "SpeakFriendAndEnter");
TempDirectory temp_dir;
const auto invalid_decryptor = minifi::Decryptor::create(temp_dir.getPath() / "there.is.no.such.directory");
REQUIRE_FALSE(invalid_decryptor);
}