diff options
Diffstat (limited to 'mozglue/android')
-rw-r--r-- | mozglue/android/APKOpen.cpp | 465 | ||||
-rw-r--r-- | mozglue/android/APKOpen.h | 39 | ||||
-rw-r--r-- | mozglue/android/NSSBridge.cpp | 295 | ||||
-rw-r--r-- | mozglue/android/NSSBridge.h | 46 | ||||
-rw-r--r-- | mozglue/android/NativeCrypto.cpp | 132 | ||||
-rw-r--r-- | mozglue/android/NativeCrypto.h | 53 | ||||
-rw-r--r-- | mozglue/android/SQLiteBridge.cpp | 413 | ||||
-rw-r--r-- | mozglue/android/SQLiteBridge.h | 34 | ||||
-rw-r--r-- | mozglue/android/SharedMemNatives.cpp | 65 | ||||
-rw-r--r-- | mozglue/android/moz.build | 58 | ||||
-rw-r--r-- | mozglue/android/nsGeckoUtils.cpp | 124 | ||||
-rw-r--r-- | mozglue/android/pbkdf2_sha256.c | 432 | ||||
-rw-r--r-- | mozglue/android/pbkdf2_sha256.h | 70 |
13 files changed, 2226 insertions, 0 deletions
diff --git a/mozglue/android/APKOpen.cpp b/mozglue/android/APKOpen.cpp new file mode 100644 index 000000000..1aabc155e --- /dev/null +++ b/mozglue/android/APKOpen.cpp @@ -0,0 +1,465 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * This custom library loading code is only meant to be called + * during initialization. As a result, it takes no special + * precautions to be threadsafe. Any of the library loading functions + * like mozload should not be available to other code. + */ + +#include <jni.h> +#include <android/log.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <sys/limits.h> +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <zlib.h> +#include "dlfcn.h" +#include "APKOpen.h" +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/prctl.h> +#include "sqlite3.h" +#include "SQLiteBridge.h" +#include "NSSBridge.h" +#include "ElfLoader.h" +#include "application.ini.h" + +#include "mozilla/TimeStamp.h" +#include "mozilla/UniquePtr.h" +#include "XREChildData.h" + +/* Android headers don't define RUSAGE_THREAD */ +#ifndef RUSAGE_THREAD +#define RUSAGE_THREAD 1 +#endif + +#ifndef RELEASE_OR_BETA +/* Official builds have the debuggable flag set to false, which disables + * the backtrace dumper from bionic. However, as it is useful for native + * crashes happening before the crash reporter is registered, re-enable + * it on non release builds (i.e. nightly and aurora). + * Using a constructor so that it is re-enabled as soon as libmozglue.so + * is loaded. + */ +__attribute__((constructor)) +void make_dumpable() { + prctl(PR_SET_DUMPABLE, 1); +} +#endif + +extern "C" { +/* + * To work around http://code.google.com/p/android/issues/detail?id=23203 + * we don't link with the crt objects. In some configurations, this means + * a lack of the __dso_handle symbol because it is defined there, and + * depending on the android platform and ndk versions used, it may or may + * not be defined in libc.so. In the latter case, we fail to link. Defining + * it here as weak makes us provide the symbol when it's not provided by + * the crt objects, making the change transparent for future NDKs that + * would fix the original problem. On older NDKs, it is not a problem + * either because the way __dso_handle was used was already broken (and + * the custom linker works around it). + */ + NS_EXPORT __attribute__((weak)) void *__dso_handle; +} + +typedef int mozglueresult; + +enum StartupEvent { +#define mozilla_StartupTimeline_Event(ev, z) ev, +#include "StartupTimeline.h" +#undef mozilla_StartupTimeline_Event + MAX_STARTUP_EVENT_ID +}; + +using namespace mozilla; + +static const int MAX_MAPPING_INFO = 32; +static mapping_info lib_mapping[MAX_MAPPING_INFO]; + +NS_EXPORT const struct mapping_info * +getLibraryMapping() +{ + return lib_mapping; +} + +void +JNI_Throw(JNIEnv* jenv, const char* classname, const char* msg) +{ + __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Throw\n"); + jclass cls = jenv->FindClass(classname); + if (cls == nullptr) { + __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't find exception class (or exception pending) %s\n", classname); + exit(FAILURE); + } + int rc = jenv->ThrowNew(cls, msg); + if (rc < 0) { + __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Error throwing exception %s\n", msg); + exit(FAILURE); + } + jenv->DeleteLocalRef(cls); +} + +namespace { + JavaVM* sJavaVM; + pthread_t sJavaUiThread; +} + +void +abortThroughJava(const char* msg) +{ + struct sigaction sigact = {}; + if (SEGVHandler::__wrap_sigaction(SIGSEGV, nullptr, &sigact)) { + return; // sigaction call failed. + } + + Dl_info info = {}; + if ((sigact.sa_flags & SA_SIGINFO) && + __wrap_dladdr(reinterpret_cast<void*>(sigact.sa_sigaction), &info) && + info.dli_fname && strstr(info.dli_fname, "libxul.so")) { + + return; // Existing signal handler is in libxul (i.e. we have crash reporter). + } + + JNIEnv* env = nullptr; + if (!sJavaVM || sJavaVM->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) { + return; + } + + if (!env || env->PushLocalFrame(2) != JNI_OK) { + return; + } + + jclass loader = env->FindClass("org/mozilla/gecko/mozglue/GeckoLoader"); + if (!loader) { + return; + } + + jmethodID method = env->GetStaticMethodID(loader, "abort", "(Ljava/lang/String;)V"); + jstring str = env->NewStringUTF(msg); + + if (method && str) { + env->CallStaticVoidMethod(loader, method, str); + } + + env->PopLocalFrame(nullptr); +} + +NS_EXPORT pthread_t +getJavaUiThread() +{ + return sJavaUiThread; +} + +extern "C" NS_EXPORT void MOZ_JNICALL +Java_org_mozilla_gecko_GeckoThread_registerUiThread(JNIEnv*, jclass) +{ + sJavaUiThread = pthread_self(); +} + +static void * xul_handle = nullptr; +#ifndef MOZ_FOLD_LIBS +static void * sqlite_handle = nullptr; +static void * nspr_handle = nullptr; +static void * plc_handle = nullptr; +#else +#define sqlite_handle nss_handle +#define nspr_handle nss_handle +#define plc_handle nss_handle +#endif +static void * nss_handle = nullptr; + +template <typename T> inline void +xul_dlsym(const char *symbolName, T *value) +{ + *value = (T) (uintptr_t) __wrap_dlsym(xul_handle, symbolName); +} + +static int mapping_count = 0; + +extern "C" void +report_mapping(char *name, void *base, uint32_t len, uint32_t offset) +{ + if (mapping_count >= MAX_MAPPING_INFO) + return; + + struct mapping_info *info = &lib_mapping[mapping_count++]; + info->name = strdup(name); + info->base = (uintptr_t)base; + info->len = len; + info->offset = offset; +} + +extern "C" void +delete_mapping(const char *name) +{ + for (int pos = 0; pos < mapping_count; ++pos) { + struct mapping_info *info = &lib_mapping[pos]; + if (!strcmp(info->name, name)) { + struct mapping_info *last = &lib_mapping[mapping_count - 1]; + free(info->name); + *info = *last; + --mapping_count; + break; + } + } +} + +static void* +dlopenAPKLibrary(const char* apkName, const char* libraryName) +{ +#define APK_ASSETS_PATH "!/assets/" ANDROID_CPU_ARCH "/" + size_t filenameLength = strlen(apkName) + + sizeof(APK_ASSETS_PATH) + // includes \0 terminator + strlen(libraryName); + auto file = MakeUnique<char[]>(filenameLength); + snprintf(file.get(), filenameLength, "%s" APK_ASSETS_PATH "%s", + apkName, libraryName); + return __wrap_dlopen(file.get(), RTLD_GLOBAL | RTLD_LAZY); +#undef APK_ASSETS_PATH +} +static mozglueresult +loadGeckoLibs(const char *apkName) +{ + TimeStamp t0 = TimeStamp::Now(); + struct rusage usage1_thread, usage1; + getrusage(RUSAGE_THREAD, &usage1_thread); + getrusage(RUSAGE_SELF, &usage1); + + xul_handle = dlopenAPKLibrary(apkName, "libxul.so"); + if (!xul_handle) { + __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libxul!"); + return FAILURE; + } + + void (*XRE_StartupTimelineRecord)(int, TimeStamp); + xul_dlsym("XRE_StartupTimelineRecord", &XRE_StartupTimelineRecord); + + TimeStamp t1 = TimeStamp::Now(); + struct rusage usage2_thread, usage2; + getrusage(RUSAGE_THREAD, &usage2_thread); + getrusage(RUSAGE_SELF, &usage2); + +#define RUSAGE_TIMEDIFF(u1, u2, field) \ + ((u2.ru_ ## field.tv_sec - u1.ru_ ## field.tv_sec) * 1000 + \ + (u2.ru_ ## field.tv_usec - u1.ru_ ## field.tv_usec) / 1000) + + __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Loaded libs in %fms total, %ldms(%ldms) user, %ldms(%ldms) system, %ld(%ld) faults", + (t1 - t0).ToMilliseconds(), + RUSAGE_TIMEDIFF(usage1_thread, usage2_thread, utime), + RUSAGE_TIMEDIFF(usage1, usage2, utime), + RUSAGE_TIMEDIFF(usage1_thread, usage2_thread, stime), + RUSAGE_TIMEDIFF(usage1, usage2, stime), + usage2_thread.ru_majflt - usage1_thread.ru_majflt, + usage2.ru_majflt - usage1.ru_majflt); + + XRE_StartupTimelineRecord(LINKER_INITIALIZED, t0); + XRE_StartupTimelineRecord(LIBRARIES_LOADED, t1); + return SUCCESS; +} + +static mozglueresult loadNSSLibs(const char *apkName); + +static mozglueresult +loadSQLiteLibs(const char *apkName) +{ + if (sqlite_handle) + return SUCCESS; + +#ifdef MOZ_FOLD_LIBS + if (loadNSSLibs(apkName) != SUCCESS) + return FAILURE; +#else + + sqlite_handle = dlopenAPKLibrary(apkName, "libmozsqlite3.so"); + if (!sqlite_handle) { + __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libmozsqlite3!"); + return FAILURE; + } +#endif + + setup_sqlite_functions(sqlite_handle); + return SUCCESS; +} + +static mozglueresult +loadNSSLibs(const char *apkName) +{ + if (nss_handle && nspr_handle && plc_handle) + return SUCCESS; + + nss_handle = dlopenAPKLibrary(apkName, "libnss3.so"); + +#ifndef MOZ_FOLD_LIBS + nspr_handle = dlopenAPKLibrary(apkName, "libnspr4.so"); + + plc_handle = dlopenAPKLibrary(apkName, "libplc4.so"); +#endif + + if (!nss_handle) { + __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libnss3!"); + return FAILURE; + } + +#ifndef MOZ_FOLD_LIBS + if (!nspr_handle) { + __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libnspr4!"); + return FAILURE; + } + + if (!plc_handle) { + __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Couldn't get a handle to libplc4!"); + return FAILURE; + } +#endif + + return setup_nss_functions(nss_handle, nspr_handle, plc_handle); +} + +extern "C" NS_EXPORT void MOZ_JNICALL +Java_org_mozilla_gecko_mozglue_GeckoLoader_extractGeckoLibsNative( + JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName) +{ + MOZ_ALWAYS_TRUE(!jenv->GetJavaVM(&sJavaVM)); + + const char* apkName = jenv->GetStringUTFChars(jApkName, nullptr); + if (apkName == nullptr) { + return; + } + + // Extract and cache native lib to allow for efficient startup from cache. + void* handle = dlopenAPKLibrary(apkName, "libxul.so"); + if (handle) { + __android_log_print(ANDROID_LOG_INFO, "GeckoLibLoad", + "Extracted and cached libxul.so."); + // We have extracted and cached the lib, we can close it now. + __wrap_dlclose(handle); + } else { + JNI_Throw(jenv, "java/lang/Exception", "Error extracting gecko libraries"); + } + + jenv->ReleaseStringUTFChars(jApkName, apkName); +} + +extern "C" NS_EXPORT void MOZ_JNICALL +Java_org_mozilla_gecko_mozglue_GeckoLoader_loadGeckoLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName) +{ + jenv->GetJavaVM(&sJavaVM); + + const char* str; + // XXX: java doesn't give us true UTF8, we should figure out something + // better to do here + str = jenv->GetStringUTFChars(jApkName, nullptr); + if (str == nullptr) + return; + + int res = loadGeckoLibs(str); + if (res != SUCCESS) { + JNI_Throw(jenv, "java/lang/Exception", "Error loading gecko libraries"); + } + jenv->ReleaseStringUTFChars(jApkName, str); +} + +extern "C" NS_EXPORT void MOZ_JNICALL +Java_org_mozilla_gecko_mozglue_GeckoLoader_loadSQLiteLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName) { + const char* str; + // XXX: java doesn't give us true UTF8, we should figure out something + // better to do here + str = jenv->GetStringUTFChars(jApkName, nullptr); + if (str == nullptr) + return; + + __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load sqlite start\n"); + mozglueresult rv = loadSQLiteLibs(str); + if (rv != SUCCESS) { + JNI_Throw(jenv, "java/lang/Exception", "Error loading sqlite libraries"); + } + __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load sqlite done\n"); + jenv->ReleaseStringUTFChars(jApkName, str); +} + +extern "C" NS_EXPORT void MOZ_JNICALL +Java_org_mozilla_gecko_mozglue_GeckoLoader_loadNSSLibsNative(JNIEnv *jenv, jclass jGeckoAppShellClass, jstring jApkName) { + const char* str; + // XXX: java doesn't give us true UTF8, we should figure out something + // better to do here + str = jenv->GetStringUTFChars(jApkName, nullptr); + if (str == nullptr) + return; + + __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load nss start\n"); + mozglueresult rv = loadNSSLibs(str); + if (rv != SUCCESS) { + JNI_Throw(jenv, "java/lang/Exception", "Error loading nss libraries"); + } + __android_log_print(ANDROID_LOG_ERROR, "GeckoLibLoad", "Load nss done\n"); + jenv->ReleaseStringUTFChars(jApkName, str); +} + +typedef void (*GeckoStart_t)(JNIEnv*, char*, const nsXREAppData*); + +extern "C" NS_EXPORT void MOZ_JNICALL +Java_org_mozilla_gecko_mozglue_GeckoLoader_nativeRun(JNIEnv *jenv, jclass jc, jstring jargs) +{ + GeckoStart_t GeckoStart; + xul_dlsym("GeckoStart", &GeckoStart); + if (GeckoStart == nullptr) + return; + // XXX: java doesn't give us true UTF8, we should figure out something + // better to do here + int len = jenv->GetStringUTFLength(jargs); + // GeckoStart needs to write in the args buffer, so we need a copy. + char *args = (char *) malloc(len + 1); + jenv->GetStringUTFRegion(jargs, 0, len, args); + args[len] = '\0'; + ElfLoader::Singleton.ExpectShutdown(false); + GeckoStart(jenv, args, &sAppData); + ElfLoader::Singleton.ExpectShutdown(true); + free(args); +} + +typedef int GeckoProcessType; + +extern "C" NS_EXPORT mozglueresult +ChildProcessInit(int argc, char* argv[]) +{ + int i; + for (i = 0; i < (argc - 1); i++) { + if (strcmp(argv[i], "-greomni")) + continue; + + i = i + 1; + break; + } + + if (loadNSSLibs(argv[i]) != SUCCESS) { + return FAILURE; + } + if (loadSQLiteLibs(argv[i]) != SUCCESS) { + return FAILURE; + } + if (loadGeckoLibs(argv[i]) != SUCCESS) { + return FAILURE; + } + + void (*fXRE_SetProcessType)(char*); + xul_dlsym("XRE_SetProcessType", &fXRE_SetProcessType); + + mozglueresult (*fXRE_InitChildProcess)(int, char**, void*); + xul_dlsym("XRE_InitChildProcess", &fXRE_InitChildProcess); + + fXRE_SetProcessType(argv[--argc]); + + XREChildData childData; + return fXRE_InitChildProcess(argc, argv, &childData); +} + diff --git a/mozglue/android/APKOpen.h b/mozglue/android/APKOpen.h new file mode 100644 index 000000000..f28458feb --- /dev/null +++ b/mozglue/android/APKOpen.h @@ -0,0 +1,39 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef APKOpen_h +#define APKOpen_h + +#include <jni.h> +#include <pthread.h> + +#ifndef NS_EXPORT +#define NS_EXPORT __attribute__ ((visibility("default"))) +#endif + +struct mapping_info { + char * name; + uintptr_t base; + size_t len; + size_t offset; +}; + +NS_EXPORT const struct mapping_info * getLibraryMapping(); +NS_EXPORT void abortThroughJava(const char* msg); +NS_EXPORT pthread_t getJavaUiThread(); + +static const int SUCCESS = 0; +static const int FAILURE = 1; +void JNI_Throw(JNIEnv* jenv, const char* classname, const char* msg); + +// Bug 1207642 - Work around Dalvik bug by realigning stack on JNI entry +#ifndef MOZ_JNICALL +# ifdef __i386__ +# define MOZ_JNICALL JNICALL __attribute__((force_align_arg_pointer)) +# else +# define MOZ_JNICALL JNICALL +# endif +#endif + +#endif /* APKOpen_h */ diff --git a/mozglue/android/NSSBridge.cpp b/mozglue/android/NSSBridge.cpp new file mode 100644 index 000000000..2a9b5771d --- /dev/null +++ b/mozglue/android/NSSBridge.cpp @@ -0,0 +1,295 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <stdlib.h> +#include "dlfcn.h" +#include "NSSBridge.h" +#include "APKOpen.h" +#ifdef ANDROID +#include <jni.h> +#include <android/log.h> +#endif + +#include "ElfLoader.h" + +#ifdef DEBUG +#define LOG(x...) __android_log_print(ANDROID_LOG_INFO, "GeckoJNI", x) +#else +#define LOG(x...) +#endif + +static bool initialized = false; + +#define NSS_WRAPPER_INT(name) name ## _t f_ ## name; +NSS_WRAPPER_INT(NSS_Initialize) +NSS_WRAPPER_INT(NSS_Shutdown) +NSS_WRAPPER_INT(SECITEM_ZfreeItem) +NSS_WRAPPER_INT(PK11SDR_Encrypt) +NSS_WRAPPER_INT(PK11SDR_Decrypt) +NSS_WRAPPER_INT(PK11_GetInternalKeySlot) +NSS_WRAPPER_INT(PK11_NeedUserInit) +NSS_WRAPPER_INT(PK11_InitPin) +NSS_WRAPPER_INT(PR_ErrorToString) +NSS_WRAPPER_INT(PR_GetError) +NSS_WRAPPER_INT(PR_Free) +NSS_WRAPPER_INT(PL_Base64Encode) +NSS_WRAPPER_INT(PL_Base64Decode) +NSS_WRAPPER_INT(PL_strfree) + +int +setup_nss_functions(void *nss_handle, + void *nspr_handle, + void *plc_handle) +{ + if (nss_handle == nullptr || nspr_handle == nullptr || plc_handle == nullptr) { + LOG("Missing handle\n"); + return FAILURE; + } +#define GETFUNC(name) f_ ## name = (name ## _t) (uintptr_t) __wrap_dlsym(nss_handle, #name); \ + if (!f_ ##name) { __android_log_print(ANDROID_LOG_ERROR, "GeckoJNI", "missing %s", #name); return FAILURE; } + GETFUNC(NSS_Initialize); + GETFUNC(NSS_Shutdown); + GETFUNC(PK11SDR_Encrypt); + GETFUNC(PK11SDR_Decrypt); + GETFUNC(PK11_GetInternalKeySlot); + GETFUNC(PK11_NeedUserInit); + GETFUNC(PK11_InitPin); + GETFUNC(SECITEM_ZfreeItem); +#undef GETFUNC +#define NSPRFUNC(name) f_ ## name = (name ## _t) (uintptr_t) __wrap_dlsym(nspr_handle, #name); \ + if (!f_ ##name) { __android_log_print(ANDROID_LOG_ERROR, "GeckoJNI", "missing %s", #name); return FAILURE; } + NSPRFUNC(PR_ErrorToString); + NSPRFUNC(PR_GetError); + NSPRFUNC(PR_Free); +#undef NSPRFUNC +#define PLCFUNC(name) f_ ## name = (name ## _t) (uintptr_t) __wrap_dlsym(plc_handle, #name); \ + if (!f_ ##name) { __android_log_print(ANDROID_LOG_ERROR, "GeckoJNI", "missing %s", #name); return FAILURE; } + PLCFUNC(PL_Base64Encode); + PLCFUNC(PL_Base64Decode); + PLCFUNC(PL_strfree); +#undef PLCFUNC + + return SUCCESS; +} + +/* Throws the current NSS error. */ +static void +throwError(JNIEnv* jenv, const char * funcString) { + char *msg; + + PRErrorCode perr = f_PR_GetError(); + char * errString = f_PR_ErrorToString(perr, 0); + asprintf(&msg, "%s returned error %d: %s\n", funcString, perr, errString); + LOG("Throwing error: %s\n", msg); + + JNI_Throw(jenv, "java/lang/Exception", msg); + free(msg); + LOG("Error thrown\n"); +} + +extern "C" NS_EXPORT jstring MOZ_JNICALL +Java_org_mozilla_gecko_NSSBridge_nativeEncrypt(JNIEnv* jenv, jclass, + jstring jPath, + jstring jValue) +{ + jstring ret = jenv->NewStringUTF(""); + + const char* path; + path = jenv->GetStringUTFChars(jPath, nullptr); + + const char* value; + value = jenv->GetStringUTFChars(jValue, nullptr); + + char* result; + SECStatus rv = doCrypto(jenv, path, value, &result, true); + if (rv == SECSuccess) { + ret = jenv->NewStringUTF(result); + free(result); + } + + jenv->ReleaseStringUTFChars(jValue, value); + jenv->ReleaseStringUTFChars(jPath, path); + + return ret; +} + +extern "C" NS_EXPORT jstring MOZ_JNICALL +Java_org_mozilla_gecko_NSSBridge_nativeDecrypt(JNIEnv* jenv, jclass, + jstring jPath, + jstring jValue) +{ + jstring ret = jenv->NewStringUTF(""); + + const char* path; + path = jenv->GetStringUTFChars(jPath, nullptr); + + const char* value; + value = jenv->GetStringUTFChars(jValue, nullptr); + + char* result; + SECStatus rv = doCrypto(jenv, path, value, &result, false); + if (rv == SECSuccess) { + ret = jenv->NewStringUTF(result); + free(result); + } + + jenv->ReleaseStringUTFChars(jValue, value); + jenv->ReleaseStringUTFChars(jPath, path); + + return ret; +} + + +/* Encrypts or decrypts a string. result should be freed with free() when done */ +SECStatus +doCrypto(JNIEnv* jenv, const char *path, const char *value, char** result, bool encrypt) +{ + SECStatus rv; + PK11SlotInfo *slot; + if (!initialized) { + LOG("Initialize crypto in %s\n", path); + rv = f_NSS_Initialize(path, "", "", "secmod.db", NSS_INIT_NOROOTINIT); + if (rv != SECSuccess) { + throwError(jenv, "NSS_Initialize"); + return rv; + } + initialized = true; + } + + slot = f_PK11_GetInternalKeySlot(); + if (!slot) { + throwError(jenv, "PK11_GetInternalKeySlot"); + return SECFailure; + } + + if (f_PK11_NeedUserInit(slot)) { + LOG("Initializing key3.db with default blank password.\n"); + rv = f_PK11_InitPin(slot, nullptr, nullptr); + if (rv != SECSuccess) { + throwError(jenv, "PK11_InitPin"); + return rv; + } + } + + SECItem request; + SECItem reply; + + reply.data = 0; + reply.len = 0; + + if (encrypt) { + // This can print sensitive data. Uncomment if you need it. + // LOG("Encrypting: %s\n", value); + request.data = (unsigned char*)value; + request.len = strlen(value); + + SECItem keyid; + keyid.data = 0; + keyid.len = 0; + rv = f_PK11SDR_Encrypt(&keyid, &request, &reply, nullptr); + + if (rv != SECSuccess) { + throwError(jenv, "PK11SDR_Encrypt"); + goto done; + } + + rv = encode(reply.data, reply.len, result); + if (rv != SECSuccess) { + throwError(jenv, "encode"); + goto done; + } + LOG("Encrypted: %s\n", *result); + } else { + LOG("Decoding: %s\n", value); + rv = decode(value, &request.data, (int32_t*)&request.len); + if (rv != SECSuccess) { + throwError(jenv, "decode"); + return rv; + } + + rv = f_PK11SDR_Decrypt(&request, &reply, nullptr); + if (rv != SECSuccess) { + throwError(jenv, "PK11SDR_Decrypt"); + goto done; + } + + *result = (char *)malloc(reply.len+1); + strncpy(*result, (char *)reply.data, reply.len); + (*result)[reply.len] = '\0'; + + // This can print sensitive data. Uncomment if you need it. + // LOG("Decoded %i letters: %s\n", reply.len, *result); + free(request.data); + } + +done: + f_SECITEM_ZfreeItem(&reply, false); + return rv; +} + +/* + * Base64 encodes the data passed in. The caller must deallocate _retval using free(); + */ +SECStatus +encode(const unsigned char *data, int32_t dataLen, char **_retval) +{ + SECStatus rv = SECSuccess; + char *encoded = f_PL_Base64Encode((const char *)data, dataLen, nullptr); + if (!encoded) + rv = SECFailure; + if (!*encoded) + rv = SECFailure; + + if (rv == SECSuccess) { + *_retval = (char *)malloc(strlen(encoded)+1); + strcpy(*_retval, encoded); + } + + if (encoded) { + f_PR_Free(encoded); + } + + return rv; +} + +/* + * Base64 decodes the data passed in. The caller must deallocate result using free(); + */ +SECStatus +decode(const char *data, unsigned char **result, int32_t *length) +{ + SECStatus rv = SECSuccess; + uint32_t len = strlen(data); + int adjust = 0; + + /* Compute length adjustment */ + if (len > 0 && data[len-1] == '=') { + adjust++; + if (data[len-2] == '=') adjust++; + } + + char *decoded; + decoded = f_PL_Base64Decode(data, len, nullptr); + if (!decoded) { + return SECFailure; + } + if (!*decoded) { + return SECFailure; + } + + *length = (len*3)/4 - adjust; + LOG("Decoded %i chars into %i chars\n", len, *length); + + *result = (unsigned char*)malloc((size_t)len); + + if (!*result) { + rv = SECFailure; + } else { + memcpy((char*)*result, decoded, len); + } + f_PR_Free(decoded); + return rv; +} + + diff --git a/mozglue/android/NSSBridge.h b/mozglue/android/NSSBridge.h new file mode 100644 index 000000000..77bcd1172 --- /dev/null +++ b/mozglue/android/NSSBridge.h @@ -0,0 +1,46 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef NSSBridge_h +#define NSSBridge_h + +#include "nss.h" +#include "pk11func.h" +#include "pk11sdr.h" +#include "seccomon.h" +#include "secitem.h" +#include "secmodt.h" + +#include "prerror.h" +#include "plstr.h" +#include "prmem.h" + +#include <jni.h> + +int setup_nss_functions(void *nss_handle, void *nssutil_handle, void *plc_handle); + +#define NSS_WRAPPER(name, return_type, args...) \ +typedef return_type (*name ## _t)(args); \ +extern name ## _t f_ ## name; + +NSS_WRAPPER(NSS_Initialize, SECStatus, const char*, const char*, const char*, const char*, uint32_t) +NSS_WRAPPER(NSS_Shutdown, void, void) +NSS_WRAPPER(PK11SDR_Encrypt, SECStatus, SECItem *, SECItem *, SECItem *, void *) +NSS_WRAPPER(PK11SDR_Decrypt, SECStatus, SECItem *, SECItem *, void *) +NSS_WRAPPER(SECITEM_ZfreeItem, void, SECItem*, PRBool) +NSS_WRAPPER(PR_ErrorToString, char *, PRErrorCode, PRLanguageCode) +NSS_WRAPPER(PR_GetError, PRErrorCode, void) +NSS_WRAPPER(PR_Free, PRErrorCode, char *) +NSS_WRAPPER(PL_Base64Encode, char*, const char*, uint32_t, char*) +NSS_WRAPPER(PL_Base64Decode, char*, const char*, uint32_t, char*) +NSS_WRAPPER(PL_strfree, void, char*) +NSS_WRAPPER(PK11_GetInternalKeySlot, PK11SlotInfo *, void) +NSS_WRAPPER(PK11_NeedUserInit, PRBool, PK11SlotInfo *) +NSS_WRAPPER(PK11_InitPin, SECStatus, PK11SlotInfo*, const char*, const char*) + +bool setPassword(PK11SlotInfo *slot); +SECStatus doCrypto(JNIEnv* jenv, const char *path, const char *value, char** result, bool doEncrypt); +SECStatus encode(const unsigned char *data, int32_t dataLen, char **_retval); +SECStatus decode(const char *data, unsigned char **result, int32_t * _retval); +#endif /* NSS_h */ diff --git a/mozglue/android/NativeCrypto.cpp b/mozglue/android/NativeCrypto.cpp new file mode 100644 index 000000000..9a3632e8d --- /dev/null +++ b/mozglue/android/NativeCrypto.cpp @@ -0,0 +1,132 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "NativeCrypto.h" +#include "APKOpen.h" + +#include <jni.h> + +#include <errno.h> +#include <stdlib.h> +#include <inttypes.h> + +#include "mozilla/SHA1.h" +#include "pbkdf2_sha256.h" + +/** + * Helper function to invoke native PBKDF2 function with JNI + * arguments. + */ +extern "C" JNIEXPORT jbyteArray MOZ_JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_pbkdf2SHA256 + (JNIEnv *env, jclass jc, jbyteArray jpassword, jbyteArray jsalt, jint c, jint dkLen) { + if (dkLen < 0) { + env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"), + "dkLen should not be less than 0"); + return nullptr; + } + + jbyte *password = env->GetByteArrayElements(jpassword, nullptr); + size_t passwordLen = env->GetArrayLength(jpassword); + + jbyte *salt = env->GetByteArrayElements(jsalt, nullptr); + size_t saltLen = env->GetArrayLength(jsalt); + + uint8_t hashResult[dkLen]; + PBKDF2_SHA256((uint8_t *) password, passwordLen, (uint8_t *) salt, saltLen, + (uint64_t) c, hashResult, (size_t) dkLen); + + env->ReleaseByteArrayElements(jpassword, password, JNI_ABORT); + env->ReleaseByteArrayElements(jsalt, salt, JNI_ABORT); + + jbyteArray out = env->NewByteArray(dkLen); + if (out == nullptr) { + return nullptr; + } + env->SetByteArrayRegion(out, 0, dkLen, (jbyte *) hashResult); + + return out; +} + +using namespace mozilla; + +/** + * Helper function to invoke native SHA-1 function with JNI arguments. + */ +extern "C" JNIEXPORT jbyteArray MOZ_JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha1 + (JNIEnv *env, jclass jc, jbyteArray jstr) { + jbyte *str = env->GetByteArrayElements(jstr, nullptr); + size_t strLen = env->GetArrayLength(jstr); + + SHA1Sum sha1; + SHA1Sum::Hash hashResult; + sha1.update((void *) str, (uint32_t) strLen); + sha1.finish(hashResult); + + env->ReleaseByteArrayElements(jstr, str, JNI_ABORT); + + jbyteArray out = env->NewByteArray(SHA1Sum::kHashSize); + if (out == nullptr) { + return nullptr; + } + env->SetByteArrayRegion(out, 0, SHA1Sum::kHashSize, (jbyte *) hashResult); + + return out; +} + +/** + * Helper function to invoke native SHA-256 init with JNI arguments. + */ +extern "C" JNIEXPORT jbyteArray MOZ_JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha256init + (JNIEnv *env, jclass jc) { + jbyteArray out = env->NewByteArray(sizeof(SHA256_CTX)); + if (nullptr == out) { + return nullptr; + } + + SHA256_CTX *shaContext = (SHA256_CTX*)env->GetByteArrayElements(out, nullptr); + SHA256_Init(shaContext); + + env->ReleaseByteArrayElements(out, (jbyte*)shaContext, 0); + + return out; +} + +/** + * Helper function to invoke native SHA-256 update with JNI arguments. + */ +extern "C" JNIEXPORT void MOZ_JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha256update + (JNIEnv *env, jclass jc, jbyteArray jctx, jbyteArray jstr, jint len) { + jbyte *str = env->GetByteArrayElements(jstr, nullptr); + + SHA256_CTX *shaContext = (SHA256_CTX*)env->GetByteArrayElements(jctx, nullptr); + + SHA256_Update(shaContext, (void*)str, (size_t) len); + + env->ReleaseByteArrayElements(jstr, str, JNI_ABORT); + env->ReleaseByteArrayElements(jctx, (jbyte*)shaContext, 0); + + return; +} + +/** + * Helper function to invoke native SHA-256 finalize with JNI arguments. + */ +extern "C" JNIEXPORT jbyteArray MOZ_JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha256finalize + (JNIEnv *env, jclass jc, jbyteArray jctx) { + SHA256_CTX *shaContext = (SHA256_CTX*)env->GetByteArrayElements(jctx, nullptr); + + unsigned char* digest = new unsigned char[32]; + SHA256_Final(digest, shaContext); + + env->ReleaseByteArrayElements(jctx, (jbyte*)shaContext, JNI_ABORT); + + jbyteArray out = env->NewByteArray(32); + if (nullptr != out) { + env->SetByteArrayRegion(out, 0, 32, (jbyte*)digest); + } + + delete[] digest; + + return out; +} diff --git a/mozglue/android/NativeCrypto.h b/mozglue/android/NativeCrypto.h new file mode 100644 index 000000000..2850e2bb7 --- /dev/null +++ b/mozglue/android/NativeCrypto.h @@ -0,0 +1,53 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include <jni.h> +/* Header for class org_mozilla_gecko_background_nativecode_NativeCrypto */ + +#ifndef _Included_org_mozilla_gecko_background_nativecode_NativeCrypto +#define _Included_org_mozilla_gecko_background_nativecode_NativeCrypto +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_mozilla_gecko_background_nativecode_NativeCrypto + * Method: pbkdf2SHA256 + * Signature: ([B[BII)[B + */ +JNIEXPORT jbyteArray JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_pbkdf2SHA256 + (JNIEnv *, jclass, jbyteArray, jbyteArray, jint, jint); + +/* + * Class: org_mozilla_gecko_background_nativecode_NativeCrypto + * Method: sha1 + * Signature: ([B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha1 + (JNIEnv *, jclass, jbyteArray); + +/* + * Class: org_mozilla_gecko_background_nativecode_NativeCrypto + * Method: sha256init + * Signature: ()[B + */ +JNIEXPORT jbyteArray JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha256init + (JNIEnv *, jclass); + +/* + * Class: org_mozilla_gecko_background_nativecode_NativeCrypto + * Method: sha256update + * Signature: ([B[B)V + */ +JNIEXPORT void JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha256update + (JNIEnv *, jclass, jbyteArray, jbyteArray, jint); + +/* + * Class: org_mozilla_gecko_background_nativecode_NativeCrypto + * Method: sha256finalize + * Signature: ([B)[B + */ +JNIEXPORT jbyteArray JNICALL Java_org_mozilla_gecko_background_nativecode_NativeCrypto_sha256finalize + (JNIEnv *, jclass, jbyteArray); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/mozglue/android/SQLiteBridge.cpp b/mozglue/android/SQLiteBridge.cpp new file mode 100644 index 000000000..187900bad --- /dev/null +++ b/mozglue/android/SQLiteBridge.cpp @@ -0,0 +1,413 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <stdlib.h> +#include <stdio.h> +#include <jni.h> +#include <android/log.h> +#include "dlfcn.h" +#include "APKOpen.h" +#include "ElfLoader.h" +#include "SQLiteBridge.h" + +#ifdef DEBUG +#define LOG(x...) __android_log_print(ANDROID_LOG_INFO, "GeckoJNI", x) +#else +#define LOG(x...) +#endif + +#define SQLITE_WRAPPER_INT(name) name ## _t f_ ## name; + +SQLITE_WRAPPER_INT(sqlite3_open) +SQLITE_WRAPPER_INT(sqlite3_errmsg) +SQLITE_WRAPPER_INT(sqlite3_prepare_v2) +SQLITE_WRAPPER_INT(sqlite3_bind_parameter_count) +SQLITE_WRAPPER_INT(sqlite3_bind_null) +SQLITE_WRAPPER_INT(sqlite3_bind_text) +SQLITE_WRAPPER_INT(sqlite3_step) +SQLITE_WRAPPER_INT(sqlite3_column_count) +SQLITE_WRAPPER_INT(sqlite3_finalize) +SQLITE_WRAPPER_INT(sqlite3_close) +SQLITE_WRAPPER_INT(sqlite3_column_name) +SQLITE_WRAPPER_INT(sqlite3_column_type) +SQLITE_WRAPPER_INT(sqlite3_column_blob) +SQLITE_WRAPPER_INT(sqlite3_column_bytes) +SQLITE_WRAPPER_INT(sqlite3_column_text) +SQLITE_WRAPPER_INT(sqlite3_changes) +SQLITE_WRAPPER_INT(sqlite3_last_insert_rowid) + +void setup_sqlite_functions(void *sqlite_handle) +{ +#define GETFUNC(name) f_ ## name = (name ## _t) (uintptr_t) __wrap_dlsym(sqlite_handle, #name) + GETFUNC(sqlite3_open); + GETFUNC(sqlite3_errmsg); + GETFUNC(sqlite3_prepare_v2); + GETFUNC(sqlite3_bind_parameter_count); + GETFUNC(sqlite3_bind_null); + GETFUNC(sqlite3_bind_text); + GETFUNC(sqlite3_step); + GETFUNC(sqlite3_column_count); + GETFUNC(sqlite3_finalize); + GETFUNC(sqlite3_close); + GETFUNC(sqlite3_column_name); + GETFUNC(sqlite3_column_type); + GETFUNC(sqlite3_column_blob); + GETFUNC(sqlite3_column_bytes); + GETFUNC(sqlite3_column_text); + GETFUNC(sqlite3_changes); + GETFUNC(sqlite3_last_insert_rowid); +#undef GETFUNC +} + +static bool initialized = false; +static jclass stringClass; +static jclass objectClass; +static jclass byteBufferClass; +static jclass cursorClass; +static jmethodID jByteBufferAllocateDirect; +static jmethodID jCursorConstructor; +static jmethodID jCursorAddRow; + +static jobject sqliteInternalCall(JNIEnv* jenv, sqlite3 *db, jstring jQuery, + jobjectArray jParams, jlongArray jQueryRes); + +static void throwSqliteException(JNIEnv* jenv, const char* aFormat, ...) +{ + va_list ap; + va_start(ap, aFormat); + char* msg = nullptr; + vasprintf(&msg, aFormat, ap); + LOG("Error in SQLiteBridge: %s\n", msg); + JNI_Throw(jenv, "org/mozilla/gecko/sqlite/SQLiteBridgeException", msg); + free(msg); + va_end(ap); +} + +static void +JNI_Setup(JNIEnv* jenv) +{ + if (initialized) return; + + jclass lObjectClass = jenv->FindClass("java/lang/Object"); + jclass lStringClass = jenv->FindClass("java/lang/String"); + jclass lByteBufferClass = jenv->FindClass("java/nio/ByteBuffer"); + jclass lCursorClass = jenv->FindClass("org/mozilla/gecko/sqlite/MatrixBlobCursor"); + + if (lStringClass == nullptr + || lObjectClass == nullptr + || lByteBufferClass == nullptr + || lCursorClass == nullptr) { + throwSqliteException(jenv, "FindClass error"); + return; + } + + // Those are only local references. Make them global so they work + // across calls and threads. + objectClass = (jclass)jenv->NewGlobalRef(lObjectClass); + stringClass = (jclass)jenv->NewGlobalRef(lStringClass); + byteBufferClass = (jclass)jenv->NewGlobalRef(lByteBufferClass); + cursorClass = (jclass)jenv->NewGlobalRef(lCursorClass); + + if (stringClass == nullptr || objectClass == nullptr + || byteBufferClass == nullptr + || cursorClass == nullptr) { + throwSqliteException(jenv, "NewGlobalRef error"); + return; + } + + // public static ByteBuffer allocateDirect(int capacity) + jByteBufferAllocateDirect = + jenv->GetStaticMethodID(byteBufferClass, "allocateDirect", "(I)Ljava/nio/ByteBuffer;"); + // new MatrixBlobCursor(String []) + jCursorConstructor = + jenv->GetMethodID(cursorClass, "<init>", "([Ljava/lang/String;)V"); + // public void addRow (Object[] columnValues) + jCursorAddRow = + jenv->GetMethodID(cursorClass, "addRow", "([Ljava/lang/Object;)V"); + + if (jByteBufferAllocateDirect == nullptr + || jCursorConstructor == nullptr + || jCursorAddRow == nullptr) { + throwSqliteException(jenv, "GetMethodId error"); + return; + } + + initialized = true; +} + +extern "C" NS_EXPORT jobject MOZ_JNICALL +Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCall(JNIEnv* jenv, jclass, + jstring jDb, + jstring jQuery, + jobjectArray jParams, + jlongArray jQueryRes) +{ + JNI_Setup(jenv); + + int rc; + jobject jCursor = nullptr; + const char* dbPath; + sqlite3 *db; + + dbPath = jenv->GetStringUTFChars(jDb, nullptr); + rc = f_sqlite3_open(dbPath, &db); + jenv->ReleaseStringUTFChars(jDb, dbPath); + if (rc != SQLITE_OK) { + throwSqliteException(jenv, + "Can't open database: %s", f_sqlite3_errmsg(db)); + f_sqlite3_close(db); // close db even if open failed + return nullptr; + } + jCursor = sqliteInternalCall(jenv, db, jQuery, jParams, jQueryRes); + f_sqlite3_close(db); + return jCursor; +} + +extern "C" NS_EXPORT jobject MOZ_JNICALL +Java_org_mozilla_gecko_sqlite_SQLiteBridge_sqliteCallWithDb(JNIEnv* jenv, jclass, + jlong jDb, + jstring jQuery, + jobjectArray jParams, + jlongArray jQueryRes) +{ + JNI_Setup(jenv); + + jobject jCursor = nullptr; + sqlite3 *db = (sqlite3*)jDb; + jCursor = sqliteInternalCall(jenv, db, jQuery, jParams, jQueryRes); + return jCursor; +} + +extern "C" NS_EXPORT jlong MOZ_JNICALL +Java_org_mozilla_gecko_sqlite_SQLiteBridge_openDatabase(JNIEnv* jenv, jclass, + jstring jDb) +{ + JNI_Setup(jenv); + + int rc; + const char* dbPath; + sqlite3 *db; + + dbPath = jenv->GetStringUTFChars(jDb, nullptr); + rc = f_sqlite3_open(dbPath, &db); + jenv->ReleaseStringUTFChars(jDb, dbPath); + if (rc != SQLITE_OK) { + throwSqliteException(jenv, + "Can't open database: %s", f_sqlite3_errmsg(db)); + f_sqlite3_close(db); // close db even if open failed + return 0; + } + return (jlong)db; +} + +extern "C" NS_EXPORT void MOZ_JNICALL +Java_org_mozilla_gecko_sqlite_SQLiteBridge_closeDatabase(JNIEnv* jenv, jclass, + jlong jDb) +{ + JNI_Setup(jenv); + + sqlite3 *db = (sqlite3*)jDb; + f_sqlite3_close(db); +} + +static jobject +sqliteInternalCall(JNIEnv* jenv, + sqlite3 *db, + jstring jQuery, + jobjectArray jParams, + jlongArray jQueryRes) +{ + JNI_Setup(jenv); + + jobject jCursor = nullptr; + jsize numPars = 0; + + const char *pzTail; + sqlite3_stmt *ppStmt; + int rc; + + const char* queryStr; + queryStr = jenv->GetStringUTFChars(jQuery, nullptr); + + rc = f_sqlite3_prepare_v2(db, queryStr, -1, &ppStmt, &pzTail); + if (rc != SQLITE_OK || ppStmt == nullptr) { + throwSqliteException(jenv, + "Can't prepare statement: %s", f_sqlite3_errmsg(db)); + return nullptr; + } + jenv->ReleaseStringUTFChars(jQuery, queryStr); + + // Check if number of parameters matches + if (jParams != nullptr) { + numPars = jenv->GetArrayLength(jParams); + } + int sqlNumPars; + sqlNumPars = f_sqlite3_bind_parameter_count(ppStmt); + if (numPars != sqlNumPars) { + throwSqliteException(jenv, + "Passed parameter count (%d) " + "doesn't match SQL parameter count (%d)", + numPars, sqlNumPars); + return nullptr; + } + + if (jParams != nullptr) { + // Bind parameters, if any + if (numPars > 0) { + for (int i = 0; i < numPars; i++) { + jobject jObjectParam = jenv->GetObjectArrayElement(jParams, i); + // IsInstanceOf or isAssignableFrom? String is final, so IsInstanceOf + // should be OK. + jboolean isString = jenv->IsInstanceOf(jObjectParam, stringClass); + if (isString != JNI_TRUE) { + throwSqliteException(jenv, + "Parameter is not of String type"); + return nullptr; + } + + // SQLite parameters index from 1. + if (jObjectParam == nullptr) { + rc = f_sqlite3_bind_null(ppStmt, i + 1); + } else { + jstring jStringParam = (jstring) jObjectParam; + const char* paramStr = jenv->GetStringUTFChars(jStringParam, nullptr); + rc = f_sqlite3_bind_text(ppStmt, i + 1, paramStr, -1, SQLITE_TRANSIENT); + jenv->ReleaseStringUTFChars(jStringParam, paramStr); + } + + if (rc != SQLITE_OK) { + throwSqliteException(jenv, "Error binding query parameter"); + return nullptr; + } + } + } + } + + // Execute the query and step through the results + rc = f_sqlite3_step(ppStmt); + if (rc != SQLITE_ROW && rc != SQLITE_DONE) { + throwSqliteException(jenv, + "Can't step statement: (%d) %s", rc, f_sqlite3_errmsg(db)); + return nullptr; + } + + // Get the column count and names + int cols; + cols = f_sqlite3_column_count(ppStmt); + + { + // Allocate a String[cols] + jobjectArray jStringArray = jenv->NewObjectArray(cols, + stringClass, + nullptr); + if (jStringArray == nullptr) { + throwSqliteException(jenv, "Can't allocate String[]"); + return nullptr; + } + + // Assign column names to the String[] + for (int i = 0; i < cols; i++) { + const char* colName = f_sqlite3_column_name(ppStmt, i); + jstring jStr = jenv->NewStringUTF(colName); + jenv->SetObjectArrayElement(jStringArray, i, jStr); + } + + // Construct the MatrixCursor(String[]) with given column names + jCursor = jenv->NewObject(cursorClass, + jCursorConstructor, + jStringArray); + if (jCursor == nullptr) { + throwSqliteException(jenv, "Can't allocate MatrixBlobCursor"); + return nullptr; + } + } + + // Return the id and number of changed rows in jQueryRes + { + jlong id = f_sqlite3_last_insert_rowid(db); + jenv->SetLongArrayRegion(jQueryRes, 0, 1, &id); + + jlong changed = f_sqlite3_changes(db); + jenv->SetLongArrayRegion(jQueryRes, 1, 1, &changed); + } + + // For each row, add an Object[] to the passed ArrayList, + // with that containing either String or ByteArray objects + // containing the columns + while (rc != SQLITE_DONE) { + // Process row + // Construct Object[] + jobjectArray jRow = jenv->NewObjectArray(cols, + objectClass, + nullptr); + if (jRow == nullptr) { + throwSqliteException(jenv, "Can't allocate jRow Object[]"); + return nullptr; + } + + for (int i = 0; i < cols; i++) { + int colType = f_sqlite3_column_type(ppStmt, i); + if (colType == SQLITE_BLOB) { + // Treat as blob + const void* blob = f_sqlite3_column_blob(ppStmt, i); + int colLen = f_sqlite3_column_bytes(ppStmt, i); + + // Construct ByteBuffer of correct size + jobject jByteBuffer = + jenv->CallStaticObjectMethod(byteBufferClass, + jByteBufferAllocateDirect, + colLen); + if (jByteBuffer == nullptr) { + throwSqliteException(jenv, + "Failure calling ByteBuffer.allocateDirect"); + return nullptr; + } + + // Get its backing array + void* bufferArray = jenv->GetDirectBufferAddress(jByteBuffer); + if (bufferArray == nullptr) { + throwSqliteException(jenv, + "Failure calling GetDirectBufferAddress"); + return nullptr; + } + memcpy(bufferArray, blob, colLen); + + jenv->SetObjectArrayElement(jRow, i, jByteBuffer); + jenv->DeleteLocalRef(jByteBuffer); + } else if (colType == SQLITE_NULL) { + jenv->SetObjectArrayElement(jRow, i, nullptr); + } else { + // Treat everything else as text + const char* txt = (const char*)f_sqlite3_column_text(ppStmt, i); + jstring jStr = jenv->NewStringUTF(txt); + jenv->SetObjectArrayElement(jRow, i, jStr); + jenv->DeleteLocalRef(jStr); + } + } + + // Append Object[] to Cursor + jenv->CallVoidMethod(jCursor, jCursorAddRow, jRow); + + // Clean up + jenv->DeleteLocalRef(jRow); + + // Get next row + rc = f_sqlite3_step(ppStmt); + // Real error? + if (rc != SQLITE_ROW && rc != SQLITE_DONE) { + throwSqliteException(jenv, + "Can't re-step statement:(%d) %s", rc, f_sqlite3_errmsg(db)); + return nullptr; + } + } + + rc = f_sqlite3_finalize(ppStmt); + if (rc != SQLITE_OK) { + throwSqliteException(jenv, + "Can't finalize statement: %s", f_sqlite3_errmsg(db)); + return nullptr; + } + + return jCursor; +} diff --git a/mozglue/android/SQLiteBridge.h b/mozglue/android/SQLiteBridge.h new file mode 100644 index 000000000..f2ede820f --- /dev/null +++ b/mozglue/android/SQLiteBridge.h @@ -0,0 +1,34 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef SQLiteBridge_h +#define SQLiteBridge_h + +#include "sqlite3.h" + +void setup_sqlite_functions(void *sqlite_handle); + +#define SQLITE_WRAPPER(name, return_type, args...) \ +typedef return_type (*name ## _t)(args); \ +extern name ## _t f_ ## name; + +SQLITE_WRAPPER(sqlite3_open, int, const char*, sqlite3**) +SQLITE_WRAPPER(sqlite3_errmsg, const char*, sqlite3*) +SQLITE_WRAPPER(sqlite3_prepare_v2, int, sqlite3*, const char*, int, sqlite3_stmt**, const char**) +SQLITE_WRAPPER(sqlite3_bind_parameter_count, int, sqlite3_stmt*) +SQLITE_WRAPPER(sqlite3_bind_text, int, sqlite3_stmt*, int, const char*, int, void(*)(void*)) +SQLITE_WRAPPER(sqlite3_bind_null, int, sqlite3_stmt*, int) +SQLITE_WRAPPER(sqlite3_step, int, sqlite3_stmt*) +SQLITE_WRAPPER(sqlite3_column_count, int, sqlite3_stmt*) +SQLITE_WRAPPER(sqlite3_finalize, int, sqlite3_stmt*) +SQLITE_WRAPPER(sqlite3_close, int, sqlite3*) +SQLITE_WRAPPER(sqlite3_column_name, const char*, sqlite3_stmt*, int) +SQLITE_WRAPPER(sqlite3_column_type, int, sqlite3_stmt*, int) +SQLITE_WRAPPER(sqlite3_column_blob, const void*, sqlite3_stmt*, int) +SQLITE_WRAPPER(sqlite3_column_bytes, int, sqlite3_stmt*, int) +SQLITE_WRAPPER(sqlite3_column_text, const unsigned char*, sqlite3_stmt*, int) +SQLITE_WRAPPER(sqlite3_changes, int, sqlite3*) +SQLITE_WRAPPER(sqlite3_last_insert_rowid, sqlite3_int64, sqlite3*) + +#endif /* SQLiteBridge_h */ diff --git a/mozglue/android/SharedMemNatives.cpp b/mozglue/android/SharedMemNatives.cpp new file mode 100644 index 000000000..d186d6e21 --- /dev/null +++ b/mozglue/android/SharedMemNatives.cpp @@ -0,0 +1,65 @@ +/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <jni.h> +#include <string.h> +#include <sys/mman.h> + +extern "C" { + +JNIEXPORT +void JNICALL +Java_org_mozilla_gecko_mozglue_SharedMemBuffer_nativeReadFromDirectBuffer(JNIEnv* jenv, jclass, jobject src, jlong dest, jint offset, jint size) +{ + uint8_t* from = static_cast<uint8_t*>(jenv->GetDirectBufferAddress(src)); + if (from == nullptr) { + jenv->ThrowNew(jenv->FindClass("java/lang/NullPointerException"), "Null direct buffer"); + return; + } + + void* to = reinterpret_cast<void*>(dest); + if (to == nullptr) { + jenv->ThrowNew(jenv->FindClass("java/lang/NullPointerException"), "Null shared memory buffer"); + return; + } + + memcpy(to, from + offset, size); +} + +JNIEXPORT +void JNICALL +Java_org_mozilla_gecko_mozglue_SharedMemBuffer_nativeWriteToDirectBuffer(JNIEnv* jenv, jclass, jlong src, jobject dest, jint offset, jint size) +{ + uint8_t* from = reinterpret_cast<uint8_t*>(src); + if (from == nullptr) { + jenv->ThrowNew(jenv->FindClass("java/lang/NullPointerException"), "Null shared memory buffer"); + return; + } + + void* to = jenv->GetDirectBufferAddress(dest); + if (to == nullptr) { + jenv->ThrowNew(jenv->FindClass("java/lang/NullPointerException"), "Null direct buffer"); + return; + } + + memcpy(to, from + offset, size); +} + +JNIEXPORT +jlong JNICALL +Java_org_mozilla_gecko_mozglue_SharedMemory_map(JNIEnv *env, jobject jobj, jint fd, jint length) +{ + void* address = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + return jlong(address); +} + +JNIEXPORT +void JNICALL +Java_org_mozilla_gecko_mozglue_SharedMemory_unmap(JNIEnv *env, jobject jobj, jlong address, jint size) +{ + munmap((void*)address, (size_t)size); +} + +}
\ No newline at end of file diff --git a/mozglue/android/moz.build b/mozglue/android/moz.build new file mode 100644 index 000000000..cdc5ffb3c --- /dev/null +++ b/mozglue/android/moz.build @@ -0,0 +1,58 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +EXPORTS += [ + 'APKOpen.h', +] + +SOURCES += [ + 'APKOpen.cpp', + 'NativeCrypto.cpp', + 'nsGeckoUtils.cpp', + 'NSSBridge.cpp', + 'pbkdf2_sha256.c', + 'SharedMemNatives.cpp', + 'SQLiteBridge.cpp', +] + +FINAL_LIBRARY = 'mozglue' + +for var in ('ANDROID_PACKAGE_NAME', + 'ANDROID_CPU_ARCH'): + DEFINES[var] = '"%s"' % CONFIG[var] + +if CONFIG['MOZ_FOLD_LIBS']: + DEFINES['MOZ_FOLD_LIBS'] = True + +LOCAL_INCLUDES += [ + '!/build', + '../linker', + '/db/sqlite3/src', + '/ipc/chromium/src', + '/nsprpub/lib/ds', + '/nsprpub/lib/libc/include', + '/nsprpub/pr/include', + '/security/nss/lib/base', + '/security/nss/lib/certdb', + '/security/nss/lib/cryptohi', + '/security/nss/lib/dev', + '/security/nss/lib/freebl', + '/security/nss/lib/nss', + '/security/nss/lib/pk11wrap', + '/security/nss/lib/pkcs7', + '/security/nss/lib/pki', + '/security/nss/lib/smime', + '/security/nss/lib/softoken', + '/security/nss/lib/ssl', + '/security/nss/lib/util', + '/toolkit/components/startup', + '/xpcom/build', +] + +DISABLE_STL_WRAPPING = True + +if CONFIG['GNU_CXX']: + CXXFLAGS += ['-Wno-error=shadow'] diff --git a/mozglue/android/nsGeckoUtils.cpp b/mozglue/android/nsGeckoUtils.cpp new file mode 100644 index 000000000..22818a746 --- /dev/null +++ b/mozglue/android/nsGeckoUtils.cpp @@ -0,0 +1,124 @@ +/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <jni.h> + +#include <stdlib.h> +#include <fcntl.h> +#include "APKOpen.h" +#include "Zip.h" +#include "mozilla/RefPtr.h" + +extern "C" +__attribute__ ((visibility("default"))) +void MOZ_JNICALL +Java_org_mozilla_gecko_mozglue_GeckoLoader_putenv(JNIEnv *jenv, jclass, jstring map) +{ + const char* str; + // XXX: java doesn't give us true UTF8, we should figure out something + // better to do here + str = jenv->GetStringUTFChars(map, nullptr); + if (str == nullptr) + return; + putenv(strdup(str)); + jenv->ReleaseStringUTFChars(map, str); +} + +extern "C" +__attribute__ ((visibility("default"))) +jobject MOZ_JNICALL +Java_org_mozilla_gecko_mozglue_DirectBufferAllocator_nativeAllocateDirectBuffer(JNIEnv *jenv, jclass, jlong size) +{ + jobject buffer = nullptr; + void* mem = malloc(size); + if (mem) { + buffer = jenv->NewDirectByteBuffer(mem, size); + if (!buffer) + free(mem); + } + return buffer; +} + +extern "C" +__attribute__ ((visibility("default"))) +void MOZ_JNICALL +Java_org_mozilla_gecko_mozglue_DirectBufferAllocator_nativeFreeDirectBuffer(JNIEnv *jenv, jclass, jobject buf) +{ + free(jenv->GetDirectBufferAddress(buf)); +} + +extern "C" +__attribute__ ((visibility("default"))) +jlong MOZ_JNICALL +Java_org_mozilla_gecko_mozglue_NativeZip_getZip(JNIEnv *jenv, jclass, jstring path) +{ + const char* str; + str = jenv->GetStringUTFChars(path, nullptr); + if (!str || !*str) { + if (str) + jenv->ReleaseStringUTFChars(path, str); + JNI_Throw(jenv, "java/lang/IllegalArgumentException", "Invalid path"); + return 0; + } + RefPtr<Zip> zip = ZipCollection::GetZip(str); + jenv->ReleaseStringUTFChars(path, str); + if (!zip) { + JNI_Throw(jenv, "java/lang/IllegalArgumentException", "Invalid path or invalid zip"); + return 0; + } + return reinterpret_cast<jlong>(zip.forget().take()); +} + +extern "C" +__attribute__ ((visibility("default"))) +jlong MOZ_JNICALL +Java_org_mozilla_gecko_mozglue_NativeZip_getZipFromByteBuffer(JNIEnv *jenv, jclass, jobject buffer) +{ + void *buf = jenv->GetDirectBufferAddress(buffer); + size_t size = jenv->GetDirectBufferCapacity(buffer); + RefPtr<Zip> zip = Zip::Create(buf, size); + if (!zip) { + JNI_Throw(jenv, "java/lang/IllegalArgumentException", "Invalid zip"); + return 0; + } + return reinterpret_cast<jlong>(zip.forget().take()); +} + + extern "C" +__attribute__ ((visibility("default"))) +void MOZ_JNICALL +Java_org_mozilla_gecko_mozglue_NativeZip__1release(JNIEnv *jenv, jclass, jlong obj) +{ + Zip *zip = (Zip *)obj; + zip->Release(); +} + +extern "C" +__attribute__ ((visibility("default"))) +jobject MOZ_JNICALL +Java_org_mozilla_gecko_mozglue_NativeZip__1getInputStream(JNIEnv *jenv, jobject jzip, jlong obj, jstring path) +{ + Zip *zip = (Zip *)obj; + const char* str; + str = jenv->GetStringUTFChars(path, nullptr); + + Zip::Stream stream; + bool res = zip->GetStream(str, &stream); + jenv->ReleaseStringUTFChars(path, str); + if (!res) { + return nullptr; + } + jobject buf = jenv->NewDirectByteBuffer(const_cast<void *>(stream.GetBuffer()), stream.GetSize()); + if (!buf) { + JNI_Throw(jenv, "java/lang/RuntimeException", "Failed to create ByteBuffer"); + return nullptr; + } + jclass nativeZip = jenv->GetObjectClass(jzip); + jmethodID method = jenv->GetMethodID(nativeZip, "createInputStream", "(Ljava/nio/ByteBuffer;I)Ljava/io/InputStream;"); + // Since this function is only expected to be called from Java, it is safe + // to skip exception checking for the method call below, as long as no + // other Native -> Java call doesn't happen before returning to Java. + return jenv->CallObjectMethod(jzip, method, buf, (jint) stream.GetType()); +} diff --git a/mozglue/android/pbkdf2_sha256.c b/mozglue/android/pbkdf2_sha256.c new file mode 100644 index 000000000..8e90f386a --- /dev/null +++ b/mozglue/android/pbkdf2_sha256.c @@ -0,0 +1,432 @@ +/*- + * Copyright 2005,2007,2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include <sys/types.h> + +#include <stdint.h> +#include <string.h> + +#include <sys/endian.h> + +#include "pbkdf2_sha256.h" + +static inline uint32_t +be32dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint32_t)(p[3]) + + ((uint32_t)(p[2]) << 8) + + ((uint32_t)(p[1]) << 16) + + ((uint32_t)(p[0]) << 24)); +} + +static inline void +be32enc(void *pp, uint32_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[3] = x & 0xff; + p[2] = (x >> 8) & 0xff; + p[1] = (x >> 16) & 0xff; + p[0] = (x >> 24) & 0xff; +} + +/* + * Encode a length len/4 vector of (uint32_t) into a length len vector of + * (unsigned char) in big-endian form. Assumes len is a multiple of 4. + */ +static void +be32enc_vect(unsigned char *dst, const uint32_t *src, size_t len) +{ + size_t i; + + for (i = 0; i < len / 4; i++) + be32enc(dst + i * 4, src[i]); +} + +/* + * Decode a big-endian length len vector of (unsigned char) into a length + * len/4 vector of (uint32_t). Assumes len is a multiple of 4. + */ +static void +be32dec_vect(uint32_t *dst, const unsigned char *src, size_t len) +{ + size_t i; + + for (i = 0; i < len / 4; i++) + dst[i] = be32dec(src + i * 4); +} + +/* Elementary functions used by SHA256 */ +#define Ch(x, y, z) ((x & (y ^ z)) ^ z) +#define Maj(x, y, z) ((x & (y | z)) | (y & z)) +#define SHR(x, n) (x >> n) +#define ROTR(x, n) ((x >> n) | (x << (32 - n))) +#define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) +#define s0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3)) +#define s1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10)) + +/* SHA256 round function */ +#define RND(a, b, c, d, e, f, g, h, k) \ + t0 = h + S1(e) + Ch(e, f, g) + k; \ + t1 = S0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + +/* Adjusted round function for rotating state */ +#define RNDr(S, W, i, k) \ + RND(S[(64 - i) % 8], S[(65 - i) % 8], \ + S[(66 - i) % 8], S[(67 - i) % 8], \ + S[(68 - i) % 8], S[(69 - i) % 8], \ + S[(70 - i) % 8], S[(71 - i) % 8], \ + W[i] + k) + +/* + * SHA256 block compression function. The 256-bit state is transformed via + * the 512-bit input block to produce a new state. + */ +static void +SHA256_Transform(uint32_t * state, const unsigned char block[64]) +{ + uint32_t W[64]; + uint32_t S[8]; + uint32_t t0, t1; + int i; + + /* 1. Prepare message schedule W. */ + be32dec_vect(W, block, 64); + for (i = 16; i < 64; i++) + W[i] = s1(W[i - 2]) + W[i - 7] + s0(W[i - 15]) + W[i - 16]; + + /* 2. Initialize working variables. */ + memcpy(S, state, 32); + + /* 3. Mix. */ + RNDr(S, W, 0, 0x428a2f98); + RNDr(S, W, 1, 0x71374491); + RNDr(S, W, 2, 0xb5c0fbcf); + RNDr(S, W, 3, 0xe9b5dba5); + RNDr(S, W, 4, 0x3956c25b); + RNDr(S, W, 5, 0x59f111f1); + RNDr(S, W, 6, 0x923f82a4); + RNDr(S, W, 7, 0xab1c5ed5); + RNDr(S, W, 8, 0xd807aa98); + RNDr(S, W, 9, 0x12835b01); + RNDr(S, W, 10, 0x243185be); + RNDr(S, W, 11, 0x550c7dc3); + RNDr(S, W, 12, 0x72be5d74); + RNDr(S, W, 13, 0x80deb1fe); + RNDr(S, W, 14, 0x9bdc06a7); + RNDr(S, W, 15, 0xc19bf174); + RNDr(S, W, 16, 0xe49b69c1); + RNDr(S, W, 17, 0xefbe4786); + RNDr(S, W, 18, 0x0fc19dc6); + RNDr(S, W, 19, 0x240ca1cc); + RNDr(S, W, 20, 0x2de92c6f); + RNDr(S, W, 21, 0x4a7484aa); + RNDr(S, W, 22, 0x5cb0a9dc); + RNDr(S, W, 23, 0x76f988da); + RNDr(S, W, 24, 0x983e5152); + RNDr(S, W, 25, 0xa831c66d); + RNDr(S, W, 26, 0xb00327c8); + RNDr(S, W, 27, 0xbf597fc7); + RNDr(S, W, 28, 0xc6e00bf3); + RNDr(S, W, 29, 0xd5a79147); + RNDr(S, W, 30, 0x06ca6351); + RNDr(S, W, 31, 0x14292967); + RNDr(S, W, 32, 0x27b70a85); + RNDr(S, W, 33, 0x2e1b2138); + RNDr(S, W, 34, 0x4d2c6dfc); + RNDr(S, W, 35, 0x53380d13); + RNDr(S, W, 36, 0x650a7354); + RNDr(S, W, 37, 0x766a0abb); + RNDr(S, W, 38, 0x81c2c92e); + RNDr(S, W, 39, 0x92722c85); + RNDr(S, W, 40, 0xa2bfe8a1); + RNDr(S, W, 41, 0xa81a664b); + RNDr(S, W, 42, 0xc24b8b70); + RNDr(S, W, 43, 0xc76c51a3); + RNDr(S, W, 44, 0xd192e819); + RNDr(S, W, 45, 0xd6990624); + RNDr(S, W, 46, 0xf40e3585); + RNDr(S, W, 47, 0x106aa070); + RNDr(S, W, 48, 0x19a4c116); + RNDr(S, W, 49, 0x1e376c08); + RNDr(S, W, 50, 0x2748774c); + RNDr(S, W, 51, 0x34b0bcb5); + RNDr(S, W, 52, 0x391c0cb3); + RNDr(S, W, 53, 0x4ed8aa4a); + RNDr(S, W, 54, 0x5b9cca4f); + RNDr(S, W, 55, 0x682e6ff3); + RNDr(S, W, 56, 0x748f82ee); + RNDr(S, W, 57, 0x78a5636f); + RNDr(S, W, 58, 0x84c87814); + RNDr(S, W, 59, 0x8cc70208); + RNDr(S, W, 60, 0x90befffa); + RNDr(S, W, 61, 0xa4506ceb); + RNDr(S, W, 62, 0xbef9a3f7); + RNDr(S, W, 63, 0xc67178f2); + + /* 4. Mix local working variables into global state. */ + for (i = 0; i < 8; i++) + state[i] += S[i]; + + /* Clean the stack. */ + memset(W, 0, 256); + memset(S, 0, 32); + t0 = t1 = 0; +} + +static unsigned char PAD[64] = { + 0x80, 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, 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 +}; + +/* Add padding and terminating bit-count. */ +static void +SHA256_Pad(SHA256_CTX * ctx) +{ + unsigned char len[8]; + uint32_t r, plen; + + /* + * Convert length to a vector of bytes -- we do this now rather + * than later because the length will change after we pad. + */ + be32enc_vect(len, ctx->count, 8); + + /* Add 1--64 bytes so that the resulting length is 56 mod 64. */ + r = (ctx->count[1] >> 3) & 0x3f; + plen = (r < 56) ? (56 - r) : (120 - r); + SHA256_Update(ctx, PAD, (size_t)plen); + + /* Add the terminating bit-count. */ + SHA256_Update(ctx, len, 8); +} + +/* SHA-256 initialization. Begins a SHA-256 operation. */ +void +SHA256_Init(SHA256_CTX * ctx) +{ + + /* Zero bits processed so far. */ + ctx->count[0] = ctx->count[1] = 0; + + /* Magic initialization constants. */ + ctx->state[0] = 0x6A09E667; + ctx->state[1] = 0xBB67AE85; + ctx->state[2] = 0x3C6EF372; + ctx->state[3] = 0xA54FF53A; + ctx->state[4] = 0x510E527F; + ctx->state[5] = 0x9B05688C; + ctx->state[6] = 0x1F83D9AB; + ctx->state[7] = 0x5BE0CD19; +} + +/* Add bytes into the hash. */ +void +SHA256_Update(SHA256_CTX * ctx, const void *in, size_t len) +{ + uint32_t bitlen[2]; + uint32_t r; + const unsigned char *src = in; + + /* Number of bytes left in the buffer from previous updates. */ + r = (ctx->count[1] >> 3) & 0x3f; + + /* Convert the length into a number of bits. */ + bitlen[1] = ((uint32_t)len) << 3; + bitlen[0] = (uint32_t)(len >> 29); + + /* Update number of bits. */ + if ((ctx->count[1] += bitlen[1]) < bitlen[1]) + ctx->count[0]++; + ctx->count[0] += bitlen[0]; + + /* Handle the case where we don't need to perform any transforms. */ + if (len < 64 - r) { + memcpy(&ctx->buf[r], src, len); + return; + } + + /* Finish the current block. */ + memcpy(&ctx->buf[r], src, 64 - r); + SHA256_Transform(ctx->state, ctx->buf); + src += 64 - r; + len -= 64 - r; + + /* Perform complete blocks. */ + while (len >= 64) { + SHA256_Transform(ctx->state, src); + src += 64; + len -= 64; + } + + /* Copy left over data into buffer. */ + memcpy(ctx->buf, src, len); +} + +/* + * SHA-256 finalization. Pads the input data, exports the hash value, + * and clears the context state. + */ +void +SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx) +{ + + /* Add padding. */ + SHA256_Pad(ctx); + + /* Write the hash. */ + be32enc_vect(digest, ctx->state, 32); + + /* Clear the context state. */ + memset((void *)ctx, 0, sizeof(*ctx)); +} + +/* Initialize an HMAC-SHA256 operation with the given key. */ +void +HMAC_SHA256_Init(HMAC_SHA256_CTX * ctx, const void * _K, size_t Klen) +{ + unsigned char pad[64]; + unsigned char khash[32]; + const unsigned char * K = _K; + size_t i; + + /* If Klen > 64, the key is really SHA256(K). */ + if (Klen > 64) { + SHA256_Init(&ctx->ictx); + SHA256_Update(&ctx->ictx, K, Klen); + SHA256_Final(khash, &ctx->ictx); + K = khash; + Klen = 32; + } + + /* Inner SHA256 operation is SHA256(K xor [block of 0x36] || data). */ + SHA256_Init(&ctx->ictx); + memset(pad, 0x36, 64); + for (i = 0; i < Klen; i++) + pad[i] ^= K[i]; + SHA256_Update(&ctx->ictx, pad, 64); + + /* Outer SHA256 operation is SHA256(K xor [block of 0x5c] || hash). */ + SHA256_Init(&ctx->octx); + memset(pad, 0x5c, 64); + for (i = 0; i < Klen; i++) + pad[i] ^= K[i]; + SHA256_Update(&ctx->octx, pad, 64); + + /* Clean the stack. */ + memset(khash, 0, 32); +} + +/* Add bytes to the HMAC-SHA256 operation. */ +void +HMAC_SHA256_Update(HMAC_SHA256_CTX * ctx, const void *in, size_t len) +{ + + /* Feed data to the inner SHA256 operation. */ + SHA256_Update(&ctx->ictx, in, len); +} + +/* Finish an HMAC-SHA256 operation. */ +void +HMAC_SHA256_Final(unsigned char digest[32], HMAC_SHA256_CTX * ctx) +{ + unsigned char ihash[32]; + + /* Finish the inner SHA256 operation. */ + SHA256_Final(ihash, &ctx->ictx); + + /* Feed the inner hash to the outer SHA256 operation. */ + SHA256_Update(&ctx->octx, ihash, 32); + + /* Finish the outer SHA256 operation. */ + SHA256_Final(digest, &ctx->octx); + + /* Clean the stack. */ + memset(ihash, 0, 32); +} + +/** + * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen): + * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and + * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). + */ +void +PBKDF2_SHA256(const uint8_t * passwd, size_t passwdlen, const uint8_t * salt, + size_t saltlen, uint64_t c, uint8_t * buf, size_t dkLen) +{ + HMAC_SHA256_CTX PShctx, hctx; + size_t i; + uint8_t ivec[4]; + uint8_t U[32]; + uint8_t T[32]; + uint64_t j; + int k; + size_t clen; + + /* Compute HMAC state after processing P and S. */ + HMAC_SHA256_Init(&PShctx, passwd, passwdlen); + HMAC_SHA256_Update(&PShctx, salt, saltlen); + + /* Iterate through the blocks. */ + for (i = 0; i * 32 < dkLen; i++) { + /* Generate INT(i + 1). */ + be32enc(ivec, (uint32_t)(i + 1)); + + /* Compute U_1 = PRF(P, S || INT(i)). */ + memcpy(&hctx, &PShctx, sizeof(HMAC_SHA256_CTX)); + HMAC_SHA256_Update(&hctx, ivec, 4); + HMAC_SHA256_Final(U, &hctx); + + /* T_i = U_1 ... */ + memcpy(T, U, 32); + + for (j = 2; j <= c; j++) { + /* Compute U_j. */ + HMAC_SHA256_Init(&hctx, passwd, passwdlen); + HMAC_SHA256_Update(&hctx, U, 32); + HMAC_SHA256_Final(U, &hctx); + + /* ... xor U_j ... */ + for (k = 0; k < 32; k++) + T[k] ^= U[k]; + } + + /* Copy as many bytes as necessary into buf. */ + clen = dkLen - i * 32; + if (clen > 32) + clen = 32; + memcpy(&buf[i * 32], T, clen); + } + + /* Clean PShctx, since we never called _Final on it. */ + memset(&PShctx, 0, sizeof(HMAC_SHA256_CTX)); +} diff --git a/mozglue/android/pbkdf2_sha256.h b/mozglue/android/pbkdf2_sha256.h new file mode 100644 index 000000000..ca2af7a8f --- /dev/null +++ b/mozglue/android/pbkdf2_sha256.h @@ -0,0 +1,70 @@ +/*- + * Copyright 2005,2007,2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/libmd/sha256.h,v 1.2 2006/01/17 15:35:56 phk Exp $ + */ + +#ifndef _SHA256_H_ +#define _SHA256_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <sys/types.h> + +#include <stdint.h> + +typedef struct SHA256Context { + uint32_t state[8]; + uint32_t count[2]; + unsigned char buf[64]; +} SHA256_CTX; + +typedef struct HMAC_SHA256Context { + SHA256_CTX ictx; + SHA256_CTX octx; +} HMAC_SHA256_CTX; + +void SHA256_Init(SHA256_CTX *); +void SHA256_Update(SHA256_CTX *, const void *, size_t); +void SHA256_Final(unsigned char [32], SHA256_CTX *); +void HMAC_SHA256_Init(HMAC_SHA256_CTX *, const void *, size_t); +void HMAC_SHA256_Update(HMAC_SHA256_CTX *, const void *, size_t); +void HMAC_SHA256_Final(unsigned char [32], HMAC_SHA256_CTX *); + +/** + * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen): + * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and + * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). + */ +void PBKDF2_SHA256(const uint8_t *, size_t, const uint8_t *, size_t, + uint64_t, uint8_t *, size_t); + +#ifdef __cplusplus +} +#endif + +#endif /* !_SHA256_H_ */ |