summaryrefslogtreecommitdiff
path: root/mozglue/android
diff options
context:
space:
mode:
Diffstat (limited to 'mozglue/android')
-rw-r--r--mozglue/android/APKOpen.cpp465
-rw-r--r--mozglue/android/APKOpen.h39
-rw-r--r--mozglue/android/NSSBridge.cpp295
-rw-r--r--mozglue/android/NSSBridge.h46
-rw-r--r--mozglue/android/NativeCrypto.cpp132
-rw-r--r--mozglue/android/NativeCrypto.h53
-rw-r--r--mozglue/android/SQLiteBridge.cpp413
-rw-r--r--mozglue/android/SQLiteBridge.h34
-rw-r--r--mozglue/android/SharedMemNatives.cpp65
-rw-r--r--mozglue/android/moz.build58
-rw-r--r--mozglue/android/nsGeckoUtils.cpp124
-rw-r--r--mozglue/android/pbkdf2_sha256.c432
-rw-r--r--mozglue/android/pbkdf2_sha256.h70
13 files changed, 2226 insertions, 0 deletions
diff --git a/mozglue/android/APKOpen.cpp b/mozglue/android/APKOpen.cpp
new file mode 100644
index 0000000000..1aabc155eb
--- /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 0000000000..f28458feb6
--- /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 0000000000..2a9b5771df
--- /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 0000000000..77bcd11724
--- /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 0000000000..9a3632e8d0
--- /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 0000000000..2850e2bb7a
--- /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 0000000000..187900bad9
--- /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 0000000000..f2ede820f0
--- /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 0000000000..d186d6e21e
--- /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 0000000000..cdc5ffb3cb
--- /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 0000000000..22818a746f
--- /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 0000000000..8e90f386af
--- /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 0000000000..ca2af7a8f0
--- /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_ */