Skip to content

Commit

Permalink
Implement support for PCK signing.
Browse files Browse the repository at this point in the history
  • Loading branch information
bruvzg committed Jan 29, 2024
1 parent 17e7f85 commit 38f7db6
Show file tree
Hide file tree
Showing 25 changed files with 1,308 additions and 172 deletions.
37 changes: 32 additions & 5 deletions core/SCsub
Expand Up @@ -7,12 +7,17 @@ import core_builders
env.core_sources = []


# Generate AES256 script encryption key
# Generate AES256 PCK encryption key and ECDSA PCK signature public key.
import os
import base64

txt = "0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0"
if "SCRIPT_AES256_ENCRYPTION_KEY" in os.environ:
key = os.environ["SCRIPT_AES256_ENCRYPTION_KEY"]
if ("SCRIPT_AES256_ENCRYPTION_KEY" in os.environ) or ("PCK_AES256_ENCRYPTION_KEY" in os.environ):
key = ""
if "SCRIPT_AES256_ENCRYPTION_KEY" in os.environ:
key = os.environ["SCRIPT_AES256_ENCRYPTION_KEY"]
elif "PCK_AES256_ENCRYPTION_KEY" in os.environ:
key = os.environ["PCK_AES256_ENCRYPTION_KEY"]
ec_valid = True
if len(key) != 64:
ec_valid = False
Expand All @@ -30,15 +35,37 @@ if "SCRIPT_AES256_ENCRYPTION_KEY" in os.environ:
if not ec_valid:
print("Error: Invalid AES256 encryption key, not 64 hexadecimal characters: '" + key + "'.")
print(
"Unset 'SCRIPT_AES256_ENCRYPTION_KEY' in your environment "
"Unset 'PCK_AES256_ENCRYPTION_KEY' in your environment "
"or make sure that it contains exactly 64 hexadecimal characters."
)
Exit(255)

pk_len = 0
pk_txt = ""
if "PCK_SIGNING_PUBLIC_KEY" in os.environ:
pub_key = os.environ["PCK_SIGNING_PUBLIC_KEY"]
key = base64.b64decode(pub_key).hex()

pk_len = len(key) >> 1
for i in range(pk_len):
if i > 0:
pk_txt += ","
pk_txt += "0x" + key[i * 2 : i * 2 + 2]
env.Append(CPPDEFINES=["PCK_SIGNING_ENABLED"])

# NOTE: It is safe to generate this file here, since this is still executed serially
with open("script_encryption_key.gen.cpp", "w") as f:
f.write('#include "core/config/project_settings.h"\nuint8_t script_encryption_key[32]={' + txt + "};\n")
f.write('#include "core/config/project_settings.h"\n')
f.write("uint8_t pck_encryption_key[32]={" + txt + "};\n")
f.write("uint8_t pck_sign_pub_key[" + str(pk_len) + "]={" + pk_txt + "};\n")

with open("script_encryption_key.gen.h", "w") as f:
f.write("extern uint8_t pck_encryption_key[32];\n")
f.write("extern uint8_t pck_sign_pub_key[" + str(pk_len) + "];\n")
f.write("size_t pck_sign_pub_key_len=" + str(pk_len) + ";\n")

if env["module_mbedtls_enabled"] or env.editor_build or "PCK_SIGNING_PUBLIC_KEY" in os.environ:
env.Append(CPPDEFINES=["ECDSA_ENABLED"])

# Add required thirdparty code.

Expand Down
33 changes: 29 additions & 4 deletions core/crypto/SCsub
Expand Up @@ -2,6 +2,8 @@

Import("env")

import os

env_crypto = env.Clone()

