wolfcrypt-jni/jni/jni_aesgmac.c

372 lines
11 KiB
C

/* jni_aesgmac.c
*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#include <stdint.h>
#ifdef WOLFSSL_USER_SETTINGS
#include <wolfssl/wolfcrypt/settings.h>
#elif !defined(__ANDROID__)
#include <wolfssl/options.h>
#endif
#include <wolfssl/wolfcrypt/aes.h>
#include <com_wolfssl_wolfcrypt_AesGmac.h>
#include <wolfcrypt_jni_NativeStruct.h>
#include <wolfcrypt_jni_error.h>
/* #define WOLFCRYPT_JNI_DEBUG_ON */
#include <wolfcrypt_jni_debug.h>
#include <wolfcrypt_jni_util.h>
JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_AesGmac_mallocNativeStruct_1internal(
JNIEnv* env, jobject this)
{
#ifdef HAVE_AESGCM
Gmac* gmac = NULL;
gmac = (Gmac*)XMALLOC(sizeof(Gmac), NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (gmac == NULL) {
throwOutOfMemoryException(env, "Failed to allocate Gmac object");
return (jlong)0;
}
/* Initialize the GMAC structure to a known clean state */
XMEMSET(gmac, 0, sizeof(Gmac));
LogStr("new Gmac() = %p\n", gmac);
return (jlong)(uintptr_t)gmac;
#else
throwNotCompiledInException(env);
return (jlong)0;
#endif
}
JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_AesGmac_native_1init(
JNIEnv* env, jobject this)
{
#ifdef HAVE_AESGCM
int ret = 0;
Gmac* gmac = (Gmac*) getNativeStruct(env, this);
if ((*env)->ExceptionOccurred(env)) {
/* getNativeStruct may throw exception, prevent throwing another */
return;
}
ret = wc_AesInit(&gmac->aes, NULL, INVALID_DEVID);
if (ret != 0) {
throwWolfCryptExceptionFromError(env, ret);
}
LogStr("native_init(gmac=%p)\n", gmac);
#else
throwNotCompiledInException(env);
#endif
}
JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_AesGmac_native_1free(
JNIEnv* env, jobject this)
{
#ifdef HAVE_AESGCM
Gmac* gmac = (Gmac*) getNativeStruct(env, this);
if ((*env)->ExceptionOccurred(env)) {
/* getNativeStruct may throw exception, prevent throwing another */
return;
}
LogStr("free Gmac %p\n", gmac);
if (gmac) {
/* Free AES backend resources, then clear the struct.
* NativeStruct.xfree() frees Gmac struct. */
wc_AesFree(&gmac->aes);
XMEMSET(gmac, 0, sizeof(Gmac));
}
#else
throwNotCompiledInException(env);
#endif
}
JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_AesGmac_wc_1GmacSetKey(
JNIEnv* env, jobject this, jbyteArray key_object)
{
#ifdef HAVE_AESGCM
int ret = 0;
Gmac* gmac = NULL;
byte* key = NULL;
word32 keySz = 0;
jboolean keyIsCopy = JNI_FALSE;
gmac = (Gmac*) getNativeStruct(env, this);
if ((*env)->ExceptionOccurred(env)) {
/* getNativeStruct may throw exception, prevent throwing another */
return;
}
key = getByteArrayIsCopy(env, key_object, &keyIsCopy);
keySz = getByteArrayLength(env, key_object);
if (!gmac || !key) {
ret = BAD_FUNC_ARG;
} else {
/* Initialize GMAC with the provided key */
ret = wc_GmacSetKey(gmac, key, keySz);
}
if (ret != 0)
throwWolfCryptExceptionFromError(env, ret);
LogStr("wc_GmacSetKey(gmac=%p, key, %d) = %d\n", gmac, keySz, ret);
zeroizeByteArrayCopy(key, keySz, keyIsCopy);
releaseByteArray(env, key_object, key, JNI_ABORT);
#else
throwNotCompiledInException(env);
#endif
}
JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_AesGmac_wc_1GmacUpdate(
JNIEnv* env, jobject this, jbyteArray iv_object, jbyteArray authIn_object,
jint authTagSz)
{
#ifdef HAVE_AESGCM
int ret = 0;
jbyteArray result = NULL;
Gmac* gmac = NULL;
byte* iv = NULL;
byte* authIn = NULL;
word32 ivSz = 0;
word32 authInSz = 0;
byte* authTag = NULL;
gmac = (Gmac*) getNativeStruct(env, this);
if ((*env)->ExceptionOccurred(env)) {
/* getNativeStruct may throw exception, prevent throwing another */
return NULL;
}
iv = getByteArray(env, iv_object);
ivSz = getByteArrayLength(env, iv_object);
authIn = getByteArray(env, authIn_object);
authInSz = getByteArrayLength(env, authIn_object);
if (!gmac || !iv || !authIn || authTagSz <= 0) {
ret = BAD_FUNC_ARG;
} else {
/* Allocate buffer for authentication tag */
authTag = (byte*)XMALLOC(authTagSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (authTag == NULL) {
ret = MEMORY_E;
} else {
ret = wc_GmacUpdate(gmac, iv, ivSz, authIn, authInSz,
authTag, authTagSz);
}
}
if (ret == 0) {
result = (*env)->NewByteArray(env, authTagSz);
if (result) {
(*env)->SetByteArrayRegion(env, result, 0, authTagSz,
(const jbyte*) authTag);
} else {
throwWolfCryptException(env, "Failed to allocate gmac");
}
} else {
throwWolfCryptExceptionFromError(env, ret);
}
LogStr("wc_GmacUpdate(gmac=%p, iv, %d, authIn, %d, authTag, %d) = %d\n",
gmac, ivSz, authInSz, authTagSz, ret);
if (authTag) {
XFREE(authTag, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}
releaseByteArray(env, iv_object, iv, JNI_ABORT);
releaseByteArray(env, authIn_object, authIn, JNI_ABORT);
return result;
#else
throwNotCompiledInException(env);
return NULL;
#endif
}
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_AesGmac_wc_1Gmac(
JNIEnv* env, jobject this, jbyteArray key_object, jbyteArray iv_object,
jbyteArray authIn_object, jbyteArray authTag_object)
{
#ifdef HAVE_AESGCM
int ret = 0;
Gmac gmac;
byte* key = NULL;
byte* iv = NULL;
byte* authIn = NULL;
byte* authTag = NULL;
word32 keySz = 0, ivSz = 0, authInSz = 0, authTagSz = 0;
jboolean keyIsCopy = JNI_FALSE;
key = getByteArrayIsCopy(env, key_object, &keyIsCopy);
keySz = getByteArrayLength(env, key_object);
iv = getByteArray(env, iv_object);
authIn = getByteArray(env, authIn_object);
authTag = getByteArray(env, authTag_object);
/* Initialize GMAC structure */
XMEMSET(&gmac, 0, sizeof(Gmac));
if (key == NULL || iv == NULL || authIn == NULL || authTag == NULL) {
ret = BAD_FUNC_ARG;
}
if (ret == 0) {
ivSz = getByteArrayLength(env, iv_object);
authInSz = getByteArrayLength(env, authIn_object);
authTagSz = getByteArrayLength(env, authTag_object);
ret = wc_AesInit(&gmac.aes, NULL, INVALID_DEVID);
if (ret == 0) {
ret = wc_GmacSetKey(&gmac, key, keySz);
}
if (ret == 0) {
/* Use a local buffer for the auth tag result to avoid
* corrupting Java memory */
byte* tmp = (byte*)XMALLOC(authTagSz, NULL,
DYNAMIC_TYPE_TMP_BUFFER);
if (tmp == NULL) {
ret = MEMORY_E;
} else {
/* Perform GMAC operation */
ret = wc_GmacUpdate(&gmac, iv, ivSz, authIn, authInSz,
tmp, authTagSz);
if (ret == 0) {
/* Copy result back to Java byte array */
(*env)->SetByteArrayRegion(env, authTag_object, 0,
authTagSz, (const jbyte*) tmp);
}
XFREE(tmp, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}
}
}
LogStr("GMAC operation: key=%p, keySz=%d, iv=%p, ivSz=%d, authIn=%p, "
"authInSz=%d, authTag=%p, authTagSz=%d, ret=%d\n",
key, keySz, iv, ivSz, authIn, authInSz, authTag, authTagSz, ret);
zeroizeByteArrayCopy(key, keySz, keyIsCopy);
releaseByteArray(env, key_object, key, JNI_ABORT);
releaseByteArray(env, iv_object, iv, JNI_ABORT);
releaseByteArray(env, authIn_object, authIn, JNI_ABORT);
releaseByteArray(env, authTag_object, authTag, JNI_ABORT);
wc_AesFree(&gmac.aes);
return ret;
#else
throwNotCompiledInException(env);
return NOT_COMPILED_IN;
#endif
}
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_AesGmac_wc_1GmacVerify(
JNIEnv* env, jobject this, jbyteArray key_object, jbyteArray iv_object,
jbyteArray authIn_object, jbyteArray authTag_object)
{
#ifdef HAVE_AESGCM
int ret = 0;
Gmac gmac;
byte* key = NULL;
byte* iv = NULL;
byte* authIn = NULL;
byte* authTag = NULL;
word32 keySz = 0, ivSz = 0, authInSz = 0, authTagSz = 0;
jboolean keyIsCopy = JNI_FALSE;
key = getByteArrayIsCopy(env, key_object, &keyIsCopy);
keySz = getByteArrayLength(env, key_object);
iv = getByteArray(env, iv_object);
authIn = getByteArray(env, authIn_object);
authTag = getByteArray(env, authTag_object);
/* Initialize GMAC structure */
XMEMSET(&gmac, 0, sizeof(Gmac));
if (key == NULL || iv == NULL || authIn == NULL || authTag == NULL) {
ret = BAD_FUNC_ARG;
}
if (ret == 0) {
ivSz = getByteArrayLength(env, iv_object);
authInSz = getByteArrayLength(env, authIn_object);
authTagSz = getByteArrayLength(env, authTag_object);
ret = wc_AesInit(&gmac.aes, NULL, INVALID_DEVID);
if (ret == 0) {
ret = wc_GmacSetKey(&gmac, key, keySz);
}
if (ret == 0) {
/* Generate the expected tag and compare */
byte* computedTag = (byte*)XMALLOC(authTagSz, NULL,
DYNAMIC_TYPE_TMP_BUFFER);
if (computedTag == NULL) {
ret = MEMORY_E;
} else {
ret = wc_GmacUpdate(&gmac, iv, ivSz, authIn, authInSz,
computedTag, authTagSz);
if (ret == 0) {
/* Compare the computed tag with the provided tag */
if (JNIConstantCompare(computedTag, authTag,
authTagSz) != 0) {
ret = AES_GCM_AUTH_E; /* Authentication failure */
}
}
XFREE(computedTag, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}
}
}
LogStr("GMAC verify: key=%p, keySz=%d, iv=%p, ivSz=%d, authIn=%p, "
"authInSz=%d, authTag=%p, authTagSz=%d, ret=%d\n",
key, keySz, iv, ivSz, authIn, authInSz, authTag, authTagSz, ret);
zeroizeByteArrayCopy(key, keySz, keyIsCopy);
releaseByteArray(env, key_object, key, JNI_ABORT);
releaseByteArray(env, iv_object, iv, JNI_ABORT);
releaseByteArray(env, authIn_object, authIn, JNI_ABORT);
releaseByteArray(env, authTag_object, authTag, JNI_ABORT);
wc_AesFree(&gmac.aes);
return ret;
#else
throwNotCompiledInException(env);
return NOT_COMPILED_IN;
#endif
}