mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-08 13:50:20 +00:00
822 lines
40 KiB
C++
822 lines
40 KiB
C++
#include "prx/libSceLibreSsl/include/Api.hpp"
|
|
#include "prx/libc/include/Api.hpp"
|
|
#include "prx/libc/include/ApplicationHeap.hpp"
|
|
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <source_location>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
struct TestBignum {
|
|
std::uint64_t* d;
|
|
int top;
|
|
int dmax;
|
|
int neg;
|
|
int flags;
|
|
};
|
|
|
|
struct TestRsaMethod {
|
|
const char* name;
|
|
void* operations[8];
|
|
int flags;
|
|
char* appData;
|
|
void* sign;
|
|
void* verify;
|
|
void* keygen;
|
|
};
|
|
|
|
struct TestRsa {
|
|
int pad;
|
|
std::int64_t version;
|
|
const TestRsaMethod* meth;
|
|
void* engine;
|
|
void* n;
|
|
void* rest[15];
|
|
};
|
|
|
|
static_assert(offsetof(TestRsaMethod, flags) == 72);
|
|
static_assert(offsetof(TestRsa, meth) == 16 && offsetof(TestRsa, n) == 32 && sizeof(TestRsa) == 160);
|
|
|
|
static const LibreSslRsa* Rsa(const TestRsa* rsa) {
|
|
return reinterpret_cast<const LibreSslRsa*>(rsa);
|
|
}
|
|
|
|
static void Require(bool condition, std::source_location location = std::source_location::current()) {
|
|
if (condition) return;
|
|
std::fprintf(stderr, "LibreSSL RSA check failed at line %u\n", location.line());
|
|
std::abort();
|
|
}
|
|
|
|
template<typename TAction> static void RequireThrows(TAction action, std::source_location location = std::source_location::current()) {
|
|
try { action(); } catch (const std::exception&) { return; }
|
|
Require(false, location);
|
|
}
|
|
|
|
static void TestFlags() {
|
|
TestRsaMethod method {};
|
|
TestRsa rsa {};
|
|
rsa.meth = &method;
|
|
Require(RSA_flags_nid_postfix(nullptr) == 0);
|
|
Require(RSA_flags_nid_postfix(Rsa(&rsa)) == 0);
|
|
method.flags = 0x0400;
|
|
Require(RSA_flags_nid_postfix(Rsa(&rsa)) == 0x0400);
|
|
method.flags = -1;
|
|
Require(RSA_flags_nid_postfix(Rsa(&rsa)) == -1);
|
|
rsa.meth = nullptr;
|
|
RequireThrows([&] { RSA_flags_nid_postfix(Rsa(&rsa)); });
|
|
}
|
|
|
|
static int Size(TestBignum* modulus) {
|
|
TestRsa rsa {};
|
|
rsa.n = modulus;
|
|
return RSA_size_nid_postfix(Rsa(&rsa));
|
|
}
|
|
|
|
static void TestSize() {
|
|
std::uint64_t words[32] {};
|
|
TestBignum modulus {words, 0, 32, 0, 0};
|
|
Require(Size(&modulus) == 0);
|
|
modulus.d = nullptr;
|
|
Require(Size(&modulus) == 0);
|
|
modulus.d = words;
|
|
modulus.top = 1;
|
|
words[0] = 1;
|
|
Require(Size(&modulus) == 1);
|
|
words[0] = 0xFF;
|
|
Require(Size(&modulus) == 1);
|
|
words[0] = 0x100;
|
|
Require(Size(&modulus) == 2);
|
|
words[0] = 0x8000000000000000ull;
|
|
Require(Size(&modulus) == 8);
|
|
modulus.top = 2;
|
|
words[1] = 1;
|
|
Require(Size(&modulus) == 9);
|
|
modulus.top = 32;
|
|
words[31] = 0xC000000000000000ull;
|
|
Require(Size(&modulus) == 256);
|
|
modulus.neg = 1;
|
|
Require(Size(&modulus) == 256);
|
|
modulus.neg = 0;
|
|
modulus.top = 16;
|
|
words[15] = 0x00FFFFFFFFFFFFFFull;
|
|
Require(Size(&modulus) == 127);
|
|
words[15] = 0;
|
|
Require(Size(&modulus) == 120);
|
|
modulus.top = -1;
|
|
RequireThrows([&] { Size(&modulus); });
|
|
modulus.top = 1;
|
|
modulus.d = nullptr;
|
|
RequireThrows([&] { Size(&modulus); });
|
|
RequireThrows([] { Size(nullptr); });
|
|
RequireThrows([] { RSA_size_nid_postfix(nullptr); });
|
|
}
|
|
|
|
constexpr int BignumStaticData = 0x02;
|
|
constexpr int BignumMalloced = 0x01;
|
|
|
|
struct Finishes {
|
|
int calls;
|
|
LibreSslRsa* rsa;
|
|
};
|
|
|
|
static Finishes finishes;
|
|
|
|
static int APS5_VABI RecordFinish(LibreSslRsa* rsa) {
|
|
++finishes.calls;
|
|
finishes.rsa = rsa;
|
|
return 1;
|
|
}
|
|
|
|
static void InitializeHeap() {
|
|
void* const api[10] {};
|
|
ApplicationHeapRegister_nid_no_patch(api);
|
|
}
|
|
|
|
static bool Zero(const void* data, std::size_t size) {
|
|
const auto* bytes = static_cast<const unsigned char*>(data);
|
|
for (std::size_t index = 0; index < size; ++index)
|
|
if (bytes[index]) return false;
|
|
return true;
|
|
}
|
|
|
|
static TestBignum* AllocatedBignum(std::uint64_t value) {
|
|
auto* number = static_cast<TestBignum*>(malloc_nid_postfix(sizeof(TestBignum)));
|
|
auto* words = static_cast<std::uint64_t*>(malloc_nid_postfix(2 * sizeof(std::uint64_t)));
|
|
Require(number != nullptr && words != nullptr);
|
|
words[0] = value;
|
|
words[1] = 0;
|
|
*number = {words, 1, 2, 0, BignumMalloced};
|
|
return number;
|
|
}
|
|
|
|
static void TestNew() {
|
|
auto* rsa = RSA_new_nid_postfix();
|
|
Require(rsa != nullptr && rsa->meth != nullptr);
|
|
Require(rsa->pad == 0 && rsa->version == 0 && rsa->engine == nullptr && rsa->references == 1 && rsa->flags == 0x0006);
|
|
for (void* field : {rsa->n, rsa->e, rsa->d, rsa->p, rsa->q, rsa->dmp1, rsa->dmq1, rsa->iqmp, rsa->exData}) Require(field == nullptr);
|
|
for (void* field : {rsa->methodModN, rsa->methodModP, rsa->methodModQ, rsa->blinding, rsa->mtBlinding}) Require(field == nullptr);
|
|
const auto* method = rsa->meth;
|
|
Require(std::string(method->name) == "Eric Young's PKCS#1 RSA" && method->flags == 0 && method->appData == nullptr);
|
|
for (void* operation : method->operations) Require(operation != nullptr);
|
|
Require(method->sign == nullptr && method->verify == nullptr && method->keygen == nullptr);
|
|
Require(RSA_flags_nid_postfix(rsa) == 0);
|
|
auto* second = RSA_new_nid_postfix();
|
|
Require(second != nullptr && second != rsa && second->meth == method);
|
|
RSA_free_nid_postfix(second);
|
|
RSA_free_nid_postfix(rsa);
|
|
}
|
|
|
|
static void TestFree() {
|
|
RSA_free_nid_postfix(nullptr);
|
|
auto* rsa = RSA_new_nid_postfix();
|
|
std::uint64_t words[4] {0x1234, 0x5678, 0, 0};
|
|
TestBignum stored {words, 2, 4, 0, BignumStaticData};
|
|
rsa->n = &stored;
|
|
rsa->e = AllocatedBignum(65537);
|
|
rsa->d = AllocatedBignum(3);
|
|
rsa->references = 2;
|
|
RSA_free_nid_postfix(rsa);
|
|
Require(rsa->references == 1 && rsa->n == &stored && stored.top == 2);
|
|
RSA_free_nid_postfix(rsa);
|
|
Require(Zero(&stored, sizeof(stored)) && words[0] == 0x1234 && words[1] == 0x5678);
|
|
|
|
rsa = RSA_new_nid_postfix();
|
|
LibreSslRsaMethod method = *rsa->meth;
|
|
method.operations[7] = reinterpret_cast<void*>(RecordFinish);
|
|
rsa->meth = &method;
|
|
finishes = {};
|
|
RSA_free_nid_postfix(rsa);
|
|
Require(finishes.calls == 1 && finishes.rsa == rsa);
|
|
method.operations[7] = nullptr;
|
|
rsa = RSA_new_nid_postfix();
|
|
rsa->meth = &method;
|
|
RSA_free_nid_postfix(rsa);
|
|
Require(finishes.calls == 1);
|
|
|
|
rsa = RSA_new_nid_postfix();
|
|
rsa->meth = nullptr;
|
|
RequireThrows([&] { RSA_free_nid_postfix(rsa); });
|
|
}
|
|
|
|
using Bytes = std::vector<unsigned char>;
|
|
|
|
constexpr const char* CavpModulus =
|
|
"cea80475324c1dc8347827818da58bac069d3419c614a6ea1ac6a3b510dcd72cc516954905e9fef908d45e13006adf27"
|
|
"d467a7d83c111d1a5df15ef293771aefb920032a5bb989f8e4f5e1b05093d3f130f984c07a772a3683f4dc6fb28a9681"
|
|
"5b32123ccdd13954f19d5b8b24a103e771a34c328755c65ed64e1924ffd04d30b2142cc262f6e0048fef6dbc652f2147"
|
|
"9ea1c4b1d66d28f4d46ef7185e390cbfa2e02380582f3188bb94ebbf05d31487a09aff01fcbb4cd4bfd1f0a833b38c11"
|
|
"813c84360bb53c7d4481031c40bad8713bb6b835cb08098ed15ba31ee4ba728a8c8e10f7294e1b4163b7aee57277bfd8"
|
|
"81a6f9d43e02c6925aa3a043fb7fb78d";
|
|
constexpr const char* CavpSpki =
|
|
"30820122300d06092a864886f70d01010105000382010f003082010a0282010100cea80475324c1dc8347827818da58b"
|
|
"ac069d3419c614a6ea1ac6a3b510dcd72cc516954905e9fef908d45e13006adf27d467a7d83c111d1a5df15ef293771a"
|
|
"efb920032a5bb989f8e4f5e1b05093d3f130f984c07a772a3683f4dc6fb28a96815b32123ccdd13954f19d5b8b24a103"
|
|
"e771a34c328755c65ed64e1924ffd04d30b2142cc262f6e0048fef6dbc652f21479ea1c4b1d66d28f4d46ef7185e390c"
|
|
"bfa2e02380582f3188bb94ebbf05d31487a09aff01fcbb4cd4bfd1f0a833b38c11813c84360bb53c7d4481031c40bad8"
|
|
"713bb6b835cb08098ed15ba31ee4ba728a8c8e10f7294e1b4163b7aee57277bfd881a6f9d43e02c6925aa3a043fb7fb7"
|
|
"8d0203260445";
|
|
|
|
constexpr const char* CavpPrivateExponent =
|
|
"0997634c477c1a039d44c810b2aaa3c7862b0b88d3708272e1e15f66fc9389709f8a11f3ea6a5af7effa2d01c189c50f"
|
|
"0d5bcbe3fa272e56cfc4a4e1d388a9dcd65df8628902556c8b6bb6a641709b5a35dd2622c73d4640bfa1359d0e76e1f2"
|
|
"19f8e33eb9bd0b59ec198eb2fccaae0346bd8b401e12e3c67cb629569c185a2e0f35a2f741644c1cca5ebb139d77a89a"
|
|
"2953fc5e30048c0e619f07c8d21d1e56b8af07193d0fdf3f49cd49f2ef3138b5138862f1470bd2d16e34a2b9e7777a6c"
|
|
"8c8d4cb94b4e8b5d616cd5393753e7b0f31cc7da559ba8e98d888914e334773baf498ad88d9631eb5fe32e53a4145bf0"
|
|
"ba548bf2b0a50c63f67b14e398a34b0d";
|
|
constexpr const char* CavpSignature =
|
|
"6b8be97d9e518a2ede746ff4a7d91a84a1fc665b52f154a927650db6e7348c69f8c8881f7bcf9b1a6d3366eed30c3aed"
|
|
"4e93c203c43f5528a45de791895747ade9c5fa5eee81427edee02082147aa311712a6ad5fb1732e93b3d6cd23ffd46a0"
|
|
"b3caf62a8b69957cc68ae39f9993c1a779599cdda949bdaababb77f248fcfeaa44059be5459fb9b899278e929528ee13"
|
|
"0facd53372ecbc42f3e8de2998425860406440f248d817432de687112e504d734028e6c5620fa282ca07647006cf0a2f"
|
|
"f83e19a916554cc61810c2e855305db4e5cf893a6a96767365794556ff033359084d7e38a8456e68e21155b76151314a"
|
|
"29875feee09557161cbc654541e89e42";
|
|
constexpr const char* CavpDigest = "077877895a428028f60998b985550820025a2a42c0beab27165c0802d3098150";
|
|
|
|
static Bytes Hex(const char* text) {
|
|
const auto nibble = [](char digit) { return static_cast<unsigned char>(digit <= '9' ? digit - '0' : digit - 'a' + 10); };
|
|
Bytes bytes;
|
|
for (; text[0] && text[1]; text += 2) bytes.push_back(static_cast<unsigned char>(nibble(text[0]) << 4 | nibble(text[1])));
|
|
return bytes;
|
|
}
|
|
|
|
static Bytes Concat(std::initializer_list<Bytes> parts) {
|
|
Bytes bytes;
|
|
for (const auto& part : parts) bytes.insert(bytes.end(), part.begin(), part.end());
|
|
return bytes;
|
|
}
|
|
|
|
static Bytes Encode(unsigned char tag, const Bytes& content) {
|
|
Bytes bytes {tag};
|
|
if (content.size() < 0x80) {
|
|
bytes.push_back(static_cast<unsigned char>(content.size()));
|
|
} else if (content.size() < 0x100) {
|
|
bytes.insert(bytes.end(), {0x81, static_cast<unsigned char>(content.size())});
|
|
} else {
|
|
bytes.insert(bytes.end(), {0x82, static_cast<unsigned char>(content.size() >> 8), static_cast<unsigned char>(content.size())});
|
|
}
|
|
return Concat({bytes, content});
|
|
}
|
|
|
|
static const Bytes RsaEncryption {0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01};
|
|
static const Bytes Null {0x05, 0x00};
|
|
static const Bytes CavpExponent {0x26, 0x04, 0x45};
|
|
|
|
static Bytes RsaKey(const Bytes& modulus, const Bytes& exponent) {
|
|
return Encode(0x30, Concat({Encode(0x02, modulus), Encode(0x02, exponent)}));
|
|
}
|
|
|
|
static Bytes PublicKeyInfo(const Bytes& algorithm, const Bytes& bitString) {
|
|
return Encode(0x30, Concat({Encode(0x30, algorithm), Encode(0x03, bitString)}));
|
|
}
|
|
|
|
static Bytes StandardKey() {
|
|
return RsaKey(Concat({{0x00}, Hex(CavpModulus)}), CavpExponent);
|
|
}
|
|
|
|
static LibreSslRsa* Decode(const Bytes& der, long length, long* consumed = nullptr) {
|
|
const unsigned char* data = der.data();
|
|
auto* rsa = d2i_RSA_PUBKEY_nid_postfix(nullptr, &data, length);
|
|
if (consumed) *consumed = data - der.data();
|
|
return rsa;
|
|
}
|
|
|
|
static const TestBignum& Number(const LibreSslRsa* rsa, bool modulus) {
|
|
return *static_cast<const TestBignum*>(modulus ? rsa->n : rsa->e);
|
|
}
|
|
|
|
static void RequireAccepted(const Bytes& der, std::uint64_t exponent, std::source_location location = std::source_location::current()) {
|
|
long consumed = 0;
|
|
auto* rsa = Decode(der, static_cast<long>(der.size()), &consumed);
|
|
Require(rsa != nullptr && consumed == static_cast<long>(der.size()), location);
|
|
Require(Number(rsa, false).top == (exponent ? 1 : 0) && (!exponent || Number(rsa, false).d[0] == exponent), location);
|
|
RSA_free_nid_postfix(rsa);
|
|
}
|
|
|
|
static void RequireRefused(const Bytes& der, std::source_location location = std::source_location::current()) {
|
|
long consumed = -1;
|
|
Require(Decode(der, static_cast<long>(der.size()), &consumed) == nullptr && consumed == 0, location);
|
|
}
|
|
|
|
static void TestPublicKeyInfo() {
|
|
const auto published = Hex(CavpSpki);
|
|
const auto rsaKey = StandardKey();
|
|
Require(PublicKeyInfo(Concat({RsaEncryption, Null}), Concat({{0x00}, rsaKey})) == published);
|
|
long consumed = 0;
|
|
auto* rsa = Decode(published, static_cast<long>(published.size()), &consumed);
|
|
Require(rsa != nullptr && consumed == 294 && RSA_size_nid_postfix(rsa) == 256);
|
|
auto* fresh = RSA_new_nid_postfix();
|
|
Require(rsa->meth == fresh->meth && rsa->flags == 0x0006 && rsa->references == 1 && rsa->engine == nullptr);
|
|
RSA_free_nid_postfix(fresh);
|
|
const auto& modulus = Number(rsa, true);
|
|
const auto& exponent = Number(rsa, false);
|
|
Require(modulus.dmax == 33 && modulus.top == 32 && modulus.neg == 0 && modulus.flags == BignumMalloced);
|
|
Require(modulus.d[0] == 0x5aa3a043fb7fb78dULL && modulus.d[31] == 0xcea80475324c1dc8ULL);
|
|
Require(exponent.dmax == 1 && exponent.top == 1 && exponent.d[0] == 0x260445 && exponent.flags == BignumMalloced);
|
|
for (void* field : {rsa->d, rsa->p, rsa->q, rsa->dmp1, rsa->dmq1, rsa->iqmp}) Require(field == nullptr);
|
|
|
|
LibreSslRsa* previous = RSA_new_nid_postfix();
|
|
LibreSslRsa* target = previous;
|
|
const unsigned char* data = published.data();
|
|
auto* replaced = d2i_RSA_PUBKEY_nid_postfix(&target, &data, 294);
|
|
Require(replaced != nullptr && target == replaced && data == published.data() + 294);
|
|
RSA_free_nid_postfix(replaced);
|
|
RSA_free_nid_postfix(rsa);
|
|
|
|
auto padded = Concat({published, Null});
|
|
Require((rsa = Decode(padded, static_cast<long>(padded.size()), &consumed)) != nullptr && consumed == 294);
|
|
RSA_free_nid_postfix(rsa);
|
|
Require(Decode(published, 293, &consumed) == nullptr && consumed == 0);
|
|
RequireRefused(Bytes(published.begin(), published.end() - 1));
|
|
Require(Decode(published, 0) == nullptr && Decode(published, -1) == nullptr);
|
|
RequireThrows([&] { d2i_RSA_PUBKEY_nid_postfix(nullptr, nullptr, 294); });
|
|
RequireThrows([&] {
|
|
const unsigned char* none = nullptr;
|
|
d2i_RSA_PUBKEY_nid_postfix(nullptr, &none, 294);
|
|
});
|
|
}
|
|
|
|
static void TestAddedPublicKeyObjects() {
|
|
const auto bits = Concat({{0x00}, StandardKey()});
|
|
const struct { const char* oid; const char* encoding; } cases[] {
|
|
{"1.2.840.113549.1.1.1", "2a864886f70d010101"},
|
|
{"2.5.8.1.1", "55080101"},
|
|
};
|
|
bool refused[2] {};
|
|
for (std::size_t index = 0; index < std::size(cases); ++index) {
|
|
OBJ_cleanup_nid_postfix();
|
|
ERR_clear_error_nid_postfix();
|
|
Require(OBJ_create_nid_postfix(cases[index].oid, "testKey", "Test Key") >= 956);
|
|
const auto der = PublicKeyInfo(Concat({Encode(0x06, Hex(cases[index].encoding)), Null}), bits);
|
|
const unsigned char* input = der.data();
|
|
auto* output = RSA_new_nid_postfix();
|
|
const auto* original = output;
|
|
LibreSslRsa* decoded = nullptr;
|
|
try {
|
|
decoded = d2i_RSA_PUBKEY_nid_postfix(&output, &input, static_cast<long>(der.size()));
|
|
refused[index] = decoded == nullptr;
|
|
if (decoded) std::fprintf(stderr, "registered RSA OID %s: expected null or not implemented, got a key\n", cases[index].oid);
|
|
} catch (const std::runtime_error& error) {
|
|
refused[index] = std::strstr(error.what(), "d2i_RSA_PUBKEY") && std::strstr(error.what(), "not implemented");
|
|
}
|
|
refused[index] = refused[index] && input == der.data() && output == original;
|
|
if (decoded && decoded != output) RSA_free_nid_postfix(decoded);
|
|
RSA_free_nid_postfix(output);
|
|
OBJ_cleanup_nid_postfix();
|
|
ERR_clear_error_nid_postfix();
|
|
}
|
|
Require(refused[0]);
|
|
Require(refused[1]);
|
|
}
|
|
|
|
static void TestPublicKeyInfoVariants() {
|
|
const auto rsaKey = StandardKey();
|
|
const auto bits = Concat({{0x00}, rsaKey});
|
|
RequireAccepted(PublicKeyInfo(Concat({Encode(0x06, {0x55, 0x08, 0x01, 0x01}), Null}), bits), 0x260445);
|
|
RequireAccepted(PublicKeyInfo(RsaEncryption, bits), 0x260445);
|
|
for (const Bytes& parameter : {Bytes {0x04, 0x00}, Bytes {0x30, 0x00}, Bytes {0xA0, 0x00}, Bytes {0x02, 0x00}, Bytes {0x01, 0x01, 0xFF}, Bytes {0x05, 0x81, 0x00}})
|
|
RequireAccepted(PublicKeyInfo(Concat({RsaEncryption, parameter}), bits), 0x260445);
|
|
for (const Bytes& parameter : {Bytes {0x05, 0x01, 0x00}, Bytes {0x10, 0x00}, Bytes {0x01, 0x02, 0x00, 0x00}, Bytes {0x06, 0x01, 0x80}, Bytes {0x03, 0x00}, Bytes {0x00, 0x00}})
|
|
RequireRefused(PublicKeyInfo(Concat({RsaEncryption, parameter}), bits));
|
|
RequireRefused(PublicKeyInfo(Concat({RsaEncryption, Null, Null}), bits));
|
|
RequireRefused(PublicKeyInfo(Concat({Encode(0x06, {0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01}), Null}), bits));
|
|
RequireRefused(PublicKeyInfo(Concat({Encode(0x06, {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0A}), Null}), bits));
|
|
RequireRefused(PublicKeyInfo(Concat({Encode(0x06, {0x2A, 0x80, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01}), Null}), bits));
|
|
RequireRefused(Encode(0x30, Concat({Encode(0x30, Concat({RsaEncryption, Null})), Encode(0x03, bits), Null})));
|
|
RequireRefused(Encode(0x30, Concat({Encode(0x30, Concat({RsaEncryption, Null})), Bytes {0x00, 0x00}, Encode(0x03, bits)})));
|
|
RequireRefused(Encode(0x30, Encode(0x30, Concat({RsaEncryption, Null}))));
|
|
RequireRefused(Encode(0x10, Concat({Encode(0x30, Concat({RsaEncryption, Null})), Encode(0x03, bits)})));
|
|
RequireRefused(Encode(0x31, Concat({Encode(0x30, Concat({RsaEncryption, Null})), Encode(0x03, bits)})));
|
|
const auto algorithm = Concat({RsaEncryption, Null});
|
|
RequireAccepted(PublicKeyInfo(algorithm, Concat({{0x01}, rsaKey})), 0x260444);
|
|
RequireAccepted(PublicKeyInfo(algorithm, Concat({{0x08}, rsaKey})), 0x260400);
|
|
RequireAccepted(PublicKeyInfo(algorithm, Concat({{0x00}, rsaKey, Null})), 0x260445);
|
|
RequireAccepted(PublicKeyInfo(algorithm, Concat({{0x00}, RsaKey(Hex(CavpModulus), CavpExponent)})), 0x260445);
|
|
RequireAccepted(PublicKeyInfo(algorithm, Concat({{0x00}, RsaKey(Concat({{0x00}, Hex(CavpModulus)}), {})})), 0);
|
|
RequireAccepted(PublicKeyInfo(algorithm, Concat({{0x00}, RsaKey(Concat({{0x00}, Hex(CavpModulus)}), {0x00, 0x00, 0x01, 0x00, 0x01})})), 0x10001);
|
|
RequireRefused(PublicKeyInfo(algorithm, {}));
|
|
RequireRefused(PublicKeyInfo(algorithm, {0x00}));
|
|
RequireRefused(PublicKeyInfo(algorithm, Concat({{0x00}, Encode(0x30, Encode(0x02, Hex(CavpModulus)))})));
|
|
RequireRefused(PublicKeyInfo(algorithm, Concat({{0x00}, Encode(0x30, Concat({Encode(0x02, Hex(CavpModulus)), Encode(0x02, CavpExponent), Encode(0x02, CavpExponent)}))})));
|
|
RequireRefused(PublicKeyInfo(algorithm, Concat({{0x00}, Encode(0x31, Concat({Encode(0x02, Hex(CavpModulus)), Encode(0x02, CavpExponent)}))})));
|
|
auto spki = Hex(CavpSpki);
|
|
auto longForm = Concat({{0x30, 0x83, 0x00, 0x01, 0x22}, Bytes(spki.begin() + 4, spki.end())});
|
|
RequireAccepted(longForm, 0x260445);
|
|
auto highTag = Concat({{0x3F, 0x10}, Bytes(spki.begin() + 1, spki.end())});
|
|
RequireAccepted(highTag, 0x260445);
|
|
RequireRefused(Concat({{0x30, 0x89, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x22}, Bytes(spki.begin() + 4, spki.end())}));
|
|
}
|
|
|
|
constexpr int PaddingPkcs1 = 1;
|
|
constexpr int PaddingSslv23 = 2;
|
|
constexpr int PaddingNone = 3;
|
|
constexpr int PaddingOaep = 4;
|
|
constexpr int PaddingX931 = 5;
|
|
|
|
struct StaticNumber {
|
|
std::vector<std::uint64_t> words;
|
|
TestBignum number;
|
|
};
|
|
|
|
static void SetNumber(StaticNumber& storage, const Bytes& bigEndian) {
|
|
storage.words.assign(bigEndian.size() / 8 + 1, 0);
|
|
for (std::size_t position = 0; position < bigEndian.size(); ++position)
|
|
storage.words[position / 8] |= std::uint64_t(bigEndian[bigEndian.size() - 1 - position]) << (8 * (position % 8));
|
|
int top = static_cast<int>(storage.words.size());
|
|
while (top > 0 && !storage.words[static_cast<std::size_t>(top) - 1]) --top;
|
|
storage.number = {storage.words.data(), top, static_cast<int>(storage.words.size()), 0, BignumStaticData};
|
|
}
|
|
|
|
struct TestKey {
|
|
StaticNumber modulus;
|
|
StaticNumber exponent;
|
|
LibreSslRsa* rsa;
|
|
|
|
TestKey(const Bytes& n, const Bytes& e) : rsa(RSA_new_nid_postfix()) {
|
|
SetNumber(modulus, n);
|
|
SetNumber(exponent, e);
|
|
rsa->n = &modulus.number;
|
|
rsa->e = &exponent.number;
|
|
}
|
|
TestKey(const TestKey&) = delete;
|
|
~TestKey() { RSA_free_nid_postfix(rsa); }
|
|
};
|
|
|
|
static Bytes Pattern(std::size_t length, unsigned seed) {
|
|
Bytes bytes(length);
|
|
for (std::size_t index = 0; index < length; ++index) bytes[index] = static_cast<unsigned char>(seed + 29 * index);
|
|
return bytes;
|
|
}
|
|
|
|
static Bytes Sha1(const Bytes& data) {
|
|
Sha1Context context {};
|
|
unsigned char digest[20];
|
|
Require(SHA1_Init_nid_postfix(&context) == 1 && SHA1_Update_nid_postfix(&context, data.data(), data.size()) == 1);
|
|
Require(SHA1_Final_nid_postfix(digest, &context) == 1);
|
|
return Bytes(digest, digest + 20);
|
|
}
|
|
|
|
static Bytes Mgf1(const Bytes& seed, std::size_t length) {
|
|
Bytes mask;
|
|
for (std::uint32_t counter = 0; mask.size() < length; ++counter) {
|
|
auto input = seed;
|
|
for (int shift = 24; shift >= 0; shift -= 8) input.push_back(static_cast<unsigned char>(counter >> shift));
|
|
const auto digest = Sha1(input);
|
|
mask.insert(mask.end(), digest.begin(), digest.end());
|
|
}
|
|
mask.resize(length);
|
|
return mask;
|
|
}
|
|
|
|
static Bytes Recover(const Bytes& encrypted) {
|
|
TestKey privateKey(Hex(CavpModulus), Hex(CavpPrivateExponent));
|
|
Bytes block(256);
|
|
Require(RSA_public_decrypt_nid_postfix(256, encrypted.data(), block.data(), privateKey.rsa, PaddingNone) == 256);
|
|
return block;
|
|
}
|
|
|
|
static bool Pkcs1Block(const Bytes& block, const Bytes& message) {
|
|
if (block[0] != 0 || block[1] != 2) return false;
|
|
const std::size_t separator = block.size() - message.size() - 1;
|
|
for (std::size_t index = 2; index < separator; ++index)
|
|
if (!block[index]) return false;
|
|
return block[separator] == 0 && Bytes(block.begin() + static_cast<long>(separator) + 1, block.end()) == message;
|
|
}
|
|
|
|
static bool OaepBlock(const Bytes& block, const Bytes& message) {
|
|
if (block[0]) return false;
|
|
const Bytes maskedSeed(block.begin() + 1, block.begin() + 21);
|
|
const Bytes maskedData(block.begin() + 21, block.end());
|
|
auto seed = maskedSeed;
|
|
const auto seedMask = Mgf1(maskedData, 20);
|
|
for (std::size_t index = 0; index < 20; ++index) seed[index] ^= seedMask[index];
|
|
auto data = maskedData;
|
|
const auto dataMask = Mgf1(seed, data.size());
|
|
for (std::size_t index = 0; index < data.size(); ++index) data[index] ^= dataMask[index];
|
|
if (Bytes(data.begin(), data.begin() + 20) != Sha1({})) return false;
|
|
const std::size_t one = data.size() - message.size() - 1;
|
|
for (std::size_t index = 20; index < one; ++index)
|
|
if (data[index]) return false;
|
|
return data[one] == 1 && Bytes(data.begin() + static_cast<long>(one) + 1, data.end()) == message;
|
|
}
|
|
|
|
static void TestPublicDecrypt() {
|
|
const auto spki = Hex(CavpSpki);
|
|
auto* rsa = Decode(spki, static_cast<long>(spki.size()));
|
|
const auto signature = Hex(CavpSignature);
|
|
const auto digestInfo = Concat({Hex("3031300d060960864801650304020105000420"), Hex(CavpDigest)});
|
|
Bytes out(300, 0xEE);
|
|
Require(RSA_public_decrypt_nid_postfix(256, signature.data(), out.data(), rsa, PaddingPkcs1) == 51);
|
|
Require(Bytes(out.begin(), out.begin() + 51) == digestInfo && out[51] == 0xEE);
|
|
Require(RSA_public_decrypt_nid_postfix(256, signature.data(), out.data(), rsa, PaddingNone) == 256);
|
|
Require(out[0] == 0 && out[1] == 1 && out[2] == 0xFF && out[204] == 0 && Bytes(out.begin() + 205, out.begin() + 256) == digestInfo);
|
|
auto tampered = signature;
|
|
tampered[255] ^= 1;
|
|
Require(RSA_public_decrypt_nid_postfix(256, tampered.data(), out.data(), rsa, PaddingPkcs1) == -1);
|
|
Require(RSA_public_decrypt_nid_postfix(256, tampered.data(), out.data(), rsa, PaddingNone) == 256);
|
|
Require(RSA_public_decrypt_nid_postfix(257, Concat({signature, {0}}).data(), out.data(), rsa, PaddingPkcs1) == -1);
|
|
Require(RSA_public_decrypt_nid_postfix(-1, signature.data(), out.data(), rsa, PaddingPkcs1) == -1);
|
|
const Bytes large(256, 0xFF);
|
|
Require(RSA_public_decrypt_nid_postfix(256, large.data(), out.data(), rsa, PaddingNone) == -1);
|
|
Require(RSA_public_decrypt_nid_postfix(256, Hex(CavpModulus).data(), out.data(), rsa, PaddingNone) == -1);
|
|
std::memset(out.data(), 0xEE, out.size());
|
|
Require(RSA_public_decrypt_nid_postfix(0, nullptr, out.data(), rsa, PaddingNone) == 256);
|
|
Require(Zero(out.data(), 256) && out[256] == 0xEE);
|
|
Require(RSA_public_decrypt_nid_postfix(0, nullptr, out.data(), rsa, PaddingPkcs1) == -1);
|
|
for (int padding : {0, PaddingSslv23, PaddingOaep, 6, 7}) Require(RSA_public_decrypt_nid_postfix(256, signature.data(), out.data(), rsa, padding) == -1);
|
|
RequireThrows([&] { RSA_public_decrypt_nid_postfix(256, nullptr, out.data(), rsa, PaddingPkcs1); });
|
|
RequireThrows([&] { RSA_public_decrypt_nid_postfix(256, signature.data(), nullptr, rsa, PaddingPkcs1); });
|
|
const auto payload = Pattern(245, 5);
|
|
const auto sevenPadding = Recover(Concat({{0x00, 0x01}, Bytes(7, 0xFF), {0x00}, Pattern(246, 5)}));
|
|
const auto eightPadding = Recover(Concat({{0x00, 0x01}, Bytes(8, 0xFF), {0x00}, payload}));
|
|
Require(RSA_public_decrypt_nid_postfix(256, sevenPadding.data(), out.data(), rsa, PaddingPkcs1) == -1);
|
|
Require(RSA_public_decrypt_nid_postfix(256, eightPadding.data(), out.data(), rsa, PaddingPkcs1) == 245);
|
|
Require(Bytes(out.begin(), out.begin() + 245) == payload);
|
|
RSA_free_nid_postfix(rsa);
|
|
}
|
|
|
|
static void TestPublicEncrypt() {
|
|
const auto spki = Hex(CavpSpki);
|
|
auto* rsa = Decode(spki, static_cast<long>(spki.size()));
|
|
const auto message = Pattern(32, 5);
|
|
Bytes first(256);
|
|
Bytes second(256);
|
|
for (int padding : {PaddingPkcs1, PaddingOaep}) {
|
|
Require(RSA_public_encrypt_nid_postfix(32, message.data(), first.data(), rsa, padding) == 256);
|
|
Require(RSA_public_encrypt_nid_postfix(32, message.data(), second.data(), rsa, padding) == 256);
|
|
Require(first != second);
|
|
for (const auto* encrypted : {&first, &second}) {
|
|
const auto block = Recover(*encrypted);
|
|
Require(padding == PaddingPkcs1 ? Pkcs1Block(block, message) : OaepBlock(block, message));
|
|
}
|
|
}
|
|
for (auto [padding, longest] : {std::pair {PaddingPkcs1, 245}, std::pair {PaddingOaep, 214}}) {
|
|
const auto longMessage = Pattern(static_cast<std::size_t>(longest) + 1, 9);
|
|
Require(RSA_public_encrypt_nid_postfix(longest, longMessage.data(), first.data(), rsa, padding) == 256);
|
|
const Bytes accepted(longMessage.begin(), longMessage.begin() + longest);
|
|
Require(padding == PaddingPkcs1 ? Pkcs1Block(Recover(first), accepted) : OaepBlock(Recover(first), accepted));
|
|
Require(RSA_public_encrypt_nid_postfix(longest + 1, longMessage.data(), first.data(), rsa, padding) == -1);
|
|
Require(RSA_public_encrypt_nid_postfix(0, nullptr, first.data(), rsa, padding) == 256);
|
|
Require(padding == PaddingPkcs1 ? Pkcs1Block(Recover(first), {}) : OaepBlock(Recover(first), {}));
|
|
RequireThrows([&] { RSA_public_encrypt_nid_postfix(-1, message.data(), first.data(), rsa, padding); });
|
|
}
|
|
auto raw = Concat({{0x00}, Pattern(255, 3)});
|
|
Require(RSA_public_encrypt_nid_postfix(256, raw.data(), first.data(), rsa, PaddingNone) == 256 && Recover(first) == raw);
|
|
Require(RSA_public_encrypt_nid_postfix(255, raw.data(), first.data(), rsa, PaddingNone) == -1);
|
|
Require(RSA_public_encrypt_nid_postfix(257, Concat({raw, {0}}).data(), first.data(), rsa, PaddingNone) == -1);
|
|
Require(RSA_public_encrypt_nid_postfix(-1, raw.data(), first.data(), rsa, PaddingNone) == -1);
|
|
Require(RSA_public_encrypt_nid_postfix(256, Hex(CavpModulus).data(), first.data(), rsa, PaddingNone) == -1);
|
|
for (int padding : {0, PaddingX931, 6, 7}) Require(RSA_public_encrypt_nid_postfix(32, message.data(), first.data(), rsa, padding) == -1);
|
|
RequireThrows([&] { RSA_public_encrypt_nid_postfix(32, message.data(), nullptr, rsa, PaddingPkcs1); });
|
|
RequireThrows([&] { RSA_public_encrypt_nid_postfix(32, nullptr, first.data(), rsa, PaddingPkcs1); });
|
|
RSA_free_nid_postfix(rsa);
|
|
}
|
|
|
|
struct Crypts {
|
|
int calls;
|
|
int length;
|
|
const unsigned char* from;
|
|
unsigned char* to;
|
|
LibreSslRsa* rsa;
|
|
int padding;
|
|
};
|
|
|
|
static Crypts crypts;
|
|
|
|
static int APS5_VABI RecordCrypt(int length, const unsigned char* from, unsigned char* to, LibreSslRsa* rsa, int padding) {
|
|
crypts = {crypts.calls + 1, length, from, to, rsa, padding};
|
|
return 77;
|
|
}
|
|
|
|
static void TestPublicKeyChecks() {
|
|
const auto modulus = Hex(CavpModulus);
|
|
const auto message = Pattern(256, 1);
|
|
Bytes out(4096);
|
|
{
|
|
TestKey key(modulus, {});
|
|
Require(RSA_public_encrypt_nid_postfix(256, Concat({{0}, Pattern(255, 1)}).data(), out.data(), key.rsa, PaddingNone) == 256);
|
|
Require(Zero(out.data(), 255) && out[255] == 1);
|
|
}
|
|
{
|
|
TestKey key(modulus, modulus);
|
|
Require(RSA_public_encrypt_nid_postfix(32, message.data(), out.data(), key.rsa, PaddingPkcs1) == -1);
|
|
Require(RSA_public_decrypt_nid_postfix(256, message.data(), out.data(), key.rsa, PaddingNone) == -1);
|
|
}
|
|
{
|
|
auto even = modulus;
|
|
even.back() &= 0xFE;
|
|
TestKey key(even, CavpExponent);
|
|
Require(RSA_public_encrypt_nid_postfix(32, message.data(), out.data(), key.rsa, PaddingPkcs1) == -1);
|
|
Require(RSA_public_decrypt_nid_postfix(255, message.data(), out.data(), key.rsa, PaddingNone) == -1);
|
|
}
|
|
Bytes large(385, 0);
|
|
large[0] = 0x01;
|
|
large.back() = 0x03;
|
|
Bytes wideExponent(9, 0);
|
|
wideExponent[0] = 0x01;
|
|
wideExponent.back() = 0x01;
|
|
{
|
|
TestKey key(large, wideExponent);
|
|
Require(RSA_public_encrypt_nid_postfix(385, Bytes(385, 0).data(), out.data(), key.rsa, PaddingNone) == -1);
|
|
Require(RSA_public_decrypt_nid_postfix(1, message.data(), out.data(), key.rsa, PaddingNone) == -1);
|
|
}
|
|
{
|
|
TestKey key(large, Bytes(wideExponent.begin() + 1, wideExponent.end()));
|
|
Require(RSA_public_decrypt_nid_postfix(1, message.data(), out.data(), key.rsa, PaddingNone) == 385);
|
|
}
|
|
Bytes huge(2049, 0);
|
|
huge[0] = 0x01;
|
|
huge.back() = 0x01;
|
|
{
|
|
TestKey key(huge, {0x03});
|
|
Require(RSA_public_decrypt_nid_postfix(1, message.data(), out.data(), key.rsa, PaddingNone) == -1);
|
|
}
|
|
{
|
|
TestKey key(modulus, CavpExponent);
|
|
const auto* defaultMethod = key.rsa->meth;
|
|
LibreSslRsaMethod method = *defaultMethod;
|
|
method.operations[0] = reinterpret_cast<void*>(RecordCrypt);
|
|
method.operations[1] = reinterpret_cast<void*>(RecordCrypt);
|
|
key.rsa->meth = &method;
|
|
crypts = {};
|
|
Require(RSA_public_encrypt_nid_postfix(5, message.data(), out.data(), key.rsa, 9) == 77);
|
|
Require(crypts.calls == 1 && crypts.length == 5 && crypts.from == message.data() && crypts.to == out.data() && crypts.rsa == key.rsa && crypts.padding == 9);
|
|
Require(RSA_public_decrypt_nid_postfix(6, message.data(), out.data(), key.rsa, 8) == 77 && crypts.calls == 2 && crypts.length == 6);
|
|
method = *defaultMethod;
|
|
method.operations[0] = nullptr;
|
|
RequireThrows([&] { RSA_public_encrypt_nid_postfix(32, message.data(), out.data(), key.rsa, PaddingPkcs1); });
|
|
key.rsa->meth = nullptr;
|
|
RequireThrows([&] { RSA_public_decrypt_nid_postfix(32, message.data(), out.data(), key.rsa, PaddingPkcs1); });
|
|
key.rsa->meth = defaultMethod;
|
|
key.rsa->e = nullptr;
|
|
RequireThrows([&] { RSA_public_decrypt_nid_postfix(32, message.data(), out.data(), key.rsa, PaddingPkcs1); });
|
|
}
|
|
RequireThrows([&] { RSA_public_encrypt_nid_postfix(32, message.data(), out.data(), nullptr, PaddingPkcs1); });
|
|
}
|
|
|
|
constexpr int DigestSha1 = 64;
|
|
constexpr int DigestMd5Sha1 = 114;
|
|
constexpr int DigestSha256 = 672;
|
|
constexpr int RsaFlagSignVerify = 0x0040;
|
|
|
|
static Bytes Sign(const Bytes& encoded) {
|
|
return Recover(Concat({{0x00, 0x01}, Bytes(253 - encoded.size(), 0xFF), {0x00}, encoded}));
|
|
}
|
|
|
|
static Bytes DigestInfo(const Bytes& object, const Bytes& parameter, const Bytes& digest) {
|
|
return Encode(0x30, Concat({Encode(0x30, Concat({Encode(0x06, object), parameter})), Encode(0x04, digest)}));
|
|
}
|
|
|
|
static int Verify(int type, const Bytes& digest, const Bytes& signature, LibreSslRsa* rsa) {
|
|
return RSA_verify_nid_postfix(type, digest.data(), static_cast<unsigned>(digest.size()), signature.data(), static_cast<unsigned>(signature.size()), rsa);
|
|
}
|
|
|
|
struct Verifies {
|
|
int calls;
|
|
int type;
|
|
const unsigned char* digest;
|
|
unsigned digestLength;
|
|
const unsigned char* signature;
|
|
unsigned signatureLength;
|
|
const LibreSslRsa* rsa;
|
|
};
|
|
|
|
static Verifies verifies;
|
|
|
|
static int APS5_VABI RecordVerify(int type, const unsigned char* digest, unsigned int digestLength, const unsigned char* signature, unsigned int signatureLength, const LibreSslRsa* rsa) {
|
|
verifies = {verifies.calls + 1, type, digest, digestLength, signature, signatureLength, rsa};
|
|
return 55;
|
|
}
|
|
|
|
static void TestVerify() {
|
|
const auto spki = Hex(CavpSpki);
|
|
auto* rsa = Decode(spki, static_cast<long>(spki.size()));
|
|
const auto digest = Hex(CavpDigest);
|
|
const auto signature = Hex(CavpSignature);
|
|
Require(Verify(DigestSha256, digest, signature, rsa) == 1);
|
|
auto tampered = signature;
|
|
tampered[100] ^= 0x10;
|
|
Require(Verify(DigestSha256, digest, tampered, rsa) == 0);
|
|
auto wrong = digest;
|
|
wrong[31] ^= 1;
|
|
Require(Verify(DigestSha256, wrong, signature, rsa) == 0);
|
|
Require(Verify(DigestSha256, Bytes(digest.begin(), digest.end() - 1), signature, rsa) == 0);
|
|
Require(Verify(DigestSha256, Concat({digest, {0}}), signature, rsa) == 0);
|
|
for (int type : {DigestSha1, 0, 1, 673, 675, -1, 956, DigestMd5Sha1}) Require(Verify(type, digest, signature, rsa) == 0);
|
|
Require(Verify(DigestSha256, digest, Bytes(signature.begin() + 1, signature.end()), rsa) == 0);
|
|
Require(Verify(DigestSha256, digest, Concat({{0}, signature}), rsa) == 0);
|
|
const auto ssl = Pattern(36, 7);
|
|
const auto sslSignature = Sign(ssl);
|
|
Require(Verify(DigestMd5Sha1, ssl, sslSignature, rsa) == 1);
|
|
Require(Verify(DigestMd5Sha1, Bytes(ssl.begin(), ssl.end() - 1), sslSignature, rsa) == 0);
|
|
auto otherSsl = ssl;
|
|
otherSsl[35] ^= 1;
|
|
Require(Verify(DigestMd5Sha1, otherSsl, sslSignature, rsa) == 0);
|
|
Require(Verify(DigestMd5Sha1, ssl, Sign(Bytes(ssl.begin(), ssl.end() - 1)), rsa) == 0);
|
|
Require(Verify(DigestSha256, ssl, sslSignature, rsa) == 0);
|
|
RequireThrows([&] { RSA_verify_nid_postfix(DigestMd5Sha1, nullptr, 36, sslSignature.data(), 256, rsa); });
|
|
const auto sha256 = Hex("608648016503040201");
|
|
Require(Verify(DigestSha256, digest, Sign(DigestInfo(sha256, Null, digest)), rsa) == 1);
|
|
Require(Verify(DigestSha256, digest, Sign(DigestInfo(sha256, {}, digest)), rsa) == 1);
|
|
Require(Verify(DigestSha256, digest, Sign(DigestInfo(sha256, {0x05, 0x81, 0x00}, digest)), rsa) == 1);
|
|
for (const auto& parameter : {Bytes {0x04, 0x00}, Bytes {0x05, 0x01, 0x00}, Bytes {0x30, 0x00}, Bytes {0x80, 0x00}, Bytes {0x01, 0x01, 0x00}, Concat({Null, Null})})
|
|
Require(Verify(DigestSha256, digest, Sign(DigestInfo(sha256, parameter, digest)), rsa) == 0);
|
|
const auto algorithm = Encode(0x30, Concat({Encode(0x06, sha256), Null}));
|
|
Require(Verify(DigestSha256, digest, Sign(Concat({DigestInfo(sha256, Null, digest), {0x00}})), rsa) == 0);
|
|
Require(Verify(DigestSha256, digest, Sign(Encode(0x30, Concat({algorithm, Encode(0x04, digest), Null}))), rsa) == 0);
|
|
Require(Verify(DigestSha256, digest, Sign(Encode(0x30, algorithm)), rsa) == 0);
|
|
Require(Verify(DigestSha256, digest, Sign(Encode(0x30, Concat({algorithm, Encode(0x03, digest)}))), rsa) == 0);
|
|
Require(Verify(DigestSha256, digest, Sign(Encode(0x31, Concat({algorithm, Encode(0x04, digest)}))), rsa) == 0);
|
|
Require(Verify(DigestSha256, digest, Sign(Encode(0x04, digest)), rsa) == 0);
|
|
Require(Verify(0, digest, Sign(DigestInfo({}, Null, digest)), rsa) == 0);
|
|
Require(Verify(0, digest, Sign(DigestInfo({0x2A, 0x80}, Null, digest)), rsa) == 0);
|
|
const std::pair<int, const char*> objects[] {{3, "2a864886f70d0202"}, {4, "2a864886f70d0205"}, {41, "2b0e030212"}, {64, "2b0e03021a"}, {95, "55080365"},
|
|
{117, "2b24030201"}, {257, "2a864886f70d0204"}, {672, "608648016503040201"}, {673, "608648016503040202"}, {674, "608648016503040203"},
|
|
{675, "608648016503040204"}, {804, "28cf06030037"}, {809, "2a8503020209"}, {941, "2a85030701010202"}, {942, "2a85030701010203"}};
|
|
const auto hash = Pattern(20, 11);
|
|
for (std::size_t index = 0; index < std::size(objects); ++index) {
|
|
const auto encoded = Sign(DigestInfo(Hex(objects[index].second), Null, hash));
|
|
Require(Verify(objects[index].first, hash, encoded, rsa) == 1);
|
|
Require(Verify(objects[(index + 1) % std::size(objects)].first, hash, encoded, rsa) == 0);
|
|
Require(Verify(1, hash, encoded, rsa) == 0);
|
|
}
|
|
const auto unknown = Sign(DigestInfo({0x2A, 0x03, 0x04}, Null, hash));
|
|
for (int type : {-1, 956, 2000, DigestSha1, DigestSha256}) Require(Verify(type, hash, unknown, rsa) == 0);
|
|
const auto* defaultMethod = rsa->meth;
|
|
LibreSslRsaMethod method = *defaultMethod;
|
|
method.verify = reinterpret_cast<void*>(RecordVerify);
|
|
rsa->meth = &method;
|
|
verifies = {};
|
|
Require(Verify(DigestSha256, digest, signature, rsa) == 1 && verifies.calls == 0);
|
|
rsa->flags |= RsaFlagSignVerify;
|
|
Require(Verify(5, wrong, tampered, rsa) == 55);
|
|
Require(verifies.calls == 1 && verifies.type == 5 && verifies.digest == wrong.data() && verifies.digestLength == 32);
|
|
Require(verifies.signature == tampered.data() && verifies.signatureLength == 256 && verifies.rsa == rsa);
|
|
method.verify = nullptr;
|
|
Require(Verify(DigestSha256, digest, signature, rsa) == 1 && verifies.calls == 1);
|
|
rsa->meth = nullptr;
|
|
RequireThrows([&] { Verify(DigestSha256, digest, signature, rsa); });
|
|
rsa->flags &= ~RsaFlagSignVerify;
|
|
rsa->meth = defaultMethod;
|
|
RequireThrows([&] { Verify(DigestSha256, digest, signature, nullptr); });
|
|
RequireThrows([&] { RSA_verify_nid_postfix(DigestSha256, nullptr, 32, signature.data(), 256, rsa); });
|
|
Require(RSA_verify_nid_postfix(DigestSha256, nullptr, 32, tampered.data(), 256, rsa) == 0);
|
|
Require(RSA_verify_nid_postfix(DigestSha256, nullptr, 0, signature.data(), 256, rsa) == 0);
|
|
RequireThrows([&] { RSA_verify_nid_postfix(DigestSha256, digest.data(), 32, nullptr, 256, rsa); });
|
|
Require(RSA_verify_nid_postfix(DigestSha256, digest.data(), 32, nullptr, 255, rsa) == 0);
|
|
RSA_free_nid_postfix(rsa);
|
|
}
|
|
|
|
static void TestAddedDigestObjects() {
|
|
const auto spki = Hex(CavpSpki);
|
|
auto* rsa = Decode(spki, static_cast<long>(spki.size()));
|
|
const auto digest = Pattern(20, 11);
|
|
const struct { const char* oid; const char* encoding; int type; int expected; } cases[] {
|
|
{"1.2.3.4", "2a0304", -1, 1},
|
|
{"2.16.840.1.101.3.4.2.1", "608648016503040201", DigestSha256, 0},
|
|
};
|
|
bool accepted[2] {};
|
|
for (std::size_t index = 0; index < std::size(cases); ++index) {
|
|
OBJ_cleanup_nid_postfix();
|
|
ERR_clear_error_nid_postfix();
|
|
const int nid = OBJ_create_nid_postfix(cases[index].oid, "testDigest", "Test Digest");
|
|
Require(nid >= 956);
|
|
const int type = cases[index].type < 0 ? nid : cases[index].type;
|
|
const auto signature = Sign(DigestInfo(Hex(cases[index].encoding), Null, digest));
|
|
try {
|
|
const int result = Verify(type, digest, signature, rsa);
|
|
accepted[index] = result == cases[index].expected;
|
|
if (!accepted[index]) std::fprintf(stderr, "added digest %s: expected %d or not implemented, got %d\n", cases[index].oid, cases[index].expected, result);
|
|
} catch (const std::runtime_error& error) {
|
|
Require(std::strstr(error.what(), "RSA_verify") && std::strstr(error.what(), "not implemented"));
|
|
accepted[index] = true;
|
|
}
|
|
OBJ_cleanup_nid_postfix();
|
|
ERR_clear_error_nid_postfix();
|
|
}
|
|
RSA_free_nid_postfix(rsa);
|
|
Require(accepted[0]);
|
|
Require(accepted[1]);
|
|
}
|
|
|
|
int main() {
|
|
InitializeHeap();
|
|
TestFlags();
|
|
TestSize();
|
|
TestNew();
|
|
TestFree();
|
|
TestPublicKeyInfo();
|
|
TestPublicKeyInfoVariants();
|
|
TestAddedPublicKeyObjects();
|
|
TestPublicDecrypt();
|
|
TestPublicEncrypt();
|
|
TestPublicKeyChecks();
|
|
TestVerify();
|
|
TestAddedDigestObjects();
|
|
std::puts("libSceLibreSsl RSA tests passed");
|
|
}
|