#include "prx/libSceLibreSsl/include/Api.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include #include #include #include #include static void Require(bool value, const char* what) { if (value) return; std::fprintf(stderr, "failed: %s\n", what); std::abort(); } template static void RequireThrows(TAction action, const char* what) { try { action(); } catch (const std::exception&) { return; } Require(false, what); } static std::vector Bytes(const char* hex) { std::vector bytes; for (; hex[0] && hex[1]; hex += 2) bytes.push_back(static_cast(std::stoi(std::string(hex, 2), nullptr, 16))); return bytes; } static std::string Hex(const unsigned char* data, std::size_t size) { static constexpr char digits[] = "0123456789abcdef"; std::string text; for (std::size_t i = 0; i < size; ++i) { text += digits[data[i] >> 4]; text += digits[data[i] & 15]; } return text; } static std::uint32_t Word(const std::vector& bytes, std::size_t index) { return std::uint32_t(bytes[4 * index]) << 24 | std::uint32_t(bytes[4 * index + 1]) << 16 | std::uint32_t(bytes[4 * index + 2]) << 8 | bytes[4 * index + 3]; } static AesKey Sentinel() { AesKey key; for (std::uint32_t i = 0; i < 60; ++i) key.roundKeys[i] = 0xa5a50000u | i; key.rounds = 0x5a5a; return key; } template static void RequireSchedule(TSetup setup, const char* keyHex, const char* scheduleHex, const char* what) { const auto userKey = Bytes(keyHex); const auto schedule = Bytes(scheduleHex); const std::size_t used = schedule.size() / 4; const AesKey before = Sentinel(); AesKey key = before; Require(setup(userKey.data(), static_cast(userKey.size() * 8), &key) == 0 && key.rounds == static_cast(used / 4 - 1), what); for (std::size_t i = 0; i < 60; ++i) Require(key.roundKeys[i] == (i < used ? Word(schedule, i) : before.roundKeys[i]), what); } template static void RequireRefusalsKeepKey(TSetup setup, const char* what) { const unsigned char userKey[32] {}; const AesKey before = Sentinel(); for (const int bits : {0, 64, 127, 129, 160, 224, 255, 384, 512, -128}) { AesKey key = before; Require(setup(userKey, bits, &key) == -2 && !std::memcmp(&key, &before, sizeof(key)), what); } for (const int bits : {128, 64}) { AesKey key = before; Require(setup(nullptr, bits, &key) == -1 && !std::memcmp(&key, &before, sizeof(key)), what); } Require(setup(userKey, 128, nullptr) == -1, what); } int main() { const auto plain = Bytes("00112233445566778899aabbccddeeff"); const char* keys[3] { "000102030405060708090a0b0c0d0e0f", "000102030405060708090a0b0c0d0e0f1011121314151617", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f", }; const char* ciphers[3] {"69c4e0d86a7b0430d8cdb78070b4c55a", "dda97ca4864cdfe06eaf70a0ec0d7191", "8ea2b7ca516745bfeafc49904b496089"}; for (int i = 0; i < 3; ++i) { const auto key = Bytes(keys[i]); const int bits = static_cast(key.size() * 8); AesKey encrypt {}, decrypt {}; unsigned char out[16] {}, back[16] {}; Require(AES_set_encrypt_key_nid_postfix(key.data(), bits, &encrypt) == 0 && encrypt.rounds == 10 + 2 * i, "encrypt key"); Require(AES_set_decrypt_key_nid_postfix(key.data(), bits, &decrypt) == 0 && decrypt.rounds == encrypt.rounds, "decrypt key"); const int last = 4 * encrypt.rounds; Require(!std::memcmp(decrypt.roundKeys, encrypt.roundKeys + last, 16) && !std::memcmp(decrypt.roundKeys + last, encrypt.roundKeys, 16), "decrypt key reverses the round keys"); AES_encrypt_nid_postfix(plain.data(), out, &encrypt); Require(Hex(out, 16) == ciphers[i], "FIPS-197 appendix C cipher"); AES_decrypt_nid_postfix(out, back, &decrypt); Require(!std::memcmp(back, plain.data(), 16), "FIPS-197 appendix C inverse cipher"); AES_ecb_encrypt_nid_postfix(plain.data(), out, &encrypt, 1); Require(Hex(out, 16) == ciphers[i], "ECB encrypt"); AES_ecb_encrypt_nid_postfix(out, out, &decrypt, 0); Require(!std::memcmp(out, plain.data(), 16), "ECB decrypt in place"); } AesKey fipsKey {}; Require(AES_set_encrypt_key_nid_postfix(Bytes("2b7e151628aed2a6abf7158809cf4f3c").data(), 128, &fipsKey) == 0, "FIPS-197 appendix A key"); Require(fipsKey.roundKeys[4] == 0xa0fafe17 && fipsKey.roundKeys[43] == 0xb6630ca6, "FIPS-197 appendix A.1 expansion"); AesKey bad {}; bad.rounds = 7; bad.roundKeys[0] = 1; const unsigned char zero[32] {}; Require(AES_set_encrypt_key_nid_postfix(zero, 64, &bad) == -2 && bad.rounds == 7 && bad.roundKeys[0] == 1, "unsupported key length keeps the key"); Require(AES_set_decrypt_key_nid_postfix(zero, 129, &bad) == -2 && bad.rounds == 7 && bad.roundKeys[0] == 1, "unsupported decrypt key length keeps the key"); Require(AES_set_encrypt_key_nid_postfix(nullptr, 128, &bad) == -1 && AES_set_encrypt_key_nid_postfix(zero, 128, nullptr) == -1, "null key arguments"); unsigned char block[16] {}; RequireThrows([&] { AES_encrypt_nid_postfix(zero, block, &bad); }, "key schedule with an invalid round count"); RequireRefusalsKeepKey(&AES_set_encrypt_key_nid_postfix, "refused encrypt key setup leaves the key unchanged"); RequireRefusalsKeepKey(&AES_set_decrypt_key_nid_postfix, "refused decrypt key setup leaves the key unchanged"); RequireSchedule(&AES_set_encrypt_key_nid_postfix, "2b7e151628aed2a6abf7158809cf4f3c", "2b7e151628aed2a6abf7158809cf4f3ca0fafe1788542cb123a339392a6c7605f2c295f27a96b9435935807a7359f67f3d80477d4716fe3e1e237e446d7a883b" "ef44a541a8525b7fb671253bdb0bad00d4d1c6f87c839d87caf2b8bc11f915bc6d88a37a110b3efddbf98641ca0093fd4e54f70e5f5fc9f384a64fb24ea6dc4f" "ead27321b58dbad2312bf5607f8d292fac7766f319fadc2128d12941575c006ed014f9a8c9ee2589e13f0cc8b6630ca6", "FIPS-197 appendix A.1 AES-128 encrypt schedule"); RequireSchedule(&AES_set_encrypt_key_nid_postfix, "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7bfe0c91f72402f5a5ec12068e6c827f6b0e7a95b95c56fec24db7b4bd69b5411885a74796e92538fd" "e75fad44bb095386485af05721efb14fa448f6d94d6dce24aa326360113b30e6a25e7ed583b1cf9a27f939436a94f767c0a69407d19da4e1ec1786eb6fa64971" "485f703222cb8755e26d135233f0b7b340beeb282f18a2596747d26b458c553ea7e1466c9411f1df821f750aad07d753ca4005388fcc5006282d166abc3ce7b5" "e98ba06f448c773c8ecc720401002202", "FIPS-197 appendix A.2 AES-192 encrypt schedule"); RequireSchedule(&AES_set_encrypt_key_nid_postfix, "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff49ba354118e6925afa51a8b5f2067fcdea8b09c1a93d194cdbe49846eb75d5b9a" "d59aecb85bf3c917fee94248de8ebe96b5a9328a2678a647983122292f6c79b3812c81addadf48ba24360af2fab8b46498c5bfc9bebd198e268c3ba709e04214" "68007bacb2df331696e939e46c518d80c814e20476a9fb8a5025c02d59c58239de1369676ccc5a71fa2563959674ee155886ca5d2e2f31d77e0af1fa27cf73c3" "749c47ab18501ddae2757e4f7401905acafaaae3e4d59b349adf6acebd10190dfe4890d1e6188d0b046df344706c631e", "FIPS-197 appendix A.3 AES-256 encrypt schedule"); RequireSchedule(&AES_set_decrypt_key_nid_postfix, "2b7e151628aed2a6abf7158809cf4f3c", "d014f9a8c9ee2589e13f0cc8b6630ca60c7b5a631319eafeb0398890664cfbb4df7d925a1f62b09da320626ed675732412c07647c01f22c7bc42d2f37555114a" "6efcd876d2df54807c5df034c917c3b96ea30afcbc238cf6ae82a4b4b54a338d90884413d280860a12a128421bc897397c1f13f74208c219c021ae480969bf7b" "cc7505eb3e17d1ee82296c51c94811332b3708a7f262d405bc3ebdbf4b617d622b7e151628aed2a6abf7158809cf4f3c", "AES-128 decrypt schedule"); RequireSchedule(&AES_set_decrypt_key_nid_postfix, "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", "e98ba06f448c773c8ecc720401002202ac491644e55710b746c08a75c89b2cada3979ac28e5ba6d8e12cc9e654b272baf3b42258b59ebb5cf8fb64fe491e06f3" "4d65dfa2b1e5620dea899c312dcc3c1a5b6cfe3cc745a02bf8b9a572462a9904c5ddb7f8be933c760b4f46a6fc80bdafb5dc7ad0f7cffb09a7ec43939c295e17" "5023b89a3bc51d84d04b19377b4e8b8e41b34544ab0592b9ce92f15e421381d9659763e78c817087123039436be6a51e9eb149c479d69c5dfeb4a27ceab6d7fd" "8e73b0f7da0e6452c810f32b809079e5", "AES-192 decrypt schedule"); RequireSchedule(&AES_set_decrypt_key_nid_postfix, "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4", "fe4890d1e6188d0b046df344706c631eada23f4963e23b2455427c8a5c70910457c96cf6074f07c0706abb07137f9241b668b621ce40046d36a047ae0932ed8e" "34ad1e4450866b367725bcc76315294632526c367828b24cf8e043c33f92aa20c440b289642b757227a3d7f114309581d669a7334a7ade7a80c8f18fc772e9e3" "25ba3c22a06bc7fb4388a2833393427054fb808b9c137949cab22ff547ba186c6c3d632985d1fbd9e3e36578701be0f34a7459f9c8e8f9c256a156bc8d083799" "42107758e9ec98f066329ea193f8858b8ec6bff6829ca03b9e49af7edba96125603deb1015ca71be2b73aef0857d7781", "AES-256 decrypt schedule"); const auto key = Bytes("2b7e151628aed2a6abf7158809cf4f3c"); const auto message = Bytes("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710"); AesKey encrypt {}, decrypt {}; AES_set_encrypt_key_nid_postfix(key.data(), 128, &encrypt); AES_set_decrypt_key_nid_postfix(key.data(), 128, &decrypt); std::vector out(message.size() + 16), back(message.size() + 16); auto iv = Bytes("000102030405060708090a0b0c0d0e0f"); AES_cbc_encrypt_nid_postfix(message.data(), out.data(), 64, &encrypt, iv.data(), 1); Require(Hex(out.data(), 64) == "7649abac8119b246cee98e9b12e9197d5086cb9b507219ee95db113a917678b273bed6b8e3c1743b7116e69e222295163ff1caa1681fac09120eca307586e1a7", "SP 800-38A F.2.1 CBC encrypt"); Require(Hex(iv.data(), 16) == "3ff1caa1681fac09120eca307586e1a7", "CBC encrypt returns the last cipher block as iv"); iv = Bytes("000102030405060708090a0b0c0d0e0f"); std::memcpy(back.data(), out.data(), 64); AES_cbc_encrypt_nid_postfix(back.data(), back.data(), 32, &decrypt, iv.data(), 0); AES_cbc_encrypt_nid_postfix(back.data() + 32, back.data() + 32, 32, &decrypt, iv.data(), 0); Require(!std::memcmp(back.data(), message.data(), 64) && Hex(iv.data(), 16) == "3ff1caa1681fac09120eca307586e1a7", "SP 800-38A F.2.2 CBC decrypt in place across calls"); iv = Bytes("000102030405060708090a0b0c0d0e0f"); std::vector partial(32, 0xee); AES_cbc_encrypt_nid_postfix(message.data(), partial.data(), 20, &encrypt, iv.data(), 1); Require(Hex(partial.data(), 16) == "7649abac8119b246cee98e9b12e9197d" && partial[31] != 0xee && Hex(iv.data(), 16) == Hex(partial.data() + 16, 16), "CBC encrypt pads a partial block with the iv and writes the whole block"); iv = Bytes("000102030405060708090a0b0c0d0e0f"); std::vector plainPartial(32, 0xee); AES_cbc_encrypt_nid_postfix(partial.data(), plainPartial.data(), 20, &decrypt, iv.data(), 0); Require(!std::memcmp(plainPartial.data(), message.data(), 20) && plainPartial[20] == 0xee && Hex(iv.data(), 16) == Hex(partial.data() + 16, 16), "CBC decrypt writes only the requested length"); iv = Bytes("000102030405060708090a0b0c0d0e0f"); int num = 0; AES_cfb128_encrypt_nid_postfix(message.data(), out.data(), 5, &encrypt, iv.data(), &num, 1); AES_cfb128_encrypt_nid_postfix(message.data() + 5, out.data() + 5, 32, &encrypt, iv.data(), &num, 1); Require(num == 5, "CFB128 keeps the block position"); AES_cfb128_encrypt_nid_postfix(message.data() + 37, out.data() + 37, 27, &encrypt, iv.data(), &num, 1); Require(Hex(out.data(), 64) == "3b3fd92eb72dad20333449f8e83cfb4ac8a64537a0b3a93fcde3cdad9f1ce58b26751f67a3cbb140b1808cf187a4f4dfc04b05357c5d1c0eeac4c66f9ff7f2e6" && num == 0, "SP 800-38A F.3.13 CFB128 encrypt in pieces"); iv = Bytes("000102030405060708090a0b0c0d0e0f"); num = 0; AES_cfb128_encrypt_nid_postfix(out.data(), back.data(), 64, &encrypt, iv.data(), &num, 0); Require(!std::memcmp(back.data(), message.data(), 64), "SP 800-38A F.3.14 CFB128 decrypt"); num = 16; RequireThrows([&] { AES_cfb128_encrypt_nid_postfix(message.data(), out.data(), 1, &encrypt, iv.data(), &num, 1); }, "CFB128 position out of range"); auto counter = Bytes("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"); unsigned char keystream[16] {}; unsigned position = 0; AES_ctr128_encrypt_nid_postfix(message.data(), out.data(), 7, &encrypt, counter.data(), keystream, &position); AES_ctr128_encrypt_nid_postfix(message.data() + 7, out.data() + 7, 57, &encrypt, counter.data(), keystream, &position); Require(Hex(out.data(), 64) == "874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee", "SP 800-38A F.5.1 CTR encrypt in pieces"); Require(position == 0 && Hex(counter.data(), 16) == "f0f1f2f3f4f5f6f7f8f9fafbfcfdff03", "CTR advances the counter per block"); counter = Bytes("ffffffffffffffffffffffffffffffff"); position = 0; AES_ctr128_encrypt_nid_postfix(message.data(), out.data(), 37, &encrypt, counter.data(), keystream, &position); Require(Hex(out.data(), 37) == "e13338e36cb71962e00d020b4cedbd86d3dae15b04bb352fa0f59febfcb4da3e67da610697", "CTR counter wraps across all 128 bits"); Require(position == 5 && Hex(counter.data(), 16) == "00000000000000000000000000000002", "CTR position and counter after a partial block"); position = 16; RequireThrows([&] { AES_ctr128_encrypt_nid_postfix(message.data(), out.data(), 1, &encrypt, counter.data(), keystream, &position); }, "CTR position out of range"); std::puts("libSceLibreSsl AES tests passed"); }