blob: 00783172db74822c9dffa27b44fc3dad895ce63c [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdint.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/strings/stringprintf.h>
#include <brillo/secure_blob.h>
#include <crypto/scoped_openssl_types.h>
#include <fuzzer/FuzzedDataProvider.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include "cryptohome/cryptolib.h"
#include "cryptohome/fuzzers/blob_mutator.h"
using brillo::Blob;
using brillo::BlobFromString;
using brillo::SecureBlob;
using crypto::ScopedRSA;
using cryptohome::CryptoLib;
namespace {
constexpr char kStaticFilesPath[] = "/usr/libexec/fuzzers/";
constexpr int kMaxPlaintextLength = 5;
constexpr int kMaxOaepLabelLength = 5;
struct Environment {
Environment();
// The RSA keys loaded from the data files installed next to the fuzzer.
static constexpr int kRsaKeyCount = 8;
ScopedRSA rsa_keys[kRsaKeyCount];
};
struct ScopedOpensslErrorClearer {
~ScopedOpensslErrorClearer() { ERR_clear_error(); }
};
ScopedRSA LoadRsaPrivateKeyFromPemFile(const base::FilePath& pem_file_path) {
std::string pem_data;
CHECK(base::ReadFileToString(pem_file_path, &pem_data));
crypto::ScopedBIO pem_data_bio(
BIO_new_mem_buf(pem_data.data(), pem_data.size()));
CHECK(pem_data_bio);
crypto::ScopedRSA rsa(PEM_read_bio_RSAPrivateKey(
pem_data_bio.get(), /*RSA **x=*/nullptr, /*pem_password_cb *cb=*/nullptr,
/*void *u=*/nullptr));
CHECK(rsa);
return rsa;
}
Environment::Environment() {
logging::SetMinLogLevel(logging::LOGGING_FATAL);
int key_index = 0;
for (int key_size : {512, 1024, 2048, 4096}) {
for (int current_key_number : {1, 2}) {
const base::FilePath key_file_path =
base::FilePath(kStaticFilesPath)
.AppendASCII(base::StringPrintf("cryptohome_fuzzer_key_rsa_%d_%d",
key_size, current_key_number));
CHECK_LT(key_index, kRsaKeyCount);
rsa_keys[key_index] = LoadRsaPrivateKeyFromPemFile(key_file_path);
++key_index;
}
}
}
// Returns a mutated RSA-OAEP encrypted blob of the given plaintext.
Blob FuzzedRsaOaepEncrypt(const Blob& plaintext,
const Blob& oaep_label,
RSA* rsa,
FuzzedDataProvider* fuzzed_data_provider) {
// Explicitly do the padding step first, in order to be able to mutate its
// result before the actual RSA operation.
Blob padded_blob(RSA_size(rsa));
RSA_padding_add_PKCS1_OAEP_mgf1(
padded_blob.data(), padded_blob.size(), plaintext.data(),
plaintext.size(), oaep_label.data(), oaep_label.size(), nullptr, nullptr);
Blob fuzzed_padded_blob =
MutateBlob(padded_blob, RSA_size(rsa), fuzzed_data_provider);
fuzzed_padded_blob.resize(RSA_size(rsa));
Blob ciphertext(RSA_size(rsa));
RSA_public_encrypt(fuzzed_padded_blob.size(), fuzzed_padded_blob.data(),
ciphertext.data(), rsa, RSA_NO_PADDING);
return MutateBlob(ciphertext, RSA_size(rsa), fuzzed_data_provider);
}
} // namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
static Environment environment;
// Prevent OpenSSL errors from accumulating in the error queue and leaking the
// memory across fuzzer executions.
ScopedOpensslErrorClearer scoped_openssl_error_clearer;
FuzzedDataProvider fuzzed_data_provider(data, size);
RSA* const encryption_rsa =
environment
.rsa_keys[fuzzed_data_provider.ConsumeIntegralInRange(
0, Environment::kRsaKeyCount - 1)]
.get();
RSA* const decryption_rsa =
environment
.rsa_keys[fuzzed_data_provider.ConsumeIntegralInRange(
0, Environment::kRsaKeyCount - 1)]
.get();
// Prepare fuzzed parameters for the tested function, based off real
// RSA-encoded blobs.
const Blob plaintext = BlobFromString(
fuzzed_data_provider.ConsumeRandomLengthString(kMaxPlaintextLength));
const Blob oaep_label = BlobFromString(
fuzzed_data_provider.ConsumeRandomLengthString(kMaxOaepLabelLength));
const Blob fuzzed_ciphertext = FuzzedRsaOaepEncrypt(
plaintext, oaep_label, encryption_rsa, &fuzzed_data_provider);
const Blob fuzzed_oaep_label =
MutateBlob(oaep_label, kMaxOaepLabelLength, &fuzzed_data_provider);
// Run the fuzzed function.
SecureBlob decrypted_data;
if (CryptoLib::RsaOaepDecrypt(SecureBlob(fuzzed_ciphertext),
SecureBlob(fuzzed_oaep_label), decryption_rsa,
&decrypted_data)) {
// Assert that the decryption result must be equal to the plaintext that was
// encrypted above - it's unrealistic for the fuzzer to find a blob that is
// a valid ciphertext of some different blob.
CHECK(SecureBlob(plaintext) == decrypted_data);
}
return 0;
}