From 745094c9e7baff54551de63bd92994bb623f6678 Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Wed, 1 Jul 2026 21:20:59 +0200 Subject: [PATCH] Crypto: Add tests for AESContext Consolidate core crypto tests under `tests/core/crypto` (cherry picked from commit c9899fcf9d47b02070e4e6e928c348b3207f661b) --- scu_builders.py | 1 + tests/core/crypto/test_aes_context.cpp | 334 ++++++++++++++++++ tests/core/{ => crypto}/test_crypto.cpp | 0 .../{ => crypto}/test_hashing_context.cpp | 0 4 files changed, 335 insertions(+) create mode 100644 tests/core/crypto/test_aes_context.cpp rename tests/core/{ => crypto}/test_crypto.cpp (100%) rename tests/core/{ => crypto}/test_hashing_context.cpp (100%) diff --git a/scu_builders.py b/scu_builders.py index 95acb51676..9a8b3cb492 100644 --- a/scu_builders.py +++ b/scu_builders.py @@ -423,6 +423,7 @@ def generate_scu_files(max_includes_per_scu): "tests", "/core", "/core/config", + "/core/crypto", "/core/input", "/core/io", "/core/math", diff --git a/tests/core/crypto/test_aes_context.cpp b/tests/core/crypto/test_aes_context.cpp new file mode 100644 index 0000000000..78ba2a5e1d --- /dev/null +++ b/tests/core/crypto/test_aes_context.cpp @@ -0,0 +1,334 @@ +/**************************************************************************/ +/* test_aes_context.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "tests/test_macros.h" + +TEST_FORCE_LINK(test_aes_context) + +#include "core/crypto/aes_context.h" + +namespace TestAes_context { + +bool equals(const PackedByteArray &p_a, const PackedByteArray &p_b) { + if (p_a.size() != p_b.size()) { + return false; + } + if (p_a.is_empty()) { + return true; + } + return memcmp(p_a.ptr(), p_b.ptr(), p_a.size()) == 0; +} + +Vector to_vec(String s) { + return s.to_utf8_buffer(); +} + +TEST_CASE("[AESContext] ECB start") { + AESContext::Mode modes[2] = { AESContext::MODE_ECB_ENCRYPT, AESContext::MODE_ECB_DECRYPT }; + Ref ctx; + ctx.instantiate(); + for (int i = 0; i < 2; i++) { + AESContext::Mode mode = modes[i]; + // ECB has no IV + PackedByteArray iv; + // Key must be either 16 or 32 bytes. + Vector key; + + // Valid key (16) + key.resize(16); + Error err = ctx->start(mode, key, iv); + CHECK_EQ(err, OK); + ctx->finish(); + + // Valid key (32) + key.resize(32); + err = ctx->start(mode, key, iv); + CHECK_EQ(err, OK); + + ERR_PRINT_OFF; + + // Already in use + err = ctx->start(mode, key, iv); + CHECK_EQ(err, ERR_ALREADY_IN_USE); + ctx->finish(); + + // Should ignore IV + iv.resize(1); + err = ctx->start(mode, key, iv); + CHECK_EQ(err, OK); + ctx->finish(); + iv.resize(16); + err = ctx->start(mode, key, iv); + CHECK_EQ(err, OK); + ctx->finish(); + iv.clear(); + + // Invalid keys + key.resize(0); + err = ctx->start(mode, key, iv); + CHECK_EQ(err, ERR_INVALID_PARAMETER); + key.resize(15); + err = ctx->start(mode, key, iv); + CHECK_EQ(err, ERR_INVALID_PARAMETER); + key.resize(17); + err = ctx->start(mode, key, iv); + CHECK_EQ(err, ERR_INVALID_PARAMETER); + key.resize(33); + err = ctx->start(mode, key, iv); + CHECK_EQ(err, ERR_INVALID_PARAMETER); + + ERR_PRINT_ON; + + // Valid again + key.resize(16); + err = ctx->start(mode, key, iv); + CHECK_EQ(err, OK); + ctx->finish(); + } +} + +TEST_CASE("[AESContext] ECB update") { + // ECB has no IV + PackedByteArray iv; + // Key must be either 16 or 32 bytes. + Vector key; + // Data size must be multiple of 16 bytes, apply padding if needed. + String data = "My secret text!!"; + Ref ctx; + ctx.instantiate(); + Vector enc; + for (int i = 0; i < 2; i++) { + // Test with 16 and 32 bytes key + key.resize(i ? 32 : 16); + Error err = ctx->start(AESContext::MODE_ECB_ENCRYPT, key, iv); + CHECK_EQ(err, OK); + + enc = ctx->update(to_vec(data)); + CHECK_EQ(enc.size(), to_vec(data).size()); + + enc = ctx->update(to_vec(data + data)); + CHECK_EQ(enc.size(), to_vec(data + data).size()); + + enc = ctx->update(to_vec(data + data + data)); + CHECK_EQ(enc.size(), to_vec(data + data + data).size()); + + // Update size must be a multiple of 16 + ERR_PRINT_OFF; + enc = ctx->update(to_vec(data + "1")); + CHECK_EQ(enc.size(), 0); + enc = ctx->update(to_vec(data.substr(1))); + CHECK_EQ(enc.size(), 0); + enc = ctx->update(to_vec(data + data + "1")); + CHECK_EQ(enc.size(), 0); + ERR_PRINT_ON; + ctx->finish(); + } + + // The context must be started + ERR_PRINT_OFF; + enc = ctx->update(to_vec(data)); + CHECK_EQ(enc.size(), 0); + ERR_PRINT_ON; +} + +TEST_CASE("[AESContext] ECB encrypt/decrypt") { + // ECB has no IV + Vector iv; + // Key must be either 16 or 32 bytes. + Vector key; + // Data size must be multiple of 16 bytes, apply padding if needed. + Vector data; + data.resize(64); + uint8_t *d = data.ptrw(); + for (int i = 0; i < 64; i++) { + d[i] = i; + } + Ref ctx; + ctx.instantiate(); + for (int i = 0; i < 2; i++) { + key.resize(i ? 32 : 16); + // Encrypt + Error err = ctx->start(AESContext::MODE_ECB_ENCRYPT, key, iv); + CHECK_EQ(err, OK); + Vector enc = ctx->update(data.slice(0, 16)); + CHECK_EQ(enc.size(), 16); + enc.append_array(ctx->update(data.slice(16))); + CHECK(!equals(data, enc)); + ctx->finish(); + // Decrypt + err = ctx->start(AESContext::MODE_ECB_DECRYPT, key, iv); + CHECK_EQ(err, OK); + Vector dec = ctx->update(enc.slice(0, 16)); + CHECK_EQ(dec.size(), 16); + dec.append_array(ctx->update(enc.slice(16))); + CHECK(equals(dec, data)); + ctx->finish(); + } + + // Test known values + String wk_key = "My secret key!!!"; + String wk_data = "My secret text!!My secret text!!"; + Vector wk_enc = { 103, 81, 126, 234, 18, 0, 206, 76, 28, 20, 158, 138, 208, 20, 117, 53, 103, 81, 126, 234, 18, 0, 206, 76, 28, 20, 158, 138, 208, 20, 117, 53 }; + Error err = ctx->start(AESContext::MODE_ECB_ENCRYPT, to_vec(wk_key)); + Vector enc = ctx->update(to_vec(wk_data)); + CHECK_EQ(err, OK); + CHECK(equals(wk_enc, enc)); + ctx->finish(); + + wk_key = "My secret key!!!My secret key!!!"; + wk_enc = { 154, 26, 109, 2, 146, 25, 9, 217, 179, 234, 4, 129, 2, 148, 181, 80, 154, 26, 109, 2, 146, 25, 9, 217, 179, 234, 4, 129, 2, 148, 181, 80 }; + err = ctx->start(AESContext::MODE_ECB_ENCRYPT, to_vec(wk_key)); + enc = ctx->update(to_vec(wk_data)); + CHECK_EQ(err, OK); + CHECK(equals(wk_enc, enc)); + ctx->finish(); +} + +TEST_CASE("[AESContext] CBC start") { + AESContext::Mode modes[2] = { AESContext::MODE_CBC_ENCRYPT, AESContext::MODE_CBC_DECRYPT }; + Ref ctx; + ctx.instantiate(); + for (int i = 0; i < 2; i++) { + AESContext::Mode mode = modes[i]; + // CBC requires 16 bytes of IV + PackedByteArray iv; + iv.resize(16); + // Key must be either 16 or 32 bytes. + Vector key; + + // Valid key (16) + key.resize(16); + Error err = ctx->start(mode, key, iv); + CHECK_EQ(err, OK); + ctx->finish(); + + // Valid key (32) + err = ctx->start(mode, key, iv); + CHECK_EQ(err, OK); + + ERR_PRINT_OFF; + + // Already in use + err = ctx->start(mode, key, iv); + CHECK_EQ(err, ERR_ALREADY_IN_USE); + ctx->finish(); + + // Should require exactly 16 bytes of IV + iv.resize(0); + err = ctx->start(mode, key, iv); + CHECK_EQ(err, ERR_INVALID_PARAMETER); + iv.resize(15); + err = ctx->start(mode, key, iv); + CHECK_EQ(err, ERR_INVALID_PARAMETER); + iv.resize(17); + err = ctx->start(mode, key, iv); + CHECK_EQ(err, ERR_INVALID_PARAMETER); + iv.resize(16); + + // Invalid keys + key.resize(0); + err = ctx->start(mode, key, iv); + CHECK_EQ(err, ERR_INVALID_PARAMETER); + key.resize(15); + err = ctx->start(mode, key, iv); + CHECK_EQ(err, ERR_INVALID_PARAMETER); + key.resize(17); + err = ctx->start(mode, key, iv); + CHECK_EQ(err, ERR_INVALID_PARAMETER); + key.resize(33); + err = ctx->start(mode, key, iv); + CHECK_EQ(err, ERR_INVALID_PARAMETER); + + ERR_PRINT_ON; + + // Valid again + key.resize(16); + err = ctx->start(mode, key, iv); + CHECK_EQ(err, OK); + ctx->finish(); + } +} + +TEST_CASE("[AESContext] CBC encrypt/decrypt") { + // IV must be exactly 16 bytes. + Vector iv; + iv.resize(16); + // Key must be either 16 or 32 bytes. + Vector key; + // Data size must be multiple of 16 bytes, apply padding if needed. + Vector data; + data.resize(64); + uint8_t *d = data.ptrw(); + for (int i = 0; i < 64; i++) { + d[i] = i; + } + Ref ctx; + ctx.instantiate(); + for (int i = 0; i < 2; i++) { + key.resize(i ? 32 : 16); + // Encrypt + Error err = ctx->start(AESContext::MODE_CBC_ENCRYPT, key, iv); + CHECK_EQ(err, OK); + Vector enc = ctx->update(data.slice(0, 16)); + CHECK_EQ(enc.size(), 16); + enc.append_array(ctx->update(data.slice(16))); + CHECK(!equals(data, enc)); + ctx->finish(); + // Decrypt + err = ctx->start(AESContext::MODE_CBC_DECRYPT, key, iv); + CHECK_EQ(err, OK); + Vector dec = ctx->update(enc.slice(0, 16)); + CHECK_EQ(dec.size(), 16); + dec.append_array(ctx->update(enc.slice(16))); + CHECK(equals(dec, data)); + ctx->finish(); + } + + // Test known values + String wk_key = "My secret key!!!"; + String wk_data = "My secret text!!My secret text!!"; + String wk_iv = "My secret iv!!!!"; + Vector wk_enc = { 5, 69, 219, 146, 167, 176, 184, 99, 29, 246, 79, 191, 234, 6, 46, 39, 157, 47, 126, 102, 152, 34, 50, 4, 137, 167, 31, 110, 63, 141, 84, 145 }; + Error err = ctx->start(AESContext::MODE_CBC_ENCRYPT, to_vec(wk_key), to_vec(wk_iv)); + Vector enc = ctx->update(to_vec(wk_data)); + CHECK_EQ(err, OK); + CHECK(equals(wk_enc, enc)); + ctx->finish(); + + wk_key = "My secret key!!!My secret key!!!"; + wk_enc = { 166, 176, 138, 196, 78, 18, 148, 120, 139, 21, 172, 33, 92, 181, 5, 239, 175, 111, 229, 214, 22, 186, 129, 95, 223, 46, 192, 103, 141, 105, 183, 91 }; + err = ctx->start(AESContext::MODE_CBC_ENCRYPT, to_vec(wk_key), to_vec(wk_iv)); + enc = ctx->update(to_vec(wk_data)); + CHECK_EQ(err, OK); + CHECK(equals(wk_enc, enc)); + ctx->finish(); +} + +} // namespace TestAes_context diff --git a/tests/core/test_crypto.cpp b/tests/core/crypto/test_crypto.cpp similarity index 100% rename from tests/core/test_crypto.cpp rename to tests/core/crypto/test_crypto.cpp diff --git a/tests/core/test_hashing_context.cpp b/tests/core/crypto/test_hashing_context.cpp similarity index 100% rename from tests/core/test_hashing_context.cpp rename to tests/core/crypto/test_hashing_context.cpp