Files
cli/vendor/aws-lc-sys/aws-lc/crypto/test/test_util.cc

390 lines
9.5 KiB
C++

// Copyright (c) 2015, Google Inc.
// SPDX-License-Identifier: ISC
#include "test_util.h"
#include <fstream>
#include <ostream>
#include <inttypes.h>
#include <openssl/bn.h>
#include <openssl/err.h>
#include <thread>
#if !defined(OPENSSL_WINDOWS)
#include <sys/wait.h>
#endif
#include <inttypes.h>
#include <openssl/err.h>
#include "../internal.h"
#include "../ube/fork_ube_detect.h"
#include "openssl/pem.h"
#include "openssl/rand.h"
void hexdump(FILE *fp, const char *msg, const void *in, size_t len) {
const uint8_t *data = reinterpret_cast<const uint8_t *>(in);
fputs(msg, fp);
for (size_t i = 0; i < len; i++) {
fprintf(fp, "%02x", data[i]);
}
fputs("\n", fp);
}
std::ostream &operator<<(std::ostream &os, const Bytes &in) {
if (in.span_.empty()) {
return os << "<empty Bytes>";
}
// Print a byte slice as hex.
os << EncodeHex(in.span_);
return os;
}
bool DecodeHex(std::vector<uint8_t> *out, const std::string &in) {
out->clear();
if (in.size() % 2 != 0) {
return false;
}
out->reserve(in.size() / 2);
for (size_t i = 0; i < in.size(); i += 2) {
uint8_t hi, lo;
if (!OPENSSL_fromxdigit(&hi, in[i]) ||
!OPENSSL_fromxdigit(&lo, in[i + 1])) {
return false;
}
out->push_back((hi << 4) | lo);
}
return true;
}
std::vector<uint8_t> HexToBytes(const char *str) {
std::vector<uint8_t> ret;
if (!DecodeHex(&ret, str)) {
abort();
}
return ret;
}
std::string EncodeHex(bssl::Span<const uint8_t> in) {
static const char kHexDigits[] = "0123456789abcdef";
std::string ret;
ret.reserve(in.size() * 2);
for (uint8_t b : in) {
ret += kHexDigits[b >> 4];
ret += kHexDigits[b & 0xf];
}
return ret;
}
testing::AssertionResult ErrorEquals(uint32_t err, int lib, int reason) {
if (ERR_GET_LIB(err) == lib && ERR_GET_REASON(err) == reason) {
return testing::AssertionSuccess();
}
char buf[128], expected[128];
return testing::AssertionFailure()
<< "Got \"" << ERR_error_string_n(err, buf, sizeof(buf))
<< "\", wanted \""
<< ERR_error_string_n(ERR_PACK(lib, reason), expected,
sizeof(expected))
<< "\"";
}
// CertFromPEM parses the given, NUL-terminated pem block and returns an
// |X509*|.
bssl::UniquePtr<X509> CertFromPEM(const char *pem) {
bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem, strlen(pem)));
if (!bio) {
return nullptr;
}
return bssl::UniquePtr<X509>(
PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
}
bssl::UniquePtr<RSA> RSAFromPEM(const char *pem) {
bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem, strlen(pem)));
if (!bio) {
return nullptr;
}
return bssl::UniquePtr<RSA>(
PEM_read_bio_RSAPrivateKey(bio.get(), nullptr, nullptr, nullptr));
}
bssl::UniquePtr<X509> MakeTestCert(const char *issuer,
const char *subject, EVP_PKEY *key,
bool is_ca) {
bssl::UniquePtr<X509> cert(X509_new());
if (!cert || //
!X509_set_version(cert.get(), X509_VERSION_3) ||
!X509_NAME_add_entry_by_txt(
X509_get_issuer_name(cert.get()), "CN", MBSTRING_UTF8,
reinterpret_cast<const uint8_t *>(issuer), -1, -1, 0) ||
!X509_NAME_add_entry_by_txt(
X509_get_subject_name(cert.get()), "CN", MBSTRING_UTF8,
reinterpret_cast<const uint8_t *>(subject), -1, -1, 0) ||
!X509_set_pubkey(cert.get(), key) ||
!ASN1_TIME_adj(X509_getm_notBefore(cert.get()), kReferenceTime, -1, 0) ||
!ASN1_TIME_adj(X509_getm_notAfter(cert.get()), kReferenceTime, 1, 0)) {
return nullptr;
}
bssl::UniquePtr<BASIC_CONSTRAINTS> bc(BASIC_CONSTRAINTS_new());
if (!bc) {
return nullptr;
}
bc->ca = is_ca ? ASN1_BOOLEAN_TRUE : ASN1_BOOLEAN_FALSE;
if (!X509_add1_ext_i2d(cert.get(), NID_basic_constraints, bc.get(),
/*crit=*/1, /*flags=*/0)) {
return nullptr;
}
return cert;
}
bssl::UniquePtr<STACK_OF(X509)> CertsToStack(
const std::vector<X509 *> &certs) {
bssl::UniquePtr<STACK_OF(X509)> stack(sk_X509_new_null());
if (!stack) {
return nullptr;
}
for (auto cert : certs) {
if (!bssl::PushToStack(stack.get(), bssl::UpRef(cert))) {
return nullptr;
}
}
return stack;
}
bool PEM_to_DER(const char *pem_str, uint8_t **out_der, long *out_der_len) {
char *name = nullptr;
char *header = nullptr;
// Create BIO from memory
bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem_str, strlen(pem_str)));
if (!bio) {
return false;
}
// Read PEM into DER
if (PEM_read_bio(bio.get(), &name, &header, out_der, out_der_len) <= 0) {
OPENSSL_free(name);
OPENSSL_free(header);
OPENSSL_free(*out_der);
*out_der = nullptr;
return false;
}
OPENSSL_free(name);
OPENSSL_free(header);
return true;
}
#if defined(OPENSSL_WINDOWS)
size_t createTempFILEpath(char buffer[PATH_MAX]) {
// On Windows, tmpfile() may attempt to create temp files in the root directory
// of the drive, which requires Admin privileges, resulting in test failure.
char pathname[PATH_MAX];
if(0 == GetTempPathA(PATH_MAX, pathname)) {
return 0;
}
return GetTempFileNameA(pathname, "awslctest", 0, buffer);
}
size_t createTempDirPath(char buffer[PATH_MAX]) {
char temp_path[PATH_MAX];
union {
uint8_t bytes[8];
uint64_t value;
} random_bytes;
// Get the temporary path
if (0 == GetTempPathA(PATH_MAX, temp_path)) {
return 0;
}
if (!RAND_bytes(random_bytes.bytes, sizeof(random_bytes.bytes))) {
return 0;
}
int written = snprintf(buffer, PATH_MAX, "%s\\awslctest_%" PRIX64, temp_path, random_bytes.value);
// Check for truncation of dirname
if (written < 0 || written >= PATH_MAX) {
return 0;
}
if (!CreateDirectoryA(buffer, NULL)) {
return 0;
}
return (size_t)written;
}
FILE* createRawTempFILE() {
char filename[PATH_MAX];
if(createTempFILEpath(filename) == 0) {
return nullptr;
}
return fopen(filename, "w+b");
}
#else
#include <cstdlib>
#include <unistd.h>
size_t createTempFILEpath(char buffer[PATH_MAX]) {
snprintf(buffer, PATH_MAX, "awslcTestTmpFileXXXXXX");
int fd = mkstemp(buffer);
if (fd == -1) {
return 0;
}
close(fd);
return strnlen(buffer, PATH_MAX);
}
size_t createTempDirPath(char buffer[PATH_MAX]) {
snprintf(buffer, PATH_MAX, "/tmp/awslcTestDirXXXXXX");
if (mkdtemp(buffer) == NULL) {
return 0;
}
return strnlen(buffer, PATH_MAX);
}
FILE* createRawTempFILE() {
return tmpfile();
}
#endif
TempFILE createTempFILE() {
return TempFILE(createRawTempFILE());
}
void CustomDataFree(void *parent, void *ptr, CRYPTO_EX_DATA *ad,
int index, long argl, void *argp) {
free(ptr);
}
bool osIsAmazonLinux(void) {
bool res = false;
#if defined(OPENSSL_LINUX)
// Per https://docs.aws.amazon.com/linux/al2023/ug/naming-and-versioning.html.
std::ifstream amazonLinuxSpecificFile("/etc/amazon-linux-release-cpe");
if (amazonLinuxSpecificFile.is_open()) {
// Definitely on Amazon Linux.
amazonLinuxSpecificFile.close();
return true;
}
// /etc/amazon-linux-release-cpe was introduced in AL2023. For earlier, parse
// and read /etc/system-release-cpe.
std::ifstream osRelease("/etc/system-release-cpe");
if (!osRelease.is_open()) {
return false;
}
std::string line;
while (std::getline(osRelease, line)) {
// AL2:
// $ cat /etc/system-release-cpe
// cpe:2.3:o:amazon:amazon_linux:2
//
// AL2023:
// $ cat /etc/system-release-cpe
// cpe:2.3:o:amazon:amazon_linux:2023
if (line.find("amazon") != std::string::npos) {
res = true;
} else if (line.find("amazon_linux") != std::string::npos) {
res = true;
}
}
osRelease.close();
#endif
return res;
}
bool threadTest(const size_t numberOfThreads, std::function<void(bool*)> testFunc) {
bool res = true;
#if defined(OPENSSL_THREADS)
// char to be able to pass-as-reference.
std::vector<char> retValueVec(numberOfThreads, 0);
std::vector<std::thread> threadVec;
for (size_t i = 0; i < numberOfThreads; i++) {
threadVec.emplace_back(testFunc, reinterpret_cast<bool*>(&retValueVec[i]));
}
for (auto& thread : threadVec) {
thread.join();
}
for (size_t i = 0; i < numberOfThreads; i++) {
if (!static_cast<bool>(retValueVec[i])) {
fprintf(stderr, "Thread %lu failed\n", (long unsigned int) i);
res = false;
}
}
#else
testFunc(&res);
#endif
return res;
}
bool forkAndRunTest(std::function<bool()> child_func,
std::function<bool()> parent_func) {
#if defined(OPENSSL_WINDOWS)
// fork() is not supported on Windows. We could potentially add support for
// the CreateProcess API at some point.
return false;
#else
pid_t pid = fork();
if (pid == 0) { // Child
bool success = child_func();
exit(success ? 0 : 1);
} else if (pid > 0) { // Parent
bool parent_success = parent_func();
int status;
waitpid(pid, &status, 0);
return parent_success && WIFEXITED(status) && WEXITSTATUS(status) == 0;
}
// Fork failed
return false;
#endif
}
void maybeDisableSomeForkUbeDetectMechanisms(void) {
if (getenv("AWSLC_IGNORE_FORK_UBE_DETECTION")) {
CRYPTO_fork_detect_ignore_wipeonfork_FOR_TESTING();
CRYPTO_fork_detect_ignore_inheritzero_FOR_TESTING();
}
}
bool runtimeEmulationIsIntelSde(void) {
if (getenv("RUNTIME_EMULATION_SDE")) {
return true;
}
return false;
}
bool addressSanitizerIsEnabled(void) {
#if defined(OPENSSL_ASAN)
return true;
#else
return false;
#endif
}
bssl::UniquePtr<BIGNUM> HexToBIGNUM(const char *hex) {
BIGNUM *bn = nullptr;
BN_hex2bn(&bn, hex);
return bssl::UniquePtr<BIGNUM>(bn);
}