blob: a9830eb3287d08a1428d868126bb6bd1a945a67b [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
//
// 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.
#include "src/lib/crypto_util/cipher.h"
#include <limits.h>
#include <string>
#include <vector>
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/evp.h>
#include "src/lib/crypto_util/errors.h"
#include "src/lib/crypto_util/random.h"
#include "src/lib/crypto_util/types.h"
#include "third_party/googletest/googletest/include/gtest/gtest.h"
namespace cobalt::crypto {
constexpr char const* kLine1 = "The woods are lovely, dark and deep,\n";
constexpr char const* kLine2 = "But I have promises to keep,\n";
constexpr char const* kLine3 = "And miles to go before I sleep,\n";
constexpr char const* kLine4 = "And miles to go before I sleep.";
constexpr char const* kLines[] = {kLine1, kLine2, kLine3, kLine4};
// Tests SymmetricCipher().
// This function is invoked by the SymmetricCipherTest.
// It encrypts and then decrypts |plain_text| and checks that the
// recovered text is equal to the plain text. It generates a random
// key and nonce.
void doSymmetricCipherTest(SymmetricCipher* cipher, const byte* plain_text, int ptext_len) {
// Initialize
byte key[SymmetricCipher::KEY_SIZE];
byte nonce[SymmetricCipher::NONCE_SIZE];
Random rand;
rand.RandomBytes(key, SymmetricCipher::KEY_SIZE);
rand.RandomBytes(nonce, SymmetricCipher::NONCE_SIZE);
EXPECT_TRUE(cipher->set_key(key)) << GetLastErrorMessage();
// Encrypt
std::vector<byte> cipher_text;
EXPECT_TRUE(cipher->Encrypt(nonce, plain_text, ptext_len, &cipher_text)) << GetLastErrorMessage();
// Decrypt
std::vector<byte> recovered_text;
EXPECT_TRUE(cipher->Decrypt(nonce, cipher_text.data(), cipher_text.size(), &recovered_text))
<< GetLastErrorMessage();
// Compare
EXPECT_EQ(std::string((const char*)recovered_text.data(), recovered_text.size()),
std::string((const char*)plain_text));
}
TEST(SymmetricCipherTest, TestManyStrings) {
SymmetricCipher cipher;
// Test once with each line separately.
for (auto line : kLines) {
doSymmetricCipherTest(&cipher, reinterpret_cast<const byte*>(line), strlen(line));
}
// Test once with all lines together.
std::string all_lines;
for (auto line : kLines) {
all_lines += line;
}
doSymmetricCipherTest(&cipher, reinterpret_cast<const byte*>(all_lines.data()), all_lines.size());
// Test once with a longer string: Repeat string 32 times.
for (int i = 0; i < 5; i++) {
all_lines += all_lines;
}
doSymmetricCipherTest(&cipher, reinterpret_cast<const byte*>(all_lines.data()), all_lines.size());
}
// This function is invoked by the HybridCipherTest
// It encrypts and then decrypts |plain_text| and checks that the
// recovered text is equal to the plain text.
void doHybridCipherTest(HybridCipher* hybrid_cipher, const byte* plain_text, int ptext_len,
const std::string& public_key, const std::string& private_key) {
// Encrypt
std::vector<byte> cipher_text;
ASSERT_TRUE(hybrid_cipher->set_public_key_pem(public_key)) << GetLastErrorMessage();
ASSERT_TRUE(hybrid_cipher->Encrypt(plain_text, ptext_len, &cipher_text)) << GetLastErrorMessage();
byte fingerprint[HybridCipher::PUBLIC_KEY_FINGERPRINT_SIZE];
ASSERT_TRUE(hybrid_cipher->public_key_fingerprint(fingerprint)) << GetLastErrorMessage();
// Decrypt
std::vector<byte> recovered_text;
ASSERT_TRUE(hybrid_cipher->set_private_key_pem(private_key)) << GetLastErrorMessage();
ASSERT_TRUE(hybrid_cipher->Decrypt(cipher_text.data(), cipher_text.size(), &recovered_text))
<< GetLastErrorMessage();
// Compare
EXPECT_EQ(std::string((const char*)recovered_text.data(), recovered_text.size()),
std::string((const char*)plain_text));
// Decrypt with flipped salt
cipher_text[HybridCipher::PUBLIC_KEY_SIZE] ^= 0x1; // flip a bit in the first byte of the salt
EXPECT_FALSE(hybrid_cipher->Decrypt(cipher_text.data(), cipher_text.size(), &recovered_text))
<< GetLastErrorMessage();
// Decrypt with modified public_key_part
cipher_text[HybridCipher::PUBLIC_KEY_SIZE] ^= 0x1; // flip salt bit back
cipher_text[2] ^= 0x1; // flip any bit except in first byte (due to
// X9.62 serialization)
EXPECT_FALSE(hybrid_cipher->Decrypt(cipher_text.data(), cipher_text.size(), &recovered_text))
<< GetLastErrorMessage();
}
void doGenerateKeys(std::string* public_key, std::string* private_key) {
ASSERT_TRUE(HybridCipher::GenerateKeyPairPEM(public_key, private_key)) << GetLastErrorMessage();
}
TEST(HybridCipherTest, Test) {
HybridCipher hybrid_cipher;
std::string public_key;
std::string private_key;
// Test with five different key pairs
for (int times = 0; times < 5; ++times) {
doGenerateKeys(&public_key, &private_key);
// Test once with each line separately.
for (auto line : kLines) {
doHybridCipherTest(&hybrid_cipher, reinterpret_cast<const byte*>(line), strlen(line),
public_key, private_key);
}
// Test once with all lines together.
std::string all_lines;
for (auto line : kLines) {
all_lines += line;
}
doHybridCipherTest(&hybrid_cipher, reinterpret_cast<const byte*>(all_lines.data()),
all_lines.size(), public_key, private_key);
// Test once with a longer string: Repeat string 32 times.
for (int i = 0; i < 5; i++) {
all_lines += all_lines;
}
doHybridCipherTest(&hybrid_cipher, reinterpret_cast<const byte*>(all_lines.data()),
all_lines.size(), public_key, private_key);
}
}
} // namespace cobalt::crypto