From 3f606263d5086047c87acf68b3d0456277d02654 Mon Sep 17 00:00:00 2001 From: Will Whitty Date: Thu, 20 May 2021 02:02:28 +0300 Subject: [PATCH] Backport HMac crypto to 3.x Fix headers Fix docs formatting Changes for PR Fix tests --- core/crypto/crypto.cpp | 47 ++++++++++++++ core/crypto/crypto.h | 23 +++++++ core/register_core_types.cpp | 1 + doc/classes/Crypto.xml | 26 ++++++++ doc/classes/HMACContext.xml | 85 +++++++++++++++++++++++++ main/tests/test_crypto.cpp | 99 ++++++++++++++++++++++++++++++ main/tests/test_crypto.h | 41 +++++++++++++ main/tests/test_main.cpp | 5 ++ modules/mbedtls/crypto_mbedtls.cpp | 71 ++++++++++++++++++++- modules/mbedtls/crypto_mbedtls.h | 22 ++++++- 10 files changed, 416 insertions(+), 4 deletions(-) create mode 100644 doc/classes/HMACContext.xml create mode 100644 main/tests/test_crypto.cpp create mode 100644 main/tests/test_crypto.h diff --git a/core/crypto/crypto.cpp b/core/crypto/crypto.cpp index 5ca52bafac23..78a1b1dda7e1 100644 --- a/core/crypto/crypto.cpp +++ b/core/crypto/crypto.cpp @@ -65,6 +65,22 @@ void X509Certificate::_bind_methods() { ClassDB::bind_method(D_METHOD("load", "path"), &X509Certificate::load); } +/// HMACContext + +void HMACContext::_bind_methods() { + ClassDB::bind_method(D_METHOD("start", "hash_type", "key"), &HMACContext::start); + ClassDB::bind_method(D_METHOD("update", "data"), &HMACContext::update); + ClassDB::bind_method(D_METHOD("finish"), &HMACContext::finish); +} + +HMACContext *(*HMACContext::_create)() = nullptr; +HMACContext *HMACContext::create() { + if (_create) { + return _create(); + } + ERR_FAIL_V_MSG(nullptr, "HMACContext is not available when the mbedtls module is disabled."); +} + /// Crypto void (*Crypto::_load_default_certificates)(String p_path) = nullptr; @@ -82,6 +98,35 @@ void Crypto::load_default_certificates(String p_path) { } } +PoolByteArray Crypto::hmac_digest(HashingContext::HashType p_hash_type, PoolByteArray p_key, PoolByteArray p_msg) { + Ref ctx = Ref(HMACContext::create()); + ERR_FAIL_COND_V_MSG(ctx.is_null(), PoolByteArray(), "HMAC is not available witout mbedtls module."); + Error err = ctx->start(p_hash_type, p_key); + ERR_FAIL_COND_V(err != OK, PoolByteArray()); + err = ctx->update(p_msg); + ERR_FAIL_COND_V(err != OK, PoolByteArray()); + return ctx->finish(); +} + +// Compares two HMACS for equality without leaking timing information in order to prevent timing attakcs. +// @see: https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy +bool Crypto::constant_time_compare(PoolByteArray p_trusted, PoolByteArray p_received) { + const uint8_t *t = p_trusted.read().ptr(); + const uint8_t *r = p_received.read().ptr(); + int tlen = p_trusted.size(); + int rlen = p_received.size(); + // If the lengths are different then nothing else matters. + if (tlen != rlen) { + return false; + } + + uint8_t v = 0; + for (int i = 0; i < tlen; i++) { + v |= t[i] ^ r[i]; + } + return v == 0; +} + void Crypto::_bind_methods() { ClassDB::bind_method(D_METHOD("generate_random_bytes", "size"), &Crypto::generate_random_bytes); ClassDB::bind_method(D_METHOD("generate_rsa", "size"), &Crypto::generate_rsa); @@ -90,6 +135,8 @@ void Crypto::_bind_methods() { ClassDB::bind_method(D_METHOD("verify", "hash_type", "hash", "signature", "key"), &Crypto::verify); ClassDB::bind_method(D_METHOD("encrypt", "key", "plaintext"), &Crypto::encrypt); ClassDB::bind_method(D_METHOD("decrypt", "key", "ciphertext"), &Crypto::decrypt); + ClassDB::bind_method(D_METHOD("hmac_digest", "hash_type", "key", "msg"), &Crypto::hmac_digest); + ClassDB::bind_method(D_METHOD("constant_time_compare", "trusted", "received"), &Crypto::constant_time_compare); } Crypto::Crypto() { diff --git a/core/crypto/crypto.h b/core/crypto/crypto.h index 6c00a271b165..bc7a4183fa6d 100644 --- a/core/crypto/crypto.h +++ b/core/crypto/crypto.h @@ -68,6 +68,23 @@ class X509Certificate : public Resource { virtual Error save(String p_path) = 0; }; +class HMACContext : public Reference { + GDCLASS(HMACContext, Reference); + +protected: + static void _bind_methods(); + static HMACContext *(*_create)(); + +public: + static HMACContext *create(); + + virtual Error start(HashingContext::HashType p_hash_type, PoolByteArray p_key) = 0; + virtual Error update(PoolByteArray p_data) = 0; + virtual PoolByteArray finish() = 0; + + HMACContext() {} +}; + class Crypto : public Reference { GDCLASS(Crypto, Reference); @@ -89,6 +106,12 @@ class Crypto : public Reference { virtual Vector encrypt(Ref p_key, Vector p_plaintext) = 0; virtual Vector decrypt(Ref p_key, Vector p_ciphertext) = 0; + PoolByteArray hmac_digest(HashingContext::HashType p_hash_type, PoolByteArray p_key, PoolByteArray p_msg); + + // Compares two PoolByteArrays for equality without leaking timing information in order to prevent timing attacks. + // @see: https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy + bool constant_time_compare(PoolByteArray p_trusted, PoolByteArray p_received); + Crypto(); }; diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 61ec67c364bb..c42fe7f3911e 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -162,6 +162,7 @@ void register_core_types() { ClassDB::register_class(); ClassDB::register_custom_instance_class(); ClassDB::register_custom_instance_class(); + ClassDB::register_custom_instance_class(); ClassDB::register_custom_instance_class(); ClassDB::register_custom_instance_class(); diff --git a/doc/classes/Crypto.xml b/doc/classes/Crypto.xml index e60235c8342d..77c07ae56948 100644 --- a/doc/classes/Crypto.xml +++ b/doc/classes/Crypto.xml @@ -39,6 +39,18 @@ + + + + + + + + + Compares two [PoolByteArray]s for equality without leaking timing information in order to prevent timing attacks. + See [url=https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy]this blog post[/url] for more information. + + @@ -104,6 +116,20 @@ [/codeblock] + + + + + + + + + + + Generates an [url=https://en.wikipedia.org/wiki/HMAC]HMAC[/url] digest of [code]msg[/code] using [code]key[/code]. The [code]hash_type[/code] parameter is the hashing algorithm that is used for the inner and outer hashes. + Currently, only [constant HashingContext.HASH_SHA256] and [constant HashingContext.HASH_SHA1] are supported. + + diff --git a/doc/classes/HMACContext.xml b/doc/classes/HMACContext.xml new file mode 100644 index 000000000000..77a81b0c2517 --- /dev/null +++ b/doc/classes/HMACContext.xml @@ -0,0 +1,85 @@ + + + + Used to create an HMAC for a message using a key. + + + The HMACContext class is useful for advanced HMAC use cases, such as streaming the message as it supports creating the message over time rather than providing it all at once. + [codeblock] + extends Node + var ctx = HMACContext.new() + + func _ready(): + var key = "supersecret".to_utf8() + var err = ctx.start(HashingContext.HASH_SHA256, key) + assert(err == OK) + var msg1 = "this is ".to_utf8() + var msg2 = "vewy vewy secret".to_utf8() + err = ctx.update(msg1) + assert(err == OK) + err = ctx.update(msg2) + assert(err == OK) + var hmac = ctx.finish() + print(hmac.hex_encode()) + [/codeblock] + And in C# we can use the following. + [codeblock] + using Godot; + using System; + using System.Diagnostics; + + public class CryptoNode : Node + { + private HMACContext ctx = new HMACContext(); + public override void _Ready() + { + PoolByteArray key = String("supersecret").to_utf8(); + Error err = ctx.Start(HashingContext.HASH_SHA256, key); + GD.Assert(err == OK); + PoolByteArray msg1 = String("this is ").to_utf8(); + PoolByteArray msg2 = String("vewy vew secret").to_utf8(); + err = ctx.Update(msg1); + GD.Assert(err == OK); + err = ctx.Update(msg2); + GD.Assert(err == OK); + PoolByteArray hmac = ctx.Finish(); + GD.Print(hmac.HexEncode()); + } + } + [/codeblock] + [b]Note:[/b] Not available in HTML5 exports. + + + + + + + + + Returns the resulting HMAC. If the HMAC failed, an empty [PoolByteArray] is returned. + + + + + + + + + + + Initializes the HMACContext. This method cannot be called again on the same HMACContext until [method finish] has been called. + + + + + + + + + Updates the message to be HMACed. This can be called multiple times before [method finish] is called to append [code]data[/code] to the message, but cannot be called until [method start] has been called. + + + + + + diff --git a/main/tests/test_crypto.cpp b/main/tests/test_crypto.cpp new file mode 100644 index 000000000000..ac3527345686 --- /dev/null +++ b/main/tests/test_crypto.cpp @@ -0,0 +1,99 @@ +/*************************************************************************/ +/* test_crypto.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 "core/crypto/crypto.h" +#include "core/os/os.h" + +namespace TestCrypto { + +class _MockCrypto : public Crypto { + virtual PoolByteArray generate_random_bytes(int p_bytes) { return PoolByteArray(); } + virtual Ref generate_rsa(int p_bytes) { return nullptr; } + virtual Ref generate_self_signed_certificate(Ref p_key, String p_issuer_name, String p_not_before, String p_not_after) { return nullptr; } + + virtual Vector sign(HashingContext::HashType p_hash_type, Vector p_hash, Ref p_key) { return Vector(); } + virtual bool verify(HashingContext::HashType p_hash_type, Vector p_hash, Vector p_signature, Ref p_key) { return false; } + virtual Vector encrypt(Ref p_key, Vector p_plaintext) { return Vector(); } + virtual Vector decrypt(Ref p_key, Vector p_ciphertext) { return Vector(); } + virtual PoolByteArray hmac_digest(HashingContext::HashType p_hash_type, PoolByteArray p_key, PoolByteArray p_msg) { return PoolByteArray(); } +}; + +PoolByteArray raw_to_pba(const uint8_t *arr, size_t len) { + PoolByteArray pba; + pba.resize(len); + for (size_t i = 0; i < len; i++) { + pba.set(i, arr[i]); + } + return pba; +} + +bool test_PoolByteArray_constant_time_compare() { + const uint8_t hm1[] = { 144, 140, 176, 38, 88, 113, 101, 45, 71, 105, 10, 91, 248, 16, 117, 244, 189, 30, 238, 29, 219, 134, 82, 130, 212, 114, 161, 166, 188, 169, 200, 106 }; + const uint8_t hm2[] = { 80, 30, 144, 228, 108, 38, 188, 125, 150, 64, 165, 127, 221, 118, 144, 232, 45, 100, 15, 248, 193, 244, 245, 34, 116, 147, 132, 200, 110, 27, 38, 75 }; + PoolByteArray p1 = raw_to_pba(hm1, sizeof(hm1) / sizeof(hm1[0])); + PoolByteArray p2 = raw_to_pba(hm2, sizeof(hm2) / sizeof(hm2[0])); + _MockCrypto crypto; + bool equal = crypto.constant_time_compare(p1, p1); + bool ok = true; + ok = ok && equal; + equal = crypto.constant_time_compare(p1, p2); + ok = ok && !equal; + return ok; +} + +typedef bool (*TestFunc)(); + +TestFunc test_funcs[] = { + test_PoolByteArray_constant_time_compare, + nullptr +}; + +MainLoop *test() { + int count = 0; + int passed = 0; + + while (true) { + if (!test_funcs[count]) { + break; + } + bool pass = test_funcs[count](); + if (pass) { + passed++; + } + OS::get_singleton()->print("\t%s\n", pass ? "PASS" : "FAILED"); + + count++; + } + OS::get_singleton()->print("\n"); + OS::get_singleton()->print("Passed %i of %i tests\n", passed, count); + return nullptr; +} + +} // namespace TestCrypto diff --git a/main/tests/test_crypto.h b/main/tests/test_crypto.h new file mode 100644 index 000000000000..a9c6345b6878 --- /dev/null +++ b/main/tests/test_crypto.h @@ -0,0 +1,41 @@ +/*************************************************************************/ +/* test_crypto.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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. */ +/*************************************************************************/ + +#ifndef TEST_CRYPTO_H +#define TEST_CRYPTO_H + +#include "core/os/main_loop.h" + +namespace TestCrypto { + +MainLoop *test(); +} + +#endif diff --git a/main/tests/test_main.cpp b/main/tests/test_main.cpp index be29ecefc883..dec01cbce0c5 100644 --- a/main/tests/test_main.cpp +++ b/main/tests/test_main.cpp @@ -36,6 +36,7 @@ #include "test_astar.h" #include "test_basis.h" +#include "test_crypto.h" #include "test_gdscript.h" #include "test_gui.h" #include "test_math.h" @@ -109,6 +110,10 @@ MainLoop *test_main(String p_test, const List &p_args) { return TestShaderLang::test(); } + if (p_test == "crypto") { + return TestCrypto::test(); + } + if (p_test == "gd_tokenizer") { return TestGDScript::test(TestGDScript::TEST_TOKENIZER); } diff --git a/modules/mbedtls/crypto_mbedtls.cpp b/modules/mbedtls/crypto_mbedtls.cpp index 2b62242bd37e..f4e117db0da0 100644 --- a/modules/mbedtls/crypto_mbedtls.cpp +++ b/modules/mbedtls/crypto_mbedtls.cpp @@ -44,6 +44,7 @@ #define PEM_END_CRT "-----END CERTIFICATE-----\n" #include +#include #include CryptoKey *CryptoKeyMbedTLS::create() { @@ -192,6 +193,68 @@ Error X509CertificateMbedTLS::save(String p_path) { return OK; } +bool HMACContextMbedTLS::is_md_type_allowed(mbedtls_md_type_t p_md_type) { + switch (p_md_type) { + case MBEDTLS_MD_SHA1: + case MBEDTLS_MD_SHA256: + return true; + default: + return false; + } +} + +HMACContext *HMACContextMbedTLS::create() { + return memnew(HMACContextMbedTLS); +} + +Error HMACContextMbedTLS::start(HashingContext::HashType p_hash_type, PoolByteArray p_key) { + ERR_FAIL_COND_V_MSG(ctx != nullptr, ERR_FILE_ALREADY_IN_USE, "HMACContext already started."); + + // HMAC keys can be any size. + ERR_FAIL_COND_V_MSG(p_key.empty(), ERR_INVALID_PARAMETER, "Key must not be empty."); + + hash_type = p_hash_type; + mbedtls_md_type_t ht = CryptoMbedTLS::md_type_from_hashtype(p_hash_type, hash_len); + + bool allowed = HMACContextMbedTLS::is_md_type_allowed(ht); + ERR_FAIL_COND_V_MSG(!allowed, ERR_INVALID_PARAMETER, "Unsupported hash type."); + + ctx = memalloc(sizeof(mbedtls_md_context_t)); + mbedtls_md_init((mbedtls_md_context_t *)ctx); + + mbedtls_md_setup((mbedtls_md_context_t *)ctx, mbedtls_md_info_from_type((mbedtls_md_type_t)ht), 1); + int ret = mbedtls_md_hmac_starts((mbedtls_md_context_t *)ctx, (const uint8_t *)p_key.read().ptr(), (size_t)p_key.size()); + return ret ? FAILED : OK; +} + +Error HMACContextMbedTLS::update(PoolByteArray p_data) { + ERR_FAIL_COND_V_MSG(ctx == nullptr, ERR_INVALID_DATA, "Start must be called before update."); + + ERR_FAIL_COND_V_MSG(p_data.empty(), ERR_INVALID_PARAMETER, "Src must not be empty."); + + int ret = mbedtls_md_hmac_update((mbedtls_md_context_t *)ctx, (const uint8_t *)p_data.read().ptr(), (size_t)p_data.size()); + return ret ? FAILED : OK; +} + +PoolByteArray HMACContextMbedTLS::finish() { + ERR_FAIL_COND_V_MSG(ctx == nullptr, PoolByteArray(), "Start must be called before finish."); + ERR_FAIL_COND_V_MSG(hash_len == 0, PoolByteArray(), "Unsupported hash type."); + + PoolByteArray out; + out.resize(hash_len); + + unsigned char *out_ptr = (unsigned char *)out.write().ptr(); + int ret = mbedtls_md_hmac_finish((mbedtls_md_context_t *)ctx, out_ptr); + + mbedtls_md_free((mbedtls_md_context_t *)ctx); + memfree((mbedtls_md_context_t *)ctx); + ctx = nullptr; + hash_len = 0; + + ERR_FAIL_COND_V_MSG(ret, PoolByteArray(), "Error received while finishing HMAC"); + return out; +} + Crypto *CryptoMbedTLS::create() { return memnew(CryptoMbedTLS); } @@ -205,6 +268,7 @@ void CryptoMbedTLS::initialize_crypto() { Crypto::_load_default_certificates = load_default_certificates; X509CertificateMbedTLS::make_default(); CryptoKeyMbedTLS::make_default(); + HMACContextMbedTLS::make_default(); } void CryptoMbedTLS::finalize_crypto() { @@ -216,6 +280,7 @@ void CryptoMbedTLS::finalize_crypto() { } X509CertificateMbedTLS::finalize(); CryptoKeyMbedTLS::finalize(); + HMACContextMbedTLS::finalize(); } CryptoMbedTLS::CryptoMbedTLS() { @@ -320,7 +385,7 @@ PoolByteArray CryptoMbedTLS::generate_random_bytes(int p_bytes) { return out; } -mbedtls_md_type_t CryptoMbedTLS::_md_type_from_hashtype(HashingContext::HashType p_hash_type, int &r_size) { +mbedtls_md_type_t CryptoMbedTLS::md_type_from_hashtype(HashingContext::HashType p_hash_type, int &r_size) { switch (p_hash_type) { case HashingContext::HASH_MD5: r_size = 16; @@ -339,7 +404,7 @@ mbedtls_md_type_t CryptoMbedTLS::_md_type_from_hashtype(HashingContext::HashType Vector CryptoMbedTLS::sign(HashingContext::HashType p_hash_type, Vector p_hash, Ref p_key) { int size; - mbedtls_md_type_t type = _md_type_from_hashtype(p_hash_type, size); + mbedtls_md_type_t type = CryptoMbedTLS::md_type_from_hashtype(p_hash_type, size); ERR_FAIL_COND_V_MSG(type == MBEDTLS_MD_NONE, Vector(), "Invalid hash type."); ERR_FAIL_COND_V_MSG(p_hash.size() != size, Vector(), "Invalid hash provided. Size must be " + itos(size)); Ref key = static_cast>(p_key); @@ -357,7 +422,7 @@ Vector CryptoMbedTLS::sign(HashingContext::HashType p_hash_type, Vector bool CryptoMbedTLS::verify(HashingContext::HashType p_hash_type, Vector p_hash, Vector p_signature, Ref p_key) { int size; - mbedtls_md_type_t type = _md_type_from_hashtype(p_hash_type, size); + mbedtls_md_type_t type = CryptoMbedTLS::md_type_from_hashtype(p_hash_type, size); ERR_FAIL_COND_V_MSG(type == MBEDTLS_MD_NONE, false, "Invalid hash type."); ERR_FAIL_COND_V_MSG(p_hash.size() != size, false, "Invalid hash provided. Size must be " + itos(size)); Ref key = static_cast>(p_key); diff --git a/modules/mbedtls/crypto_mbedtls.h b/modules/mbedtls/crypto_mbedtls.h index fe95c79b92f8..3e46aef4182c 100644 --- a/modules/mbedtls/crypto_mbedtls.h +++ b/modules/mbedtls/crypto_mbedtls.h @@ -101,12 +101,31 @@ class X509CertificateMbedTLS : public X509Certificate { friend class SSLContextMbedTLS; }; +class HMACContextMbedTLS : public HMACContext { +private: + HashingContext::HashType hash_type; + int hash_len = 0; + void *ctx = nullptr; + +public: + static HMACContext *create(); + static void make_default() { HMACContext::_create = create; } + static void finalize() { HMACContext::_create = nullptr; } + + static bool is_md_type_allowed(mbedtls_md_type_t p_md_type); + + virtual Error start(HashingContext::HashType p_hash_type, PoolByteArray p_key); + virtual Error update(PoolByteArray p_data); + virtual PoolByteArray finish(); + + HMACContextMbedTLS() {} +}; + class CryptoMbedTLS : public Crypto { private: mbedtls_entropy_context entropy; mbedtls_ctr_drbg_context ctr_drbg; static X509CertificateMbedTLS *default_certs; - mbedtls_md_type_t _md_type_from_hashtype(HashingContext::HashType p_hash_type, int &r_size); public: static Crypto *create(); @@ -114,6 +133,7 @@ class CryptoMbedTLS : public Crypto { static void finalize_crypto(); static X509CertificateMbedTLS *get_default_certificates(); static void load_default_certificates(String p_path); + static mbedtls_md_type_t md_type_from_hashtype(HashingContext::HashType p_hash_type, int &r_size); virtual PoolByteArray generate_random_bytes(int p_bytes); virtual Ref generate_rsa(int p_bytes);