--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include "pal_types.h"
+#include "pal_utilities.h"
+#include "pal_safecrt.h"
+
+#include "android_security.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <jni.h>
+#include <android/log.h>
+#include <assert.h>
+
+static JavaVM *gJvm;
+
+#define LOG_DEBUG(fmt, ...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "DOTNET", "%s: " fmt, __FUNCTION__, ## __VA_ARGS__))
+#define LOG_INFO(fmt, ...) ((void)__android_log_print(ANDROID_LOG_INFO, "DOTNET", "%s: " fmt, __FUNCTION__, ## __VA_ARGS__))
+#define LOG_ERROR(fmt, ...) ((void)__android_log_print(ANDROID_LOG_ERROR, "DOTNET", "%s: " fmt, __FUNCTION__, ## __VA_ARGS__))
+
+// java/security/SecureRandom
+static jclass g_randClass = NULL;
+static jmethodID g_randCtor = NULL;
+static jmethodID g_randNextBytesMethod = NULL;
+
+// java/security/MessageDigest
+static jclass g_mdClass = NULL;
+static jmethodID g_mdGetInstanceMethod = NULL;
+static jmethodID g_mdDigestMethod = NULL;
+static jmethodID g_mdDigestCurrentMethodId = NULL;
+static jmethodID g_mdResetMethod = NULL;
+static jmethodID g_mdUpdateMethod = NULL;
+
+// javax/crypto/Mac
+static jclass g_macClass = NULL;
+static jmethodID g_macGetInstanceMethod = NULL;
+static jmethodID g_macDoFinalMethod = NULL;
+static jmethodID g_macUpdateMethod = NULL;
+static jmethodID g_macInitMethod = NULL;
+static jmethodID g_macResetMethod = NULL;
+
+// javax/crypto/spec/SecretKeySpec
+static jclass g_sksClass = NULL;
+static jmethodID g_sksCtor = NULL;
+
+// javax/crypto/Cipher
+static jclass g_cipherClass = NULL;
+static jmethodID g_cipherGetInstanceMethod = NULL;
+static jmethodID g_cipherDoFinalMethod = NULL;
+static jmethodID g_cipherUpdateMethod = NULL;
+static jmethodID g_cipherInitMethod = NULL;
+static jmethodID g_getBlockSizeMethod = NULL;
+
+// javax/crypto/spec/IvParameterSpec
+static jclass g_ivPsClass = NULL;
+static jmethodID g_ivPsCtor = NULL;
+
+static jobject ToGRef(JNIEnv *env, jobject lref)
+{
+ if (!lref)
+ return NULL;
+ jobject gref = (*env)->NewGlobalRef(env, lref);
+ (*env)->DeleteLocalRef(env, lref);
+ return gref;
+}
+
+static void ReleaseGRef(JNIEnv *env, jobject gref)
+{
+ if (gref)
+ (*env)->DeleteGlobalRef(env, gref);
+}
+
+static jclass GetClassGRef(JNIEnv *env, const char* name)
+{
+ LOG_DEBUG("Finding %s class", name);
+ jclass klass = ToGRef(env, (*env)->FindClass (env, name));
+ if (!klass) {
+ LOG_ERROR("class %s was not found", name);
+ assert(klass);
+ }
+ return klass;
+}
+
+static bool CheckJNIExceptions(JNIEnv* env)
+{
+ if ((*env)->ExceptionCheck(env))
+ {
+ (*env)->ExceptionDescribe(env);
+ (*env)->ExceptionClear(env);
+ return true;
+ }
+ return false;
+}
+
+static jmethodID GetMethod(JNIEnv *env, bool isStatic, jclass klass, const char* name, const char* sig)
+{
+ LOG_DEBUG("Finding %s method", name);
+ jmethodID mid = isStatic ? (*env)->GetStaticMethodID(env, klass, name, sig) : (*env)->GetMethodID(env, klass, name, sig);
+ if (!mid) {
+ LOG_ERROR("method %s %s was not found", name, sig);
+ assert(mid);
+ }
+ return mid;
+}
+
+static JNIEnv* GetJniEnv()
+{
+ JNIEnv *env;
+ (*gJvm)->GetEnv(gJvm, (void**)&env, JNI_VERSION_1_6);
+ if (env)
+ return env;
+ jint ret = (*gJvm)->AttachCurrentThreadAsDaemon(gJvm, &env, NULL);
+ assert(ret == JNI_OK && "Unable to attach thread to JVM");
+ return env;
+}
+
+PALEXPORT JNIEXPORT jint JNICALL
+JNI_OnLoad(JavaVM *vm, void *reserved)
+{
+ (void)reserved;
+ LOG_INFO("JNI_OnLoad in android_security.c");
+ gJvm = vm;
+
+ JNIEnv* env = GetJniEnv();
+
+ // cache some classes and methods while we're in the thread-safe JNI_OnLoad
+ g_randClass = GetClassGRef(env, "java/security/SecureRandom");
+ g_randCtor = GetMethod(env, false, g_randClass, "<init>", "()V");
+ g_randNextBytesMethod = GetMethod(env, false, g_randClass, "nextBytes", "([B)V");
+
+ g_mdClass = GetClassGRef(env, "java/security/MessageDigest");
+ g_mdGetInstanceMethod = GetMethod(env, true, g_mdClass, "getInstance", "(Ljava/lang/String;)Ljava/security/MessageDigest;");
+ g_mdResetMethod = GetMethod(env, false, g_mdClass, "reset", "()V");
+ g_mdDigestMethod = GetMethod(env, false, g_mdClass, "digest", "([B)[B");
+ g_mdDigestCurrentMethodId = GetMethod(env, false, g_mdClass, "digest", "()[B");
+ g_mdUpdateMethod = GetMethod(env, false, g_mdClass, "update", "([B)V");
+
+ g_macClass = GetClassGRef(env, "javax/crypto/Mac");
+ g_macGetInstanceMethod = GetMethod(env, true, g_macClass, "getInstance", "(Ljava/lang/String;)Ljavax/crypto/Mac;");
+ g_macDoFinalMethod = GetMethod(env, false, g_macClass, "doFinal", "()[B");
+ g_macUpdateMethod = GetMethod(env, false, g_macClass, "update", "([B)V");
+ g_macInitMethod = GetMethod(env, false, g_macClass, "init", "(Ljava/security/Key;)V");
+ g_macResetMethod = GetMethod(env, false, g_macClass, "reset", "()V");
+
+ g_sksClass = GetClassGRef(env, "javax/crypto/spec/SecretKeySpec");
+ g_sksCtor = GetMethod(env, false, g_sksClass, "<init>", "([BLjava/lang/String;)V");
+
+ g_cipherClass = GetClassGRef(env, "javax/crypto/Cipher");
+ g_cipherGetInstanceMethod = GetMethod(env, true, g_cipherClass, "getInstance", "(Ljava/lang/String;)Ljavax/crypto/Cipher;");
+ g_getBlockSizeMethod = GetMethod(env, false, g_cipherClass, "getBlockSize", "()I");
+ g_cipherDoFinalMethod = GetMethod(env, false, g_cipherClass, "doFinal", "([BI)I");
+ g_cipherUpdateMethod = GetMethod(env, false, g_cipherClass, "update", "([B)[B");
+ g_cipherInitMethod = GetMethod(env, false, g_cipherClass, "init", "(ILjava/security/Key;Ljava/security/spec/AlgorithmParameterSpec;)V");
+
+ g_ivPsClass = GetClassGRef(env, "javax/crypto/spec/IvParameterSpec");
+ g_ivPsCtor = GetMethod(env, false, g_ivPsClass, "<init>", "([B)V");
+
+ return JNI_VERSION_1_6;
+}
+
+int32_t CryptoNative_EnsureOpenSslInitialized(void)
+{
+ return 0;
+}
+
+int32_t CryptoNative_GetRandomBytes(uint8_t* buff, int32_t len)
+{
+ JNIEnv* env = GetJniEnv();
+ jobject randObj = (*env)->NewObject(env, g_randClass, g_randCtor);
+ assert(randObj && "Unable to create an instance of java/security/SecureRandom");
+
+ jbyteArray buffArray = (*env)->NewByteArray(env, len);
+ (*env)->SetByteArrayRegion(env, buffArray, 0, len, (jbyte*)buff);
+ (*env)->CallVoidMethod(env, randObj, g_randNextBytesMethod, buffArray);
+ (*env)->GetByteArrayRegion(env, buffArray, 0, len, (jbyte*)buff);
+
+ (*env)->DeleteLocalRef(env, buffArray);
+ (*env)->DeleteLocalRef(env, randObj);
+
+ return CheckJNIExceptions(env) ? FAIL : SUCCESS;
+}
+
+// just some unique numbers
+intptr_t CryptoNative_EvpMd5() { return 101; }
+intptr_t CryptoNative_EvpSha1() { return 102; }
+intptr_t CryptoNative_EvpSha256() { return 103; }
+intptr_t CryptoNative_EvpSha384() { return 104; }
+intptr_t CryptoNative_EvpSha512() { return 105; }
+intptr_t CryptoNative_EvpAes128Ecb() { return 1001; }
+intptr_t CryptoNative_EvpAes128Cbc() { return 1002; }
+intptr_t CryptoNative_EvpAes128Cfb8() { return 1003; }
+intptr_t CryptoNative_EvpAes128Cfb128() { return 1004; }
+intptr_t CryptoNative_EvpAes128Gcm() { return 1005; }
+intptr_t CryptoNative_EvpAes128Ccm() { return 1006; }
+intptr_t CryptoNative_EvpAes192Ecb() { return 1007; }
+intptr_t CryptoNative_EvpAes192Cbc() { return 1008; }
+intptr_t CryptoNative_EvpAes192Cfb8() { return 1009; }
+intptr_t CryptoNative_EvpAes192Cfb128() { return 1010; }
+intptr_t CryptoNative_EvpAes192Gcm() { return 1011; }
+intptr_t CryptoNative_EvpAes192Ccm() { return 1012; }
+intptr_t CryptoNative_EvpAes256Ecb() { return 1013; }
+intptr_t CryptoNative_EvpAes256Cbc() { return 1014; }
+intptr_t CryptoNative_EvpAes256Cfb8() { return 1015; }
+intptr_t CryptoNative_EvpAes256Cfb128() { return 1016; }
+intptr_t CryptoNative_EvpAes256Gcm() { return 1017; }
+intptr_t CryptoNative_EvpAes256Ccm() { return 1018; }
+intptr_t CryptoNative_EvpDes3Ecb() { return 1019; }
+intptr_t CryptoNative_EvpDes3Cbc() { return 1020; }
+intptr_t CryptoNative_EvpDes3Cfb8() { return 1021; }
+intptr_t CryptoNative_EvpDes3Cfb64() { return 1022; }
+intptr_t CryptoNative_EvpDesEcb() { return 1023; }
+intptr_t CryptoNative_EvpDesCfb8() { return 1024; }
+intptr_t CryptoNative_EvpDesCbc() { return 1025; }
+intptr_t CryptoNative_EvpRC2Ecb() { return 1026; }
+intptr_t CryptoNative_EvpRC2Cbc() { return 1027; }
+
+int32_t CryptoNative_EvpMdSize(intptr_t md)
+{
+ if (md == CryptoNative_EvpSha1()) return 20;
+ if (md == CryptoNative_EvpSha256()) return 32;
+ if (md == CryptoNative_EvpSha384()) return 48;
+ if (md == CryptoNative_EvpSha512()) return 64;
+ if (md == CryptoNative_EvpMd5()) return 16;
+ assert(0 && "unexpected type");
+ return -1;
+}
+
+int32_t CryptoNative_GetMaxMdSize()
+{
+ return EVP_MAX_MD_SIZE;
+}
+
+static jobject GetMessageDigestInstance(JNIEnv* env, intptr_t type)
+{
+ jobject mdName = NULL;
+ if (type == CryptoNative_EvpSha1())
+ mdName = (*env)->NewStringUTF(env, "SHA-1");
+ else if (type == CryptoNative_EvpSha256())
+ mdName = (*env)->NewStringUTF(env, "SHA-256");
+ else if (type == CryptoNative_EvpSha384())
+ mdName = (*env)->NewStringUTF(env, "SHA-384");
+ else if (type == CryptoNative_EvpSha512())
+ mdName = (*env)->NewStringUTF(env, "SHA-512");
+ else if (type == CryptoNative_EvpMd5())
+ mdName = (*env)->NewStringUTF(env, "MD5");
+ else
+ return NULL;
+
+ jobject mdObj = (*env)->CallStaticObjectMethod(env, g_mdClass, g_mdGetInstanceMethod, mdName);
+ (*env)->DeleteLocalRef(env, mdName);
+
+ return CheckJNIExceptions(env) ? FAIL : mdObj;
+}
+
+int32_t CryptoNative_EvpDigestOneShot(intptr_t type, void* source, int32_t sourceSize, uint8_t* md, uint32_t* mdSize)
+{
+ if (!type || !md || !mdSize || sourceSize < 0)
+ return FAIL;
+
+ JNIEnv* env = GetJniEnv();
+
+ jobject mdObj = GetMessageDigestInstance(env, type);
+ if (!mdObj)
+ return FAIL;
+
+ jbyteArray bytes = (*env)->NewByteArray(env, sourceSize);
+ (*env)->SetByteArrayRegion(env, bytes, 0, sourceSize, (jbyte*) source);
+ jbyteArray hashedBytes = (jbyteArray)(*env)->CallObjectMethod(env, mdObj, g_mdDigestMethod, bytes);
+ assert(hashedBytes && "MessageDigest.digest(...) was not expected to return null");
+
+ jsize hashedBytesLen = (*env)->GetArrayLength(env, hashedBytes);
+ (*env)->GetByteArrayRegion(env, hashedBytes, 0, hashedBytesLen, (jbyte*) md);
+ *mdSize = (uint32_t)hashedBytesLen;
+
+ (*env)->DeleteLocalRef(env, bytes);
+ (*env)->DeleteLocalRef(env, hashedBytes);
+ (*env)->DeleteLocalRef(env, mdObj);
+
+ return CheckJNIExceptions(env) ? FAIL : SUCCESS;
+}
+
+void* CryptoNative_EvpMdCtxCreate(intptr_t type)
+{
+ JNIEnv* env = GetJniEnv();
+ return (void*)ToGRef(env, GetMessageDigestInstance(env, type));
+}
+
+int32_t CryptoNative_EvpDigestReset(void* ctx, intptr_t type)
+{
+ if (!ctx)
+ return FAIL;
+
+ (void)type; // not used
+
+ JNIEnv* env = GetJniEnv();
+ jobject mdObj = (jobject)ctx;
+ (*env)->CallVoidMethod(env, mdObj, g_mdResetMethod);
+
+ return CheckJNIExceptions(env) ? FAIL : SUCCESS;
+}
+
+int32_t CryptoNative_EvpDigestUpdate(void* ctx, void* d, int32_t cnt)
+{
+ if (!ctx)
+ return FAIL;
+
+ JNIEnv* env = GetJniEnv();
+ jobject mdObj = (jobject)ctx;
+
+ jbyteArray bytes = (*env)->NewByteArray(env, cnt);
+ (*env)->SetByteArrayRegion(env, bytes, 0, cnt, (jbyte*) d);
+ (*env)->CallVoidMethod(env, mdObj, g_mdUpdateMethod, bytes);
+ (*env)->DeleteLocalRef(env, bytes);
+
+ return CheckJNIExceptions(env) ? FAIL : SUCCESS;
+}
+
+int32_t CryptoNative_EvpDigestFinalEx(void* ctx, uint8_t* md, uint32_t* s)
+{
+ return CryptoNative_EvpDigestCurrent(ctx, md, s);
+}
+
+int32_t CryptoNative_EvpDigestCurrent(void* ctx, uint8_t* md, uint32_t* s)
+{
+ if (!ctx)
+ return FAIL;
+
+ JNIEnv* env = GetJniEnv();
+ jobject mdObj = (jobject)ctx;
+
+ jbyteArray bytes = (jbyteArray)(*env)->CallObjectMethod(env, mdObj, g_mdDigestCurrentMethodId);
+ assert(bytes && "digest() was not expected to return null");
+ jsize bytesLen = (*env)->GetArrayLength(env, bytes);
+ *s = (uint32_t)bytesLen;
+ (*env)->GetByteArrayRegion(env, bytes, 0, bytesLen, (jbyte*) md);
+ (*env)->DeleteLocalRef(env, bytes);
+
+ return CheckJNIExceptions(env) ? FAIL : SUCCESS;
+}
+
+void CryptoNative_EvpMdCtxDestroy(void* ctx)
+{
+ ReleaseGRef(GetJniEnv(), (jobject)ctx);
+}
+
+void* CryptoNative_HmacCreate(uint8_t* key, int32_t keyLen, intptr_t type)
+{
+ assert(key || (keyLen == 0));
+ assert(keyLen >= 0);
+
+ // Mac mac = Mac.getInstance(algName);
+ // SecretKeySpec key = new SecretKeySpec(key, algName);
+ // mac.init(key);
+
+ JNIEnv* env = GetJniEnv();
+
+ jstring macName = NULL;
+ if (type == CryptoNative_EvpSha1())
+ macName = (jstring)(*env)->NewStringUTF(env, "HmacSHA1");
+ else if (type == CryptoNative_EvpSha256())
+ macName = (jstring)(*env)->NewStringUTF(env, "HmacSHA256");
+ else if (type == CryptoNative_EvpSha384())
+ macName = (jstring)(*env)->NewStringUTF(env, "HmacSHA384");
+ else if (type == CryptoNative_EvpSha512())
+ macName = (jstring)(*env)->NewStringUTF(env, "HmacSHA512");
+ else if (type == CryptoNative_EvpMd5())
+ macName = (jstring)(*env)->NewStringUTF(env, "HmacMD5");
+ else
+ return FAIL;
+
+ jbyteArray keyBytes = (*env)->NewByteArray(env, keyLen);
+ (*env)->SetByteArrayRegion(env, keyBytes, 0, keyLen, (jbyte*)key);
+ jobject sksObj = (*env)->NewObject(env, g_sksClass, g_sksCtor, keyBytes, macName);
+ assert(sksObj && "Unable to create an instance of SecretKeySpec");
+ jobject macObj = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_macClass, g_macGetInstanceMethod, macName));
+ (*env)->CallVoidMethod(env, macObj, g_macInitMethod, sksObj);
+ (*env)->DeleteLocalRef(env, keyBytes);
+ (*env)->DeleteLocalRef(env, sksObj);
+ (*env)->DeleteLocalRef(env, macName);
+
+ return CheckJNIExceptions(env) ? FAIL : macObj;
+}
+
+int32_t CryptoNative_HmacReset(void* ctx)
+{
+ if (!ctx)
+ return FAIL;
+
+ JNIEnv* env = GetJniEnv();
+ jobject macObj = (jobject)ctx;
+ (*env)->CallVoidMethod(env, macObj, g_macResetMethod);
+
+ return CheckJNIExceptions(env) ? FAIL : SUCCESS;
+}
+
+int32_t CryptoNative_HmacUpdate(void* ctx, uint8_t* data, int32_t len)
+{
+ if (!ctx)
+ return FAIL;
+
+ JNIEnv* env = GetJniEnv();
+ jobject macObj = (jobject)ctx;
+ jbyteArray dataBytes = (*env)->NewByteArray(env, len);
+ (*env)->SetByteArrayRegion(env, dataBytes, 0, len, (jbyte*)data);
+ (*env)->CallVoidMethod(env, macObj, g_macUpdateMethod, dataBytes);
+ (*env)->DeleteLocalRef(env, dataBytes);
+
+ return CheckJNIExceptions(env) ? FAIL : SUCCESS;
+}
+
+int32_t CryptoNative_HmacFinal(void* ctx, uint8_t* data, int32_t* len)
+{
+ return CryptoNative_HmacCurrent(ctx, data, len);
+}
+
+int32_t CryptoNative_HmacCurrent(void* ctx, uint8_t* data, int32_t* len)
+{
+ if (!ctx)
+ return FAIL;
+
+ JNIEnv* env = GetJniEnv();
+ jobject macObj = (jobject)ctx;
+ jbyteArray dataBytes = (jbyteArray)(*env)->CallObjectMethod(env, macObj, g_macDoFinalMethod);
+ jsize dataBytesLen = (*env)->GetArrayLength(env, dataBytes);
+ *len = (int32_t)dataBytesLen;
+ (*env)->GetByteArrayRegion(env, dataBytes, 0, dataBytesLen, (jbyte*) data);
+ (*env)->DeleteLocalRef(env, dataBytes);
+
+ return CheckJNIExceptions(env) ? FAIL : SUCCESS;
+}
+
+void CryptoNative_HmacDestroy(void* ctx)
+{
+ ReleaseGRef(GetJniEnv(), (jobject)ctx);
+}
+
+// TODO: AES/DES
+
+void* CryptoNative_EvpCipherCreate2(intptr_t type, uint8_t* key, int32_t keyLength, int32_t effectiveKeyLength, uint8_t* iv, int32_t enc)
+{
+ if (effectiveKeyLength != 0)
+ {
+ LOG_ERROR("Non-zero effectiveKeyLength is not supported");
+ return FAIL;
+ }
+
+ // input: 0 for Decrypt, 1 for Encrypt
+ // Cipher: 2 for Decrypt, 1 for Encrypt
+ assert(enc == 0 || enc == 1);
+ int encMode = enc == 0 ? 2 : 1;
+
+ JNIEnv* env = GetJniEnv();
+ // Cipher cipher = Cipher.getInstance("AES");
+ // int ivSize = cipher.getBlockSize();
+ // SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES");
+ // IvParameterSpec ivSpec = new IvParameterSpec(IV);
+ // cipher.init(encMode, keySpec, ivSpec);
+
+ jobject algName = NULL;
+
+ if ((type == CryptoNative_EvpAes128Cbc()) ||
+ (type == CryptoNative_EvpAes192Cbc()) ||
+ (type == CryptoNative_EvpAes256Cbc()))
+ {
+ algName = (*env)->NewStringUTF(env, "AES/CBC/NoPadding");
+ }
+ else if (
+ (type == CryptoNative_EvpAes128Ecb()) ||
+ (type == CryptoNative_EvpAes192Ecb()) ||
+ (type == CryptoNative_EvpAes256Ecb()))
+ {
+ algName = (*env)->NewStringUTF(env, "AES/ECB/NoPadding");
+ }
+ else if (
+ (type == CryptoNative_EvpAes128Cfb8()) ||
+ (type == CryptoNative_EvpAes192Cfb8()) ||
+ (type == CryptoNative_EvpAes256Cfb8()))
+ {
+ algName = (*env)->NewStringUTF(env, "AES/CFB/NoPadding");
+ }
+ else
+ {
+ LOG_ERROR("unexpected type: %d", (int)type);
+ return FAIL;
+ }
+
+ jobject cipherObj = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_cipherClass, g_cipherGetInstanceMethod, algName));
+
+ int blockSize = (*env)->CallIntMethod(env, cipherObj, g_getBlockSizeMethod);
+ jbyteArray keyBytes = (*env)->NewByteArray(env, keyLength / 8); // bits to bytes, e.g. 256 -> 32
+ (*env)->SetByteArrayRegion(env, keyBytes, 0, keyLength / 8, (jbyte*)key);
+ jbyteArray ivBytes = (*env)->NewByteArray(env, blockSize);
+ (*env)->SetByteArrayRegion(env, ivBytes, 0, blockSize, (jbyte*)iv);
+
+ jobject sksObj = (*env)->NewObject(env, g_sksClass, g_sksCtor, keyBytes, algName);
+ jobject ivPsObj = (*env)->NewObject(env, g_ivPsClass, g_ivPsCtor, ivBytes);
+ (*env)->CallVoidMethod(env, cipherObj, g_cipherInitMethod, encMode, sksObj, ivPsObj);
+
+ (*env)->DeleteLocalRef(env, algName);
+ (*env)->DeleteLocalRef(env, sksObj);
+ (*env)->DeleteLocalRef(env, ivPsObj);
+ (*env)->DeleteLocalRef(env, keyBytes);
+ (*env)->DeleteLocalRef(env, ivBytes);
+
+ return CheckJNIExceptions(env) ? FAIL : cipherObj;
+}
+
+
+int32_t CryptoNative_EvpCipherUpdate(void* ctx, uint8_t* outm, int32_t* outl, uint8_t* in, int32_t inl)
+{
+ if (!ctx)
+ return FAIL;
+
+ JNIEnv* env = GetJniEnv();
+ jobject cipherObj = (jobject)ctx;
+ jbyteArray inDataBytes = (*env)->NewByteArray(env, inl);
+ (*env)->SetByteArrayRegion(env, inDataBytes, 0, inl, (jbyte*)in);
+ jbyteArray outDataBytes = (jbyteArray)(*env)->CallObjectMethod(env, cipherObj, g_cipherUpdateMethod, inDataBytes);
+ if (outDataBytes) {
+ jsize outDataBytesLen = (*env)->GetArrayLength(env, outDataBytes);
+ *outl = (int32_t)outDataBytesLen;
+ (*env)->GetByteArrayRegion(env, outDataBytes, 0, outDataBytesLen, (jbyte*) outm);
+ (*env)->DeleteLocalRef(env, outDataBytes);
+ } else {
+ *outl = 0;
+ }
+
+ (*env)->DeleteLocalRef(env, inDataBytes);
+
+ return CheckJNIExceptions(env) ? FAIL : SUCCESS;
+}
+
+int32_t CryptoNative_EvpCipherFinalEx(void* ctx, uint8_t* outm, int32_t* outl)
+{
+ if (!ctx)
+ return FAIL;
+
+ JNIEnv* env = GetJniEnv();
+ jobject cipherObj = (jobject)ctx;
+
+ int blockSize = (*env)->CallIntMethod(env, cipherObj, g_getBlockSizeMethod);
+ jbyteArray outBytes = (*env)->NewByteArray(env, blockSize);
+ int written = (*env)->CallIntMethod(env, cipherObj, g_cipherDoFinalMethod, outBytes, 0 /*offset*/);
+ if (written > 0)
+ (*env)->GetByteArrayRegion(env, outBytes, 0, blockSize, (jbyte*) outm);
+ *outl = written;
+ (*env)->DeleteLocalRef(env, outBytes);
+
+ return CheckJNIExceptions(env) ? FAIL : SUCCESS;
+}
+
+int32_t CryptoNative_EvpCipherCtxSetPadding(void* x, int32_t padding)
+{
+ if (padding == 0)
+ {
+ return SUCCESS;
+ }
+ else
+ {
+ LOG_ERROR("Non-zero padding (%d) is not supported", (int)padding);
+ return FAIL;
+ }
+}
+
+int32_t CryptoNative_EvpCipherReset(void* ctx)
+{
+ // there is no "reset()" API for an existing Cipher object
+ return SUCCESS;
+}
+
+void CryptoNative_EvpCipherDestroy(void* ctx)
+{
+ ReleaseGRef(GetJniEnv(), (jobject)ctx);
+}