this-city/engine/core/crypto/crypto_core.cpp
2026-07-10 17:04:34 +02:00

314 lines
10 KiB
C++

/**************************************************************************/
/* crypto_core.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 "crypto_core.h"
#include "core/os/os.h"
#include "core/string/ustring.h"
#include <mbedtls/base64.h>
#include <psa/crypto.h>
// MD5
CryptoCore::MD5Context::MD5Context() {
ctx = memalloc_zeroed(sizeof(psa_hash_operation_t));
}
CryptoCore::MD5Context::~MD5Context() {
psa_hash_abort((psa_hash_operation_t *)ctx);
memfree((psa_hash_operation_t *)ctx);
}
Error CryptoCore::MD5Context::start() {
int ret = psa_hash_setup((psa_hash_operation_t *)ctx, PSA_ALG_MD5);
return ret ? FAILED : OK;
}
Error CryptoCore::MD5Context::update(const uint8_t *p_src, size_t p_len) {
int ret = psa_hash_update((psa_hash_operation_t *)ctx, p_src, p_len);
return ret ? FAILED : OK;
}
Error CryptoCore::MD5Context::finish(unsigned char r_hash[16]) {
size_t size = 0;
int ret = psa_hash_finish((psa_hash_operation_t *)ctx, r_hash, 16, &size);
if (ret) {
psa_hash_abort((psa_hash_operation_t *)ctx);
return FAILED;
}
ERR_FAIL_COND_V(size != 16, ERR_BUG);
return OK;
}
// SHA1
CryptoCore::SHA1Context::SHA1Context() {
ctx = memalloc_zeroed(sizeof(psa_hash_operation_t));
}
CryptoCore::SHA1Context::~SHA1Context() {
psa_hash_abort((psa_hash_operation_t *)ctx);
memfree((psa_hash_operation_t *)ctx);
}
Error CryptoCore::SHA1Context::start() {
int ret = psa_hash_setup((psa_hash_operation_t *)ctx, PSA_ALG_SHA_1);
return ret ? FAILED : OK;
}
Error CryptoCore::SHA1Context::update(const uint8_t *p_src, size_t p_len) {
int ret = psa_hash_update((psa_hash_operation_t *)ctx, p_src, p_len);
return ret ? FAILED : OK;
}
Error CryptoCore::SHA1Context::finish(unsigned char r_hash[20]) {
size_t size = 0;
int ret = psa_hash_finish((psa_hash_operation_t *)ctx, r_hash, 20, &size);
if (ret) {
psa_hash_abort((psa_hash_operation_t *)ctx);
return FAILED;
}
ERR_FAIL_COND_V(size != 20, ERR_BUG);
return OK;
}
// SHA256
CryptoCore::SHA256Context::SHA256Context() {
ctx = memalloc_zeroed(sizeof(psa_hash_operation_t));
}
CryptoCore::SHA256Context::~SHA256Context() {
psa_hash_abort((psa_hash_operation_t *)ctx);
memfree((psa_hash_operation_t *)ctx);
}
Error CryptoCore::SHA256Context::start() {
int ret = psa_hash_setup((psa_hash_operation_t *)ctx, PSA_ALG_SHA_256);
return ret ? FAILED : OK;
}
Error CryptoCore::SHA256Context::update(const uint8_t *p_src, size_t p_len) {
int ret = psa_hash_update((psa_hash_operation_t *)ctx, p_src, p_len);
return ret ? FAILED : OK;
}
Error CryptoCore::SHA256Context::finish(unsigned char r_hash[32]) {
size_t size = 0;
int ret = psa_hash_finish((psa_hash_operation_t *)ctx, r_hash, 32, &size);
if (ret) {
psa_hash_abort((psa_hash_operation_t *)ctx);
return FAILED;
}
ERR_FAIL_COND_V(size != 32, ERR_BUG);
return OK;
}
// AES256
CryptoCore::AESContext::AESContext() {
ctx = memalloc_zeroed(sizeof(psa_cipher_operation_t));
}
CryptoCore::AESContext::~AESContext() {
psa_cipher_abort((psa_cipher_operation_t *)ctx);
psa_destroy_key(key_id);
memfree((psa_cipher_operation_t *)ctx);
}
void CryptoCore::AESContext::reset() {
psa_cipher_abort((psa_cipher_operation_t *)ctx);
psa_destroy_key(key_id);
key_id = PSA_KEY_ID_NULL;
alg = PSA_ALG_NONE;
}
Error CryptoCore::AESContext::setup(Mode p_mode, Cipher p_cipher, const uint8_t *p_key, size_t p_key_length, const uint8_t *p_iv, size_t p_iv_size) {
reset();
psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT;
switch (p_mode) {
case Mode::NONE:
return OK;
case Mode::ENCRYPT:
case Mode::DECRYPT:
break;
default:
return ERR_INVALID_PARAMETER;
}
switch (p_cipher) {
case Cipher::CBC:
alg = PSA_ALG_CBC_NO_PADDING;
break;
case Cipher::ECB:
alg = PSA_ALG_ECB_NO_PADDING;
break;
case Cipher::CFB:
alg = PSA_ALG_CFB;
break;
default:
return ERR_INVALID_PARAMETER;
}
psa_set_key_usage_flags(&attr, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT);
psa_set_key_algorithm(&attr, alg);
psa_set_key_type(&attr, PSA_KEY_TYPE_AES);
psa_set_key_bits(&attr, p_key_length << 3);
int ret = psa_import_key(&attr, p_key, p_key_length, &key_id);
if (ret) {
return FAILED;
}
if (p_mode == Mode::ENCRYPT) {
ret = psa_cipher_encrypt_setup((psa_cipher_operation_t *)ctx, key_id, alg);
} else {
ret = psa_cipher_decrypt_setup((psa_cipher_operation_t *)ctx, key_id, alg);
}
if (ret == PSA_SUCCESS && p_iv_size) {
ret = psa_cipher_set_iv((psa_cipher_operation_t *)ctx, p_iv, p_iv_size);
}
if (ret) {
reset();
return FAILED;
}
return OK;
}
Error CryptoCore::AESContext::update(const uint8_t *p_src, size_t p_src_size, uint8_t *r_dst, size_t p_dst_size) {
size_t size = 0;
int ret = psa_cipher_update((psa_cipher_operation_t *)ctx, p_src, p_src_size, r_dst, p_dst_size, &size);
if (ret) {
return FAILED;
}
ERR_FAIL_COND_V(p_dst_size != size, ERR_BUG);
return OK;
}
Error CryptoCore::AESContext::finish(uint8_t *r_dst, size_t p_dst_size) {
size_t size = 0;
int ret = psa_cipher_finish((psa_cipher_operation_t *)ctx, r_dst, p_dst_size, &size);
reset();
if (ret) {
return FAILED;
}
ERR_FAIL_COND_V(p_dst_size != size, ERR_BUG);
return OK;
}
// CryptoCore
Error CryptoCore::generate_random(uint8_t *r_buffer, size_t p_buffer_len) {
if (unlikely(!initialized)) {
// Fall back to raw get_entropy (might affect performance).
return OS::get_singleton()->get_entropy(r_buffer, p_buffer_len);
}
int ret = psa_generate_random(r_buffer, p_buffer_len);
ERR_FAIL_COND_V(ret, FAILED);
return OK;
}
String CryptoCore::b64_encode_str(const uint8_t *p_src, size_t p_src_len) {
size_t b64len = p_src_len / 3 * 4 + 4 + 1;
Vector<uint8_t> b64buff;
b64buff.resize(b64len);
uint8_t *w64 = b64buff.ptrw();
size_t strlen = 0;
int ret = b64_encode(&w64[0], b64len, &strlen, p_src, p_src_len);
w64[strlen] = 0;
return ret ? String() : (const char *)&w64[0];
}
Error CryptoCore::b64_encode(uint8_t *r_dst, size_t p_dst_len, size_t *r_len, const uint8_t *p_src, size_t p_src_len) {
int ret = mbedtls_base64_encode(r_dst, p_dst_len, r_len, p_src, p_src_len);
return ret ? FAILED : OK;
}
Error CryptoCore::b64_decode(uint8_t *r_dst, size_t p_dst_len, size_t *r_len, const uint8_t *p_src, size_t p_src_len) {
int ret = mbedtls_base64_decode(r_dst, p_dst_len, r_len, p_src, p_src_len);
return ret ? FAILED : OK;
}
Error CryptoCore::md5(const uint8_t *p_src, size_t p_src_len, unsigned char r_hash[16]) {
ERR_FAIL_COND_V(PSA_HASH_LENGTH(PSA_ALG_MD5) != 16, ERR_BUG);
size_t size = 0;
int ret = psa_hash_compute(PSA_ALG_MD5, p_src, p_src_len, r_hash, 16, &size);
return ret ? FAILED : OK;
}
Error CryptoCore::sha1(const uint8_t *p_src, size_t p_src_len, unsigned char r_hash[20]) {
ERR_FAIL_COND_V(PSA_HASH_LENGTH(PSA_ALG_SHA_1) != 20, ERR_BUG);
size_t size = 0;
int ret = psa_hash_compute(PSA_ALG_SHA_1, p_src, p_src_len, r_hash, 20, &size);
return ret ? FAILED : OK;
}
Error CryptoCore::sha256(const uint8_t *p_src, size_t p_src_len, unsigned char r_hash[32]) {
ERR_FAIL_COND_V(PSA_HASH_LENGTH(PSA_ALG_SHA_256) != 32, ERR_BUG);
size_t size = 0;
int ret = psa_hash_compute(PSA_ALG_SHA_256, p_src, p_src_len, r_hash, 32, &size);
return ret ? FAILED : OK;
}
Error CryptoCore::encrypt_cfb(const uint8_t *p_src, uint8_t *r_dst, size_t p_length, const uint8_t *p_key, size_t p_key_length, const uint8_t p_iv[16]) {
AESContext ctx;
Error err = ctx.setup(AESContext::Mode::ENCRYPT, AESContext::Cipher::CFB, p_key, p_key_length, p_iv, 16);
ERR_FAIL_COND_V(err, err);
err = ctx.update(p_src, p_length, r_dst, p_length);
ERR_FAIL_COND_V(err, err);
err = ctx.finish(nullptr, 0);
ERR_FAIL_COND_V(err, err);
return OK;
}
Error CryptoCore::decrypt_cfb(const uint8_t *p_src, uint8_t *r_dst, size_t p_length, const uint8_t *p_key, size_t p_key_length, const uint8_t p_iv[16]) {
AESContext ctx;
Error err = ctx.setup(AESContext::Mode::DECRYPT, AESContext::Cipher::CFB, p_key, p_key_length, p_iv, 16);
ERR_FAIL_COND_V(err, err);
err = ctx.update(p_src, p_length, r_dst, p_length);
ERR_FAIL_COND_V(err, err);
err = ctx.finish(nullptr, 0);
ERR_FAIL_COND_V(err, err);
return OK;
}
bool CryptoCore::initialized = false;
void CryptoCore::initialize() {
ERR_FAIL_COND(initialized);
#ifdef GODOT_MBEDTLS_PLATFORM
godot_mbedtls_platform_init();
#endif
int status = psa_crypto_init();
ERR_FAIL_COND_MSG(status != PSA_SUCCESS, "Failed to initialize psa crypto. Cryptographic functions will not work.");
initialized = true;
}
void CryptoCore::finalize() {
if (!initialized) {
return;
}
initialized = false;
mbedtls_psa_crypto_free(); // Not part of PSA for reasons.
#ifdef GODOT_MBEDTLS_PLATFORM
godot_mbedtls_platform_free();
#endif
}