is_builtin = env["builtin_mbedtls"]
Expand All @@ -21,9 +23,14 @@ if is_builtin or not has_module:
# to make a "light" build with only the necessary mbedtls files.
if not has_module:
# Minimal mbedTLS config file
env_crypto.Append(
CPPDEFINES=[("MBEDTLS_CONFIG_FILE", '\\"thirdparty/mbedtls/include/godot_core_mbedtls_config.h\\"')]
)
if env.editor_build or "PCK_SIGNING_PUBLIC_KEY" in os.environ:
env_crypto.Append(
CPPDEFINES=[("MBEDTLS_CONFIG_FILE", '\\"thirdparty/mbedtls/include/godot_core_ecdsa_mbedtls_config.h\\"')]
)
else:
env_crypto.Append(
CPPDEFINES=[("MBEDTLS_CONFIG_FILE", '\\"thirdparty/mbedtls/include/godot_core_mbedtls_config.h\\"')]
)
# Build minimal mbedTLS library (MD5/SHA/Base64/AES).
env_thirdparty = env_crypto.Clone()
env_thirdparty.disable_warnings()
Expand All @@ -39,10 +46,28 @@ if not has_module:
"sha256.c",
"godot_core_mbedtls_platform.c",
]
if env.editor_build or "PCK_SIGNING_PUBLIC_KEY" in os.environ:
# PCK siginig is enabled or with editor.
thirdparty_mbedtls_sources += [
"asn1parse.c",
"asn1write.c",
"bignum.c",
"ecdsa.c",
"ecp.c",
"ecp_curves.c",
"hmac_drbg.c",
"md.c",
"platform.c",
"platform_util.c",
"threading.c",
]
env_thirdparty.Depends(thirdparty_obj, "#thirdparty/mbedtls/include/godot_core_ecdsa_mbedtls_config.h")
else:
env_thirdparty.Depends(thirdparty_obj, "#thirdparty/mbedtls/include/godot_core_mbedtls_config.h")

thirdparty_mbedtls_sources = [thirdparty_mbedtls_dir + file for file in thirdparty_mbedtls_sources]
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_mbedtls_sources)
# Needed to force rebuilding the library when the configuration file is updated.
env_thirdparty.Depends(thirdparty_obj, "#thirdparty/mbedtls/include/godot_core_mbedtls_config.h")
env.core_sources += thirdparty_obj
elif is_builtin:
# Module mbedTLS config file
Expand Down
105 changes: 105 additions & 0 deletions core/crypto/crypto_core.cpp
Expand Up @@ -40,6 +40,9 @@
#include <mbedtls/sha1.h>
#include <mbedtls/sha256.h>

#include <mbedtls/ecdsa.h>
#include <mbedtls/ecp.h>

