diff --git a/.github/workflows/build_android.yml b/.github/workflows/build_android.yml index 554ee04ff..47a96071c 100644 --- a/.github/workflows/build_android.yml +++ b/.github/workflows/build_android.yml @@ -36,33 +36,32 @@ jobs: env: COMMON_FLAGS: "-O1 -s -fno-stack-protector -fno-math-errno -Qn -Werror" DROID_FLAGS: "-fPIC -shared -fvisibility=hidden -rdynamic -funwind-tables" - DROID_LIBS: "-lGLESv2 -lEGL -lm -landroid -llog" + LIBS: "-lGLESv2 -lEGL -lm -landroid -llog" + SRCS: "src/*.c third_party/bearssl/src/*.c" run: | LATEST_FLAG=-DCC_COMMIT_SHA=\"${GITHUB_SHA::9}\" DROID_FLAGS="-fPIC -shared -s -O1 -fvisibility=hidden -rdynamic -funwind-tables" - DROID_LIBS="-lGLESv2 -lEGL -lm -landroid -llog" ROOT_DIR=$PWD NDK_ROOT="/opt/android-sdk-linux/ndk/25.2.9519653/toolchains/llvm/prebuilt/linux-x86_64/bin" TOOLS_ROOT=$ROOT_DIR/build-tools SDK_ROOT="/opt/android-sdk-linux/platforms/android-34" - cd $ROOT_DIR/src - $NDK_ROOT/armv7a-linux-androideabi19-clang *.c $COMMON_FLAGS $DROID_FLAGS -march=armv5 -rtlib=libgcc -L $ROOT_DIR/build-tools/runtime $DROID_LIBS $LATEST_FLAG -o cc-droid-arm_16 - $NDK_ROOT/armv7a-linux-androideabi19-clang *.c $COMMON_FLAGS $DROID_FLAGS $DROID_LIBS $LATEST_FLAG -o cc-droid-arm_32 - $NDK_ROOT/aarch64-linux-android21-clang *.c $COMMON_FLAGS $DROID_FLAGS $DROID_LIBS $LATEST_FLAG -o cc-droid-arm_64 - $NDK_ROOT/i686-linux-android21-clang *.c $COMMON_FLAGS $DROID_FLAGS $DROID_LIBS $LATEST_FLAG -o cc-droid-x86_32 - $NDK_ROOT/x86_64-linux-android21-clang *.c $COMMON_FLAGS $DROID_FLAGS $DROID_LIBS $LATEST_FLAG -o cc-droid-x86_64 + $NDK_ROOT/armv7a-linux-androideabi19-clang ${{ env.SRCS }} $COMMON_FLAGS $DROID_FLAGS -march=armv5 -rtlib=libgcc -L $ROOT_DIR/build-tools/runtime ${{ env.LIBS }} $LATEST_FLAG -o cc-droid-arm_16 + $NDK_ROOT/armv7a-linux-androideabi19-clang ${{ env.SRCS }} $COMMON_FLAGS $DROID_FLAGS ${{ env.LIBS }} $LATEST_FLAG -o cc-droid-arm_32 + $NDK_ROOT/aarch64-linux-android21-clang ${{ env.SRCS }} $COMMON_FLAGS $DROID_FLAGS ${{ env.LIBS }} $LATEST_FLAG -o cc-droid-arm_64 + $NDK_ROOT/i686-linux-android21-clang ${{ env.SRCS }} $COMMON_FLAGS $DROID_FLAGS ${{ env.LIBS }} $LATEST_FLAG -o cc-droid-x86_32 + $NDK_ROOT/x86_64-linux-android21-clang ${{ env.SRCS }} $COMMON_FLAGS $DROID_FLAGS ${{ env.LIBS }} $LATEST_FLAG -o cc-droid-x86_64 cd $ROOT_DIR/android/app/src/main # copy required native libraries mkdir lib lib/armeabi lib/armeabi-v7a lib/arm64-v8a lib/x86 lib/x86_64 - cp $ROOT_DIR/src/cc-droid-arm_16 lib/armeabi/libclassicube.so - cp $ROOT_DIR/src/cc-droid-arm_32 lib/armeabi-v7a/libclassicube.so - cp $ROOT_DIR/src/cc-droid-arm_64 lib/arm64-v8a/libclassicube.so - cp $ROOT_DIR/src/cc-droid-x86_32 lib/x86/libclassicube.so - cp $ROOT_DIR/src/cc-droid-x86_64 lib/x86_64/libclassicube.so + cp $ROOT_DIR/cc-droid-arm_16 lib/armeabi/libclassicube.so + cp $ROOT_DIR/cc-droid-arm_32 lib/armeabi-v7a/libclassicube.so + cp $ROOT_DIR/cc-droid-arm_64 lib/arm64-v8a/libclassicube.so + cp $ROOT_DIR/cc-droid-x86_32 lib/x86/libclassicube.so + cp $ROOT_DIR/cc-droid-x86_64 lib/x86_64/libclassicube.so # The following commands are for manually building an .apk, see # https://spin.atomicobject.com/2011/08/22/building-android-application-bundles-apks-by-hand/ diff --git a/.gitignore b/.gitignore index 2669370b6..7e03f9bf7 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ *.VC.VC.opendb # Android build results +android/app/.cxx/ android/.cxx/ android/.idea/ android/.gradle/ diff --git a/android/app/CMakeLists.txt b/android/app/CMakeLists.txt index 644eef881..70100b5af 100644 --- a/android/app/CMakeLists.txt +++ b/android/app/CMakeLists.txt @@ -22,7 +22,6 @@ set(${CMAKE_C_FLAGS}, "${CMAKE_C_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11 -Wall -Werror") add_library(classicube SHARED - ../../src/main.c ../../src/IsometricDrawer.c ../../src/Builder.c ../../src/ExtMath.c @@ -107,9 +106,130 @@ add_library(classicube SHARED ../../src/MenuOptions.c ../../src/FancyLighting.c ../../src/Queue.c + ../../src/SSL.c + ../../src/Certs.c + ../../third_party/bearssl/src/aes_big_cbcdec.c + ../../third_party/bearssl/src/aes_big_cbcenc.c + ../../third_party/bearssl/src/aes_big_ctr.c + ../../third_party/bearssl/src/aes_big_ctrcbc.c + ../../third_party/bearssl/src/aes_big_dec.c + ../../third_party/bearssl/src/aes_big_enc.c + ../../third_party/bearssl/src/aes_common.c + ../../third_party/bearssl/src/aesctr_drbg.c + ../../third_party/bearssl/src/aes_x86ni.c + ../../third_party/bearssl/src/aes_x86ni_cbcdec.c + ../../third_party/bearssl/src/aes_x86ni_cbcenc.c + ../../third_party/bearssl/src/aes_x86ni_ctr.c + ../../third_party/bearssl/src/aes_x86ni_ctrcbc.c + ../../third_party/bearssl/src/asn1enc.c + ../../third_party/bearssl/src/ccm.c + ../../third_party/bearssl/src/ccopy.c + ../../third_party/bearssl/src/chacha20_ct.c + ../../third_party/bearssl/src/chacha20_sse2.c + ../../third_party/bearssl/src/dec32be.c + ../../third_party/bearssl/src/dec32le.c + ../../third_party/bearssl/src/dec64be.c + ../../third_party/bearssl/src/dec64le.c + ../../third_party/bearssl/src/dig_oid.c + ../../third_party/bearssl/src/dig_size.c + ../../third_party/bearssl/src/ec_all_m31.c + ../../third_party/bearssl/src/ec_c25519_i31.c + ../../third_party/bearssl/src/ec_c25519_m31.c + ../../third_party/bearssl/src/ec_c25519_m62.c + ../../third_party/bearssl/src/ec_c25519_m64.c + ../../third_party/bearssl/src/ec_curve25519.c + ../../third_party/bearssl/src/ec_default.c + ../../third_party/bearssl/src/ecdsa_atr.c + ../../third_party/bearssl/src/ecdsa_default_vrfy_asn1.c + ../../third_party/bearssl/src/ecdsa_default_vrfy_raw.c + ../../third_party/bearssl/src/ecdsa_i31_bits.c + ../../third_party/bearssl/src/ecdsa_i31_vrfy_asn1.c + ../../third_party/bearssl/src/ecdsa_i31_vrfy_raw.c + ../../third_party/bearssl/src/ec_p256_m31.c + ../../third_party/bearssl/src/ec_p256_m62.c + ../../third_party/bearssl/src/ec_p256_m64.c + ../../third_party/bearssl/src/ec_prime_i31.c + ../../third_party/bearssl/src/ec_secp256r1.c + ../../third_party/bearssl/src/ec_secp384r1.c + ../../third_party/bearssl/src/ec_secp521r1.c + ../../third_party/bearssl/src/enc32be.c + ../../third_party/bearssl/src/enc32le.c + ../../third_party/bearssl/src/enc64be.c + ../../third_party/bearssl/src/enc64le.c + ../../third_party/bearssl/src/gcm.c + ../../third_party/bearssl/src/ghash_ctmul64.c + ../../third_party/bearssl/src/ghash_ctmul.c + ../../third_party/bearssl/src/ghash_pclmul.c + ../../third_party/bearssl/src/hmac.c + ../../third_party/bearssl/src/hmac_ct.c + ../../third_party/bearssl/src/hmac_drbg.c + ../../third_party/bearssl/src/i31_add.c + ../../third_party/bearssl/src/i31_bitlen.c + ../../third_party/bearssl/src/i31_decmod.c + ../../third_party/bearssl/src/i31_decode.c + ../../third_party/bearssl/src/i31_decred.c + ../../third_party/bearssl/src/i31_encode.c + ../../third_party/bearssl/src/i31_fmont.c + ../../third_party/bearssl/src/i31_iszero.c + ../../third_party/bearssl/src/i31_moddiv.c + ../../third_party/bearssl/src/i31_modpow2.c + ../../third_party/bearssl/src/i31_modpow.c + ../../third_party/bearssl/src/i31_montmul.c + ../../third_party/bearssl/src/i31_mulacc.c + ../../third_party/bearssl/src/i31_muladd.c + ../../third_party/bearssl/src/i31_ninv31.c + ../../third_party/bearssl/src/i31_reduce.c + ../../third_party/bearssl/src/i31_rshift.c + ../../third_party/bearssl/src/i31_sub.c + ../../third_party/bearssl/src/i31_tmont.c + ../../third_party/bearssl/src/i32_div32.c + ../../third_party/bearssl/src/i62_modpow2.c + ../../third_party/bearssl/src/md5.c + ../../third_party/bearssl/src/md5sha1.c + ../../third_party/bearssl/src/multihash.c + ../../third_party/bearssl/src/poly1305_ctmul.c + ../../third_party/bearssl/src/poly1305_ctmulq.c + ../../third_party/bearssl/src/prf.c + ../../third_party/bearssl/src/prf_md5sha1.c + ../../third_party/bearssl/src/prf_sha256.c + ../../third_party/bearssl/src/prf_sha384.c + ../../third_party/bearssl/src/rsa_default_pkcs1_vrfy.c + ../../third_party/bearssl/src/rsa_default_priv.c + ../../third_party/bearssl/src/rsa_default_pub.c + ../../third_party/bearssl/src/rsa_i31_pkcs1_vrfy.c + ../../third_party/bearssl/src/rsa_i31_priv.c + ../../third_party/bearssl/src/rsa_i31_pub.c + ../../third_party/bearssl/src/rsa_i62_pkcs1_vrfy.c + ../../third_party/bearssl/src/rsa_i62_priv.c + ../../third_party/bearssl/src/rsa_i62_pub.c + ../../third_party/bearssl/src/rsa_pkcs1_sig_unpad.c + ../../third_party/bearssl/src/sha1.c + ../../third_party/bearssl/src/sha2big.c + ../../third_party/bearssl/src/sha2small.c + ../../third_party/bearssl/src/ssl_client.c + ../../third_party/bearssl/src/ssl_client_default_rsapub.c + ../../third_party/bearssl/src/ssl_client_full.c + ../../third_party/bearssl/src/ssl_engine.c + ../../third_party/bearssl/src/ssl_engine_default_aescbc.c + ../../third_party/bearssl/src/ssl_engine_default_aesccm.c + ../../third_party/bearssl/src/ssl_engine_default_aesgcm.c + ../../third_party/bearssl/src/ssl_engine_default_chapol.c + ../../third_party/bearssl/src/ssl_engine_default_ec.c + ../../third_party/bearssl/src/ssl_engine_default_ecdsa.c + ../../third_party/bearssl/src/ssl_engine_default_rsavrfy.c + ../../third_party/bearssl/src/ssl_hashes.c + ../../third_party/bearssl/src/ssl_hs_client.c + ../../third_party/bearssl/src/ssl_io.c + ../../third_party/bearssl/src/ssl_rec_cbc.c + ../../third_party/bearssl/src/ssl_rec_ccm.c + ../../third_party/bearssl/src/ssl_rec_chapol.c + ../../third_party/bearssl/src/ssl_rec_gcm.c + ../../third_party/bearssl/src/x509_minimal.c + ../../third_party/bearssl/src/x509_minimal_full.c ) -# add lib dependencies +target_include_directories(classicube PRIVATE + ../../third_party/bearssl/inc) target_link_libraries(classicube android EGL diff --git a/android/app/src/main/java/com/classicube/MainActivity.java b/android/app/src/main/java/com/classicube/MainActivity.java index 81f00a674..ec9885fdb 100644 --- a/android/app/src/main/java/com/classicube/MainActivity.java +++ b/android/app/src/main/java/com/classicube/MainActivity.java @@ -1,14 +1,16 @@ package com.classicube; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.lang.reflect.Method; import java.net.HttpURLConnection; import java.net.URL; -import java.nio.file.Files; +import java.security.KeyStore; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import java.util.Queue; @@ -28,25 +30,23 @@ import android.net.Uri; import android.os.Bundle; import android.provider.OpenableColumns; import android.provider.Settings.Secure; -import android.text.Editable; import android.text.InputType; -import android.text.Selection; -import android.text.SpannableStringBuilder; import android.util.DisplayMetrics; import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceHolder; -import android.view.SurfaceView; import android.view.WindowManager; import android.view.View; import android.view.Window; -import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + // This class contains all the glue/interop code for bridging ClassiCube to the java Android world. // Some functionality is only available on later Android versions - try {} catch {} is used in such places // to ensure that the game can still run on earlier Android versions (albeit with reduced functionality) @@ -1016,4 +1016,68 @@ public class MainActivity extends Activity native static void httpParseHeader(String header); native static void httpAppendData(byte[] data, int len); + + + // ====================================================================== + // -------------------------------- SSL --------------------------------- + // ====================================================================== + static X509TrustManager trust; + static CertificateFactory certFactory; + static ArrayList chain = new ArrayList(); + + static X509TrustManager CreateTrust() throws Exception { + TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + // Load default Trust Anchor certificates + factory.init((KeyStore)null); + + TrustManager[] trustManagers = factory.getTrustManagers(); + for (int i = 0; i < trustManagers.length; i++) + { + // Should be first entry, e.g. X509TrustManagerImpl + if (trustManagers[i] instanceof X509TrustManager) + return (X509TrustManager)trustManagers[i]; + } + return null; + } + + public static int sslCreateTrust() { + try { + trust = CreateTrust(); + return 1; + } catch (Exception ex) { + ex.printStackTrace(); + return 0; + } + } + + public static void sslAddCert(byte[] data) { + try { + if (certFactory == null) certFactory = CertificateFactory.getInstance("X.509"); + if (certFactory == null) return; + + InputStream in = new ByteArrayInputStream(data); + X509Certificate cert = (X509Certificate) certFactory.generateCertificate(in); + chain.add(cert); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + public static int sslVerifyChain() { + int result = -200; + try { + X509Certificate[] certs = new X509Certificate[chain.size()]; + for (int i = 0; i < chain.size(); i++) certs[i] = chain.get(i); + + trust.checkServerTrusted(certs, "INV"); + // standard java checks auth type, but android doesn't + // See https://issues.chromium.org/issues/40955801 + result = 0; + } catch (Exception ex) { + ex.printStackTrace(); + } + + chain.clear(); + return result; + } } diff --git a/src/Certs.c b/src/Certs.c index ae81ca155..baf3046c3 100644 --- a/src/Certs.c +++ b/src/Certs.c @@ -1,8 +1,7 @@ #include "Certs.h" - -#ifndef CC_CRT_BACKEND #include "Errors.h" +#ifndef CC_CRT_BACKEND void CertsBackend_Init(void) { } void Certs_BeginChain(struct X509CertContext* ctx) { } @@ -57,7 +56,6 @@ void Certs_FreeChain( struct X509CertContext* ctx) { } #if CC_CRT_BACKEND == CC_CRT_BACKEND_OPENSSL -#include "Errors.h" #include "Funcs.h" /* === BEGIN OPENSSL HEADERS === */ typedef struct X509_ X509; @@ -104,20 +102,13 @@ static cc_bool ossl_loaded; void CertsBackend_Init(void) { static const struct DynamicLibSym funcs[] = { - DynamicLib_ReqSym(OPENSSL_sk_new_null), - DynamicLib_ReqSym(OPENSSL_sk_push), - DynamicLib_ReqSym(OPENSSL_sk_pop_free), - DynamicLib_ReqSym(d2i_X509), - DynamicLib_ReqSym(X509_new), - DynamicLib_ReqSym(X509_free), - DynamicLib_ReqSym(X509_STORE_new), - DynamicLib_ReqSym(X509_STORE_set_default_paths), - DynamicLib_ReqSym(X509_STORE_CTX_new), - DynamicLib_ReqSym(X509_STORE_CTX_free), - DynamicLib_ReqSym(X509_STORE_CTX_get_error), - DynamicLib_ReqSym(X509_STORE_CTX_init), - DynamicLib_ReqSym(X509_verify_cert), - DynamicLib_ReqSym(X509_verify_cert_error_string), + DynamicLib_ReqSym(d2i_X509), DynamicLib_ReqSym(OPENSSL_sk_new_null), + DynamicLib_ReqSym(OPENSSL_sk_push), DynamicLib_ReqSym(OPENSSL_sk_pop_free), + DynamicLib_ReqSym(X509_new), DynamicLib_ReqSym(X509_free), + DynamicLib_ReqSym(X509_STORE_new), DynamicLib_ReqSym(X509_STORE_set_default_paths), + DynamicLib_ReqSym(X509_STORE_CTX_new), DynamicLib_ReqSym(X509_STORE_CTX_free), + DynamicLib_ReqSym(X509_STORE_CTX_init), DynamicLib_ReqSym(X509_STORE_CTX_get_error), + DynamicLib_ReqSym(X509_verify_cert), DynamicLib_ReqSym(X509_verify_cert_error_string), }; void* lib; @@ -148,7 +139,6 @@ int Certs_VerifyChain(struct X509CertContext* chain) { _X509_STORE_set_default_paths(store); } - if (!chain->numCerts) return ERR_INVALID_ARGUMENT; /* End/Leaf certificate */ cert = ToOpenSSLCert(&chain->certs[0]); @@ -190,7 +180,6 @@ int Certs_VerifyChain(struct X509CertContext* chain) { #include #include #endif -#include "Errors.h" void CertsBackend_Init(void) { @@ -248,8 +237,6 @@ int Certs_VerifyChain(struct X509CertContext* x509) { if (!policy) CreateX509Policy(); if (!policy) return ERR_OUT_OF_MEMORY; - if (!x509->numCerts) return ERR_NOT_SUPPORTED; - chain = CFArrayCreateMutable(NULL, x509->numCerts, &kCFTypeArrayCallBacks); if (!chain) return ERR_OUT_OF_MEMORY; @@ -269,6 +256,38 @@ int Certs_VerifyChain(struct X509CertContext* x509) { CFRelease(chain); return res; } +#elif CC_CRT_BACKEND == CC_CRT_BACKEND_ANDROID +static jmethodID JAVA_sslCreateTrust, JAVA_sslAddCert, JAVA_sslVerifyChain; +static int created_trust; + +void CertsBackend_Init(void) { + JNIEnv* env; + JavaGetCurrentEnv(env); + + JAVA_sslCreateTrust = JavaGetSMethod(env, "sslCreateTrust", "()I"); + JAVA_sslAddCert = JavaGetSMethod(env, "sslAddCert", "([B)V"); + JAVA_sslVerifyChain = JavaGetSMethod(env, "sslVerifyChain", "()I"); +} + +int Certs_VerifyChain(struct X509CertContext* x509) { + JNIEnv* env; + jvalue args[1]; + int i; + JavaGetCurrentEnv(env); + + if (!created_trust) created_trust = JavaSCall_Int(env, JAVA_sslCreateTrust, NULL); + if (!created_trust) return ERR_NOT_SUPPORTED; + + for (i = 0; i < x509->numCerts; i++) + { + struct X509Cert* cert = &x509->certs[i]; + args[0].l = JavaMakeBytes(env, cert->data, cert->offset); + JavaSCall_Void(env, JAVA_sslAddCert, args); + (*env)->DeleteLocalRef(env, args[0].l); + } + + return JavaSCall_Int(env, JAVA_sslVerifyChain, NULL); +} #endif #endif diff --git a/src/Core.h b/src/Core.h index 577eeacae..9cf21c090 100644 --- a/src/Core.h +++ b/src/Core.h @@ -156,6 +156,7 @@ typedef cc_uint8 cc_bool; #define CC_CRT_BACKEND_OPENSSL 1 #define CC_CRT_BACKEND_APPLESEC 2 +#define CC_CRT_BACKEND_ANDROID 3 #define CC_AUD_BACKEND_OPENAL 1 #define CC_AUD_BACKEND_WINMM 2 @@ -227,6 +228,9 @@ typedef cc_uint8 cc_bool; #define DEFAULT_AUD_BACKEND CC_AUD_BACKEND_OPENSLES #define DEFAULT_GFX_BACKEND CC_GFX_BACKEND_GL2 #define DEFAULT_WIN_BACKEND CC_WIN_BACKEND_ANDROID + #define DEFAULT_NET_BACKEND CC_NET_BACKEND_BUILTIN + #define DEFAULT_SSL_BACKEND CC_SSL_BACKEND_BEARSSL + #define DEFAULT_CRT_BACKEND CC_CRT_BACKEND_ANDROID #elif defined __serenity__ #define CC_BUILD_SERENITY #define CC_BUILD_POSIX diff --git a/src/SSL.c b/src/SSL.c index ba323ff61..07f6cf471 100644 --- a/src/SSL.c +++ b/src/SSL.c @@ -470,14 +470,22 @@ static unsigned x509_maybe_skip_verify(unsigned r) { return r; } +static unsigned x509_maybe_system_verify(struct SSLContext* ssl, unsigned r) { + /* Only fallback to system verification for potentially untrusted certificate case */ + /* This ensures consistent validation behaviour in other cases between all platforms */ + if (r != BR_ERR_X509_NOT_TRUSTED) return r; + if (!ssl->x509.numCerts) return r; + + if (Certs_VerifyChain(&ssl->x509) == 0) r = 0; + return r; +} + static unsigned x509_end_chain(const br_x509_class** ctx) { struct SSLContext* ssl = (struct SSLContext*)ctx; - + unsigned r = br_x509_minimal_vtable.end_chain(ctx); r = x509_maybe_skip_verify(r); - - /* Fallback to system specific certificate validation */ - if (r == BR_ERR_X509_NOT_TRUSTED && Certs_VerifyChain(&ssl->x509) == 0) r = 0; + r = x509_maybe_system_verify(ssl, r); Certs_FreeChain(&ssl->x509); return r;