mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-06 13:30:00 +00:00
445 lines
23 KiB
C++
445 lines
23 KiB
C++
#include "prx/libSceLibreSsl/libSceLibreSsl.hpp"
|
|
#include "prx/libSceLibreSsl/include/Cmac.hpp"
|
|
#include "prx/libSceLibreSsl/ErrorQueue.hpp"
|
|
#include "prx/libc/include/ApplicationHeap.hpp"
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <memory>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <utility>
|
|
#include <vector>
|
|
#include <mbedtls/cipher.h>
|
|
#include <mbedtls/cmac.h>
|
|
#include <mbedtls/aes.h>
|
|
#include <mbedtls/des.h>
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#else
|
|
#include <dlfcn.h>
|
|
#endif
|
|
|
|
#ifdef ANYPS5_CMAC_DYNAMIC
|
|
namespace {
|
|
decltype(&CMAC_CTX_new_nid_postfix) cmacNew;
|
|
decltype(&CMAC_CTX_free_nid_postfix) cmacFree;
|
|
decltype(&CMAC_Init_nid_postfix) cmacInit;
|
|
decltype(&CMAC_Update_nid_postfix) cmacUpdate;
|
|
decltype(&CMAC_Final_nid_postfix) cmacFinal;
|
|
decltype(&EVP_aes_128_cbc_nid_postfix) aes128Method;
|
|
decltype(&EVP_aes_192_cbc_nid_postfix) aes192Method;
|
|
decltype(&EVP_aes_256_cbc_nid_postfix) aes256Method;
|
|
decltype(&ERR_clear_error_nid_postfix) clearError;
|
|
decltype(&ERR_get_error_nid_postfix) getError;
|
|
decltype(&ApplicationHeapRegister_nid_no_patch) registerHeap;
|
|
|
|
void* Load(const std::string& path) {
|
|
#ifdef _WIN32
|
|
auto* library = LoadLibraryExA(path.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
|
|
#else
|
|
auto* library = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
|
|
#endif
|
|
if (library == nullptr) throw std::runtime_error("failed to load patched CMAC dependency: " + path);
|
|
return library;
|
|
}
|
|
|
|
template<typename TFunction> void Resolve(TFunction& destination, void* library, const char* name) {
|
|
#ifdef _WIN32
|
|
destination = reinterpret_cast<TFunction>(GetProcAddress(static_cast<HMODULE>(library), name));
|
|
#else
|
|
destination = reinterpret_cast<TFunction>(dlsym(library, name));
|
|
#endif
|
|
if (destination == nullptr) throw std::runtime_error(std::string("missing patched CMAC dependency: ") + name);
|
|
}
|
|
|
|
void InitializePatched(const char* directory) {
|
|
const auto root = std::string(directory) + "/";
|
|
auto* libc = Load(root + "libc.prx");
|
|
auto* library = Load(root + "libSceLibreSsl.prx");
|
|
Resolve(cmacNew, library, "mwJYST4Bb3U");
|
|
Resolve(cmacFree, library, "cBNGAG1DkXc");
|
|
Resolve(cmacInit, library, "LATrF+dIZYI");
|
|
Resolve(cmacUpdate, library, "XpAJ1Ckg-q4");
|
|
Resolve(cmacFinal, library, "6MM7ng9P+18");
|
|
Resolve(aes128Method, library, "rV07jlO-+6Q");
|
|
Resolve(aes192Method, library, "0X5DnA3olsk");
|
|
Resolve(aes256Method, library, "J44dsZUcK4I");
|
|
Resolve(clearError, library, "sieQ--PINWg");
|
|
Resolve(getError, library, "DJg+qWX1rwY");
|
|
Resolve(registerHeap, libc, "ApplicationHeapRegister_nid_no_patch");
|
|
}
|
|
}
|
|
|
|
#define CMAC_CTX_new_nid_postfix cmacNew
|
|
#define CMAC_CTX_free_nid_postfix cmacFree
|
|
#define CMAC_Init_nid_postfix cmacInit
|
|
#define CMAC_Update_nid_postfix cmacUpdate
|
|
#define CMAC_Final_nid_postfix cmacFinal
|
|
#define EVP_aes_128_cbc_nid_postfix aes128Method
|
|
#define EVP_aes_192_cbc_nid_postfix aes192Method
|
|
#define EVP_aes_256_cbc_nid_postfix aes256Method
|
|
#define ERR_clear_error_nid_postfix clearError
|
|
#define ERR_get_error_nid_postfix getError
|
|
#define ApplicationHeapRegister_nid_no_patch registerHeap
|
|
#endif
|
|
|
|
namespace {
|
|
|
|
void Require(bool condition, const char* message) {
|
|
if (condition) return;
|
|
std::fprintf(stderr, "failed: %s\n", message);
|
|
std::abort();
|
|
}
|
|
|
|
int allocationsUntilFailure = -1;
|
|
std::vector<std::pair<void*, std::size_t>> allocations;
|
|
|
|
void* APS5_VABI Allocate(std::size_t size) {
|
|
if (allocationsUntilFailure == 0) return nullptr;
|
|
if (allocationsUntilFailure > 0) --allocationsUntilFailure;
|
|
auto* pointer = std::malloc(size);
|
|
if (pointer != nullptr) allocations.emplace_back(pointer, size);
|
|
return pointer;
|
|
}
|
|
void* APS5_VABI Reallocate(void*, std::size_t) { throw std::logic_error("unexpected CMAC reallocation"); }
|
|
void* APS5_VABI Align(std::size_t, std::size_t) { throw std::logic_error("unexpected CMAC aligned allocation"); }
|
|
void* APS5_VABI Realign(void*, std::size_t, std::size_t) { throw std::logic_error("unexpected CMAC realignment"); }
|
|
int APS5_VABI PosixAlign(void**, std::size_t, std::size_t) { throw std::logic_error("unexpected CMAC aligned allocation"); }
|
|
|
|
void* APS5_VABI Calloc(std::size_t count, std::size_t size) {
|
|
if (allocationsUntilFailure == 0) return nullptr;
|
|
if (allocationsUntilFailure > 0) --allocationsUntilFailure;
|
|
auto* pointer = std::calloc(count, size);
|
|
if (pointer != nullptr) allocations.emplace_back(pointer, count * size);
|
|
return pointer;
|
|
}
|
|
|
|
void APS5_VABI Free(void* pointer) {
|
|
if (pointer == nullptr) return;
|
|
const auto found = std::find_if(allocations.begin(), allocations.end(), [&](const auto& entry) { return entry.first == pointer; });
|
|
Require(found != allocations.end(), "free a guest-owned allocation");
|
|
const auto* bytes = static_cast<const unsigned char*>(pointer);
|
|
Require(std::all_of(bytes, bytes + found->second, [](auto value) { return value == 0; }), "wipe contexts and key storage before freeing");
|
|
allocations.erase(found);
|
|
std::free(pointer);
|
|
}
|
|
|
|
void InitializeHeap() {
|
|
const std::array<void*, 10> api{
|
|
reinterpret_cast<void*>(Allocate), reinterpret_cast<void*>(Free), reinterpret_cast<void*>(Calloc),
|
|
reinterpret_cast<void*>(Reallocate), reinterpret_cast<void*>(Align), reinterpret_cast<void*>(Realign),
|
|
reinterpret_cast<void*>(PosixAlign)};
|
|
ApplicationHeapRegister_nid_no_patch(api.data());
|
|
}
|
|
|
|
template<typename TAction> void RequireThrows(TAction action) {
|
|
try { action(); } catch (const std::exception&) { return; }
|
|
Require(false, "unsupported input must throw");
|
|
}
|
|
|
|
std::vector<unsigned char> Hex(std::string_view text) {
|
|
Require(text.size() % 2 == 0, "hex fixture has complete bytes");
|
|
std::vector<unsigned char> bytes;
|
|
for (std::size_t index = 0; index < text.size(); index += 2) {
|
|
const auto nibble = [](char value) { return value <= '9' ? value - '0' : value - 'a' + 10; };
|
|
bytes.push_back(static_cast<unsigned char>((nibble(text[index]) << 4) | nibble(text[index + 1])));
|
|
}
|
|
return bytes;
|
|
}
|
|
|
|
using CmacFree = void (APS5_VABI*)(LibreSslCmac*);
|
|
using Context = std::unique_ptr<LibreSslCmac, CmacFree>;
|
|
|
|
Context NewContext() {
|
|
Context context(CMAC_CTX_new_nid_postfix(), CMAC_CTX_free_nid_postfix);
|
|
Require(context != nullptr, "allocate CMAC context");
|
|
return context;
|
|
}
|
|
|
|
std::array<unsigned char, 16> Final(LibreSslCmac* context) {
|
|
std::array<unsigned char, 18> output;
|
|
output.fill(0xa5);
|
|
std::size_t length = 99;
|
|
Require(CMAC_Final_nid_postfix(context, output.data() + 1, &length) == 1, "finalize CMAC");
|
|
Require(length == 16 && output.front() == 0xa5 && output.back() == 0xa5, "tag size and output bounds");
|
|
std::array<unsigned char, 16> result;
|
|
std::copy_n(output.data() + 1, 16, result.data());
|
|
return result;
|
|
}
|
|
|
|
const LibreSslCipher* Methods[3];
|
|
|
|
void RfcVectors() {
|
|
const auto key = Hex("2b7e151628aed2a6abf7158809cf4f3c");
|
|
const auto message = Hex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710");
|
|
const std::size_t lengths[] = {0, 16, 40, 64};
|
|
const char* tags[] = {"bb1d6929e95937287fa37d129b756746", "070a16b46b4d4144f79bdd9dd04a287c", "dfa66747de9ae63030ca32611497c827", "51f0bebf7e3b9d92fc49741779363cfe"};
|
|
auto context = NewContext();
|
|
for (std::size_t vector = 0; vector < 4; ++vector) {
|
|
const auto expected = Hex(tags[vector]);
|
|
for (std::size_t split = 0; split <= lengths[vector]; ++split) {
|
|
Require(CMAC_Init_nid_postfix(context.get(), key.data(), key.size(), Methods[0], nullptr) == 1, "RFC key initialization");
|
|
Require(CMAC_Update_nid_postfix(context.get(), message.data(), split) == 1, "RFC first update");
|
|
Require(CMAC_Update_nid_postfix(context.get(), nullptr, 0) == 1, "empty update");
|
|
Require(CMAC_Update_nid_postfix(context.get(), message.data() + split, lengths[vector] - split) == 1, "RFC second update");
|
|
std::size_t length = 99;
|
|
Require(CMAC_Final_nid_postfix(context.get(), nullptr, &length) == 1 && length == 16, "length-only final");
|
|
Require(CMAC_Final_nid_postfix(context.get(), nullptr, &length) == 1 && length == 16, "repeat length query");
|
|
const auto actual = Final(context.get());
|
|
if (!std::equal(actual.begin(), actual.end(), expected.begin())) {
|
|
std::fprintf(stderr, "RFC vector %zu split %zu: ", vector, split);
|
|
for (auto byte : actual) std::fprintf(stderr, "%02x", byte);
|
|
std::fputc('\n', stderr);
|
|
}
|
|
Require(std::equal(actual.begin(), actual.end(), expected.begin()), "RFC 4493 tag");
|
|
}
|
|
}
|
|
}
|
|
|
|
void Differential() {
|
|
const mbedtls_cipher_type_t types[] = {MBEDTLS_CIPHER_AES_128_ECB, MBEDTLS_CIPHER_AES_192_ECB, MBEDTLS_CIPHER_AES_256_ECB};
|
|
std::array<unsigned char, 32> key;
|
|
std::array<unsigned char, 257> message;
|
|
for (std::size_t index = 0; index < key.size(); ++index) key[index] = static_cast<unsigned char>(index * 73 + 41);
|
|
for (std::size_t index = 0; index < message.size(); ++index) message[index] = static_cast<unsigned char>(index * 197 + 17);
|
|
auto context = NewContext();
|
|
for (std::size_t method = 0; method < 3; ++method) {
|
|
const auto* cipher = Methods[method];
|
|
Require(cipher->nid == 419 + static_cast<int>(method) * 4 && cipher->blockSize == 16 && cipher->keyLength == 16 + static_cast<int>(method) * 8 && cipher->ivLength == 16 && cipher->flags == 0x5002, "AES descriptor metadata");
|
|
const auto* reference = mbedtls_cipher_info_from_type(types[method]);
|
|
Require(reference != nullptr, "reference cipher available");
|
|
for (std::size_t length = 0; length <= message.size(); ++length) {
|
|
std::array<unsigned char, 16> expected;
|
|
Require(mbedtls_cipher_cmac(reference, key.data(), cipher->keyLength * 8, message.data(), length, expected.data()) == 0, "reference CMAC");
|
|
for (const auto chunk : {1u, 7u, 15u, 16u, 17u, 64u, 257u}) {
|
|
Require(CMAC_Init_nid_postfix(context.get(), key.data(), cipher->keyLength, cipher, nullptr) == 1, "differential initialization");
|
|
for (std::size_t offset = 0; offset < length;) {
|
|
const auto take = std::min(static_cast<std::size_t>(chunk), length - offset);
|
|
Require(CMAC_Update_nid_postfix(context.get(), message.data() + offset, take) == 1, "differential update");
|
|
offset += take;
|
|
}
|
|
Require(Final(context.get()) == expected, "CMAC agrees with independent backend");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Lifecycle() {
|
|
auto context = NewContext();
|
|
const std::array<unsigned char, 32> key{};
|
|
const std::array<unsigned char, 19> message{};
|
|
std::array<unsigned char, 16> output;
|
|
output.fill(0xa5);
|
|
std::size_t length = 99;
|
|
Require(CMAC_Update_nid_postfix(context.get(), nullptr, 1) == 0, "uninitialized update");
|
|
Require(CMAC_Final_nid_postfix(context.get(), output.data(), &length) == 0 && length == 99 && output[0] == 0xa5, "uninitialized final preserves outputs");
|
|
Require(CMAC_Final_nid_postfix(context.get(), nullptr, nullptr) == 0, "uninitialized final precedes null output length");
|
|
Require(CMAC_Init_nid_postfix(context.get(), nullptr, 0, nullptr, nullptr) == 0, "uninitialized restart");
|
|
Require(CMAC_Init_nid_postfix(context.get(), key.data(), 16, nullptr, nullptr) == 0, "key without cipher");
|
|
Require(CMAC_Init_nid_postfix(context.get(), nullptr, 1, nullptr, nullptr) == 1, "nonzero key length without a key is not restart");
|
|
Require(CMAC_Init_nid_postfix(context.get(), nullptr, 0, Methods[0], nullptr) == 1, "stage cipher selection");
|
|
Require(CMAC_Update_nid_postfix(context.get(), message.data(), 1) == 0, "cipher-only context has no key");
|
|
ERR_clear_error_nid_postfix();
|
|
Require(CMAC_Init_nid_postfix(context.get(), key.data(), 15, nullptr, nullptr) == 0, "reject invalid key length");
|
|
Require(ERR_get_error_nid_postfix() == 0x06fff082, "invalid key length publishes EVP error");
|
|
Require(CMAC_Init_nid_postfix(context.get(), key.data(), 16, nullptr, nullptr) == 1, "finish staged initialization");
|
|
Require(CMAC_Update_nid_postfix(context.get(), message.data(), message.size()) == 1, "first message");
|
|
const auto expected = Final(context.get());
|
|
const auto repeated = Final(context.get());
|
|
Require(repeated != expected, "final advances the CBC state");
|
|
Require(CMAC_Init_nid_postfix(context.get(), nullptr, 0, nullptr, nullptr) == 1, "restart reuses the key");
|
|
Require(CMAC_Update_nid_postfix(context.get(), message.data(), message.size()) == 1 && Final(context.get()) == expected, "restart resets state");
|
|
Require(CMAC_Init_nid_postfix(context.get(), key.data(), (std::size_t{1} << 32) + 16, nullptr, nullptr) == 1, "upstream key length narrows to int");
|
|
Require(CMAC_Update_nid_postfix(context.get(), message.data(), message.size()) == 1 && Final(context.get()) == expected, "narrowed key length tag");
|
|
Require(CMAC_Init_nid_postfix(context.get(), key.data(), 24, Methods[1], nullptr) == 1, "switch AES key size");
|
|
Require(CMAC_Init_nid_postfix(context.get(), key.data(), 32, Methods[2], nullptr) == 1, "switch AES key size again");
|
|
RequireThrows([&] { CMAC_Init_nid_postfix(nullptr, key.data(), 16, Methods[0], nullptr); });
|
|
RequireThrows([&] { CMAC_Update_nid_postfix(nullptr, nullptr, 0); });
|
|
RequireThrows([&] { CMAC_Final_nid_postfix(nullptr, output.data(), &length); });
|
|
RequireThrows([&] { CMAC_Update_nid_postfix(context.get(), nullptr, 1); });
|
|
RequireThrows([&] { CMAC_Final_nid_postfix(context.get(), output.data(), nullptr); });
|
|
CMAC_CTX_free_nid_postfix(nullptr);
|
|
}
|
|
|
|
void AllocationFailures() {
|
|
allocationsUntilFailure = 0;
|
|
Require(CMAC_CTX_new_nid_postfix() == nullptr, "context allocation failure");
|
|
allocationsUntilFailure = 1;
|
|
auto context = NewContext();
|
|
const std::array<unsigned char, 16> key{};
|
|
ERR_clear_error_nid_postfix();
|
|
Require(CMAC_Init_nid_postfix(context.get(), key.data(), key.size(), Methods[0], nullptr) == 0, "cipher allocation failure");
|
|
Require(ERR_get_error_nid_postfix() == 0x06fff041, "cipher allocation failure publishes EVP error");
|
|
allocationsUntilFailure = -1;
|
|
Require(CMAC_Init_nid_postfix(context.get(), key.data(), key.size(), Methods[0], nullptr) == 1, "recover from failed allocation");
|
|
}
|
|
|
|
void CipherCallbacks() {
|
|
std::array<unsigned char, 32> key{};
|
|
std::array<unsigned char, 16> iv;
|
|
for (std::size_t index = 0; index < iv.size(); ++index) iv[index] = static_cast<unsigned char>(index + 1);
|
|
for (const auto* method : Methods) {
|
|
for (std::size_t length = 0; length <= 48; ++length) {
|
|
const auto padded = (length + 15) / 16 * 16;
|
|
std::vector<unsigned char> input(padded);
|
|
for (std::size_t index = 0; index < length; ++index) input[index] = static_cast<unsigned char>(index * 31 + 17);
|
|
std::vector<unsigned char> expected(padded);
|
|
auto referenceIv = iv;
|
|
mbedtls_aes_context reference;
|
|
mbedtls_aes_init(&reference);
|
|
Require(mbedtls_aes_setkey_enc(&reference, key.data(), method->keyLength * 8) == 0, "reference AES key");
|
|
Require(mbedtls_aes_crypt_cbc(&reference, MBEDTLS_AES_ENCRYPT, padded, referenceIv.data(), input.data(), expected.data()) == 0, "reference CBC");
|
|
mbedtls_aes_free(&reference);
|
|
std::vector<unsigned char> storage(method->contextSize);
|
|
LibreSslCipherContext context{};
|
|
context.cipher = method;
|
|
context.cipherData = storage.data();
|
|
context.keyLength = method->keyLength;
|
|
for (const bool inPlace : {false, true}) {
|
|
context.encrypt = 1;
|
|
std::copy(iv.begin(), iv.end(), context.iv);
|
|
Require(method->init(&context, key.data(), nullptr, 1) == 1, "AES descriptor encryption setup");
|
|
auto output = input;
|
|
output.push_back(0xa5);
|
|
Require(method->cipher(&context, output.data(), inPlace ? output.data() : input.data(), length) == 1, "AES descriptor partial encryption");
|
|
Require(output.back() == 0xa5 && std::equal(expected.begin(), expected.end(), output.begin()), "CBC partial encryption and bounds");
|
|
Require(std::equal(referenceIv.begin(), referenceIv.end(), context.iv), "CBC encryption IV");
|
|
context.encrypt = 0;
|
|
std::copy(iv.begin(), iv.end(), context.iv);
|
|
Require(method->init(&context, key.data(), nullptr, 0) == 1, "AES descriptor decryption setup");
|
|
if (!inPlace) output.assign(padded + 1, 0xa5);
|
|
const auto before = output;
|
|
Require(method->cipher(&context, output.data(), inPlace ? output.data() : expected.data(), length) == 1, "AES descriptor partial decryption");
|
|
Require(std::equal(input.begin(), input.begin() + length, output.begin()), "CBC decrypted prefix");
|
|
Require(std::equal(before.begin() + length, before.end(), output.begin() + length), "partial decryption preserves trailing bytes");
|
|
Require(std::equal(referenceIv.begin(), referenceIv.end(), context.iv), "CBC decryption IV");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int APS5_VABI DesInit(LibreSslCipherContext* context, const unsigned char* key, const unsigned char*, int) {
|
|
auto* des = static_cast<mbedtls_des3_context*>(context->cipherData);
|
|
mbedtls_des3_init(des);
|
|
return mbedtls_des3_set3key_enc(des, key) == 0;
|
|
}
|
|
|
|
int APS5_VABI DesCipher(LibreSslCipherContext* context, unsigned char* output, const unsigned char* input, std::size_t length) {
|
|
return mbedtls_des3_crypt_cbc(static_cast<mbedtls_des3_context*>(context->cipherData), MBEDTLS_DES_ENCRYPT, length, context->iv, input, output) == 0;
|
|
}
|
|
|
|
void GuestDes() {
|
|
const LibreSslCipher method{44, 8, 24, 8, 2, DesInit, DesCipher, nullptr, sizeof(mbedtls_des3_context), nullptr, nullptr, nullptr, nullptr};
|
|
std::array<unsigned char, 24> key;
|
|
std::array<unsigned char, 33> message;
|
|
for (std::size_t index = 0; index < key.size(); ++index) key[index] = static_cast<unsigned char>(index * 31 + 7);
|
|
for (std::size_t index = 0; index < message.size(); ++index) message[index] = static_cast<unsigned char>(index * 71 + 3);
|
|
const auto* reference = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_DES_EDE3_ECB);
|
|
Require(reference != nullptr, "reference 64-bit cipher");
|
|
auto context = NewContext();
|
|
for (std::size_t length = 0; length <= message.size(); ++length) {
|
|
std::array<unsigned char, 8> expected;
|
|
Require(mbedtls_cipher_cmac(reference, key.data(), 192, message.data(), length, expected.data()) == 0, "reference 64-bit CMAC");
|
|
Require(CMAC_Init_nid_postfix(context.get(), key.data(), key.size(), &method, nullptr) == 1, "guest 64-bit cipher initialization");
|
|
for (std::size_t index = 0; index < length; ++index) Require(CMAC_Update_nid_postfix(context.get(), message.data() + index, 1) == 1, "guest 64-bit cipher update");
|
|
std::array<unsigned char, 10> actual;
|
|
actual.fill(0xa5);
|
|
std::size_t outputLength = 99;
|
|
Require(CMAC_Final_nid_postfix(context.get(), actual.data() + 1, &outputLength) == 1, "guest 64-bit cipher final");
|
|
Require(outputLength == 8 && actual.front() == 0xa5 && actual.back() == 0xa5 && std::equal(expected.begin(), expected.end(), actual.begin() + 1), "64-bit CMAC tag and output bounds");
|
|
}
|
|
}
|
|
|
|
const LibreSslCipher* delegated;
|
|
int initCalls;
|
|
int cipherCalls;
|
|
int cleanupCalls;
|
|
int failAt;
|
|
|
|
int APS5_VABI GuestInit(LibreSslCipherContext* context, const unsigned char* key, const unsigned char* iv, int encrypt) {
|
|
++initCalls;
|
|
Require(context->encrypt == 1 && context->keyLength == 16 && context->cipherData != nullptr, "guest callback context");
|
|
return delegated->init(context, key, iv, encrypt);
|
|
}
|
|
|
|
int APS5_VABI GuestCipher(LibreSslCipherContext* context, unsigned char* output, const unsigned char* input, std::size_t length) {
|
|
++cipherCalls;
|
|
if (failAt == cipherCalls) {
|
|
std::memset(output, 0x5a, length);
|
|
return 0;
|
|
}
|
|
return delegated->cipher(context, output, input, length);
|
|
}
|
|
|
|
int APS5_VABI GuestCleanup(LibreSslCipherContext*) {
|
|
++cleanupCalls;
|
|
return 1;
|
|
}
|
|
|
|
int APS5_VABI GuestControl(LibreSslCipherContext* context, int command, int argument, void*) {
|
|
if (command == 0) return 1;
|
|
if (command == 1 && argument == 16) {
|
|
context->keyLength = argument;
|
|
return 1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void Callbacks() {
|
|
delegated = Methods[0];
|
|
auto method = *delegated;
|
|
method.init = GuestInit;
|
|
method.cipher = GuestCipher;
|
|
method.cleanup = GuestCleanup;
|
|
method.control = GuestControl;
|
|
method.flags |= 0xc0;
|
|
std::array<unsigned char, 16> key{};
|
|
std::array<unsigned char, 33> message{};
|
|
{
|
|
auto context = NewContext();
|
|
Require(CMAC_Init_nid_postfix(context.get(), key.data(), key.size(), &method, nullptr) == 1 && initCalls == 1, "guest initialization callback");
|
|
Require(CMAC_Update_nid_postfix(context.get(), message.data(), message.size()) == 1 && cipherCalls == 3, "guest cipher callbacks");
|
|
const auto tag = Final(context.get());
|
|
Require(CMAC_Init_nid_postfix(context.get(), nullptr, 0, nullptr, nullptr) == 1, "guest callback restart");
|
|
Require(CMAC_Update_nid_postfix(context.get(), message.data(), message.size()) == 1 && Final(context.get()) == tag, "guest-defined cipher matches builtin");
|
|
failAt = cipherCalls + 1;
|
|
std::array<unsigned char, 16> output;
|
|
output.fill(0xa5);
|
|
std::size_t length = 99;
|
|
Require(CMAC_Final_nid_postfix(context.get(), output.data(), &length) == 0 && length == 16, "cipher failure propagates");
|
|
Require(std::all_of(output.begin(), output.end(), [](auto value) { return value == 0; }), "failed final clears output");
|
|
failAt = 0;
|
|
}
|
|
Require(cleanupCalls == 1, "guest cleanup callback");
|
|
auto context = NewContext();
|
|
method.blockSize = 32;
|
|
ERR_clear_error_nid_postfix();
|
|
Require(CMAC_Init_nid_postfix(context.get(), key.data(), key.size(), &method, nullptr) == 0, "reject unsupported block size");
|
|
Require(ERR_get_error_nid_postfix() == 0x06fff088, "bad block length publishes EVP error");
|
|
}
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
#ifdef ANYPS5_CMAC_DYNAMIC
|
|
Require(argc == 2, "patched test needs the library directory");
|
|
InitializePatched(argv[1]);
|
|
#endif
|
|
InitializeHeap();
|
|
Methods[0] = EVP_aes_128_cbc_nid_postfix();
|
|
Methods[1] = EVP_aes_192_cbc_nid_postfix();
|
|
Methods[2] = EVP_aes_256_cbc_nid_postfix();
|
|
RfcVectors();
|
|
Differential();
|
|
Lifecycle();
|
|
Callbacks();
|
|
AllocationFailures();
|
|
CipherCallbacks();
|
|
GuestDes();
|
|
Require(allocations.empty(), "all guest allocations released");
|
|
std::puts("CMAC vectors, streaming, lifecycle and callback checks passed");
|
|
}
|