// RandomGenerator
CryptoCore::RandomGenerator::RandomGenerator() {
entropy = memalloc(sizeof(mbedtls_entropy_context));
Expand Down Expand Up @@ -246,3 +249,105 @@ Error CryptoCore::sha256(const uint8_t *p_src, int p_src_len, unsigned char r_ha
int ret = mbedtls_sha256_ret(p_src, p_src_len, r_hash, 0);
return ret ? FAILED : OK;
}

#ifdef ECDSA_ENABLED

#define CHECK_COND_V(m_cond, m_retval) \
if (unlikely(m_cond)) { \
if (silent) { \
return m_retval; \
} else { \
ERR_FAIL_COND_V(m_cond, m_retval); \
} \
}

CryptoCore::ECDSAContext::ECDSAContext(CryptoCore::ECDSAContext::CurveType p_curve) {
curve_type = p_curve;
entropy = memalloc(sizeof(mbedtls_entropy_context));
mbedtls_entropy_init((mbedtls_entropy_context *)entropy);

ctr_drbg = memalloc(sizeof(mbedtls_ctr_drbg_context));
mbedtls_ctr_drbg_init((mbedtls_ctr_drbg_context *)ctr_drbg);
mbedtls_ctr_drbg_seed((mbedtls_ctr_drbg_context *)ctr_drbg, mbedtls_entropy_func, (mbedtls_entropy_context *)entropy, nullptr, 0);

ctx = memalloc(sizeof(mbedtls_ecdsa_context));
mbedtls_ecdsa_init((mbedtls_ecdsa_context *)ctx);

keypair = memalloc(sizeof(mbedtls_ecp_keypair));
mbedtls_ecp_keypair_init((mbedtls_ecp_keypair *)keypair);
}

CryptoCore::ECDSAContext::~ECDSAContext() {
mbedtls_ecp_keypair_free((mbedtls_ecp_keypair *)keypair);
mbedtls_ecdsa_free((mbedtls_ecdsa_context *)ctx);
mbedtls_entropy_free((mbedtls_entropy_context *)entropy);
mbedtls_ctr_drbg_free((mbedtls_ctr_drbg_context *)ctr_drbg);
}

void CryptoCore::ECDSAContext::set_silent(bool p_silent) {
silent = p_silent;
}

Error CryptoCore::ECDSAContext::validate_private_key(const uint8_t *p_priv_key, size_t p_priv_len) {
mbedtls_ecp_keypair keypair_val;
mbedtls_ecp_keypair_init(&keypair_val);

int priv_ok = mbedtls_ecp_read_key((mbedtls_ecp_group_id)curve_type, &keypair_val, (const unsigned char *)p_priv_key, p_priv_len);
if (priv_ok == 0) {
priv_ok = mbedtls_ecp_check_privkey(&(keypair_val.grp), &(keypair_val.d));
}
mbedtls_ecp_keypair_free(&keypair_val);

return (priv_ok == 0) ? OK : FAILED;
}

Error CryptoCore::ECDSAContext::validate_public_key(const uint8_t *p_pub_key, size_t p_pub_len) {
mbedtls_ecp_keypair keypair_val;
mbedtls_ecp_keypair_init(&keypair_val);
mbedtls_ecp_group_load(&(keypair_val.grp), (mbedtls_ecp_group_id)curve_type);

int pub_ok = mbedtls_ecp_point_read_binary(&(keypair_val.grp), &(keypair_val.Q), (const unsigned char *)p_pub_key, p_pub_len);
if (pub_ok == 0) {
pub_ok = mbedtls_ecp_check_pubkey(&(keypair_val.grp), &(keypair_val.Q));
}
mbedtls_ecp_keypair_free(&keypair_val);

return (pub_ok == 0) ? OK : FAILED;
}

Error CryptoCore::ECDSAContext::generate_key_pair(uint8_t *p_priv_key, size_t p_priv_len, size_t *r_priv_len, uint8_t *p_pub_key, size_t p_pub_len, size_t *r_pub_len) {
CHECK_COND_V(mbedtls_ecp_gen_key((mbedtls_ecp_group_id)curve_type, (mbedtls_ecp_keypair *)keypair, mbedtls_ctr_drbg_random, (mbedtls_ctr_drbg_context *)ctr_drbg) != 0, FAILED);
CHECK_COND_V(mbedtls_ecdsa_from_keypair((mbedtls_ecdsa_context *)ctx, (mbedtls_ecp_keypair *)keypair) != 0, FAILED);
size_t len = MIN(size_t((((mbedtls_ecp_keypair *)keypair)->grp.pbits + 7) / 8), p_priv_len);
CHECK_COND_V(mbedtls_ecp_write_key((mbedtls_ecp_keypair *)keypair, (unsigned char *)p_priv_key, len) != 0, FAILED);
*r_priv_len = len;
CHECK_COND_V(mbedtls_ecp_point_write_binary(&(((mbedtls_ecp_keypair *)keypair)->grp), &(((mbedtls_ecp_keypair *)keypair)->Q), MBEDTLS_ECP_PF_UNCOMPRESSED, r_pub_len, (unsigned char *)p_pub_key, p_pub_len) != 0, FAILED);
return OK;
}

Error CryptoCore::ECDSAContext::set_public_key(const uint8_t *p_key, size_t p_len) {
mbedtls_ecp_group_load(&(((mbedtls_ecp_keypair *)keypair)->grp), (mbedtls_ecp_group_id)curve_type);
CHECK_COND_V(mbedtls_ecp_point_read_binary(&(((mbedtls_ecp_keypair *)keypair)->grp), &(((mbedtls_ecp_keypair *)keypair)->Q), (const unsigned char *)p_key, p_len) != 0, FAILED);
CHECK_COND_V(mbedtls_ecdsa_from_keypair((mbedtls_ecdsa_context *)ctx, (mbedtls_ecp_keypair *)keypair) != 0, FAILED);
return OK;
}

Error CryptoCore::ECDSAContext::set_private_key(const uint8_t *p_key, size_t p_len) {
CHECK_COND_V(mbedtls_ecp_read_key((mbedtls_ecp_group_id)curve_type, (mbedtls_ecp_keypair *)keypair, (const unsigned char *)p_key, p_len) != 0, FAILED);
CHECK_COND_V(mbedtls_ecdsa_from_keypair((mbedtls_ecdsa_context *)ctx, (mbedtls_ecp_keypair *)keypair) != 0, FAILED);
return OK;
}

Error CryptoCore::ECDSAContext::sign(const unsigned char *p_hash_sha256, uint8_t *r_signature, size_t *r_signature_len) {
CHECK_COND_V(mbedtls_ecdsa_write_signature((mbedtls_ecdsa_context *)ctx, MBEDTLS_MD_SHA256, p_hash_sha256, 32, r_signature, r_signature_len, mbedtls_ctr_drbg_random, (mbedtls_ctr_drbg_context *)ctr_drbg) != 0, FAILED);
return OK;
}

Error CryptoCore::ECDSAContext::verify(const unsigned char *p_hash_sha256, uint8_t *p_signature, size_t p_signature_len) {
CHECK_COND_V(mbedtls_ecdsa_read_signature((mbedtls_ecdsa_context *)ctx, p_hash_sha256, 32, p_signature, p_signature_len) != 0, FAILED);
return OK;
}

#undef CHECK_COND_V

#endif //ECDSA_ENABLED
49 changes: 49 additions & 0 deletions core/crypto/crypto_core.h
Expand Up @@ -114,6 +114,55 @@ class CryptoCore {
static Error md5(const uint8_t *p_src, int p_src_len, unsigned char r_hash[16]);
static Error sha1(const uint8_t *p_src, int p_src_len, unsigned char r_hash[20]);
static Error sha256(const uint8_t *p_src, int p_src_len, unsigned char r_hash[32]);

#ifdef ECDSA_ENABLED

class ECDSAContext {
public:
enum CurveType {
ECP_DP_NONE,
ECP_DP_SECP192R1,
ECP_DP_SECP224R1,
ECP_DP_SECP256R1,
ECP_DP_SECP384R1,
ECP_DP_SECP521R1,
ECP_DP_BP256R1,
ECP_DP_BP384R1,
ECP_DP_BP512R1,
ECP_DP_CURVE25519,
ECP_DP_SECP192K1,
ECP_DP_SECP224K1,
ECP_DP_SECP256K1,
ECP_DP_CURVE448,
};

private:
CurveType curve_type = ECP_DP_SECP256R1;
void *entropy = nullptr;
void *ctr_drbg = nullptr;
void *ctx = nullptr;
void *keypair = nullptr;
bool silent = false;

public:
ECDSAContext(CurveType p_curve = ECP_DP_BP256R1);
~ECDSAContext();

void set_silent(bool p_silent);

Error validate_private_key(const uint8_t *p_priv_key, size_t p_priv_len);
Error validate_public_key(const uint8_t *p_pub_key, size_t p_pub_len);

Error generate_key_pair(uint8_t *p_priv_key, size_t p_priv_len, size_t *r_priv_len, uint8_t *p_pub_key, size_t p_pub_len, size_t *r_pub_len);

Error set_public_key(const uint8_t *p_key, size_t p_len);
Error set_private_key(const uint8_t *p_key, size_t p_len);

Error sign(const unsigned char *p_hash_sha256, uint8_t *r_signature, size_t *r_signature_len);
Error verify(const unsigned char *p_hash_sha256, uint8_t *p_signature, size_t p_signature_len);
};

#endif
};

#endif // CRYPTO_CORE_H

0 comments on commit 38f7db6

Please sign in to comment.