summaryrefslogtreecommitdiffstats
path: root/mozglue/android/NSSBridge.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mozglue/android/NSSBridge.cpp')
-rw-r--r--mozglue/android/NSSBridge.cpp295
1 files changed, 295 insertions, 0 deletions
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;
+}
+
+