// Copyright (c) 2015, Google Inc. // SPDX-License-Identifier: ISC #include "test_util.h" #include #include #include #include #include #include #if !defined(OPENSSL_WINDOWS) #include #endif #include #include #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(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 << ""; } // Print a byte slice as hex. os << EncodeHex(in.span_); return os; } bool DecodeHex(std::vector *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 HexToBytes(const char *str) { std::vector ret; if (!DecodeHex(&ret, str)) { abort(); } return ret; } std::string EncodeHex(bssl::Span 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 CertFromPEM(const char *pem) { bssl::UniquePtr bio(BIO_new_mem_buf(pem, strlen(pem))); if (!bio) { return nullptr; } return bssl::UniquePtr( PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)); } bssl::UniquePtr RSAFromPEM(const char *pem) { bssl::UniquePtr bio(BIO_new_mem_buf(pem, strlen(pem))); if (!bio) { return nullptr; } return bssl::UniquePtr( PEM_read_bio_RSAPrivateKey(bio.get(), nullptr, nullptr, nullptr)); } bssl::UniquePtr MakeTestCert(const char *issuer, const char *subject, EVP_PKEY *key, bool is_ca) { bssl::UniquePtr 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(issuer), -1, -1, 0) || !X509_NAME_add_entry_by_txt( X509_get_subject_name(cert.get()), "CN", MBSTRING_UTF8, reinterpret_cast(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 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 CertsToStack( const std::vector &certs) { bssl::UniquePtr 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_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 #include 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 testFunc) { bool res = true; #if defined(OPENSSL_THREADS) // char to be able to pass-as-reference. std::vector retValueVec(numberOfThreads, 0); std::vector threadVec; for (size_t i = 0; i < numberOfThreads; i++) { threadVec.emplace_back(testFunc, reinterpret_cast(&retValueVec[i])); } for (auto& thread : threadVec) { thread.join(); } for (size_t i = 0; i < numberOfThreads; i++) { if (!static_cast(retValueVec[i])) { fprintf(stderr, "Thread %lu failed\n", (long unsigned int) i); res = false; } } #else testFunc(&res); #endif return res; } bool forkAndRunTest(std::function child_func, std::function 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 HexToBIGNUM(const char *hex) { BIGNUM *bn = nullptr; BN_hex2bn(&bn, hex); return bssl::UniquePtr(bn); }