blob: ddeaf80679dbba741df1a23be63a34e04365f000 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "org_apache_commons_crypto.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// export the native interfaces
#ifdef JNIEXPORT
#undef JNIEXPORT
#endif
#define JNIEXPORT __attribute__((__visibility__("default")))
#include "OpenSslNative.h"
#ifdef UNIX
static EVP_CIPHER_CTX * (*dlsym_EVP_CIPHER_CTX_new)(void);
static void (*dlsym_EVP_CIPHER_CTX_free)(EVP_CIPHER_CTX *);
static int (*dlsym_EVP_CIPHER_CTX_set_padding)(EVP_CIPHER_CTX *, int);
static int (*dlsym_EVP_CIPHER_CTX_ctrl)(EVP_CIPHER_CTX *, int, int, void *);
static int (*dlsym_EVP_CIPHER_CTX_block_size)(EVP_CIPHER_CTX *);
static EVP_CIPHER * (*dlsym_EVP_CIPHER_CTX_cipher)(EVP_CIPHER_CTX *);
static unsigned long (*dlsym_EVP_CIPHER_flags)(const EVP_CIPHER *);
static int (*dlsym_EVP_CIPHER_CTX_test_flags)(const EVP_CIPHER_CTX *, int);
static int (*dlsym_EVP_CipherInit_ex)(EVP_CIPHER_CTX *, const EVP_CIPHER *, \
ENGINE *, const unsigned char *, const unsigned char *, int);
static int (*dlsym_EVP_CipherUpdate)(EVP_CIPHER_CTX *, unsigned char *, \
int *, const unsigned char *, int);
static int (*dlsym_EVP_CipherFinal_ex)(EVP_CIPHER_CTX *, unsigned char *, int *);
static EVP_CIPHER * (*dlsym_EVP_aes_256_ctr)(void);
static EVP_CIPHER * (*dlsym_EVP_aes_192_ctr)(void);
static EVP_CIPHER * (*dlsym_EVP_aes_128_ctr)(void);
static EVP_CIPHER * (*dlsym_EVP_aes_256_cbc)(void);
static EVP_CIPHER * (*dlsym_EVP_aes_192_cbc)(void);
static EVP_CIPHER * (*dlsym_EVP_aes_128_cbc)(void);
static EVP_CIPHER * (*dlsym_EVP_aes_256_gcm)(void);
static EVP_CIPHER * (*dlsym_EVP_aes_192_gcm)(void);
static EVP_CIPHER * (*dlsym_EVP_aes_128_gcm)(void);
#endif
#ifdef WINDOWS
typedef EVP_CIPHER_CTX * (__cdecl *__dlsym_EVP_CIPHER_CTX_new)(void);
typedef void (__cdecl *__dlsym_EVP_CIPHER_CTX_free)(EVP_CIPHER_CTX *);
typedef int (__cdecl *__dlsym_EVP_CIPHER_CTX_set_padding)(EVP_CIPHER_CTX *, int);
typedef int (__cdecl *__dlsym_EVP_CIPHER_CTX_ctrl)(EVP_CIPHER_CTX *, int, int, void *);
typedef int (__cdecl *__dlsym_EVP_CIPHER_CTX_block_size)(EVP_CIPHER_CTX *);
typedef EVP_CIPHER * (__cdecl *__dlsym_EVP_CIPHER_CTX_cipher)(EVP_CIPHER_CTX *);
typedef unsigned long (__cdecl *__dlsym_EVP_CIPHER_flags)(const EVP_CIPHER *);
typedef int (__cdecl *__dlsym_EVP_CIPHER_CTX_test_flags)(const EVP_CIPHER_CTX *, int);
typedef int (__cdecl *__dlsym_EVP_CipherInit_ex)(EVP_CIPHER_CTX *, \
const EVP_CIPHER *, ENGINE *, const unsigned char *, \
const unsigned char *, int);
typedef int (__cdecl *__dlsym_EVP_CipherUpdate)(EVP_CIPHER_CTX *, \
unsigned char *, int *, const unsigned char *, int);
typedef int (__cdecl *__dlsym_EVP_CipherFinal_ex)(EVP_CIPHER_CTX *, \
unsigned char *, int *);
typedef EVP_CIPHER * (__cdecl *__dlsym_EVP_aes_256_ctr)(void);
typedef EVP_CIPHER * (__cdecl *__dlsym_EVP_aes_192_ctr)(void);
typedef EVP_CIPHER * (__cdecl *__dlsym_EVP_aes_128_ctr)(void);
typedef EVP_CIPHER * (__cdecl *__dlsym_EVP_aes_256_cbc)(void);
typedef EVP_CIPHER * (__cdecl *__dlsym_EVP_aes_192_cbc)(void);
typedef EVP_CIPHER * (__cdecl *__dlsym_EVP_aes_128_cbc)(void);
typedef EVP_CIPHER * (__cdecl *__dlsym_EVP_aes_256_gcm)(void);
typedef EVP_CIPHER * (__cdecl *__dlsym_EVP_aes_192_gcm)(void);
typedef EVP_CIPHER * (__cdecl *__dlsym_EVP_aes_128_gcm)(void);
static __dlsym_EVP_CIPHER_CTX_new dlsym_EVP_CIPHER_CTX_new;
static __dlsym_EVP_CIPHER_CTX_free dlsym_EVP_CIPHER_CTX_free;
static __dlsym_EVP_CIPHER_CTX_set_padding dlsym_EVP_CIPHER_CTX_set_padding;
static __dlsym_EVP_CIPHER_CTX_ctrl dlsym_EVP_CIPHER_CTX_ctrl;
static __dlsym_EVP_CIPHER_CTX_block_size dlsym_EVP_CIPHER_CTX_block_size;
static __dlsym_EVP_CIPHER_CTX_cipher dlsym_EVP_CIPHER_CTX_cipher;
static __dlsym_EVP_CIPHER_flags dlsym_EVP_CIPHER_flags;
static __dlsym_EVP_CIPHER_CTX_test_flags dlsym_EVP_CIPHER_CTX_test_flags;
static __dlsym_EVP_CipherInit_ex dlsym_EVP_CipherInit_ex;
static __dlsym_EVP_CipherUpdate dlsym_EVP_CipherUpdate;
static __dlsym_EVP_CipherFinal_ex dlsym_EVP_CipherFinal_ex;
static __dlsym_EVP_aes_256_ctr dlsym_EVP_aes_256_ctr;
static __dlsym_EVP_aes_192_ctr dlsym_EVP_aes_192_ctr;
static __dlsym_EVP_aes_128_ctr dlsym_EVP_aes_128_ctr;
static __dlsym_EVP_aes_256_cbc dlsym_EVP_aes_256_cbc;
static __dlsym_EVP_aes_192_cbc dlsym_EVP_aes_192_cbc;
static __dlsym_EVP_aes_128_cbc dlsym_EVP_aes_128_cbc;
static __dlsym_EVP_aes_256_gcm dlsym_EVP_aes_256_gcm;
static __dlsym_EVP_aes_192_gcm dlsym_EVP_aes_192_gcm;
static __dlsym_EVP_aes_128_gcm dlsym_EVP_aes_128_gcm;
#endif
#ifdef UNIX
static void loadAes(JNIEnv *env, void *openssl)
#endif
#ifdef WINDOWS
static void loadAes(JNIEnv *env, HMODULE openssl)
#endif
{
#ifdef UNIX
LOAD_DYNAMIC_SYMBOL(dlsym_EVP_aes_256_ctr, env, openssl, "EVP_aes_256_ctr");
LOAD_DYNAMIC_SYMBOL(dlsym_EVP_aes_192_ctr, env, openssl, "EVP_aes_192_ctr");
LOAD_DYNAMIC_SYMBOL(dlsym_EVP_aes_128_ctr, env, openssl, "EVP_aes_128_ctr");
LOAD_DYNAMIC_SYMBOL(dlsym_EVP_aes_256_cbc, env, openssl, "EVP_aes_256_cbc");
LOAD_DYNAMIC_SYMBOL(dlsym_EVP_aes_192_cbc, env, openssl, "EVP_aes_192_cbc");
LOAD_DYNAMIC_SYMBOL(dlsym_EVP_aes_128_cbc, env, openssl, "EVP_aes_128_cbc");
LOAD_DYNAMIC_SYMBOL(dlsym_EVP_aes_256_gcm, env, openssl, "EVP_aes_256_gcm");
LOAD_DYNAMIC_SYMBOL(dlsym_EVP_aes_192_gcm, env, openssl, "EVP_aes_192_gcm");
LOAD_DYNAMIC_SYMBOL(dlsym_EVP_aes_128_gcm, env, openssl, "EVP_aes_128_gcm");
#endif
#ifdef WINDOWS
LOAD_DYNAMIC_SYMBOL(__dlsym_EVP_aes_256_ctr, dlsym_EVP_aes_256_ctr, \
env, openssl, "EVP_aes_256_ctr");
LOAD_DYNAMIC_SYMBOL(__dlsym_EVP_aes_192_ctr, dlsym_EVP_aes_192_ctr, \
env, openssl, "EVP_aes_192_ctr");
LOAD_DYNAMIC_SYMBOL(__dlsym_EVP_aes_128_ctr, dlsym_EVP_aes_128_ctr, \
env, openssl, "EVP_aes_128_ctr");
LOAD_DYNAMIC_SYMBOL(__dlsym_EVP_aes_256_cbc, dlsym_EVP_aes_256_cbc, \
env, openssl, "EVP_aes_256_cbc");
LOAD_DYNAMIC_SYMBOL(__dlsym_EVP_aes_192_cbc, dlsym_EVP_aes_192_cbc, \
env, openssl, "EVP_aes_192_cbc");
LOAD_DYNAMIC_SYMBOL(__dlsym_EVP_aes_128_cbc, dlsym_EVP_aes_128_cbc, \
env, openssl, "EVP_aes_128_cbc");
LOAD_DYNAMIC_SYMBOL(__dlsym_EVP_aes_256_gcm, dlsym_EVP_aes_256_gcm, \
env, openssl, "EVP_aes_256_gcm");
LOAD_DYNAMIC_SYMBOL(__dlsym_EVP_aes_192_gcm, dlsym_EVP_aes_192_gcm, \
env, openssl, "EVP_aes_192_gcm");
LOAD_DYNAMIC_SYMBOL(__dlsym_EVP_aes_128_gcm, dlsym_EVP_aes_128_gcm, \
env, openssl, "EVP_aes_128_gcm");
#endif
}
JNIEXPORT void JNICALL Java_org_apache_commons_crypto_cipher_OpenSslNative_initIDs
(JNIEnv *env, jclass clazz)
{
char msg[1000];
#ifdef UNIX
void *openssl = dlopen(COMMONS_CRYPTO_OPENSSL_LIBRARY, RTLD_LAZY | RTLD_GLOBAL);
#endif
#ifdef WINDOWS
HMODULE openssl = LoadLibrary(TEXT(COMMONS_CRYPTO_OPENSSL_LIBRARY));
#endif
if (!openssl) {
#ifdef UNIX
snprintf(msg, sizeof(msg), "Cannot load %s (%s)!", COMMONS_CRYPTO_OPENSSL_LIBRARY, \
dlerror());
#endif
#ifdef WINDOWS
snprintf(msg, sizeof(msg), "Cannot load %s (%d)!", COMMONS_CRYPTO_OPENSSL_LIBRARY, \
GetLastError());
#endif
THROW(env, "java/lang/UnsatisfiedLinkError", msg);
return;
}
#ifdef UNIX
dlerror(); // Clear any existing error
LOAD_DYNAMIC_SYMBOL(dlsym_EVP_CIPHER_CTX_new, env, openssl, "EVP_CIPHER_CTX_new");
LOAD_DYNAMIC_SYMBOL(dlsym_EVP_CIPHER_CTX_free, env, openssl, "EVP_CIPHER_CTX_free");
LOAD_DYNAMIC_SYMBOL(dlsym_EVP_CIPHER_CTX_set_padding, env, openssl, "EVP_CIPHER_CTX_set_padding");
LOAD_DYNAMIC_SYMBOL(dlsym_EVP_CIPHER_CTX_ctrl, env, openssl, "EVP_CIPHER_CTX_ctrl");
LOAD_DYNAMIC_SYMBOL(dlsym_EVP_CIPHER_CTX_block_size, env, openssl, "EVP_CIPHER_CTX_block_size");
LOAD_DYNAMIC_SYMBOL(dlsym_EVP_CIPHER_CTX_cipher, env, openssl, "EVP_CIPHER_CTX_cipher");
LOAD_DYNAMIC_SYMBOL(dlsym_EVP_CIPHER_flags, env, openssl, "EVP_CIPHER_flags");
LOAD_DYNAMIC_SYMBOL(dlsym_EVP_CIPHER_CTX_test_flags, env, openssl, "EVP_CIPHER_CTX_test_flags");
LOAD_DYNAMIC_SYMBOL(dlsym_EVP_CipherInit_ex, env, openssl, "EVP_CipherInit_ex");
LOAD_DYNAMIC_SYMBOL(dlsym_EVP_CipherUpdate, env, openssl, "EVP_CipherUpdate");
LOAD_DYNAMIC_SYMBOL(dlsym_EVP_CipherFinal_ex, env, openssl, "EVP_CipherFinal_ex");
#endif
#ifdef WINDOWS
LOAD_DYNAMIC_SYMBOL(__dlsym_EVP_CIPHER_CTX_new, dlsym_EVP_CIPHER_CTX_new, \
env, openssl, "EVP_CIPHER_CTX_new");
LOAD_DYNAMIC_SYMBOL(__dlsym_EVP_CIPHER_CTX_free, dlsym_EVP_CIPHER_CTX_free, \
env, openssl, "EVP_CIPHER_CTX_free");
LOAD_DYNAMIC_SYMBOL(__dlsym_EVP_CIPHER_CTX_set_padding, dlsym_EVP_CIPHER_CTX_set_padding, \
env, openssl, "EVP_CIPHER_CTX_set_padding");
LOAD_DYNAMIC_SYMBOL(__dlsym_EVP_CIPHER_CTX_ctrl, dlsym_EVP_CIPHER_CTX_ctrl, \
env, openssl, "EVP_CIPHER_CTX_ctrl");
LOAD_DYNAMIC_SYMBOL(__dlsym_EVP_CIPHER_CTX_block_size, dlsym_EVP_CIPHER_CTX_block_size, \
env, openssl, "EVP_CIPHER_CTX_block_size");
LOAD_DYNAMIC_SYMBOL(__dlsym_EVP_CIPHER_CTX_cipher, dlsym_EVP_CIPHER_CTX_cipher, \
env, openssl, "EVP_CIPHER_CTX_cipher");
LOAD_DYNAMIC_SYMBOL(__dlsym_EVP_CIPHER_flags, dlsym_EVP_CIPHER_flags, \
env, openssl, "EVP_CIPHER_flags");
LOAD_DYNAMIC_SYMBOL(__dlsym_EVP_CIPHER_CTX_test_flags, dlsym_EVP_CIPHER_CTX_test_flags, \
env, openssl, "EVP_CIPHER_CTX_test_flags");
LOAD_DYNAMIC_SYMBOL(__dlsym_EVP_CipherInit_ex, dlsym_EVP_CipherInit_ex, \
env, openssl, "EVP_CipherInit_ex");
LOAD_DYNAMIC_SYMBOL(__dlsym_EVP_CipherUpdate, dlsym_EVP_CipherUpdate, \
env, openssl, "EVP_CipherUpdate");
LOAD_DYNAMIC_SYMBOL(__dlsym_EVP_CipherFinal_ex, dlsym_EVP_CipherFinal_ex, \
env, openssl, "EVP_CipherFinal_ex");
#endif
loadAes(env, openssl);
jthrowable jthr = (*env)->ExceptionOccurred(env);
if (jthr) {
(*env)->DeleteLocalRef(env, jthr);
THROW(env, "java/lang/UnsatisfiedLinkError", \
"Cannot find AES-CTR support, is your version of Openssl new enough?");
return;
}
}
typedef struct EVP_CTX_Wrapper {
int initialized;
int encrypt; // ENCRYPT_MODE or DECRYPT_MODE
EVP_CIPHER_CTX *ctx;
} EVP_CTX_Wrapper;
static int check_update_max_output_len(EVP_CTX_Wrapper *wrapper, int input_len, int max_output_len);
static int check_doFinal_max_output_len(JNIEnv *env, EVP_CIPHER_CTX *context, int max_output_len);
static int is_bad_tag(EVP_CTX_Wrapper *wrapper);
#define CTX_WRAPPER(addr) ((EVP_CTX_Wrapper *)(ptrdiff_t) addr)
static EVP_CTX_Wrapper * new_context_wrapper(JNIEnv *env) {
EVP_CTX_Wrapper *wrapper = calloc(1, sizeof(EVP_CTX_Wrapper));
if (wrapper == NULL) {
THROW(env, "java/lang/OutOfMemoryError", NULL);
return NULL;
}
wrapper->ctx = dlsym_EVP_CIPHER_CTX_new();
if (wrapper->ctx == NULL) {
free(wrapper);
wrapper = NULL;
THROW(env, "java/lang/OutOfMemoryError", NULL);
return NULL;
}
return wrapper;
}
static void free_context_wrapper(EVP_CTX_Wrapper *wrapper) {
if (wrapper != NULL) {
if (wrapper->ctx != NULL) {
dlsym_EVP_CIPHER_CTX_free(wrapper->ctx);
}
free(wrapper);
}
}
static EVP_CIPHER_CTX * get_context(JNIEnv *env, jlong addr) {
EVP_CTX_Wrapper *wrapper = CTX_WRAPPER(addr);
if (wrapper == NULL || wrapper->ctx == NULL) {
THROW(env, "java/lang/NullPointerException", "Context address is null.");
return NULL;
}
if (!wrapper->initialized) {
THROW(env, "java/lang/IllegalStateException", "Context is not initialized.");
return NULL;
}
return wrapper->ctx;
}
JNIEXPORT jlong JNICALL Java_org_apache_commons_crypto_cipher_OpenSslNative_initContext
(JNIEnv *env, jclass clazz, jint alg, jint padding)
{
if (alg != AES_CTR && alg != AES_CBC && alg != AES_GCM) {
THROW(env, "java/security/NoSuchAlgorithmException", NULL);
return (jlong)0;
}
if (!(alg == AES_CTR && padding == NOPADDING)
&& !(alg == AES_CBC && (padding == NOPADDING|| padding == PKCS5PADDING))
&& !(alg == AES_GCM && padding == NOPADDING)) {
THROW(env, "javax/crypto/NoSuchPaddingException", NULL);
return (jlong)0;
}
if (dlsym_EVP_aes_256_ctr == NULL ||
dlsym_EVP_aes_192_ctr == NULL || dlsym_EVP_aes_128_ctr == NULL) {
THROW(env, "java/security/NoSuchAlgorithmException", \
"Doesn't support AES CTR.");
return (jlong)0;
}
if (dlsym_EVP_aes_256_cbc == NULL ||
dlsym_EVP_aes_192_cbc == NULL || dlsym_EVP_aes_128_cbc == NULL) {
THROW(env, "java/security/NoSuchAlgorithmException", \
"Doesn't support AES CBC.");
return (jlong)0;
}
if (dlsym_EVP_aes_256_gcm == NULL ||
dlsym_EVP_aes_192_gcm == NULL || dlsym_EVP_aes_128_gcm == NULL) {
THROW(env, "java/security/NoSuchAlgorithmException", \
"Doesn't support AES GCM.");
return (jlong)0;
}
EVP_CTX_Wrapper *wrapper = new_context_wrapper(env);
return JLONG(wrapper);
}
// Only supports AES-CTR and AES-CBC currently
static EVP_CIPHER * getEvpCipher(int alg, int keyLen)
{
EVP_CIPHER *cipher = NULL;
if (alg == AES_CTR) {
if (keyLen == KEY_LENGTH_256) {
cipher = dlsym_EVP_aes_256_ctr();
} else if (keyLen == KEY_LENGTH_192) {
cipher = dlsym_EVP_aes_192_ctr();
} else if (keyLen == KEY_LENGTH_128) {
cipher = dlsym_EVP_aes_128_ctr();
}
} else if (alg == AES_CBC) {
if (keyLen == KEY_LENGTH_256) {
cipher = dlsym_EVP_aes_256_cbc();
} else if (keyLen == KEY_LENGTH_192) {
cipher = dlsym_EVP_aes_192_cbc();
} else if (keyLen == KEY_LENGTH_128) {
cipher = dlsym_EVP_aes_128_cbc();
}
} else if (alg == AES_GCM) {
if (keyLen == KEY_LENGTH_256) {
cipher = dlsym_EVP_aes_256_gcm();
} else if (keyLen == KEY_LENGTH_192) {
cipher = dlsym_EVP_aes_192_gcm();
} else if (keyLen == KEY_LENGTH_128) {
cipher = dlsym_EVP_aes_128_gcm();
}
}
return cipher;
}
JNIEXPORT jlong JNICALL Java_org_apache_commons_crypto_cipher_OpenSslNative_init
(JNIEnv *env, jclass clazz, jlong ctx, jint mode, jint alg, jint padding,
jbyteArray key, jbyteArray iv)
{
EVP_CTX_Wrapper *wrapper = CTX_WRAPPER(ctx);
int is_new_context = 0;
if (wrapper == NULL) {
wrapper = new_context_wrapper(env);
if (wrapper == NULL) {
return JLONG(NULL);
}
is_new_context = 1;
}
EVP_CIPHER_CTX *context = wrapper->ctx;
jbyte *jKey = NULL;
jbyte *jIv = NULL;
int jKeyLen = (*env)->GetArrayLength(env, key);
int jIvLen = (*env)->GetArrayLength(env, iv);
if (jKeyLen != KEY_LENGTH_128 && jKeyLen != KEY_LENGTH_192
&& jKeyLen != KEY_LENGTH_256) {
char str[64] = {0};
snprintf(str, sizeof(str), "Invalid AES key length: %d bytes", jKeyLen);
THROW(env, "java/security/InvalidKeyException", str);
goto cleanup;
}
if ((alg != AES_GCM) && (jIvLen != IV_LENGTH)) {
THROW(env, "java/security/InvalidAlgorithmParameterException", "Wrong IV length: must be 16 bytes long");
goto cleanup;
}
jKey = (*env)->GetByteArrayElements(env, key, NULL);
if (jKey == NULL) {
THROW(env, "java/lang/InternalError", "Cannot get bytes array for key.");
goto cleanup;
}
jIv = (*env)->GetByteArrayElements(env, iv, NULL);
if (jIv == NULL) {
THROW(env, "java/lang/InternalError", "Cannot get bytes array for iv.");
goto cleanup;
}
if (!(alg == AES_CTR || alg == AES_CBC || alg == AES_GCM)) {
THROW(env, "java/security/NoSuchAlgorithmException", "The algorithm is not supported.");
goto cleanup;
}
// initialize cipher & mode
EVP_CIPHER *cipher = getEvpCipher(alg, jKeyLen);
if (cipher == NULL) {
THROW(env, "java/security/InvalidKeyException", "Invalid key length.");
goto cleanup;
}
int rc = dlsym_EVP_CipherInit_ex(context, cipher, NULL, NULL, NULL, mode == ENCRYPT_MODE);
if (rc == 0) {
THROW(env, "java/lang/InternalError", "Error in EVP_CipherInit_ex.");
goto cleanup;
}
// Set IV length if default 12 bytes (96 bits) is not appropriate
// Note: set IV length after cipher is initialized, before iv is initialized.
if (alg == AES_GCM) {
rc = dlsym_EVP_CIPHER_CTX_ctrl(context, EVP_CTRL_GCM_SET_IVLEN, jIvLen, NULL);
if (rc == 0) {
THROW(env, "java/lang/InternalError", "Error setting GCM initial vector length.");
goto cleanup;
}
}
rc = dlsym_EVP_CipherInit_ex(context, NULL, NULL, \
(unsigned char *)jKey, (unsigned char *)jIv, mode == ENCRYPT_MODE);
if (rc == 0) {
THROW(env, "java/lang/InternalError", "Error in EVP_CipherInit_ex.");
goto cleanup;
}
if (padding == NOPADDING) {
dlsym_EVP_CIPHER_CTX_set_padding(context, 0);
} else if (padding == PKCS5PADDING) {
dlsym_EVP_CIPHER_CTX_set_padding(context, 1);
}
// everything is OK,
wrapper->encrypt = mode;
wrapper->initialized = 1;
cleanup:
if (jKey != NULL) {
(*env)->ReleaseByteArrayElements(env, key, jKey, 0);
}
if (jIv != NULL) {
(*env)->ReleaseByteArrayElements(env, iv, jIv, 0);
}
if (is_new_context && !wrapper->initialized) {
free_context_wrapper(wrapper);
wrapper = NULL;
}
return JLONG(wrapper);
}
JNIEXPORT jint JNICALL Java_org_apache_commons_crypto_cipher_OpenSslNative_update
(JNIEnv *env, jclass clazz, jlong ctx, jobject input, jint input_offset,
jint input_len, jobject output, jint output_offset, jint max_output_len)
{
EVP_CIPHER_CTX *context = get_context(env, ctx);
if (context == NULL) {
return 0;
}
if (!check_update_max_output_len(CTX_WRAPPER(ctx), input_len, max_output_len)) {
THROW(env, "javax/crypto/ShortBufferException", \
"Output buffer is not sufficient.");
return 0;
}
unsigned char *input_bytes = (*env)->GetDirectBufferAddress(env, input);
unsigned char *output_bytes = (*env)->GetDirectBufferAddress(env, output);
if (input_bytes == NULL || output_bytes == NULL) {
THROW(env, "java/lang/InternalError", "Cannot get buffer address.");
return 0;
}
input_bytes = input_bytes + input_offset;
output_bytes = output_bytes + output_offset;
int output_len = 0;
if (!dlsym_EVP_CipherUpdate(context, output_bytes, &output_len, \
input_bytes, input_len)) {
THROW(env, "java/lang/InternalError", "Error in EVP_CipherUpdate.");
return 0;
}
return output_len;
}
JNIEXPORT jint JNICALL Java_org_apache_commons_crypto_cipher_OpenSslNative_updateByteArray
(JNIEnv *env, jclass clazz, jlong ctx, jbyteArray input, jint input_offset,
jint input_len, jbyteArray output, jint output_offset, jint max_output_len)
{
EVP_CIPHER_CTX *context = get_context(env, ctx);
if (context == NULL) {
return 0;
}
// when provide AAD to EVP cipher, output is NULL.
if (output != NULL && !check_update_max_output_len(CTX_WRAPPER(ctx), input_len, max_output_len)) {
THROW(env, "javax/crypto/ShortBufferException", "Output buffer is not sufficient.");
return 0;
}
unsigned char *input_bytes = NULL;
unsigned char *output_bytes = NULL;
int output_len = 0;
input_bytes = (unsigned char *) (*env)->GetByteArrayElements(env, input, 0);
// output is NULL when updateAAD
if (output != NULL) {
output_bytes = (unsigned char *) (*env)->GetByteArrayElements(env, output, 0);
}
if (input_bytes == NULL || (output != NULL && output_bytes == NULL)) {
THROW(env, "java/lang/InternalError", "Cannot get buffer address.");
goto cleanup;
}
int rc = dlsym_EVP_CipherUpdate(context, output_bytes + output_offset, &output_len, \
input_bytes + input_offset, input_len);
if (rc == 0) {
THROW(env, "java/lang/InternalError", "Error in EVP_CipherUpdate.");
output_len = 0;
}
cleanup:
if (input_bytes != NULL) {
(*env)->ReleaseByteArrayElements(env, input, (jbyte *) input_bytes, 0);
}
if (output_bytes != NULL) {
(*env)->ReleaseByteArrayElements(env, output, (jbyte *) output_bytes, 0);
}
return output_len;
}
JNIEXPORT jint JNICALL Java_org_apache_commons_crypto_cipher_OpenSslNative_updateByteArrayByteBuffer
(JNIEnv *env, jclass clazz, jlong ctx, jbyteArray input, jint input_offset,
jint input_len, jobject output, jint output_offset, jint max_output_len)
{
EVP_CIPHER_CTX *context = get_context(env, ctx);
if (context == NULL) {
return 0;
}
if (!check_update_max_output_len(CTX_WRAPPER(ctx), input_len, max_output_len)) {
THROW(env, "javax/crypto/ShortBufferException", \
"Output buffer is not sufficient.");
return 0;
}
unsigned char *input_bytes = (unsigned char *) (*env)->GetByteArrayElements(env, input, 0);
unsigned char *output_bytes = (*env)->GetDirectBufferAddress(env, output);
int output_len = 0;
if (input_bytes == NULL || output_bytes == NULL) {
THROW(env, "java/lang/InternalError", "Cannot get buffer address.");
goto cleanup;
}
int rc = dlsym_EVP_CipherUpdate(context, output_bytes + output_offset, &output_len,
input_bytes + input_offset, input_len);
if (rc == 0) {
THROW(env, "java/lang/InternalError", "Error in EVP_CipherUpdate.");
}
cleanup:
if (input_bytes != NULL) {
(*env)->ReleaseByteArrayElements(env, input, (jbyte *) input_bytes, 0);
}
return output_len;
}
JNIEXPORT jint JNICALL Java_org_apache_commons_crypto_cipher_OpenSslNative_doFinal
(JNIEnv *env, jclass clazz, jlong ctx, jobject output, jint offset,
jint max_output_len)
{
EVP_CIPHER_CTX *context = get_context(env, ctx);
if (context == NULL) {
return 0;
}
if (!check_doFinal_max_output_len(env, context, max_output_len)) {
THROW(env, "javax/crypto/ShortBufferException", \
"Output buffer is not sufficient.");
return 0;
}
unsigned char *output_bytes = (*env)->GetDirectBufferAddress(env, output);
if (output_bytes == NULL) {
THROW(env, "java/lang/InternalError", "Cannot get buffer address.");
return 0;
}
output_bytes = output_bytes + offset;
int output_len = 0;
if (!dlsym_EVP_CipherFinal_ex(context, output_bytes, &output_len)) {
// validate tag in GCM mode when decrypt
if (is_bad_tag(CTX_WRAPPER(ctx))) {
THROW(env, "javax/crypto/AEADBadTagException", "Tag mismatch!");
} else {
THROW(env, "java/lang/InternalError", "Error in EVP_CipherFinal_ex.");
}
return 0;
}
return output_len;
}
JNIEXPORT jint JNICALL Java_org_apache_commons_crypto_cipher_OpenSslNative_doFinalByteArray
(JNIEnv *env, jclass clazz, jlong ctx, jbyteArray output, jint offset,
jint max_output_len)
{
EVP_CIPHER_CTX *context = get_context(env, ctx);
if (context == NULL) {
return 0;
}
if (!check_doFinal_max_output_len(env, context, max_output_len)) {
THROW(env, "javax/crypto/ShortBufferException", \
"Output buffer is not sufficient.");
return 0;
}
unsigned char *output_bytes = (unsigned char *) (*env)->GetByteArrayElements(env, output, 0);
if (output_bytes == NULL) {
THROW(env, "java/lang/InternalError", "Cannot get buffer address.");
return 0;
}
int output_len = 0;
int rc = dlsym_EVP_CipherFinal_ex(context, output_bytes + offset, &output_len);
(*env)->ReleaseByteArrayElements(env, output, (jbyte *) output_bytes, 0);
if (rc == 0) {
// validate tag in GCM mode when decrypt
if (is_bad_tag(CTX_WRAPPER(ctx))) {
THROW(env, "javax/crypto/AEADBadTagException", "Tag mismatch!");
} else {
THROW(env, "java/lang/InternalError", "Error in EVP_CipherFinal_ex.");
}
return 0;
}
return output_len;
}
JNIEXPORT jint JNICALL Java_org_apache_commons_crypto_cipher_OpenSslNative_ctrl
(JNIEnv *env, jclass clazz, jlong ctx, jint type, jint arg, jbyteArray data)
{
EVP_CIPHER_CTX *context = get_context(env, ctx);
if (context == NULL) {
return 0;
}
int rc = 0;
void *data_ptr = NULL;
if (data != NULL) {
data_ptr = (void*) (*env)->GetByteArrayElements(env, data, 0);
if (data_ptr == NULL) {
THROW(env, "java/lang/InternalError", "Cannot get buffer address.");
return 0;
}
}
// get/set tag for GCM
if (type == EVP_CTRL_GCM_GET_TAG || type == EVP_CTRL_GCM_SET_TAG) {
if (arg <= 0 || arg > 16) {
THROW(env, "javax/crypto/AEADBadTagException", "TAG_LENGTH_INTERNAL_ERROR");
goto exit_;
}
if (data == NULL) {
THROW(env, "javax/crypto/AEADBadTagException", "tag is null");
goto exit_;
}
unsigned char *tag = (unsigned char*) data_ptr;
rc = dlsym_EVP_CIPHER_CTX_ctrl(context, type, arg, tag);
if (!rc) {
THROW(env, "javax/crypto/AEADBadTagException", "TAG_SET_ERROR or TAG_RETRIEVE_ERROR");
goto exit_;
}
} else {
THROW(env, "java.lang.UnsupportedOperationException", "Not implemented yet!");
goto exit_;
}
exit_:
if (data_ptr != NULL) {
(*env)->ReleaseByteArrayElements(env, data, (jbyte *) data_ptr, 0);
}
return rc;
}
JNIEXPORT void JNICALL Java_org_apache_commons_crypto_cipher_OpenSslNative_clean
(JNIEnv *env, jclass clazz, jlong ctx)
{
EVP_CTX_Wrapper *wrapper = CTX_WRAPPER(ctx);
free_context_wrapper(wrapper);
}
static int check_update_max_output_len(EVP_CTX_Wrapper *wrapper, int input_len, int max_output_len)
{
if (dlsym_EVP_CIPHER_CTX_test_flags(wrapper->ctx, EVP_CIPH_NO_PADDING) == EVP_CIPH_NO_PADDING) {
if (max_output_len >= input_len) {
return 1;
}
return 0;
} else {
int b = dlsym_EVP_CIPHER_CTX_block_size(wrapper->ctx);
if (wrapper->encrypt) {
if (max_output_len >= input_len + b - 1) {
return 1;
}
} else {
if (max_output_len >= input_len + b) {
return 1;
}
}
return 0;
}
}
static int check_doFinal_max_output_len(JNIEnv *env, EVP_CIPHER_CTX *context, int max_output_len)
{
if (dlsym_EVP_CIPHER_CTX_test_flags(context, EVP_CIPH_NO_PADDING) == EVP_CIPH_NO_PADDING) {
return 1;
} else {
int b = dlsym_EVP_CIPHER_CTX_block_size(context);
if (max_output_len >= b) {
return 1;
}
return 0;
}
}
static int is_bad_tag(EVP_CTX_Wrapper *wrapper)
{
EVP_CIPHER* ciph = dlsym_EVP_CIPHER_CTX_cipher(wrapper->ctx);
unsigned long flags = dlsym_EVP_CIPHER_flags(ciph);
if ((flags & EVP_CIPH_MODE) == EVP_CIPH_GCM_MODE && wrapper->encrypt == DECRYPT_MODE) {
return 1;
}
return 0;
}