Merge pull request #62 from cconlon/aesgcm

Add AES-GCM and Cipher AES/GCM/NoPadding Support
pull/64/head
David Garske 2024-02-13 13:26:55 -08:00 committed by GitHub
commit f256662464
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 3284 additions and 89 deletions

View File

@ -39,6 +39,7 @@ The JCE provider currently supports the following algorithms:
Cipher Class
AES/CBC/NoPadding
AES/CBC/PKCS5Padding
AES/GCM/NoPadding
DESede/CBC/NoPadding
RSA
RSA/ECB/PKCS1Padding

View File

@ -0,0 +1,63 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_wolfssl_wolfcrypt_AesGcm */
#ifndef _Included_com_wolfssl_wolfcrypt_AesGcm
#define _Included_com_wolfssl_wolfcrypt_AesGcm
#ifdef __cplusplus
extern "C" {
#endif
#undef com_wolfssl_wolfcrypt_AesGcm_NULL
#define com_wolfssl_wolfcrypt_AesGcm_NULL 0LL
/*
* Class: com_wolfssl_wolfcrypt_AesGcm
* Method: mallocNativeStruct_internal
* Signature: ()J
*/
JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_AesGcm_mallocNativeStruct_1internal
(JNIEnv *, jobject);
/*
* Class: com_wolfssl_wolfcrypt_AesGcm
* Method: wc_AesInit
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_AesGcm_wc_1AesInit
(JNIEnv *, jobject);
/*
* Class: com_wolfssl_wolfcrypt_AesGcm
* Method: wc_AesFree
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_AesGcm_wc_1AesFree
(JNIEnv *, jobject);
/*
* Class: com_wolfssl_wolfcrypt_AesGcm
* Method: wc_AesGcmSetKey
* Signature: ([B)V
*/
JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_AesGcm_wc_1AesGcmSetKey
(JNIEnv *, jobject, jbyteArray);
/*
* Class: com_wolfssl_wolfcrypt_AesGcm
* Method: wc_AesGcmEncrypt
* Signature: ([B[B[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_AesGcm_wc_1AesGcmEncrypt
(JNIEnv *, jobject, jbyteArray, jbyteArray, jbyteArray, jbyteArray);
/*
* Class: com_wolfssl_wolfcrypt_AesGcm
* Method: wc_AesGcmDecrypt
* Signature: ([B[B[B[B)[B
*/
JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_AesGcm_wc_1AesGcmDecrypt
(JNIEnv *, jobject, jbyteArray, jbyteArray, jbyteArray, jbyteArray);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -23,6 +23,14 @@ JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_Md5Enabled
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_ShaEnabled
(JNIEnv *, jclass);
/*
* Class: com_wolfssl_wolfcrypt_FeatureDetect
* Method: Sha224Enabled
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_Sha224Enabled
(JNIEnv *, jclass);
/*
* Class: com_wolfssl_wolfcrypt_FeatureDetect
* Method: Sha256Enabled
@ -47,6 +55,62 @@ JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_Sha384Enable
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_Sha512Enabled
(JNIEnv *, jclass);
/*
* Class: com_wolfssl_wolfcrypt_FeatureDetect
* Method: Aes128Enabled
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_Aes128Enabled
(JNIEnv *, jclass);
/*
* Class: com_wolfssl_wolfcrypt_FeatureDetect
* Method: Aes192Enabled
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_Aes192Enabled
(JNIEnv *, jclass);
/*
* Class: com_wolfssl_wolfcrypt_FeatureDetect
* Method: Aes256Enabled
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_Aes256Enabled
(JNIEnv *, jclass);
/*
* Class: com_wolfssl_wolfcrypt_FeatureDetect
* Method: AesCbcEnabled
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_AesCbcEnabled
(JNIEnv *, jclass);
/*
* Class: com_wolfssl_wolfcrypt_FeatureDetect
* Method: AesGcmEnabled
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_AesGcmEnabled
(JNIEnv *, jclass);
/*
* Class: com_wolfssl_wolfcrypt_FeatureDetect
* Method: AesGcmStreamEnabled
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_AesGcmStreamEnabled
(JNIEnv *, jclass);
/*
* Class: com_wolfssl_wolfcrypt_FeatureDetect
* Method: Des3Enabled
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_Des3Enabled
(JNIEnv *, jclass);
/*
* Class: com_wolfssl_wolfcrypt_FeatureDetect
* Method: HmacMd5Enabled
@ -87,6 +151,54 @@ JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacSha384En
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacSha512Enabled
(JNIEnv *, jclass);
/*
* Class: com_wolfssl_wolfcrypt_FeatureDetect
* Method: RsaEnabled
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_RsaEnabled
(JNIEnv *, jclass);
/*
* Class: com_wolfssl_wolfcrypt_FeatureDetect
* Method: RsaKeyGenEnabled
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_RsaKeyGenEnabled
(JNIEnv *, jclass);
/*
* Class: com_wolfssl_wolfcrypt_FeatureDetect
* Method: DhEnabled
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_DhEnabled
(JNIEnv *, jclass);
/*
* Class: com_wolfssl_wolfcrypt_FeatureDetect
* Method: EccEnabled
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_EccEnabled
(JNIEnv *, jclass);
/*
* Class: com_wolfssl_wolfcrypt_FeatureDetect
* Method: EccKeyGenEnabled
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_EccKeyGenEnabled
(JNIEnv *, jclass);
/*
* Class: com_wolfssl_wolfcrypt_FeatureDetect
* Method: EccDheEnabled
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_EccDheEnabled
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif

409
jni/jni_aesgcm.c 100644
View File

@ -0,0 +1,409 @@
/* jni_aesgcm.c
*
* Copyright (C) 2006-2024 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 2 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_AesGcm.h>
#include <wolfcrypt_jni_NativeStruct.h>
#include <wolfcrypt_jni_error.h>
/* #define WOLFCRYPT_JNI_DEBUG_ON */
#include <wolfcrypt_jni_debug.h>
JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_AesGcm_mallocNativeStruct_1internal
(JNIEnv* env, jobject this)
{
#ifndef NO_AES
Aes* aes = NULL;
(void)this;
aes = (Aes*)XMALLOC(sizeof(Aes), NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (aes == NULL) {
throwOutOfMemoryException(env, "Failed to allocate Aes object");
}
else {
XMEMSET(aes, 0, sizeof(Aes));
}
LogStr("new AesGcm() = %p\n", aes);
return (jlong)(uintptr_t)aes;
#else
(void)this;
throwNotCompiledInException(env);
return (jlong)0;
#endif
}
JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_AesGcm_wc_1AesInit
(JNIEnv* env, jobject this)
{
#ifndef NO_AES
int ret = 0;
Aes* aes = NULL;
(void)this;
aes = (Aes*) getNativeStruct(env, this);
if ((*env)->ExceptionOccurred(env)) {
/* getNativeStruct may throw exception, if so stop and return */
return;
}
ret = wc_AesInit(aes, NULL, INVALID_DEVID);
if (ret != 0) {
throwWolfCryptExceptionFromError(env, ret);
}
LogStr("wc_AesInit(aes=%p)\n", aes);
#else
(void)this;
throwNotCompiledInException(env);
#endif
}
JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_AesGcm_wc_1AesFree
(JNIEnv* env, jobject this)
{
#ifndef NO_AES
Aes* aes = NULL;
(void)this;
aes = (Aes*) getNativeStruct(env, this);
if ((*env)->ExceptionOccurred(env)) {
/* getNativeStruct may throw exception, if so stop and return */
return;
}
wc_AesFree(aes);
LogStr("wc_AesFree(aes=%p)\n", aes);
#else
(void)this;
throwNotCompiledInException(env);
#endif
}
JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_AesGcm_wc_1AesGcmSetKey
(JNIEnv* env, jobject this, jbyteArray keyArr)
{
#if !defined(NO_AES) && defined(HAVE_AESGCM)
int ret = 0;
Aes* aes = NULL;
const byte* key = NULL;
word32 keyLen = 0;
aes = (Aes*) getNativeStruct(env, this);
if ((*env)->ExceptionOccurred(env)) {
/* getNativeStruct may throw exception, if so stop and return */
return;
}
if (keyArr != NULL) {
key = (const byte*)(*env)->GetByteArrayElements(env, keyArr, NULL);
keyLen = (*env)->GetArrayLength(env, keyArr);
}
if (key == NULL || keyLen == 0) {
ret = BAD_FUNC_ARG;
}
if (ret == 0) {
ret = wc_AesGcmSetKey(aes, key, keyLen);
}
if (keyArr != NULL) {
(*env)->ReleaseByteArrayElements(env, keyArr, (jbyte*)key, JNI_ABORT);
}
if (ret != 0) {
throwWolfCryptExceptionFromError(env, ret);
}
LogStr("wc_AesGcmSetKey(aes = %p, keylen = %d)\n", aes, keyLen);
#else
(void)this;
throwNotCompiledInException(env);
#endif
}
JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_AesGcm_wc_1AesGcmEncrypt
(JNIEnv* env, jobject this, jbyteArray inputArr, jbyteArray ivArr, jbyteArray authTagArr, jbyteArray authInArr)
{
#if !defined(NO_AES) && defined(HAVE_AESGCM)
int ret = 0;
Aes* aes = NULL;
const byte* in = NULL;
const byte* iv = NULL;
byte* authTag = NULL;
const byte* authIn = NULL;
word32 inLen = 0;
word32 ivSz = 0;
word32 authTagSz = 0;
word32 authInSz = 0;
byte* out = NULL;
jbyteArray outArr = NULL;
aes = (Aes*) getNativeStruct(env, this);
if ((*env)->ExceptionOccurred(env)) {
/* getNativeStruct may throw exception, if so stop and return */
return NULL;
}
if (inputArr != NULL) {
in = (const byte*)(*env)->GetByteArrayElements(env, inputArr, NULL);
inLen = (*env)->GetArrayLength(env, inputArr);
}
if (ivArr != NULL) {
iv = (byte*)(*env)->GetByteArrayElements(env, ivArr, NULL);
ivSz = (*env)->GetArrayLength(env, ivArr);
}
if (authTagArr != NULL) {
authTag = (byte*)(*env)->GetByteArrayElements(env, authTagArr, NULL);
authTagSz = (*env)->GetArrayLength(env, authTagArr);
}
if (authInArr != NULL) {
authIn = (byte*)(*env)->GetByteArrayElements(env, authInArr, NULL);
authInSz = (*env)->GetArrayLength(env, authInArr);
}
/* authIn can be null */
if (in == NULL || inLen == 0 || iv == NULL || ivSz == 0 ||
authTag == NULL || authTagSz == 0) {
ret = BAD_FUNC_ARG;
}
/* Allocate new buffer to hold ciphertext */
if (ret == 0) {
out = (byte*)XMALLOC(inLen, NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (out == NULL) {
ret = MEMORY_E;
}
else {
XMEMSET(out, 0, inLen);
}
}
if (ret == 0) {
ret = wc_AesGcmEncrypt(aes, out, in, inLen, iv, ivSz,
authTag, authTagSz, authIn, authInSz);
}
/* Create new jbyteArray to return output */
if (ret == 0) {
outArr = (*env)->NewByteArray(env, inLen);
if (outArr == NULL) {
ret = MEMORY_E;
}
else {
(*env)->SetByteArrayRegion(env, outArr, 0, inLen, (jbyte*)out);
if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
(*env)->DeleteLocalRef(env, outArr);
outArr = NULL;
ret = -1;
}
}
}
/* Commit authTag changes back to original Java array on success */
if (authTagArr != NULL) {
if (ret == 0) {
(*env)->ReleaseByteArrayElements(env, authTagArr,
(jbyte*)authTag, JNI_COMMIT);
}
else {
(*env)->ReleaseByteArrayElements(env, authTagArr,
(jbyte*)authTag, JNI_ABORT);
}
}
/* Release all other byte arrays without changing original arrays */
if (inputArr != NULL) {
(*env)->ReleaseByteArrayElements(env, inputArr, (jbyte*)in,
JNI_ABORT);
}
if (ivArr != NULL) {
(*env)->ReleaseByteArrayElements(env, ivArr, (jbyte*)iv,
JNI_ABORT);
}
if (authInArr != NULL) {
(*env)->ReleaseByteArrayElements(env, authInArr, (jbyte*)authIn,
JNI_ABORT);
}
if (out != NULL) {
XFREE(out, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}
LogStr("wc_AesGcmEncrypt(aes = %p, inLen = %d, ivSz = %d, "
"authTagSz = %d, authInSz = %d)\n", aes, inLen, ivSz,
authTagSz, authInSz);
if (ret != 0) {
throwWolfCryptExceptionFromError(env, ret);
return NULL;
}
return outArr;
#else
(void)this;
(void)input;
(void)iv;
(void)authTag;
(void)authIn;
throwNotCompiledInException(env);
return NULL;
#endif
}
JNIEXPORT jbyteArray JNICALL Java_com_wolfssl_wolfcrypt_AesGcm_wc_1AesGcmDecrypt
(JNIEnv* env, jobject this, jbyteArray inputArr, jbyteArray ivArr, jbyteArray authTagArr, jbyteArray authInArr)
{
#if !defined(NO_AES) && defined(HAVE_AESGCM)
int ret = 0;
Aes* aes = NULL;
const byte* in = NULL;
const byte* iv = NULL;
const byte* authTag = NULL;
const byte* authIn = NULL;
word32 inLen = 0;
word32 ivSz = 0;
word32 authTagSz = 0;
word32 authInSz = 0;
byte* out = NULL;
jbyteArray outArr = NULL;
aes = (Aes*) getNativeStruct(env, this);
if ((*env)->ExceptionOccurred(env)) {
/* getNativeStruct may throw exception, if so stop and return */
return NULL;
}
if (inputArr != NULL) {
in = (byte*)(*env)->GetByteArrayElements(env, inputArr, NULL);
inLen = (*env)->GetArrayLength(env, inputArr);
}
if (ivArr != NULL) {
iv = (byte*)(*env)->GetByteArrayElements(env, ivArr, NULL);
ivSz = (*env)->GetArrayLength(env, ivArr);
}
if (authTagArr != NULL) {
authTag = (byte*)(*env)->GetByteArrayElements(env, authTagArr, NULL);
authTagSz = (*env)->GetArrayLength(env, authTagArr);
}
if (authInArr != NULL) {
authIn = (byte*)(*env)->GetByteArrayElements(env, authInArr, NULL);
authInSz = (*env)->GetArrayLength(env, authInArr);
}
if (in == NULL || inLen == 0 || iv == NULL || ivSz == 0 ||
authTag == NULL || authTagSz == 0) {
ret = BAD_FUNC_ARG;
}
if (ret == 0) {
out = (byte*)XMALLOC(inLen, NULL, DYNAMIC_TYPE_TMP_BUFFER);
if (out == NULL) {
ret = MEMORY_E;
}
else {
XMEMSET(out, 0, inLen);
}
}
if (ret == 0) {
ret = wc_AesGcmDecrypt(aes, out, in, inLen, iv, ivSz,
authTag, authTagSz, authIn, authInSz);
}
/* Create new jbyteArray to return output */
if (ret == 0) {
outArr = (*env)->NewByteArray(env, inLen);
if (outArr == NULL) {
ret = MEMORY_E;
}
else {
(*env)->SetByteArrayRegion(env, outArr, 0, inLen, (jbyte*)out);
if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
(*env)->DeleteLocalRef(env, outArr);
outArr = NULL;
ret = -1;
}
}
}
/* Release all byte arrays without changing original arrays */
if (inputArr != NULL) {
(*env)->ReleaseByteArrayElements(env, inputArr, (jbyte*)in,
JNI_ABORT);
}
if (ivArr != NULL) {
(*env)->ReleaseByteArrayElements(env, ivArr, (jbyte*)iv,
JNI_ABORT);
}
if (authInArr != NULL) {
(*env)->ReleaseByteArrayElements(env, authInArr, (jbyte*)authIn,
JNI_ABORT);
}
if (authTagArr != NULL) {
(*env)->ReleaseByteArrayElements(env, authTagArr, (jbyte*)authTag,
JNI_ABORT);
}
if (out != NULL) {
XMEMSET(out, 0, inLen);
XFREE(out, NULL, DYNAMIC_TYPE_TMP_BUFFER);
}
LogStr("wc_AesGcmDecrypt(aes = %p, inLen = %d, ivSz = %d, "
"authTagSz = %d, authInSz = %d)\n", aes, inLen, ivSz,
authTagSz, authInSz);
if (ret != 0) {
throwWolfCryptExceptionFromError(env, ret);
}
return outArr;
#else
(void)this;
(void)input;
(void)iv;
(void)authTag;
(void)authIn;
throwNotCompiledInException(env);
return NULL;
#endif
}

View File

@ -52,6 +52,18 @@ JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_ShaEnabled
#endif
}
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_Sha224Enabled
(JNIEnv* env, jclass jcl)
{
(void)env;
(void)jcl;
#ifdef WOLFSSL_SHA224
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
}
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_Sha256Enabled
(JNIEnv* env, jclass jcl)
{
@ -88,6 +100,90 @@ JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_Sha512Enable
#endif
}
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_Aes128Enabled
(JNIEnv* env, jclass jcl)
{
(void)env;
(void)jcl;
#if !defined(NO_AES) && defined(WOLFSSL_AES_128)
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
}
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_Aes192Enabled
(JNIEnv* env, jclass jcl)
{
(void)env;
(void)jcl;
#if !defined(NO_AES) && defined(WOLFSSL_AES_192)
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
}
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_Aes256Enabled
(JNIEnv* env, jclass jcl)
{
(void)env;
(void)jcl;
#if !defined(NO_AES) && defined(WOLFSSL_AES_256)
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
}
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_AesCbcEnabled
(JNIEnv* env, jclass jcl)
{
(void)env;
(void)jcl;
#if !defined(NO_AES) && defined(HAVE_AES_CBC)
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
}
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_AesGcmEnabled
(JNIEnv* env, jclass jcl)
{
(void)env;
(void)jcl;
#if !defined(NO_AES) && defined(HAVE_AESGCM)
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
}
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_AesGcmStreamEnabled
(JNIEnv* env, jclass jcl)
{
(void)env;
(void)jcl;
#if !defined(NO_AES) && defined(WOLFSSL_AESGCM_STREAM)
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
}
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_Des3Enabled
(JNIEnv* env, jclass jcl)
{
(void)env;
(void)jcl;
#ifndef NO_DES3
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
}
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacMd5Enabled
(JNIEnv* env, jclass jcl)
{
@ -148,3 +244,75 @@ JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_HmacSha512En
#endif
}
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_RsaEnabled
(JNIEnv* env, jclass jcl)
{
(void)env;
(void)jcl;
#ifndef NO_RSA
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
}
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_RsaKeyGenEnabled
(JNIEnv* env, jclass jcl)
{
(void)env;
(void)jcl;
#if !defined(NO_RSA) && defined(WOLFSSL_KEY_GEN)
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
}
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_DhEnabled
(JNIEnv* env, jclass jcl)
{
(void)env;
(void)jcl;
#ifndef NO_DH
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
}
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_EccEnabled
(JNIEnv* env, jclass jcl)
{
(void)env;
(void)jcl;
#ifdef HAVE_ECC
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
}
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_EccKeyGenEnabled
(JNIEnv* env, jclass jcl)
{
(void)env;
(void)jcl;
#if defined(HAVE_ECC) && defined(WOLFSSL_KEY_GEN)
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
}
JNIEXPORT jboolean JNICALL Java_com_wolfssl_wolfcrypt_FeatureDetect_EccDheEnabled
(JNIEnv* env, jclass jcl)
{
(void)env;
(void)jcl;
#if defined(HAVE_ECC) && defined(HAVE_ECC_DHE)
return JNI_TRUE;
#else
return JNI_FALSE;
#endif
}

View File

@ -26,10 +26,10 @@ endif
DIST_FILES = AUTHORS build.xml COPYING examples jni LICENSING makefile makefile.linux makefile.macosx \
pom.xml README_JCE.md README.md rpm src
OBJ_LIST = jni_fips.o jni_native_struct.o jni_aes.o jni_des3.o jni_md5.o \
jni_sha.o jni_hmac.o jni_rng.o jni_rsa.o jni_dh.o jni_ecc.o \
jni_ed25519.o jni_curve25519.o jni_chacha.o jni_error.o jni_asn.o \
jni_logging.o jni_feature_detect.o jni_wolfobject.o
OBJ_LIST = jni_fips.o jni_native_struct.o jni_aes.o jni_aesgcm.o jni_des3.o \
jni_md5.o jni_sha.o jni_hmac.o jni_rng.o jni_rsa.o jni_dh.o \
jni_ecc.o jni_ed25519.o jni_curve25519.o jni_chacha.o jni_error.o \
jni_asn.o jni_logging.o jni_feature_detect.o jni_wolfobject.o
OBJS = $(patsubst %,$(OUT_PATH)/%,$(OBJ_LIST))
TARGET = $(OUT_PATH)/libwolfcryptjni.so

View File

@ -2,10 +2,10 @@ OUT_PATH = lib
SRC_PATH = jni
INC_PATH = $(SRC_PATH)/include
OBJ_LIST = jni_fips.o jni_native_struct.o jni_aes.o jni_des3.o jni_md5.o \
jni_sha.o jni_hmac.o jni_rng.o jni_rsa.o jni_dh.o jni_ecc.o \
jni_ed25519.o jni_curve25519.o jni_chacha.o jni_error.o jni_asn.o \
jni_logging.o jni_feature_detect.o jni_wolfobject.o
OBJ_LIST = jni_fips.o jni_native_struct.o jni_aes.o jni_aesgcm.o jni_des3.o \
jni_md5.o jni_sha.o jni_hmac.o jni_rng.o jni_rsa.o jni_dh.o \
jni_ecc.o jni_ed25519.o jni_curve25519.o jni_chacha.o jni_error.o \
jni_asn.o jni_logging.o jni_feature_detect.o jni_wolfobject.o
OBJS = $(patsubst %,$(OUT_PATH)/%,$(OBJ_LIST))
TARGET = $(OUT_PATH)/libwolfcryptjni.dylib

View File

@ -18,6 +18,7 @@
infer run -- javac \
src/main/java/com/wolfssl/wolfcrypt/Aes.java \
src/main/java/com/wolfssl/wolfcrypt/AesGcm.java \
src/main/java/com/wolfssl/wolfcrypt/Asn.java \
src/main/java/com/wolfssl/wolfcrypt/BlockCipher.java \
src/main/java/com/wolfssl/wolfcrypt/Chacha.java \

View File

@ -22,6 +22,7 @@
package com.wolfssl.provider.jce;
import java.util.Arrays;
import java.nio.ByteBuffer;
import javax.crypto.Cipher;
import javax.crypto.CipherSpi;
@ -31,6 +32,7 @@ import javax.crypto.ShortBufferException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.GCMParameterSpec;
import java.security.SecureRandom;
import java.security.AlgorithmParameters;
@ -46,6 +48,7 @@ import java.security.interfaces.RSAPublicKey;
import com.wolfssl.wolfcrypt.WolfCrypt;
import com.wolfssl.wolfcrypt.Asn;
import com.wolfssl.wolfcrypt.Aes;
import com.wolfssl.wolfcrypt.AesGcm;
import com.wolfssl.wolfcrypt.Des3;
import com.wolfssl.wolfcrypt.Rsa;
import com.wolfssl.wolfcrypt.Rng;
@ -65,7 +68,8 @@ public class WolfCryptCipher extends CipherSpi {
enum CipherMode {
WC_ECB,
WC_CBC
WC_CBC,
WC_GCM
}
enum PaddingType {
@ -92,10 +96,11 @@ public class WolfCryptCipher extends CipherSpi {
private int blockSize = 0;
private Aes aes = null;
private Des3 des3 = null;
private Rsa rsa = null;
private Rng rng = null;
private Aes aes = null;
private AesGcm aesGcm = null;
private Des3 des3 = null;
private Rsa rsa = null;
private Rng rng = null;
/* for debug logging */
private WolfCryptDebug debug;
@ -107,6 +112,18 @@ public class WolfCryptCipher extends CipherSpi {
private AlgorithmParameterSpec storedSpec = null;
private byte[] iv = null;
/* AES-GCM tag length (bytes) */
private int gcmTagLen = 0;
/* AAD data for AES-GCM, populated via engineUpdateAAD() */
private byte[] aadData = null;
/* Has update/final been called yet, gates setting of AAD for GCM */
private boolean operationStarted = false;
/* Has this Cipher been inintialized? */
private boolean cipherInitialized = false;
/* buffered data from update calls */
private byte[] buffered = new byte[0];
@ -122,19 +139,12 @@ public class WolfCryptCipher extends CipherSpi {
switch (cipherType) {
case WC_AES:
aes = new Aes();
blockSize = Aes.BLOCK_SIZE;
break;
case WC_DES3:
des3 = new Des3();
blockSize = Des3.BLOCK_SIZE;
break;
case WC_RSA:
rsa = new Rsa();
rsa.setRng(this.rng);
break;
}
if (debug.DEBUG) {
@ -143,6 +153,48 @@ public class WolfCryptCipher extends CipherSpi {
}
}
/**
* Reset / re-create internal native struct for algorithm.
* Should be called during wolfCryptInit() and wolfCryptFinal()
*/
private void InitializeNativeStructs() {
switch (this.cipherType) {
case WC_AES:
if (cipherMode == CipherMode.WC_CBC) {
if (aes != null) {
aes.releaseNativeStruct();
aes = null;
}
aes = new Aes();
}
else if (cipherMode == CipherMode.WC_GCM) {
if (aesGcm != null) {
aesGcm.releaseNativeStruct();
aesGcm = null;
}
aesGcm = new AesGcm();
}
break;
case WC_DES3:
if (des3 != null) {
des3.releaseNativeStruct();
des3 = null;
}
des3 = new Des3();
break;
case WC_RSA:
if (rsa != null) {
rsa.releaseNativeStruct();
rsa = null;
}
rsa = new Rsa();
rsa.setRng(this.rng);
break;
}
}
@Override
protected void engineSetMode(String mode)
throws NoSuchAlgorithmException {
@ -156,8 +208,9 @@ public class WolfCryptCipher extends CipherSpi {
cipherMode = CipherMode.WC_ECB;
supported = 1;
if (debug.DEBUG)
if (debug.DEBUG) {
log("set mode to ECB");
}
}
} else if (mode.equals("CBC")) {
@ -168,14 +221,28 @@ public class WolfCryptCipher extends CipherSpi {
cipherMode = CipherMode.WC_CBC;
supported = 1;
if (debug.DEBUG)
if (debug.DEBUG) {
log("set mode to CBC");
}
}
} else if (mode.equals("GCM")) {
/* AES supports GCM */
if (cipherType == CipherType.WC_AES) {
cipherMode = CipherMode.WC_GCM;
supported = 1;
if (debug.DEBUG) {
log("set mode to GCM");
}
}
}
if (supported == 0) {
throw new NoSuchAlgorithmException(
"Unsupported cipher mode for active algorithm choice");
"Unsupported cipher mode for active algorithm choice: " +
mode);
}
}
@ -192,8 +259,9 @@ public class WolfCryptCipher extends CipherSpi {
paddingType = PaddingType.WC_NONE;
supported = 1;
if (debug.DEBUG)
if (debug.DEBUG) {
log("set padding to NoPadding");
}
}
} else if (padding.equals("PKCS1Padding")) {
@ -202,24 +270,29 @@ public class WolfCryptCipher extends CipherSpi {
paddingType = PaddingType.WC_PKCS1;
supported = 1;
if (debug.DEBUG)
if (debug.DEBUG) {
log("set padding to PKCS1Padding");
}
}
} else if (padding.equals("PKCS5Padding")) {
if (cipherType == CipherType.WC_AES) {
if ((cipherType == CipherType.WC_AES) &&
(cipherMode == CipherMode.WC_CBC)) {
paddingType = PaddingType.WC_PKCS5;
supported = 1;
if (debug.DEBUG)
if (debug.DEBUG) {
log("set padding to PKCS5Padding");
}
}
}
if (supported == 0) {
throw new NoSuchPaddingException(
"Unsupported padding type for active algorithm choice");
"Unsupported padding type for active algorithm choice: " +
padding);
}
}
@ -234,12 +307,24 @@ public class WolfCryptCipher extends CipherSpi {
int size = 0;
if (!this.cipherInitialized) {
throw new IllegalStateException(
"Cipher has not been initialized yet");
}
switch (this.cipherType) {
case WC_AES:
if (paddingType == PaddingType.WC_NONE) {
/* wolfCrypt expects input to be padded by application to
* block size, thus output is same size as input */
size = inputLen;
if (cipherMode == CipherMode.WC_GCM) {
/* In AES-GCM mode we append the authentication tag
* to the end of ciphertext */
size = inputLen + this.gcmTagLen;
}
else {
/* wolfCrypt expects input to be padded by application to
* block size, thus output is same size as input */
size = inputLen;
}
}
else if (paddingType == PaddingType.WC_PKCS5) {
size = buffered.length + inputLen;
@ -330,21 +415,48 @@ public class WolfCryptCipher extends CipherSpi {
}
} else {
if (!(spec instanceof IvParameterSpec)) {
throw new InvalidAlgorithmParameterException(
"AlgorithmParameterSpec must be of type IvParameterSpec");
if (cipherMode == CipherMode.WC_GCM) {
if (!(spec instanceof GCMParameterSpec)) {
throw new InvalidAlgorithmParameterException(
"AlgorithmParameterSpec must be of type " +
"GCMParameterSpec");
}
GCMParameterSpec gcmSpec = (GCMParameterSpec)spec;
if (gcmSpec.getIV() == null ||
gcmSpec.getIV().length == 0) {
throw new InvalidAlgorithmParameterException(
"AES-GCM IV is null or 0 length");
}
this.iv = gcmSpec.getIV().clone();
/* store tag length as bytes */
if (gcmSpec.getTLen() == 0) {
throw new InvalidAlgorithmParameterException(
"Tag length cannot be zero");
}
this.gcmTagLen = (gcmSpec.getTLen() / 8);
}
else {
if (!(spec instanceof IvParameterSpec)) {
throw new InvalidAlgorithmParameterException(
"AlgorithmParameterSpec must be of type " +
"IvParameterSpec");
}
IvParameterSpec ivSpec = (IvParameterSpec)spec;
IvParameterSpec ivSpec = (IvParameterSpec)spec;
/* IV should be of block size length */
if (ivSpec.getIV().length != this.blockSize) {
throw new InvalidAlgorithmParameterException(
"Bad IV length (" + ivSpec.getIV().length +
"), must be " + blockSize + " bytes long");
/* IV should be of block size length */
if (ivSpec.getIV().length != this.blockSize) {
throw new InvalidAlgorithmParameterException(
"Bad IV length (" + ivSpec.getIV().length +
"), must be " + blockSize + " bytes long");
}
this.iv = ivSpec.getIV().clone();
}
this.iv = ivSpec.getIV();
}
}
@ -380,15 +492,26 @@ public class WolfCryptCipher extends CipherSpi {
/* import key */
encodedKey = key.getEncoded();
if (encodedKey == null)
if (encodedKey == null) {
throw new InvalidKeyException("Key does not support encoding");
}
switch (cipherType) {
case WC_AES:
if (this.direction == OpMode.WC_ENCRYPT) {
this.aes.setKey(encodedKey, iv, Aes.ENCRYPT_MODE);
if (cipherMode == CipherMode.WC_GCM) {
this.aesGcm.setKey(encodedKey);
}
else {
this.aes.setKey(encodedKey, iv, Aes.ENCRYPT_MODE);
}
} else {
this.aes.setKey(encodedKey, iv, Aes.DECRYPT_MODE);
if (cipherMode == CipherMode.WC_GCM) {
this.aesGcm.setKey(encodedKey);
}
else {
this.aes.setKey(encodedKey, iv, Aes.DECRYPT_MODE);
}
}
break;
@ -425,9 +548,12 @@ public class WolfCryptCipher extends CipherSpi {
AlgorithmParameterSpec spec, SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException {
InitializeNativeStructs();
wolfCryptSetDirection(opmode);
wolfCryptSetIV(spec, random);
wolfCryptSetKey(key);
this.operationStarted = false;
this.cipherInitialized = true;
}
@Override
@ -438,8 +564,9 @@ public class WolfCryptCipher extends CipherSpi {
wolfCryptCipherInit(opmode, key, null, random);
if (debug.DEBUG)
if (debug.DEBUG) {
log("initialized with key");
}
} catch (InvalidAlgorithmParameterException iape) {
throw new InvalidKeyException("Invalid algorithm parameters");
@ -453,8 +580,9 @@ public class WolfCryptCipher extends CipherSpi {
wolfCryptCipherInit(opmode, key, params, random);
if (debug.DEBUG)
if (debug.DEBUG) {
log("initialized with key and AlgorithmParameterSpec");
}
}
@Override
@ -466,10 +594,16 @@ public class WolfCryptCipher extends CipherSpi {
try {
spec = params.getParameterSpec(IvParameterSpec.class);
if (this.cipherMode == CipherMode.WC_GCM) {
spec = params.getParameterSpec(GCMParameterSpec.class);
}
else {
spec = params.getParameterSpec(IvParameterSpec.class);
}
if (debug.DEBUG)
if (debug.DEBUG) {
log("initialized with key and AlgorithmParameters");
}
} catch (InvalidParameterSpecException ipe) {
throw new InvalidAlgorithmParameterException(ipe);
@ -490,6 +624,7 @@ public class WolfCryptCipher extends CipherSpi {
break;
default:
isBlockCipher = false;
break;
};
return isBlockCipher;
@ -500,16 +635,19 @@ public class WolfCryptCipher extends CipherSpi {
private int isValidBlockLength(int length) {
/* skip if not a block cipher */
if (isBlockCipher() == false)
if (isBlockCipher() == false) {
return 1;
}
if ((length % this.blockSize) != 0)
if ((length % this.blockSize) != 0) {
return 0;
}
return 1;
}
private byte[] wolfCryptUpdate(byte[] input, int inputOffset, int len) {
private byte[] wolfCryptUpdate(byte[] input, int inputOffset, int len)
throws IllegalArgumentException {
int blocks = 0;
int remaining = 0;
@ -521,6 +659,8 @@ public class WolfCryptCipher extends CipherSpi {
if (input == null || len < 0)
throw new IllegalArgumentException("Null input buffer or len < 0");
this.operationStarted = true;
if ((buffered.length + len) == 0) {
/* no data to process */
return null;
@ -534,9 +674,12 @@ public class WolfCryptCipher extends CipherSpi {
buffered = tmpIn;
}
/* keep buffered data if RSA or data is less than block size */
if (cipherType == CipherType.WC_RSA ||
buffered.length < blockSize) {
/* keep buffered data if RSA or data is less than block size, or doing
* AES-GCM without stream mode compiled natively */
if ((cipherType == CipherType.WC_RSA) ||
((cipherType == CipherType.WC_AES) &&
(cipherMode == CipherMode.WC_GCM)) ||
(buffered.length < blockSize)) {
return new byte[0];
}
@ -568,6 +711,8 @@ public class WolfCryptCipher extends CipherSpi {
/* process tmpIn[] */
switch (this.cipherType) {
/* Only CBC mode reaches this point currently, GCM caches all
* data internally above until final call */
case WC_AES:
output = this.aes.update(tmpIn, 0, tmpIn.length);
@ -603,9 +748,12 @@ public class WolfCryptCipher extends CipherSpi {
byte tmpIn[] = null;
byte tmpOut[] = null;
this.operationStarted = true;
totalSz = buffered.length + len;
/* AES-GCM does not require block size inputs */
if (isBlockCipher() &&
(cipherMode != CipherMode.WC_GCM) &&
(this.direction == OpMode.WC_DECRYPT ||
(this.direction == OpMode.WC_ENCRYPT &&
this.paddingType != PaddingType.WC_PKCS5)) &&
@ -636,10 +784,39 @@ public class WolfCryptCipher extends CipherSpi {
switch (this.cipherType) {
case WC_AES:
tmpOut = this.aes.update(tmpIn, 0, tmpIn.length);
if (cipherMode == CipherMode.WC_GCM) {
if (this.direction == OpMode.WC_ENCRYPT) {
byte[] tag = new byte[this.gcmTagLen];
tmpOut = this.aesGcm.encrypt(tmpIn, this.iv, tag,
this.aadData);
/* truncate */
tmpOut = Arrays.copyOfRange(tmpOut, 0, tmpIn.length);
/* Concatenate auth tag to end of ciphertext */
byte[] totalOut = new byte[tmpOut.length + tag.length];
System.arraycopy(tmpOut, 0, totalOut, 0, tmpOut.length);
System.arraycopy(tag, 0, totalOut, tmpOut.length,
tag.length);
tmpOut = totalOut;
}
else {
/* Get auth tag from end of ciphertext */
byte[] tag = Arrays.copyOfRange(tmpIn,
tmpIn.length - this.gcmTagLen,
tmpIn.length);
/* Shrink ciphertext array down to not include tag */
tmpIn = Arrays.copyOfRange(tmpIn, 0,
tmpIn.length - this.gcmTagLen);
tmpOut = this.aesGcm.decrypt(tmpIn, this.iv, tag,
this.aadData);
}
}
else {
tmpOut = this.aes.update(tmpIn, 0, tmpIn.length);
/* truncate */
tmpOut = Arrays.copyOfRange(tmpOut, 0, tmpIn.length);
}
/* strip PKCS#5/PKCS#7 padding if required */
if (this.direction == OpMode.WC_DECRYPT &&
@ -690,15 +867,21 @@ public class WolfCryptCipher extends CipherSpi {
/* reset state, user doesn't need to call init again before use */
try {
buffered = new byte[0];
if (this.direction == OpMode.WC_ENCRYPT) {
wolfCryptSetDirection(Cipher.ENCRYPT_MODE);
} else {
wolfCryptSetDirection(Cipher.DECRYPT_MODE);
}
InitializeNativeStructs();
wolfCryptSetIV(storedSpec, null);
wolfCryptSetKey(storedKey);
this.aadData = null;
this.operationStarted = false;
this.cipherInitialized = true;
} catch (InvalidKeyException e) {
throw new RuntimeException(e.getMessage());
} catch (InvalidAlgorithmParameterException e) {
@ -709,10 +892,16 @@ public class WolfCryptCipher extends CipherSpi {
}
@Override
protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen)
throws IllegalStateException {
byte output[];
if (!this.cipherInitialized) {
throw new IllegalStateException(
"Cipher has not been initialized yet");
}
if (debug.DEBUG)
log("update (offset: " + inputOffset + ", len: " +
inputLen + ")");
@ -725,10 +914,15 @@ public class WolfCryptCipher extends CipherSpi {
@Override
protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
byte[] output, int outputOffset)
throws ShortBufferException {
throws IllegalStateException, ShortBufferException {
byte tmpOut[];
if (!this.cipherInitialized) {
throw new IllegalStateException(
"Cipher has not been initialized yet");
}
if (debug.DEBUG)
log("update (in offset: " + inputOffset + ", len: " +
inputLen + ", out offset: " + outputOffset + ")");
@ -755,7 +949,13 @@ public class WolfCryptCipher extends CipherSpi {
@Override
protected byte[] engineDoFinal(byte[] input, int inputOffset,
int inputLen)
throws IllegalBlockSizeException, BadPaddingException {
throws IllegalStateException, IllegalBlockSizeException,
BadPaddingException {
if (!this.cipherInitialized) {
throw new IllegalStateException(
"Cipher has not been initialized yet");
}
if (debug.DEBUG)
log("final (offset: " + inputOffset + ", len: " +
@ -767,11 +967,16 @@ public class WolfCryptCipher extends CipherSpi {
@Override
protected int engineDoFinal(byte[] input, int inputOffset,
int inputLen, byte[] output, int outputOffset)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
throws IllegalStateException, ShortBufferException,
IllegalBlockSizeException, BadPaddingException {
byte tmpOut[];
if (!this.cipherInitialized) {
throw new IllegalStateException(
"Cipher has not been initialized yet");
}
if (debug.DEBUG)
log("final (in offset: " + inputOffset + ", len: " +
inputLen + ", out offset: " + outputOffset + ")");
@ -816,6 +1021,71 @@ public class WolfCryptCipher extends CipherSpi {
return encodedKey.length;
}
@Override
protected void engineUpdateAAD(byte[] src, int offset, int len)
throws IllegalArgumentException, IllegalStateException {
if (this.cipherType != CipherType.WC_AES ||
this.cipherMode != CipherMode.WC_GCM) {
throw new IllegalStateException(
"AAD only supported for AES-GCM");
}
if (this.operationStarted) {
throw new IllegalStateException(
"Must set AAD before calling Cipher.update/final");
}
if (!this.cipherInitialized || this.aesGcm == null) {
throw new IllegalStateException(
"Cipher not initialized yet");
}
if (src == null || offset < 0 || len < 0 ||
(src.length < (offset + len))) {
throw new IllegalArgumentException(
"Source buffer is null or bad offset/len");
}
if (this.aadData == null) {
/* Store as new array inside object */
this.aadData = new byte[len];
System.arraycopy(src, offset, this.aadData, 0, len);
}
else {
/* Append to existing AAD array held inside object */
byte[] tmp = new byte[this.aadData.length + len];
System.arraycopy(this.aadData, 0, tmp, 0, this.aadData.length);
System.arraycopy(src, offset, tmp, this.aadData.length, len);
this.aadData = tmp;
}
}
@Override
protected void engineUpdateAAD(ByteBuffer src)
throws IllegalArgumentException, IllegalStateException {
int originalPos = 0;
byte[] remaining = null;
if (src == null) {
throw new IllegalArgumentException("Source buffer is null");
}
originalPos = src.position();
remaining = new byte[src.remaining()];
src.get(remaining);
try {
engineUpdateAAD(remaining, 0, remaining.length);
} catch (IllegalStateException | IllegalArgumentException e) {
/* restore state of ByteBuffer on state error before returning */
src.position(originalPos);
throw e;
}
}
private String typeToString(CipherType type) {
switch (type) {
case WC_AES:
@ -835,6 +1105,8 @@ public class WolfCryptCipher extends CipherSpi {
return "ECB";
case WC_CBC:
return "CBC";
case WC_GCM:
return "GCM";
default:
return "None";
}
@ -848,17 +1120,30 @@ public class WolfCryptCipher extends CipherSpi {
@Override
protected void finalize() throws Throwable {
try {
if (this.aes != null)
if (this.aes != null) {
this.aes.releaseNativeStruct();
this.aes = null;
}
if (this.des3 != null)
if (this.aesGcm != null) {
this.aesGcm.releaseNativeStruct();
this.aesGcm = null;
}
if (this.des3 != null) {
this.des3.releaseNativeStruct();
this.des3 = null;
}
if (this.rsa != null)
if (this.rsa != null) {
this.rsa.releaseNativeStruct();
this.rsa = null;
}
if (this.rng != null)
if (this.rng != null) {
this.rng.releaseNativeStruct();
this.rng = null;
}
zeroArray(this.iv);
@ -894,6 +1179,18 @@ public class WolfCryptCipher extends CipherSpi {
}
}
/**
* Class for AES-GCM with no padding
*/
public static final class wcAESGCMNoPadding extends WolfCryptCipher {
/**
* Create new wcAESGCMNoPadding object
*/
public wcAESGCMNoPadding() {
super(CipherType.WC_AES, CipherMode.WC_GCM, PaddingType.WC_NONE);
}
}
/**
* Class for DES-EDE-CBC with no padding
*/

View File

@ -121,34 +121,58 @@ public final class WolfCryptProvider extends Provider {
}
/* Cipher */
put("Cipher.AES/CBC/NoPadding",
if (FeatureDetect.AesCbcEnabled()) {
put("Cipher.AES/CBC/NoPadding",
"com.wolfssl.provider.jce.WolfCryptCipher$wcAESCBCNoPadding");
put("Cipher.AES/CBC/PKCS5Padding",
put("Cipher.AES/CBC/PKCS5Padding",
"com.wolfssl.provider.jce.WolfCryptCipher$wcAESCBCPKCS5Padding");
}
if (FeatureDetect.AesGcmEnabled()) {
put("Cipher.AES/GCM/NoPadding",
"com.wolfssl.provider.jce.WolfCryptCipher$wcAESGCMNoPadding");
}
put("Cipher.DESede/CBC/NoPadding",
if (FeatureDetect.Des3Enabled()) {
put("Cipher.DESede/CBC/NoPadding",
"com.wolfssl.provider.jce.WolfCryptCipher$wcDESedeCBCNoPadding");
}
put("Cipher.RSA",
if (FeatureDetect.RsaEnabled()) {
put("Cipher.RSA",
"com.wolfssl.provider.jce.WolfCryptCipher$wcRSAECBPKCS1Padding");
put("Cipher.RSA/ECB/PKCS1Padding",
put("Cipher.RSA/ECB/PKCS1Padding",
"com.wolfssl.provider.jce.WolfCryptCipher$wcRSAECBPKCS1Padding");
}
/* KeyAgreement */
put("KeyAgreement.DiffieHellman",
if (FeatureDetect.DhEnabled()) {
put("KeyAgreement.DiffieHellman",
"com.wolfssl.provider.jce.WolfCryptKeyAgreement$wcDH");
put("Alg.Alias.KeyAgreement.DH", "DiffieHellman");
put("KeyAgreement.ECDH",
put("Alg.Alias.KeyAgreement.DH", "DiffieHellman");
}
if (FeatureDetect.EccDheEnabled()) {
put("KeyAgreement.ECDH",
"com.wolfssl.provider.jce.WolfCryptKeyAgreement$wcECDH");
}
/* KeyPairGenerator */
put("KeyPairGenerator.RSA",
if (FeatureDetect.RsaKeyGenEnabled()) {
put("KeyPairGenerator.RSA",
"com.wolfssl.provider.jce.WolfCryptKeyPairGenerator$wcKeyPairGenRSA");
put("KeyPairGenerator.EC",
}
if (FeatureDetect.EccKeyGenEnabled()) {
put("KeyPairGenerator.EC",
"com.wolfssl.provider.jce.WolfCryptKeyPairGenerator$wcKeyPairGenECC");
put("KeyPairGenerator.DH",
}
if (FeatureDetect.DhEnabled()) {
put("KeyPairGenerator.DH",
"com.wolfssl.provider.jce.WolfCryptKeyPairGenerator$wcKeyPairGenDH");
put("Alg.Alias.KeyPairGenerator.DiffieHellman", "DH");
put("Alg.Alias.KeyPairGenerator.DiffieHellman", "DH");
}
/* KeyStore */
put("KeyStore.WKS",
"com.wolfssl.provider.jce.WolfSSLKeyStore$WolfSSLKeyStoreWKS");
/* If using a FIPS version of wolfCrypt, allow private key to be
* exported for use. Only applicable to FIPS 140-3 */

View File

@ -0,0 +1,216 @@
/* AesGcm.java
*
* Copyright (C) 2006-2024 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 2 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
*/
package com.wolfssl.wolfcrypt;
/**
* Wrapper for native wolfCrypt AES-GCM implementation.
*/
public class AesGcm extends NativeStruct {
private WolfCryptState state = WolfCryptState.UNINITIALIZED;
/* Lock around object state */
protected final Object stateLock = new Object();
/* Lock around native Aes poiner use */
private final Object aesLock = new Object();
/* Native JNI methods, implemented in jni/jni_aesgcm.c */
private native long mallocNativeStruct_internal() throws OutOfMemoryError;
private native void wc_AesInit();
private native void wc_AesFree();
private native void wc_AesGcmSetKey(byte[] key);
private native byte[] wc_AesGcmEncrypt(byte[] input, byte[] iv,
byte[] authTagOut, byte[] authIn);
private native byte[] wc_AesGcmDecrypt(byte[] input, byte[] iv,
byte[] authTag, byte[] authIn);
/**
* Create and initialize new AesGcm object
*/
public AesGcm() {
init();
}
public AesGcm(byte[] key) {
init();
setKey(key);
}
/**
* Malloc native Aes structure via JNI. Called by NativeStruct
* constructor when this object is created.
*
* @return native allocated pointer
*
* @throws OutOfMemoryError when malloc fails with memory error
*/
@Override
protected long mallocNativeStruct()
throws OutOfMemoryError {
synchronized (pointerLock) {
return mallocNativeStruct_internal();
}
}
/**
* Release native Aes structure memory via JNI. Either called explicitly
* by application or from NativeStruct finalize() method upon object
* cleanup.
*/
@Override
public synchronized void releaseNativeStruct() {
free();
super.releaseNativeStruct();
}
/** Initialize AesGcm object and underlying native Aes structure */
protected void init() {
synchronized (stateLock) {
if (state == WolfCryptState.UNINITIALIZED) {
synchronized (pointerLock) {
wc_AesInit();
}
state = WolfCryptState.INITIALIZED;
} else {
throw new IllegalStateException(
"Native resources already initialized");
}
}
}
/** Free AesGcm object and underlying native Aes structure */
protected synchronized void free() {
synchronized (stateLock) {
if (state != WolfCryptState.UNINITIALIZED) {
synchronized (pointerLock) {
wc_AesFree();
}
state = WolfCryptState.UNINITIALIZED;
}
}
}
/**
* Set AES-GCM key.
*
* @param key AES key as byte array. Supported key lengths include:
* 16 bytes (128-bit)
* 24 bytes (192-bit)
* 32 bytes (256-bit)
*
* @throws WolfCryptException if native operation fails
* @throws IllegalStateException if key has already been set
*/
public synchronized void setKey(byte[] key)
throws WolfCryptException, IllegalStateException {
synchronized (stateLock) {
if (state == WolfCryptState.INITIALIZED) {
synchronized (pointerLock) {
wc_AesGcmSetKey(key);
}
state = WolfCryptState.READY;
} else {
throw new IllegalStateException(
"Key has already been set for this AesGcm object, " +
"or object not initialized");
}
}
}
/**
* Encrypt data with AES-GCM.
*
* @param input input data to be encrypted
* @param iv IV for AES-GCM operation
* @param authTagOut output byte array for auth tag to be placed. Should
* be sized to desired tag size, which can be between wolfSSL
* minimum auth tag size (default 12) and AES block size (16).
* User compiling native wolfSSL can override default minimum
* auth tag size by defining WOLFSSL_MIN_AUTH_TAG_SZ.
* @param authIn additional data to be authenticated but not encrypted,
* can be null if no additional data desired or available.
*
* @return encrypted cipertext buffer
*
* @throws WolfCryptException if native operation fails
* @throws IllegalStateException if key has already been set
*/
public synchronized byte[] encrypt(byte[] input, byte[] iv,
byte[] authTagOut, byte[] authIn)
throws IllegalStateException, WolfCryptException {
byte[] output = null;
synchronized (stateLock) {
if (state == WolfCryptState.READY) {
synchronized (pointerLock) {
output = wc_AesGcmEncrypt(input, iv, authTagOut, authIn);
}
}
else {
throw new IllegalStateException(
"Object has not bee initialized or set up");
}
return output;
}
}
/**
* Decrypt data with AES-GCM.
*
* @param input ciphertext to be decrypted
* @param iv IV for AES-GCM operation
* @param authTag authentication tag generated during encryption operation
* @param authIn additional data to be authenticated but not decrypted
*
* @return decrypted plaintext buffer
*
* @throws WolfCryptException if native operation fails
* @throws IllegalStateException if key has already been set
*/
public synchronized byte[] decrypt(byte[] input, byte[] iv, byte[] authTag,
byte[] authIn) throws IllegalStateException, WolfCryptException {
byte[] output = null;
synchronized (stateLock) {
if (state == WolfCryptState.READY) {
synchronized (pointerLock) {
output = wc_AesGcmDecrypt(input, iv, authTag, authIn);
}
}
else {
throw new IllegalStateException(
"Object has not been initialized or set up");
}
return output;
}
}
}

View File

@ -41,6 +41,13 @@ public class FeatureDetect {
*/
public static native boolean ShaEnabled();
/**
* Tests if SHA-224 is compiled into the native wolfSSL library.
*
* @return true if enabled, otherwise false if not compiled in.
*/
public static native boolean Sha224Enabled();
/**
* Tests if SHA-256 is compiled into the native wolfSSL library.
*
@ -62,6 +69,57 @@ public class FeatureDetect {
*/
public static native boolean Sha512Enabled();
/**
* Tests if AES-128 is compiled into the native wolfSSL library.
*
* @return true if enabled, otherwise false if not compiled in.
*/
public static native boolean Aes128Enabled();
/**
* Tests if AES-192 is compiled into the native wolfSSL library.
*
* @return true if enabled, otherwise false if not compiled in.
*/
public static native boolean Aes192Enabled();
/**
* Tests if AES-256 is compiled into the native wolfSSL library.
*
* @return true if enabled, otherwise false if not compiled in.
*/
public static native boolean Aes256Enabled();
/**
* Tests if AES-CBC is compiled into the native wolfSSL library.
*
* @return true if enabled, otherwise false if not compiled in.
*/
public static native boolean AesCbcEnabled();
/**
* Tests if AES-GCM is compiled into the native wolfSSL library.
*
* @return true if enabled, otherwise false if not compiled in.
*/
public static native boolean AesGcmEnabled();
/**
* Tests if AES-GCM stream mode (WOLFSSL_AESGCM_STREAM) is compiled into
* native wolfSSL library.
*
* @return true if enabled, otherwise false if not compiled in.
*/
public static native boolean AesGcmStreamEnabled();
/**
* Tests if 3DES is compiled into the native wolfSSL library.
*
* @return true if enabled, otherwise false if not compiled in.
*/
public static native boolean Des3Enabled();
/**
* Tests if HMAC-MD5 is compiled into the native wolfSSL library and
* available for use.
@ -102,6 +160,49 @@ public class FeatureDetect {
*/
public static native boolean HmacSha512Enabled();
/**
* Tests if RSA is compiled into the native wolfSSL library.
*
* @return true if enabled, otherwise false if not compiled in.
*/
public static native boolean RsaEnabled();
/**
* Tests if RSA key generation is compiled into the native wolfSSL library.
*
* @return true if enabled, otherwise false if not compiled in.
*/
public static native boolean RsaKeyGenEnabled();
/**
* Tests if DH is compiled into the native wolfSSL library.
*
* @return true if enabled, otherwise false if not compiled in.
*/
public static native boolean DhEnabled();
/**
* Tests if ECC is compiled into the native wolfSSL library.
*
* @return true if enabled, otherwise false if not compiled in.
*/
public static native boolean EccEnabled();
/**
* Tests if ECC key generation is compiled into the native wolfSSL library.
*
* @return true if enabled, otherwise false if not compiled in.
*/
public static native boolean EccKeyGenEnabled();
/**
* Tests if ECDHE / wc_ecc_shared_secret() is compiled into the native
* wolfSSL library.
*
* @return true if enabled, otherwise false if not compiled in.
*/
public static native boolean EccDheEnabled();
/**
* Loads JNI library.
*

View File

@ -34,10 +34,12 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.nio.ByteBuffer;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.BadPaddingException;
@ -55,6 +57,7 @@ import java.security.InvalidKeyException;
import java.security.InvalidAlgorithmParameterException;
import com.wolfssl.wolfcrypt.WolfCrypt;
import com.wolfssl.wolfcrypt.FeatureDetect;
import com.wolfssl.wolfcrypt.Fips;
import com.wolfssl.provider.jce.WolfCryptProvider;
import com.wolfssl.wolfcrypt.WolfCryptException;
@ -65,6 +68,7 @@ public class WolfCryptCipherTest {
private static String supportedJCEAlgos[] = {
"AES/CBC/NoPadding",
"AES/CBC/PKCS5Padding",
"AES/GCM/NoPadding",
"DESede/CBC/NoPadding",
"RSA",
"RSA/ECB/PKCS1Padding"
@ -111,6 +115,7 @@ public class WolfCryptCipherTest {
/* fill expected block size HashMap */
expectedBlockSizes.put("AES/CBC/NoPadding", 16);
expectedBlockSizes.put("AES/CBC/PKCS5Padding", 16);
expectedBlockSizes.put("AES/GCM/NoPadding", 16);
expectedBlockSizes.put("DESede/CBC/NoPadding", 8);
expectedBlockSizes.put("RSA", 0);
expectedBlockSizes.put("RSA/ECB/PKCS1Padding", 0);
@ -172,7 +177,7 @@ public class WolfCryptCipherTest {
BadPaddingException {
CipherVector vectors[] = new CipherVector[] {
/* test vectors {key, iv, input, output } */
/* test vectors {key, iv, input, output, tag, aad } */
new CipherVector(
new byte[] {
(byte)0x30, (byte)0x31, (byte)0x32, (byte)0x33,
@ -197,7 +202,8 @@ public class WolfCryptCipherTest {
(byte)0x5f, (byte)0x42, (byte)0x81, (byte)0x53,
(byte)0x2c, (byte)0xcc, (byte)0x9d, (byte)0x46,
(byte)0x77, (byte)0xa2, (byte)0x33, (byte)0xcb
}
},
null, null
)
};
@ -215,7 +221,19 @@ public class WolfCryptCipherTest {
SecretKeySpec key = new SecretKeySpec(vectors[i].getKey(), "AES");
IvParameterSpec spec = new IvParameterSpec(vectors[i].getIV());
/* getOutputSize() before init() should throw exception */
try {
cipher.getOutputSize(vectors[i].getInput().length);
fail("getOutputSize() before init() should fail");
} catch (IllegalStateException e) {
/* expected, continue */
}
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
assertEquals(vectors[i].getOutput().length,
cipher.getOutputSize(vectors[i].getInput().length));
output = cipher.doFinal(vectors[i].input);
assertArrayEquals(output, vectors[i].output);
@ -758,7 +776,8 @@ public class WolfCryptCipherTest {
(byte)0x44, (byte)0xaa, (byte)0xb5, (byte)0xf0,
(byte)0x5f, (byte)0x34, (byte)0xb4, (byte)0xde,
(byte)0xb5, (byte)0xbd, (byte)0x2a, (byte)0xbb
}
},
null, null
)
};
@ -1474,6 +1493,772 @@ public class WolfCryptCipherTest {
}
}
/* test vectors {key, iv, input, output, tag, aad } */
CipherVector aesGcmVectors[] = new CipherVector[] {
/* AES-GCM-128 */
/* The following is an interesting test case from the example
* FIPS test vectors for AES-GCM. IVlen = 1 byte */
new CipherVector(
new byte[] { /* key (k3 from test.c) */
(byte)0xbb, (byte)0x01, (byte)0xd7, (byte)0x03,
(byte)0x81, (byte)0x1c, (byte)0x10, (byte)0x1a,
(byte)0x35, (byte)0xe0, (byte)0xff, (byte)0xd2,
(byte)0x91, (byte)0xba, (byte)0xf2, (byte)0x4b
},
new byte[] { /* iv (iv3 from test.c) */
(byte)0xca
},
new byte[] { /* input (p3 from test.c) */
(byte)0x57, (byte)0xce, (byte)0x45, (byte)0x1f,
(byte)0xa5, (byte)0xe2, (byte)0x35, (byte)0xa5,
(byte)0x8e, (byte)0x1a, (byte)0xa2, (byte)0x3b,
(byte)0x77, (byte)0xcb, (byte)0xaf, (byte)0xe2
},
new byte[] { /* output (c3 from test.c) */
(byte)0x6b, (byte)0x5f, (byte)0xb3, (byte)0x9d,
(byte)0xc1, (byte)0xc5, (byte)0x7a, (byte)0x4f,
(byte)0xf3, (byte)0x51, (byte)0x4d, (byte)0xc2,
(byte)0xd5, (byte)0xf0, (byte)0xd0, (byte)0x07
},
new byte[] { /* tag (t3 from test.c) */
(byte)0x06, (byte)0x90, (byte)0xed, (byte)0x01,
(byte)0x34, (byte)0xdd, (byte)0xc6, (byte)0x95,
(byte)0x31, (byte)0x2e, (byte)0x2a, (byte)0xf9,
(byte)0x57, (byte)0x7a, (byte)0x1e, (byte)0xa6
},
new byte[] { /* aad (a3 from test.c) */
(byte)0x40, (byte)0xfc, (byte)0xdc, (byte)0xd7,
(byte)0x4a, (byte)0xd7, (byte)0x8b, (byte)0xf1,
(byte)0x3e, (byte)0x7c, (byte)0x60, (byte)0x55,
(byte)0x50, (byte)0x51, (byte)0xdd, (byte)0x54
}
),
/* AES-GCM-192 */
/* FIPS, QAT and PIC32MZ HW Crypto only support 12-byte IV */
/* Test Case 12, uses same plaintext and AAD data. */
new CipherVector(
new byte[] { /* key (k2 from test.c) */
(byte)0xfe, (byte)0xff, (byte)0xe9, (byte)0x92,
(byte)0x86, (byte)0x65, (byte)0x73, (byte)0x1c,
(byte)0x6d, (byte)0x6a, (byte)0x8f, (byte)0x94,
(byte)0x67, (byte)0x30, (byte)0x83, (byte)0x08,
(byte)0xfe, (byte)0xff, (byte)0xe9, (byte)0x92,
(byte)0x86, (byte)0x65, (byte)0x73, (byte)0x1c
},
new byte[] { /* iv (iv2 from test.c) */
(byte)0x93, (byte)0x13, (byte)0x22, (byte)0x5d,
(byte)0xf8, (byte)0x84, (byte)0x06, (byte)0xe5,
(byte)0x55, (byte)0x90, (byte)0x9c, (byte)0x5a,
(byte)0xff, (byte)0x52, (byte)0x69, (byte)0xaa,
(byte)0x6a, (byte)0x7a, (byte)0x95, (byte)0x38,
(byte)0x53, (byte)0x4f, (byte)0x7d, (byte)0xa1,
(byte)0xe4, (byte)0xc3, (byte)0x03, (byte)0xd2,
(byte)0xa3, (byte)0x18, (byte)0xa7, (byte)0x28,
(byte)0xc3, (byte)0xc0, (byte)0xc9, (byte)0x51,
(byte)0x56, (byte)0x80, (byte)0x95, (byte)0x39,
(byte)0xfc, (byte)0xf0, (byte)0xe2, (byte)0x42,
(byte)0x9a, (byte)0x6b, (byte)0x52, (byte)0x54,
(byte)0x16, (byte)0xae, (byte)0xdb, (byte)0xf5,
(byte)0xa0, (byte)0xde, (byte)0x6a, (byte)0x57,
(byte)0xa6, (byte)0x37, (byte)0xb3, (byte)0x9b
},
new byte[] { /* input (p from test.c) */
(byte)0xd9, (byte)0x31, (byte)0x32, (byte)0x25,
(byte)0xf8, (byte)0x84, (byte)0x06, (byte)0xe5,
(byte)0xa5, (byte)0x59, (byte)0x09, (byte)0xc5,
(byte)0xaf, (byte)0xf5, (byte)0x26, (byte)0x9a,
(byte)0x86, (byte)0xa7, (byte)0xa9, (byte)0x53,
(byte)0x15, (byte)0x34, (byte)0xf7, (byte)0xda,
(byte)0x2e, (byte)0x4c, (byte)0x30, (byte)0x3d,
(byte)0x8a, (byte)0x31, (byte)0x8a, (byte)0x72,
(byte)0x1c, (byte)0x3c, (byte)0x0c, (byte)0x95,
(byte)0x95, (byte)0x68, (byte)0x09, (byte)0x53,
(byte)0x2f, (byte)0xcf, (byte)0x0e, (byte)0x24,
(byte)0x49, (byte)0xa6, (byte)0xb5, (byte)0x25,
(byte)0xb1, (byte)0x6a, (byte)0xed, (byte)0xf5,
(byte)0xaa, (byte)0x0d, (byte)0xe6, (byte)0x57,
(byte)0xba, (byte)0x63, (byte)0x7b, (byte)0x39
},
new byte[] { /* output (c2 from test.c) */
(byte)0xd2, (byte)0x7e, (byte)0x88, (byte)0x68,
(byte)0x1c, (byte)0xe3, (byte)0x24, (byte)0x3c,
(byte)0x48, (byte)0x30, (byte)0x16, (byte)0x5a,
(byte)0x8f, (byte)0xdc, (byte)0xf9, (byte)0xff,
(byte)0x1d, (byte)0xe9, (byte)0xa1, (byte)0xd8,
(byte)0xe6, (byte)0xb4, (byte)0x47, (byte)0xef,
(byte)0x6e, (byte)0xf7, (byte)0xb7, (byte)0x98,
(byte)0x28, (byte)0x66, (byte)0x6e, (byte)0x45,
(byte)0x81, (byte)0xe7, (byte)0x90, (byte)0x12,
(byte)0xaf, (byte)0x34, (byte)0xdd, (byte)0xd9,
(byte)0xe2, (byte)0xf0, (byte)0x37, (byte)0x58,
(byte)0x9b, (byte)0x29, (byte)0x2d, (byte)0xb3,
(byte)0xe6, (byte)0x7c, (byte)0x03, (byte)0x67,
(byte)0x45, (byte)0xfa, (byte)0x22, (byte)0xe7,
(byte)0xe9, (byte)0xb7, (byte)0x37, (byte)0x3b
},
new byte[] { /* tag (t2 from test.c) */
(byte)0xdc, (byte)0xf5, (byte)0x66, (byte)0xff,
(byte)0x29, (byte)0x1c, (byte)0x25, (byte)0xbb,
(byte)0xb8, (byte)0x56, (byte)0x8f, (byte)0xc3,
(byte)0xd3, (byte)0x76, (byte)0xa6, (byte)0xd9
},
new byte[] { /* aad (a from test.c) */
(byte)0xfe, (byte)0xed, (byte)0xfa, (byte)0xce,
(byte)0xde, (byte)0xad, (byte)0xbe, (byte)0xef,
(byte)0xfe, (byte)0xed, (byte)0xfa, (byte)0xce,
(byte)0xde, (byte)0xad, (byte)0xbe, (byte)0xef,
(byte)0xab, (byte)0xad, (byte)0xda, (byte)0xd2
}
),
/* AES-GCM-256 */
/* This is Test Case 16 from the document Galois/Counter Mode of
* Operation (GCM) by McGrew and Viega. */
new CipherVector(
new byte[] { /* key (k1 from test.c) */
(byte)0xfe, (byte)0xff, (byte)0xe9, (byte)0x92,
(byte)0x86, (byte)0x65, (byte)0x73, (byte)0x1c,
(byte)0x6d, (byte)0x6a, (byte)0x8f, (byte)0x94,
(byte)0x67, (byte)0x30, (byte)0x83, (byte)0x08,
(byte)0xfe, (byte)0xff, (byte)0xe9, (byte)0x92,
(byte)0x86, (byte)0x65, (byte)0x73, (byte)0x1c,
(byte)0x6d, (byte)0x6a, (byte)0x8f, (byte)0x94,
(byte)0x67, (byte)0x30, (byte)0x83, (byte)0x08
},
new byte[] { /* iv (iv1 from test.c) */
(byte)0xca, (byte)0xfe, (byte)0xba, (byte)0xbe,
(byte)0xfa, (byte)0xce, (byte)0xdb, (byte)0xad,
(byte)0xde, (byte)0xca, (byte)0xf8, (byte)0x88
},
new byte[] { /* input (p from test.c) */
(byte)0xd9, (byte)0x31, (byte)0x32, (byte)0x25,
(byte)0xf8, (byte)0x84, (byte)0x06, (byte)0xe5,
(byte)0xa5, (byte)0x59, (byte)0x09, (byte)0xc5,
(byte)0xaf, (byte)0xf5, (byte)0x26, (byte)0x9a,
(byte)0x86, (byte)0xa7, (byte)0xa9, (byte)0x53,
(byte)0x15, (byte)0x34, (byte)0xf7, (byte)0xda,
(byte)0x2e, (byte)0x4c, (byte)0x30, (byte)0x3d,
(byte)0x8a, (byte)0x31, (byte)0x8a, (byte)0x72,
(byte)0x1c, (byte)0x3c, (byte)0x0c, (byte)0x95,
(byte)0x95, (byte)0x68, (byte)0x09, (byte)0x53,
(byte)0x2f, (byte)0xcf, (byte)0x0e, (byte)0x24,
(byte)0x49, (byte)0xa6, (byte)0xb5, (byte)0x25,
(byte)0xb1, (byte)0x6a, (byte)0xed, (byte)0xf5,
(byte)0xaa, (byte)0x0d, (byte)0xe6, (byte)0x57,
(byte)0xba, (byte)0x63, (byte)0x7b, (byte)0x39
},
new byte[] { /* output (c1 from test.c) */
(byte)0x52, (byte)0x2d, (byte)0xc1, (byte)0xf0,
(byte)0x99, (byte)0x56, (byte)0x7d, (byte)0x07,
(byte)0xf4, (byte)0x7f, (byte)0x37, (byte)0xa3,
(byte)0x2a, (byte)0x84, (byte)0x42, (byte)0x7d,
(byte)0x64, (byte)0x3a, (byte)0x8c, (byte)0xdc,
(byte)0xbf, (byte)0xe5, (byte)0xc0, (byte)0xc9,
(byte)0x75, (byte)0x98, (byte)0xa2, (byte)0xbd,
(byte)0x25, (byte)0x55, (byte)0xd1, (byte)0xaa,
(byte)0x8c, (byte)0xb0, (byte)0x8e, (byte)0x48,
(byte)0x59, (byte)0x0d, (byte)0xbb, (byte)0x3d,
(byte)0xa7, (byte)0xb0, (byte)0x8b, (byte)0x10,
(byte)0x56, (byte)0x82, (byte)0x88, (byte)0x38,
(byte)0xc5, (byte)0xf6, (byte)0x1e, (byte)0x63,
(byte)0x93, (byte)0xba, (byte)0x7a, (byte)0x0a,
(byte)0xbc, (byte)0xc9, (byte)0xf6, (byte)0x62
},
new byte[] { /* tag (t1 from test.c) */
(byte)0x76, (byte)0xfc, (byte)0x6e, (byte)0xce,
(byte)0x0f, (byte)0x4e, (byte)0x17, (byte)0x68,
(byte)0xcd, (byte)0xdf, (byte)0x88, (byte)0x53,
(byte)0xbb, (byte)0x2d, (byte)0x55, (byte)0x1b
},
new byte[] { /* aad (a from test.c) */
(byte)0xfe, (byte)0xed, (byte)0xfa, (byte)0xce,
(byte)0xde, (byte)0xad, (byte)0xbe, (byte)0xef,
(byte)0xfe, (byte)0xed, (byte)0xfa, (byte)0xce,
(byte)0xde, (byte)0xad, (byte)0xbe, (byte)0xef,
(byte)0xab, (byte)0xad, (byte)0xda, (byte)0xd2
}
)
};
/*
* Test Cipher("AES/GCM/NoPadding") processing with single call to
* doFinal().
*/
@Test
public void testAesGcmNoPadding() throws NoSuchAlgorithmException,
InvalidKeyException, IllegalBlockSizeException, NoSuchProviderException,
InvalidAlgorithmParameterException, BadPaddingException,
NoSuchPaddingException {
byte output[] = null;
byte plain[] = null;
if (!enabledJCEAlgos.contains("AES/GCM/NoPadding")) {
/* skip if AES-GCM is not enabled */
return;
}
Cipher enc = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
Cipher dec = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
for (int i = 0; i < aesGcmVectors.length; i++) {
/* skip AES-128 vector if not compiled in native library */
if ((i == 0) && (!FeatureDetect.Aes128Enabled())) {
continue;
}
/* skip AES-192 vector if not compiled in native library, or if
* using wolfCrypt FIPS since it only supports 12-byte IVs */
if ((i == 1) && (!FeatureDetect.Aes192Enabled() || Fips.enabled)) {
continue;
}
/* skip AES-256 vector if not compiled in native library */
if ((i == 2) && (!FeatureDetect.Aes256Enabled())) {
continue;
}
byte[] vOut = aesGcmVectors[i].getOutput();
byte[] vTag = aesGcmVectors[i].getTag();
byte[] tmpOut = new byte[vOut.length + vTag.length];
SecretKeySpec key = new SecretKeySpec(
aesGcmVectors[i].getKey(), "AES");
GCMParameterSpec spec = new GCMParameterSpec(
(vTag.length * 8), aesGcmVectors[i].getIV());
if (i == 0) {
/* getOutputSize() before init() should throw exception */
try {
enc.getOutputSize(aesGcmVectors[i].getInput().length);
fail("getOutputSize() before init() should fail");
} catch (IllegalStateException e) {
/* expected, continue */
}
}
/* Encrypt */
enc.init(Cipher.ENCRYPT_MODE, key, spec);
enc.updateAAD(aesGcmVectors[i].getAAD());
assertEquals(tmpOut.length,
enc.getOutputSize(aesGcmVectors[i].getInput().length));
output = enc.doFinal(aesGcmVectors[i].getInput());
/* Concatenate tag to ciphertext, JCE Cipher does this internally */
System.arraycopy(vOut, 0, tmpOut, 0, vOut.length);
System.arraycopy(vTag, 0, tmpOut, vOut.length, vTag.length);
assertArrayEquals(tmpOut, output);
/* Decrypt */
dec.init(Cipher.DECRYPT_MODE, key, spec);
dec.updateAAD(aesGcmVectors[i].getAAD());
plain = dec.doFinal(output);
/* plain is just ciphertext, no tag */
assertArrayEquals(aesGcmVectors[i].getInput(), plain);
/* ----- confirm wrong result if no AAD given but needed ----- */
enc = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
enc.init(Cipher.ENCRYPT_MODE, key, spec);
output = enc.doFinal(aesGcmVectors[i].getInput());
/* Concatenate tag to ciphertext, JCE Cipher does this internally */
System.arraycopy(vOut, 0, tmpOut, 0, vOut.length);
System.arraycopy(vTag, 0, tmpOut, vOut.length, vTag.length);
if (Arrays.equals(tmpOut, output)) {
fail("Encrypt without needed AAD should not match expected");
}
}
}
/*
* Test Cipher("AES/GCM/NoPadding") with multiple calls to update()
* on both encrypt and decrypt.
*/
@Test
public void testAesGcmNoPaddingWithUpdate()
throws NoSuchAlgorithmException, InvalidKeyException,
IllegalBlockSizeException, NoSuchProviderException,
InvalidAlgorithmParameterException, BadPaddingException,
NoSuchPaddingException {
byte tmp[] = null;
byte output[] = null;
byte plain[] = null;
if (!enabledJCEAlgos.contains("AES/GCM/NoPadding")) {
/* skip if AES-GCM is not enabled */
return;
}
Cipher enc = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
Cipher dec = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
for (int i = 0; i < aesGcmVectors.length; i++) {
int inIdx = 0;
int outIdx = 0;
int fourByteBlocks = 0;
int remainingBytes = 0;
byte[] vIn = aesGcmVectors[i].getInput();
byte[] vOut = aesGcmVectors[i].getOutput();
byte[] vTag = aesGcmVectors[i].getTag();
byte[] tmpOut = new byte[vOut.length + vTag.length];
SecretKeySpec key = new SecretKeySpec(
aesGcmVectors[i].getKey(), "AES");
GCMParameterSpec spec = new GCMParameterSpec(
(vTag.length * 8), aesGcmVectors[i].getIV());
/* skip AES-128 vector if not compiled in native library */
if ((i == 0) && (!FeatureDetect.Aes128Enabled())) {
continue;
}
/* skip AES-192 vector if not compiled in native library, or if
* using wolfCrypt FIPS since it only supports 12-byte IVs */
if ((i == 1) && (!FeatureDetect.Aes192Enabled() || Fips.enabled)) {
continue;
}
/* skip AES-256 vector if not compiled in native library */
if ((i == 2) && (!FeatureDetect.Aes256Enabled())) {
continue;
}
/* ----- ENCRYPT (vIn -> output) ----- */
enc.init(Cipher.ENCRYPT_MODE, key, spec);
enc.updateAAD(aesGcmVectors[i].getAAD());
/* Expected output is size vOut + vTag.length */
output = new byte[vOut.length + vTag.length];
/* Process via update() by 4-byte blocks */
fourByteBlocks = vIn.length / 4;
remainingBytes = vIn.length % 4;
for (int j = 0; j < fourByteBlocks; j++) {
tmp = enc.update(Arrays.copyOfRange(vIn, inIdx, inIdx + 4));
assertNotNull(tmp);
if (FeatureDetect.AesGcmStreamEnabled()) {
assertEquals(4, tmp.length);
} else {
assertEquals(0, tmp.length);
}
System.arraycopy(tmp, 0, output, outIdx, tmp.length);
inIdx += 4;
outIdx += tmp.length;
}
/* Process any remaining data (CipherVector.input length was not
* a multiple of 4 bytes */
if (remainingBytes > 0) {
tmp = enc.update(Arrays.copyOfRange(vIn, inIdx,
inIdx + remainingBytes));
assertNotNull(tmp);
if (FeatureDetect.AesGcmStreamEnabled()) {
assertEquals(remainingBytes, tmp.length);
} else {
assertEquals(0, tmp.length);
}
System.arraycopy(tmp, 0, output, outIdx, tmp.length);
inIdx += remainingBytes;
outIdx += tmp.length;
}
/* doFinal() should get tag (or whole ciphertext if AES-GCM
* streaming is not enabled at native wolfSSL level) */
tmp = enc.doFinal();
assertNotNull(tmp);
if (FeatureDetect.AesGcmStreamEnabled()) {
assertEquals(vTag.length, tmp.length);
} else {
assertEquals(tmpOut.length, tmp.length);
}
System.arraycopy(tmp, 0, output, outIdx, tmp.length);
outIdx += tmp.length;
/* Sanity check on total length written */
assertEquals(output.length, outIdx);
/* Concatenate tag to end of ciphertext from our test vector, JCE
* Cipher class already does this internally and will be the format
* returned from update/final */
System.arraycopy(vOut, 0, tmpOut, 0, vOut.length);
System.arraycopy(vTag, 0, tmpOut, vOut.length, vTag.length);
/* Encrypted matches vector output? */
assertArrayEquals(tmpOut, output);
/* ----- DECRYPT (output -> plain) ----- */
/* Sanity check, makes sure we built up output correctly */
assertArrayEquals(tmpOut, output);
/* Decrypt */
dec.init(Cipher.DECRYPT_MODE, key, spec);
dec.updateAAD(aesGcmVectors[i].getAAD());
/* plain is just plaintext, no tag */
plain = new byte[vIn.length];
/* Process via update() by 4-byte blocks */
fourByteBlocks = output.length / 4;
remainingBytes = output.length % 4;
inIdx = 0;
outIdx = 0;
for (int j = 0; j < fourByteBlocks; j++) {
tmp = dec.update(Arrays.copyOfRange(output, inIdx, inIdx + 4));
assertNotNull(tmp);
if (FeatureDetect.AesGcmStreamEnabled()) {
assertEquals(4, tmp.length);
} else {
assertEquals(0, tmp.length);
}
System.arraycopy(tmp, 0, plain, outIdx, tmp.length);
inIdx += 4;
outIdx += tmp.length;
}
/* Process any remaining data (output length was not
* a multiple of 4 bytes */
if (remainingBytes > 0) {
tmp = dec.update(Arrays.copyOfRange(output, inIdx,
inIdx + remainingBytes));
assertNotNull(tmp);
if (FeatureDetect.AesGcmStreamEnabled()) {
assertEquals(remainingBytes, tmp.length);
} else {
assertEquals(0, tmp.length);
}
System.arraycopy(tmp, 0, plain, outIdx, tmp.length);
inIdx += remainingBytes;
outIdx += tmp.length;
}
/* doFinal() will return whole plaintext if AES-GCM
* streaming is not enabled at native wolfSSL level */
tmp = dec.doFinal();
assertNotNull(tmp);
if (FeatureDetect.AesGcmStreamEnabled()) {
assertEquals(0, tmp.length);
} else {
assertEquals(vIn.length, tmp.length);
}
System.arraycopy(tmp, 0, plain, outIdx, tmp.length);
outIdx += tmp.length;
/* Sanity check on total length written */
assertEquals(plain.length, outIdx);
/* Decrypted matches vector input? */
assertArrayEquals(vIn, plain);
}
}
/*
* Test Cipher("AES/GCM/NoPadding") to make sure updateAAD() correctly
* throws an exception when called after a call to update or doFinal.
*/
@Test
public void testAesGcmNoPaddingUpdateAADByteArray()
throws NoSuchAlgorithmException, InvalidKeyException,
IllegalBlockSizeException, NoSuchProviderException,
InvalidAlgorithmParameterException, BadPaddingException,
NoSuchPaddingException {
int i = 0;
byte output[] = null;
byte plain[] = null;
CipherVector vect = null;
byte[] vOut = null;
byte[] vIn = null;
byte[] vKey = null;
byte[] vIV = null;
byte[] vTag = null;
byte[] vAAD = null;
if (!enabledJCEAlgos.contains("AES/GCM/NoPadding")) {
/* skip if AES-GCM is not enabled */
return;
}
Cipher enc = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
Cipher dec = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
/* Try to pick a vector based on what is compiled in natively,
* doesn't matter too much which vector we get */
if (FeatureDetect.Aes128Enabled()) {
vect = aesGcmVectors[0];
}
else if (FeatureDetect.Aes192Enabled() && !Fips.enabled) {
vect = aesGcmVectors[1];
}
else if (FeatureDetect.Aes256Enabled()) {
vect = aesGcmVectors[2];
}
else {
/* No test vector found, skipping test */
return;
}
vOut = vect.getOutput();
vIn = vect.getInput();
vKey = vect.getKey();
vIV = vect.getIV();
vTag = vect.getTag();
vAAD = vect.getAAD();
byte[] tmpOut = new byte[vOut.length + vTag.length];
SecretKeySpec key = new SecretKeySpec(vKey, "AES");
GCMParameterSpec spec = new GCMParameterSpec(
(vTag.length * 8), vIV);
/* Encrypt, test calling updateAAD() multiple times */
enc.init(Cipher.ENCRYPT_MODE, key, spec);
for (i = 0; i < vAAD.length; i++) {
enc.updateAAD(new byte[] {vAAD[i]});
}
output = enc.doFinal(vIn);
/* Concatenate tag to end of ciphertext, JCE Cipher class already
* does this internally */
System.arraycopy(vOut, 0, tmpOut, 0, vOut.length);
System.arraycopy(vTag, 0, tmpOut, vOut.length, vTag.length);
assertArrayEquals(tmpOut, output);
/* Decrypt, test calling updateAAD() multiple times */
dec.init(Cipher.DECRYPT_MODE, key, spec);
for (i = 0; i < vAAD.length; i++) {
dec.updateAAD(new byte[] {vAAD[i]});
}
plain = dec.doFinal(output);
/* plain is just ciphertext, no tag */
assertArrayEquals(vIn, plain);
/* ----- updateAAD() after update() should fail ----- */
enc = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
enc.init(Cipher.ENCRYPT_MODE, key, spec);
enc.update(vIn);
try {
enc.updateAAD(vAAD);
fail("updateAAD() after update() should throw exception");
} catch (IllegalStateException e) {
/* expected */
}
/* ----- updateAAD() throws exception if called before init ----- */
enc = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
try {
enc.updateAAD(vAAD);
fail("updateAAD() before init() should throw exception");
} catch (IllegalStateException e) {
/* expected */
}
}
/*
* Test Cipher("AES/GCM/NoPadding") to make sure updateAAD() correctly
* throws an exception when called after a call to update or doFinal,
* using ByteBuffer method.
*/
@Test
public void testAesGcmNoPaddingUpdateAADByteBuffer()
throws NoSuchAlgorithmException, InvalidKeyException,
IllegalBlockSizeException, NoSuchProviderException,
InvalidAlgorithmParameterException, BadPaddingException,
NoSuchPaddingException {
int i = 0;
byte output[] = null;
byte plain[] = null;
CipherVector vect = null;
byte[] vOut = null;
byte[] vIn = null;
byte[] vKey = null;
byte[] vIV = null;
byte[] vTag = null;
byte[] vAAD = null;
if (!enabledJCEAlgos.contains("AES/GCM/NoPadding")) {
/* skip if AES-GCM is not enabled */
return;
}
Cipher enc = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
Cipher dec = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
/* Try to pick a vector based on what is compiled in natively,
* doesn't matter too much which vector we get */
if (FeatureDetect.Aes128Enabled()) {
vect = aesGcmVectors[0];
}
else if (FeatureDetect.Aes192Enabled() && !Fips.enabled) {
vect = aesGcmVectors[1];
}
else if (FeatureDetect.Aes256Enabled()) {
vect = aesGcmVectors[2];
}
else {
/* No test vector found, skipping test */
return;
}
vOut = vect.getOutput();
vIn = vect.getInput();
vKey = vect.getKey();
vIV = vect.getIV();
vTag = vect.getTag();
vAAD = vect.getAAD();
byte[] tmpOut = new byte[vOut.length + vTag.length];
SecretKeySpec key = new SecretKeySpec(vKey, "AES");
GCMParameterSpec spec = new GCMParameterSpec(
(vTag.length * 8), vIV);
/* Encrypt, test calling updateAAD() multiple times */
enc.init(Cipher.ENCRYPT_MODE, key, spec);
for (i = 0; i < vAAD.length; i++) {
enc.updateAAD(ByteBuffer.wrap(new byte[] {vAAD[i]}));
}
output = enc.doFinal(vIn);
/* Concatenate tag to end of ciphertext, JCE Cipher class already
* does this internally */
System.arraycopy(vOut, 0, tmpOut, 0, vOut.length);
System.arraycopy(vTag, 0, tmpOut, vOut.length, vTag.length);
assertArrayEquals(tmpOut, output);
/* Decrypt, test calling updateAAD() multiple times */
dec.init(Cipher.DECRYPT_MODE, key, spec);
for (i = 0; i < vAAD.length; i++) {
dec.updateAAD(ByteBuffer.wrap(new byte[] {vAAD[i]}));
}
plain = dec.doFinal(output);
/* plain is just ciphertext, no tag */
assertArrayEquals(vIn, plain);
/* ----- updateAAD() after update() should fail ----- */
enc = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
enc.init(Cipher.ENCRYPT_MODE, key, spec);
enc.update(vIn);
try {
enc.updateAAD(ByteBuffer.wrap(vAAD));
fail("updateAAD() after update() should throw exception");
} catch (IllegalStateException e) {
/* expected */
}
/* ----- updateAAD() throws exception if called before init ----- */
enc = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
try {
enc.updateAAD(ByteBuffer.wrap(vAAD));
fail("updateAAD() before init() should throw exception");
} catch (IllegalStateException e) {
/* expected */
}
}
/*
* Test Cipher("AES/GCM/NoPadding") interop if other provider is available.
*/
@Test
public void testAesGcmNoPaddingInterop() throws NoSuchAlgorithmException,
InvalidKeyException, IllegalBlockSizeException, NoSuchProviderException,
InvalidAlgorithmParameterException, BadPaddingException,
NoSuchPaddingException {
byte cipher[] = null;
byte plain[] = null;
if (!enabledJCEAlgos.contains("AES/GCM/NoPadding")) {
/* skip if AES-GCM is not enabled */
return;
}
if (interopProvider == null) {
/* no interop provider available, skip */
return;
}
Cipher ciphA = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
Cipher ciphB = Cipher.getInstance("AES/GCM/NoPadding", interopProvider);
for (int i = 0; i < aesGcmVectors.length; i++) {
/* skip AES-128 vector if not compiled in native library */
if ((i == 0) && (!FeatureDetect.Aes128Enabled())) {
continue;
}
/* skip AES-192 vector if not compiled in native library, or if
* using wolfCrypt FIPS since it only supports 12-byte IVs */
if ((i == 1) && (!FeatureDetect.Aes192Enabled() || Fips.enabled)) {
continue;
}
/* skip AES-256 vector if not compiled in native library */
if ((i == 2) && (!FeatureDetect.Aes256Enabled())) {
continue;
}
byte[] vOut = aesGcmVectors[i].getOutput();
byte[] vIn = aesGcmVectors[i].getInput();
byte[] vKey = aesGcmVectors[i].getKey();
byte[] vIV = aesGcmVectors[i].getIV();
byte[] vTag = aesGcmVectors[i].getTag();
byte[] vAAD = aesGcmVectors[i].getAAD();
/* Concatenate tag to ciphertext, JCE Cipher does this internally */
byte[] tmpOut = new byte[vOut.length + vTag.length];
System.arraycopy(vOut, 0, tmpOut, 0, vOut.length);
System.arraycopy(vTag, 0, tmpOut, vOut.length, vTag.length);
SecretKeySpec key = new SecretKeySpec(vKey, "AES");
GCMParameterSpec spec = new GCMParameterSpec(
(vTag.length * 8), vIV);
/* Encrypt with wolfJCE */
ciphA.init(Cipher.ENCRYPT_MODE, key, spec);
ciphA.updateAAD(vAAD);
cipher = ciphA.doFinal(vIn);
assertArrayEquals(tmpOut, cipher);
/* Decrypt with INTEROP provider */
ciphB.init(Cipher.DECRYPT_MODE, key, spec);
ciphB.updateAAD(vAAD);
plain = ciphB.doFinal(cipher);
assertArrayEquals(vIn, plain);
/* Reset Cipher (same IV can't be used twice) */
ciphA = Cipher.getInstance("AES/GCM/NoPadding", jceProvider);
ciphB = Cipher.getInstance("AES/GCM/NoPadding", interopProvider);
/* Encrypt with INTEROP provider */
ciphB.init(Cipher.ENCRYPT_MODE, key, spec);
ciphB.updateAAD(vAAD);
cipher = ciphB.doFinal(vIn);
assertArrayEquals(tmpOut, cipher);
/* Decrypt with wolfJCE */
ciphA.init(Cipher.DECRYPT_MODE, key, spec);
ciphA.updateAAD(vAAD);
plain = ciphA.doFinal(cipher);
assertArrayEquals(vIn, plain);
}
}
@Test
public void testDESedeCbcNoPadding()
throws NoSuchProviderException, NoSuchAlgorithmException,
@ -1511,13 +2296,15 @@ public class WolfCryptCipherTest {
(byte)0x12, (byte)0xd5, (byte)0x08, (byte)0x98,
(byte)0x18, (byte)0x94, (byte)0x15, (byte)0x74,
(byte)0x87, (byte)0x12, (byte)0x7d, (byte)0xb0
}
},
null, null
)
};
byte output[];
if (!enabledJCEAlgos.contains("DESede/CBC/NoPadding")) {
if (!enabledJCEAlgos.contains("DESede/CBC/NoPadding") ||
!FeatureDetect.Des3Enabled()) {
/* bail out if 3DES is not enabled */
return;
}
@ -1579,7 +2366,8 @@ public class WolfCryptCipherTest {
byte tmp[];
if (!enabledJCEAlgos.contains("DESede/CBC/NoPadding")) {
if (!enabledJCEAlgos.contains("DESede/CBC/NoPadding") ||
!FeatureDetect.Des3Enabled()) {
/* bail out if 3DES is not enabled */
return;
}
@ -1636,7 +2424,8 @@ public class WolfCryptCipherTest {
(byte)0x90, (byte)0xab, (byte)0xcd, (byte)0xef,
};
if (!enabledJCEAlgos.contains("DESede/CBC/NoPadding")) {
if (!enabledJCEAlgos.contains("DESede/CBC/NoPadding") ||
!FeatureDetect.Des3Enabled()) {
/* skip if DESede/CBC/NoPadding is not enabled */
return;
}
@ -1742,7 +2531,7 @@ public class WolfCryptCipherTest {
(byte)0x20, (byte)0x6f, (byte)0x66, (byte)0x66,
(byte)0x2e
},
null
null, null, null
)
};
@ -2125,7 +2914,7 @@ public class WolfCryptCipherTest {
(byte)0x20, (byte)0x6f, (byte)0x66, (byte)0x66,
(byte)0x2e
},
null
null, null, null
)
};
@ -2236,13 +3025,17 @@ public class WolfCryptCipherTest {
private byte iv[];
private byte input[];
private byte output[];
private byte tag[]; /* AES-GCM auth tag */
private byte aad[]; /* AES-GCM additional auth data */
public CipherVector(byte[] key, byte[] iv, byte[] input,
byte[] output) {
byte[] output, byte[] tag, byte[] aad) {
this.key = key;
this.iv = iv;
this.input = input;
this.output = output;
this.tag = tag;
this.aad = aad;
}
public void setKey(byte[] key) {
@ -2261,6 +3054,14 @@ public class WolfCryptCipherTest {
this.output = output;
}
public void setTag(byte[] tag) {
this.tag = tag;
}
public void setAad(byte[] aad) {
this.aad = aad;
}
public byte[] getKey() {
return this.key;
}
@ -2276,6 +3077,14 @@ public class WolfCryptCipherTest {
public byte[] getOutput() {
return this.output;
}
public byte[] getTag() {
return this.tag;
}
public byte[] getAAD() {
return this.aad;
}
}
}

View File

@ -0,0 +1,993 @@
/* AesGcmTest.java
*
* Copyright (C) 2006-2024 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 2 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
*/
package com.wolfssl.wolfcrypt.test;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.Random;
import java.util.Iterator;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import javax.crypto.ShortBufferException;
import java.nio.ByteBuffer;
import javax.crypto.ShortBufferException;
import org.junit.Assume;
import org.junit.BeforeClass;
import org.junit.Test;
import com.wolfssl.wolfcrypt.Fips;
import com.wolfssl.wolfcrypt.AesGcm;
import com.wolfssl.wolfcrypt.FeatureDetect;
import com.wolfssl.wolfcrypt.NativeStruct;
import com.wolfssl.wolfcrypt.WolfCryptError;
import com.wolfssl.wolfcrypt.WolfCryptException;
public class AesGcmTest {
/*
* This is Test Case 16 from the document Galois/
* Counter Mode of Operation (GCM) by McGrew and
* Viega.
*/
byte[] p = new byte[] {
(byte)0xd9, (byte)0x31, (byte)0x32, (byte)0x25,
(byte)0xf8, (byte)0x84, (byte)0x06, (byte)0xe5,
(byte)0xa5, (byte)0x59, (byte)0x09, (byte)0xc5,
(byte)0xaf, (byte)0xf5, (byte)0x26, (byte)0x9a,
(byte)0x86, (byte)0xa7, (byte)0xa9, (byte)0x53,
(byte)0x15, (byte)0x34, (byte)0xf7, (byte)0xda,
(byte)0x2e, (byte)0x4c, (byte)0x30, (byte)0x3d,
(byte)0x8a, (byte)0x31, (byte)0x8a, (byte)0x72,
(byte)0x1c, (byte)0x3c, (byte)0x0c, (byte)0x95,
(byte)0x95, (byte)0x68, (byte)0x09, (byte)0x53,
(byte)0x2f, (byte)0xcf, (byte)0x0e, (byte)0x24,
(byte)0x49, (byte)0xa6, (byte)0xb5, (byte)0x25,
(byte)0xb1, (byte)0x6a, (byte)0xed, (byte)0xf5,
(byte)0xaa, (byte)0x0d, (byte)0xe6, (byte)0x57,
(byte)0xba, (byte)0x63, (byte)0x7b, (byte)0x39
};
byte[] a = new byte[] {
(byte)0xfe, (byte)0xed, (byte)0xfa, (byte)0xce,
(byte)0xde, (byte)0xad, (byte)0xbe, (byte)0xef,
(byte)0xfe, (byte)0xed, (byte)0xfa, (byte)0xce,
(byte)0xde, (byte)0xad, (byte)0xbe, (byte)0xef,
(byte)0xab, (byte)0xad, (byte)0xda, (byte)0xd2
};
/* AES-256 test vectors */
byte[] k1 = new byte[] {
(byte)0xfe, (byte)0xff, (byte)0xe9, (byte)0x92,
(byte)0x86, (byte)0x65, (byte)0x73, (byte)0x1c,
(byte)0x6d, (byte)0x6a, (byte)0x8f, (byte)0x94,
(byte)0x67, (byte)0x30, (byte)0x83, (byte)0x08,
(byte)0xfe, (byte)0xff, (byte)0xe9, (byte)0x92,
(byte)0x86, (byte)0x65, (byte)0x73, (byte)0x1c,
(byte)0x6d, (byte)0x6a, (byte)0x8f, (byte)0x94,
(byte)0x67, (byte)0x30, (byte)0x83, (byte)0x08
};
byte[] iv1 = new byte[] {
(byte)0xca, (byte)0xfe, (byte)0xba, (byte)0xbe,
(byte)0xfa, (byte)0xce, (byte)0xdb, (byte)0xad,
(byte)0xde, (byte)0xca, (byte)0xf8, (byte)0x88
};
byte[] c1 = new byte[] {
(byte)0x52, (byte)0x2d, (byte)0xc1, (byte)0xf0,
(byte)0x99, (byte)0x56, (byte)0x7d, (byte)0x07,
(byte)0xf4, (byte)0x7f, (byte)0x37, (byte)0xa3,
(byte)0x2a, (byte)0x84, (byte)0x42, (byte)0x7d,
(byte)0x64, (byte)0x3a, (byte)0x8c, (byte)0xdc,
(byte)0xbf, (byte)0xe5, (byte)0xc0, (byte)0xc9,
(byte)0x75, (byte)0x98, (byte)0xa2, (byte)0xbd,
(byte)0x25, (byte)0x55, (byte)0xd1, (byte)0xaa,
(byte)0x8c, (byte)0xb0, (byte)0x8e, (byte)0x48,
(byte)0x59, (byte)0x0d, (byte)0xbb, (byte)0x3d,
(byte)0xa7, (byte)0xb0, (byte)0x8b, (byte)0x10,
(byte)0x56, (byte)0x82, (byte)0x88, (byte)0x38,
(byte)0xc5, (byte)0xf6, (byte)0x1e, (byte)0x63,
(byte)0x93, (byte)0xba, (byte)0x7a, (byte)0x0a,
(byte)0xbc, (byte)0xc9, (byte)0xf6, (byte)0x62
};
byte[] t1 = new byte[] {
(byte)0x76, (byte)0xfc, (byte)0x6e, (byte)0xce,
(byte)0x0f, (byte)0x4e, (byte)0x17, (byte)0x68,
(byte)0xcd, (byte)0xdf, (byte)0x88, (byte)0x53,
(byte)0xbb, (byte)0x2d, (byte)0x55, (byte)0x1b
};
/* AES-192 test vectors */
/* FIPS, QAT and PIC32MZ HW Crypto only support 12-byte IV */
/* Test Case 12, uses same plaintext and AAD data. */
byte[] k2 = new byte[] {
(byte)0xfe, (byte)0xff, (byte)0xe9, (byte)0x92,
(byte)0x86, (byte)0x65, (byte)0x73, (byte)0x1c,
(byte)0x6d, (byte)0x6a, (byte)0x8f, (byte)0x94,
(byte)0x67, (byte)0x30, (byte)0x83, (byte)0x08,
(byte)0xfe, (byte)0xff, (byte)0xe9, (byte)0x92,
(byte)0x86, (byte)0x65, (byte)0x73, (byte)0x1c
};
byte[] iv2 = new byte[] {
(byte)0x93, (byte)0x13, (byte)0x22, (byte)0x5d,
(byte)0xf8, (byte)0x84, (byte)0x06, (byte)0xe5,
(byte)0x55, (byte)0x90, (byte)0x9c, (byte)0x5a,
(byte)0xff, (byte)0x52, (byte)0x69, (byte)0xaa,
(byte)0x6a, (byte)0x7a, (byte)0x95, (byte)0x38,
(byte)0x53, (byte)0x4f, (byte)0x7d, (byte)0xa1,
(byte)0xe4, (byte)0xc3, (byte)0x03, (byte)0xd2,
(byte)0xa3, (byte)0x18, (byte)0xa7, (byte)0x28,
(byte)0xc3, (byte)0xc0, (byte)0xc9, (byte)0x51,
(byte)0x56, (byte)0x80, (byte)0x95, (byte)0x39,
(byte)0xfc, (byte)0xf0, (byte)0xe2, (byte)0x42,
(byte)0x9a, (byte)0x6b, (byte)0x52, (byte)0x54,
(byte)0x16, (byte)0xae, (byte)0xdb, (byte)0xf5,
(byte)0xa0, (byte)0xde, (byte)0x6a, (byte)0x57,
(byte)0xa6, (byte)0x37, (byte)0xb3, (byte)0x9b
};
byte[] c2 = new byte[] {
(byte)0xd2, (byte)0x7e, (byte)0x88, (byte)0x68,
(byte)0x1c, (byte)0xe3, (byte)0x24, (byte)0x3c,
(byte)0x48, (byte)0x30, (byte)0x16, (byte)0x5a,
(byte)0x8f, (byte)0xdc, (byte)0xf9, (byte)0xff,
(byte)0x1d, (byte)0xe9, (byte)0xa1, (byte)0xd8,
(byte)0xe6, (byte)0xb4, (byte)0x47, (byte)0xef,
(byte)0x6e, (byte)0xf7, (byte)0xb7, (byte)0x98,
(byte)0x28, (byte)0x66, (byte)0x6e, (byte)0x45,
(byte)0x81, (byte)0xe7, (byte)0x90, (byte)0x12,
(byte)0xaf, (byte)0x34, (byte)0xdd, (byte)0xd9,
(byte)0xe2, (byte)0xf0, (byte)0x37, (byte)0x58,
(byte)0x9b, (byte)0x29, (byte)0x2d, (byte)0xb3,
(byte)0xe6, (byte)0x7c, (byte)0x03, (byte)0x67,
(byte)0x45, (byte)0xfa, (byte)0x22, (byte)0xe7,
(byte)0xe9, (byte)0xb7, (byte)0x37, (byte)0x3b
};
byte[] t2 = new byte[] {
(byte)0xdc, (byte)0xf5, (byte)0x66, (byte)0xff,
(byte)0x29, (byte)0x1c, (byte)0x25, (byte)0xbb,
(byte)0xb8, (byte)0x56, (byte)0x8f, (byte)0xc3,
(byte)0xd3, (byte)0x76, (byte)0xa6, (byte)0xd9
};
/* AES-128 test vectors */
/* The following is an interesting test case from the example
* FIPS test vectors for AES-GCM. IVlen = 1 byte */
byte[] p3 = new byte[] {
(byte)0x57, (byte)0xce, (byte)0x45, (byte)0x1f,
(byte)0xa5, (byte)0xe2, (byte)0x35, (byte)0xa5,
(byte)0x8e, (byte)0x1a, (byte)0xa2, (byte)0x3b,
(byte)0x77, (byte)0xcb, (byte)0xaf, (byte)0xe2
};
byte[] k3 = new byte[] {
(byte)0xbb, (byte)0x01, (byte)0xd7, (byte)0x03,
(byte)0x81, (byte)0x1c, (byte)0x10, (byte)0x1a,
(byte)0x35, (byte)0xe0, (byte)0xff, (byte)0xd2,
(byte)0x91, (byte)0xba, (byte)0xf2, (byte)0x4b
};
byte[] iv3 = new byte[] {
(byte)0xca
};
byte[] c3 = new byte[] {
(byte)0x6b, (byte)0x5f, (byte)0xb3, (byte)0x9d,
(byte)0xc1, (byte)0xc5, (byte)0x7a, (byte)0x4f,
(byte)0xf3, (byte)0x51, (byte)0x4d, (byte)0xc2,
(byte)0xd5, (byte)0xf0, (byte)0xd0, (byte)0x07
};
byte[] a3 = new byte[] {
(byte)0x40, (byte)0xfc, (byte)0xdc, (byte)0xd7,
(byte)0x4a, (byte)0xd7, (byte)0x8b, (byte)0xf1,
(byte)0x3e, (byte)0x7c, (byte)0x60, (byte)0x55,
(byte)0x50, (byte)0x51, (byte)0xdd, (byte)0x54
};
byte[] t3 = new byte[] {
(byte)0x06, (byte)0x90, (byte)0xed, (byte)0x01,
(byte)0x34, (byte)0xdd, (byte)0xc6, (byte)0x95,
(byte)0x31, (byte)0x2e, (byte)0x2a, (byte)0xf9,
(byte)0x57, (byte)0x7a, (byte)0x1e, (byte)0xa6
};
/**
* Make sure AesGcm class is available and not compiled out in native lib
*/
@BeforeClass
public static void checkAvailability() {
try {
new AesGcm();
} catch (WolfCryptException e) {
if (e.getError() == WolfCryptError.NOT_COMPILED_IN)
System.out.println("AES-GCM test skipped: " + e.getError());
Assume.assumeNoException(e);
}
}
/**
* AesGcm() constructor should initialize internal NativeStruct object
*/
@Test
public void constructorShouldInitializeNativeStruct() {
assertNotEquals(NativeStruct.NULL, new AesGcm().getNativeStruct());
}
/**
* Basic argument checks on AesGcm.setKey()
*/
@Test
public void testSetKey() throws WolfCryptException {
AesGcm aes = null;
/* Setting null key in constructor should fail */
try {
aes = new AesGcm(null);
fail("AesGcm(null) should throw exception");
} catch (WolfCryptException e) {
/* expected */
}
/* Test setting key after object creation */
/* 128-bit key */
if (FeatureDetect.Aes128Enabled()) {
aes = new AesGcm();
aes.setKey(k3);
aes.releaseNativeStruct();
}
/* 192-bit key */
if (FeatureDetect.Aes192Enabled()) {
aes = new AesGcm();
aes.setKey(k2);
aes.releaseNativeStruct();
}
/* 256-bit key */
if (FeatureDetect.Aes256Enabled()) {
aes = new AesGcm();
aes.setKey(k1);
aes.releaseNativeStruct();
}
}
@Test
public void testAesGcm128() throws WolfCryptException {
AesGcm enc = new AesGcm();
AesGcm dec = new AesGcm();
byte[] cipher = null;
byte[] plain = null;
byte[] tag = new byte[t3.length];
/* skip test if AES-128 is not compiled in native library */
if (!FeatureDetect.Aes128Enabled()) {
return;
}
/* encrypt before key setup should throw exception */
try {
enc.encrypt(null, null, null, null);
fail("encrypt() before setKey() should throw exception");
} catch (IllegalStateException e) {
/* expected */
}
/* decrypt before key setup should throw exception */
try {
dec.decrypt(null, null, null, null);
fail("decrypt() before setKey() should throw exception");
} catch (IllegalStateException e) {
/* expected */
}
enc.setKey(k3);
dec.setKey(k3);
/* success case */
cipher = enc.encrypt(p3, iv3, tag, a3);
assertArrayEquals(c3, cipher);
assertArrayEquals(t3, tag);
plain = dec.decrypt(cipher, iv3, tag, a3);
assertArrayEquals(p3, plain);
/* bad encrypt arguments: null input */
try {
enc.encrypt(null, iv3, tag, a3);
fail("encrypt() with null input should fail");
} catch (WolfCryptException e) {
/* expected */
}
/* bad encrypt arguments: null iv */
try {
enc.encrypt(p3, null, tag, a3);
fail("encrypt() with null IV should fail");
} catch (WolfCryptException e) {
/* expected */
}
/* bad encrypt arguments: null tag */
try {
enc.encrypt(p3, iv3, null, a3);
fail("encrypt() with null auth tag should fail");
} catch (WolfCryptException e) {
/* expected */
}
/* bad decrypt arguments: null input */
try {
enc.decrypt(null, iv3, tag, a3);
fail("decrypt() with null input should fail");
} catch (WolfCryptException e) {
/* expected */
}
/* bad decrypt arguments: null iv */
try {
enc.decrypt(cipher, null, tag, a3);
fail("decrypt() with null IV should fail");
} catch (WolfCryptException e) {
/* expected */
}
/* bad decrypt arguments: null tag */
try {
enc.decrypt(cipher, iv3, null, a3);
fail("decrypt() with null auth tag should fail");
} catch (WolfCryptException e) {
/* expected */
}
/* release native structs */
enc.releaseNativeStruct();
dec.releaseNativeStruct();
}
@Test
public void testAesGcm192() throws WolfCryptException {
AesGcm enc = new AesGcm();
AesGcm dec = new AesGcm();
byte[] cipher = null;
byte[] plain = null;
byte[] tag = new byte[t2.length];
/* skip test if AES-192 is not compiled in native library, or if
* using wolfCrypt FIPS since it only supports 12-byte IVs */
if (!FeatureDetect.Aes192Enabled() || Fips.enabled) {
return;
}
enc.setKey(k2);
dec.setKey(k2);
/* success case */
cipher = enc.encrypt(p, iv2, tag, a);
assertNotNull(cipher);
assertArrayEquals(c2, cipher);
assertArrayEquals(t2, tag);
plain = dec.decrypt(cipher, iv2, tag, a);
assertNotNull(plain);
assertArrayEquals(p, plain);
/* bad encrypt arguments: null input */
try {
enc.encrypt(null, iv2, tag, a);
fail("encrypt() with null input should fail");
} catch (WolfCryptException e) {
/* expected */
}
/* bad encrypt arguments: null iv */
try {
enc.encrypt(p, null, tag, a);
fail("encrypt() with null IV should fail");
} catch (WolfCryptException e) {
/* expected */
}
/* bad encrypt arguments: null tag */
try {
enc.encrypt(p, iv2, null, a);
fail("encrypt() with null auth tag should fail");
} catch (WolfCryptException e) {
/* expected */
}
/* bad decrypt arguments: null input */
try {
enc.decrypt(null, iv2, tag, a);
fail("decrypt() with null input should fail");
} catch (WolfCryptException e) {
/* expected */
}
/* bad decrypt arguments: null iv */
try {
enc.decrypt(cipher, null, tag, a);
fail("decrypt() with null IV should fail");
} catch (WolfCryptException e) {
/* expected */
}
/* bad decrypt arguments: null tag */
try {
enc.decrypt(cipher, iv2, null, a);
fail("decrypt() with null auth tag should fail");
} catch (WolfCryptException e) {
/* expected */
}
/* release native structs */
enc.releaseNativeStruct();
dec.releaseNativeStruct();
}
@Test
public void testAesGcm256() throws WolfCryptException {
AesGcm enc = new AesGcm();
AesGcm dec = new AesGcm();
byte[] cipher = null;
byte[] plain = null;
byte[] tag = new byte[t1.length];
/* skip test if AES-192 is not compiled in native library, or if
* using wolfCrypt FIPS since it only supports 12-byte IVs */
if (!FeatureDetect.Aes256Enabled()) {
return;
}
enc.setKey(k1);
dec.setKey(k1);
/* success case */
cipher = enc.encrypt(p, iv1, tag, a);
assertNotNull(cipher);
assertArrayEquals(c1, cipher);
assertArrayEquals(t1, tag);
plain = dec.decrypt(cipher, iv1, tag, a);
assertNotNull(plain);
assertArrayEquals(p, plain);
/* bad encrypt arguments: null input */
try {
enc.encrypt(null, iv1, tag, a);
fail("encrypt() with null input should fail");
} catch (WolfCryptException e) {
/* expected */
}
/* bad encrypt arguments: null iv */
try {
enc.encrypt(p, null, tag, a);
fail("encrypt() with null IV should fail");
} catch (WolfCryptException e) {
/* expected */
}
/* bad encrypt arguments: null tag */
try {
enc.encrypt(p, iv1, null, a);
fail("encrypt() with null auth tag should fail");
} catch (WolfCryptException e) {
/* expected */
}
/* bad decrypt arguments: null input */
try {
enc.decrypt(null, iv1, tag, a);
fail("decrypt() with null input should fail");
} catch (WolfCryptException e) {
/* expected */
}
/* bad decrypt arguments: null iv */
try {
enc.decrypt(cipher, null, tag, a);
fail("decrypt() with null IV should fail");
} catch (WolfCryptException e) {
/* expected */
}
/* bad decrypt arguments: null tag */
try {
enc.decrypt(cipher, iv1, null, a);
fail("decrypt() with null auth tag should fail");
} catch (WolfCryptException e) {
/* expected */
}
/* release native structs */
enc.releaseNativeStruct();
dec.releaseNativeStruct();
}
@Test
public void testReleaseAndReinitObjectAes128() throws WolfCryptException {
byte[] cipher = null;
byte[] plain = null;
byte[] tag = new byte[t3.length];
/* skip test if AES-128 is not compiled in native library */
if (!FeatureDetect.Aes128Enabled()) {
return;
}
AesGcm enc = new AesGcm(k3);
AesGcm dec = new AesGcm(k3);
cipher = enc.encrypt(p3, iv3, tag, a3);
assertArrayEquals(c3, cipher);
assertArrayEquals(t3, tag);
plain = dec.decrypt(cipher, iv3, tag, a3);
assertArrayEquals(p3, plain);
/* free objects */
enc.releaseNativeStruct();
dec.releaseNativeStruct();
/* try to re-init and re-use them */
enc = new AesGcm(k3);
dec = new AesGcm(k3);
cipher = enc.encrypt(p3, iv3, tag, a3);
assertArrayEquals(c3, cipher);
assertArrayEquals(t3, tag);
plain = dec.decrypt(cipher, iv3, tag, a3);
assertArrayEquals(p3, plain);
/* free again */
enc.releaseNativeStruct();
dec.releaseNativeStruct();
}
@Test
public void testReleaseAndReinitObjectAes192() throws WolfCryptException {
byte[] cipher = null;
byte[] plain = null;
byte[] tag = new byte[t2.length];
/* skip test if AES-192 is not compiled in native library, or if
* using wolfCrypt FIPS since it only supports 12-byte IVs */
if (!FeatureDetect.Aes192Enabled() || Fips.enabled) {
return;
}
AesGcm enc = new AesGcm(k2);
AesGcm dec = new AesGcm(k2);
cipher = enc.encrypt(p, iv2, tag, a);
assertNotNull(cipher);
assertArrayEquals(c2, cipher);
assertArrayEquals(t2, tag);
plain = dec.decrypt(cipher, iv2, tag, a);
assertNotNull(plain);
assertArrayEquals(p, plain);
/* free objects */
enc.releaseNativeStruct();
dec.releaseNativeStruct();
/* try to re-init and re-use them */
enc = new AesGcm(k2);
dec = new AesGcm(k2);
cipher = enc.encrypt(p, iv2, tag, a);
assertNotNull(cipher);
assertArrayEquals(c2, cipher);
assertArrayEquals(t2, tag);
plain = dec.decrypt(cipher, iv2, tag, a);
assertNotNull(plain);
assertArrayEquals(p, plain);
/* free again */
enc.releaseNativeStruct();
dec.releaseNativeStruct();
}
@Test
public void testReleaseAndReinitObjectAes256() throws WolfCryptException {
byte[] cipher = null;
byte[] plain = null;
byte[] tag = new byte[t1.length];
/* skip test if AES-256 is not compiled in native library */
if (!FeatureDetect.Aes256Enabled()) {
return;
}
AesGcm enc = new AesGcm(k1);
AesGcm dec = new AesGcm(k1);
cipher = enc.encrypt(p, iv1, tag, a);
assertNotNull(cipher);
assertArrayEquals(c1, cipher);
assertArrayEquals(t1, tag);
plain = dec.decrypt(cipher, iv1, tag, a);
assertNotNull(plain);
assertArrayEquals(p, plain);
/* free objects */
enc.releaseNativeStruct();
dec.releaseNativeStruct();
/* try to re-init and re-use them */
enc = new AesGcm(k1);
dec = new AesGcm(k1);
cipher = enc.encrypt(p, iv1, tag, a);
assertNotNull(cipher);
assertArrayEquals(c1, cipher);
assertArrayEquals(t1, tag);
plain = dec.decrypt(cipher, iv1, tag, a);
assertNotNull(plain);
assertArrayEquals(p, plain);
/* free again */
enc.releaseNativeStruct();
dec.releaseNativeStruct();
}
@Test
public void testReuseObjectAes128() throws WolfCryptException {
byte[] cipher = null;
byte[] plain = null;
byte[] tag = new byte[t3.length];
/* skip test if AES-128 is not compiled in native library */
if (!FeatureDetect.Aes128Enabled()) {
return;
}
AesGcm enc = new AesGcm(k3);
AesGcm dec = new AesGcm(k3);
cipher = enc.encrypt(p3, iv3, tag, a3);
assertArrayEquals(c3, cipher);
assertArrayEquals(t3, tag);
plain = dec.decrypt(cipher, iv3, tag, a3);
assertArrayEquals(p3, plain);
/* now, try to reuse existing enc/dec objects */
cipher = enc.encrypt(p3, iv3, tag, a3);
assertArrayEquals(c3, cipher);
assertArrayEquals(t3, tag);
plain = dec.decrypt(cipher, iv3, tag, a3);
assertArrayEquals(p3, plain);
/* free native structs */
enc.releaseNativeStruct();
dec.releaseNativeStruct();
}
@Test
public void testReuseObjectAes192() throws WolfCryptException {
byte[] cipher = null;
byte[] plain = null;
byte[] tag = new byte[t2.length];
/* skip test if AES-192 is not compiled in native library, or if
* using wolfCrypt FIPS since it only supports 12-byte IVs */
if (!FeatureDetect.Aes192Enabled() || Fips.enabled) {
return;
}
AesGcm enc = new AesGcm(k2);
AesGcm dec = new AesGcm(k2);
cipher = enc.encrypt(p, iv2, tag, a);
assertNotNull(cipher);
assertArrayEquals(c2, cipher);
assertArrayEquals(t2, tag);
plain = dec.decrypt(cipher, iv2, tag, a);
assertNotNull(plain);
assertArrayEquals(p, plain);
/* now, try to reuse existing enc/dec objects */
cipher = enc.encrypt(p, iv2, tag, a);
assertNotNull(cipher);
assertArrayEquals(c2, cipher);
assertArrayEquals(t2, tag);
plain = dec.decrypt(cipher, iv2, tag, a);
assertNotNull(plain);
assertArrayEquals(p, plain);
/* free native structs */
enc.releaseNativeStruct();
dec.releaseNativeStruct();
}
@Test
public void testReuseObjectAes256() throws WolfCryptException {
byte[] cipher = null;
byte[] plain = null;
byte[] tag = new byte[t1.length];
/* skip test if AES-256 is not compiled in native library */
if (!FeatureDetect.Aes256Enabled()) {
return;
}
AesGcm enc = new AesGcm(k1);
AesGcm dec = new AesGcm(k1);
cipher = enc.encrypt(p, iv1, tag, a);
assertNotNull(cipher);
assertArrayEquals(c1, cipher);
assertArrayEquals(t1, tag);
plain = dec.decrypt(cipher, iv1, tag, a);
assertNotNull(plain);
assertArrayEquals(p, plain);
/* now, try to reuse existing enc/dec objects */
cipher = enc.encrypt(p, iv1, tag, a);
assertNotNull(cipher);
assertArrayEquals(c1, cipher);
assertArrayEquals(t1, tag);
plain = dec.decrypt(cipher, iv1, tag, a);
assertNotNull(plain);
assertArrayEquals(p, plain);
/* free native structs */
enc.releaseNativeStruct();
dec.releaseNativeStruct();
}
@Test
public void testThreadedAes128() throws InterruptedException {
int numThreads = 20;
ExecutorService service = Executors.newFixedThreadPool(numThreads);
final CountDownLatch latch = new CountDownLatch(numThreads);
final LinkedBlockingQueue<Integer> results = new LinkedBlockingQueue<>();
final byte[] rand2kBuf = new byte[2048];
/* skip test if AES-128 is not compiled in native library */
if (!FeatureDetect.Aes128Enabled()) {
return;
}
/* fill large input buffer with random bytes */
new Random().nextBytes(rand2kBuf);
/* encrypt / decrypt input data, make sure decrypted matches original */
for (int i = 0; i < numThreads; i++) {
service.submit(new Runnable() {
@Override public void run() {
int ret = 0;
AesGcm enc = new AesGcm(k3);
AesGcm dec = new AesGcm(k3);
byte[] cipher = new byte[2048];
byte[] plain = new byte[2048];
byte[] tag = new byte[t3.length];
try {
cipher = enc.encrypt(rand2kBuf, iv3, tag, null);
plain = dec.decrypt(cipher, iv3, tag, null);
/* make sure decrypted is same as input */
if (Arrays.equals(rand2kBuf, plain)) {
results.add(0);
}
else {
/* not equal, error case */
results.add(1);
}
} catch (Exception e) {
e.printStackTrace();
results.add(1);
} finally {
enc.releaseNativeStruct();
dec.releaseNativeStruct();
latch.countDown();
}
}
});
}
/* wait for all threads to complete */
latch.await();
/* compare all digests, all should be the same across threads */
Iterator<Integer> listIterator = results.iterator();
while (listIterator.hasNext()) {
Integer cur = listIterator.next();
if (cur == 1) {
fail("Threading error in AES-GMC-128 thread test");
}
}
}
@Test
public void testThreadedAes192() throws InterruptedException {
int numThreads = 20;
ExecutorService service = Executors.newFixedThreadPool(numThreads);
final CountDownLatch latch = new CountDownLatch(numThreads);
final LinkedBlockingQueue<Integer> results = new LinkedBlockingQueue<>();
final byte[] rand2kBuf = new byte[2048];
/* skip test if AES-192 is not compiled in native library, or if
* using wolfCrypt FIPS since it only supports 12-byte IVs */
if (!FeatureDetect.Aes192Enabled() || Fips.enabled) {
return;
}
/* fill large input buffer with random bytes */
new Random().nextBytes(rand2kBuf);
/* encrypt / decrypt input data, make sure decrypted matches original */
for (int i = 0; i < numThreads; i++) {
service.submit(new Runnable() {
@Override public void run() {
int ret = 0;
AesGcm enc = new AesGcm(k2);
AesGcm dec = new AesGcm(k2);
byte[] cipher = new byte[2048];
byte[] plain = new byte[2048];
byte[] tag = new byte[t2.length];
try {
cipher = enc.encrypt(rand2kBuf, iv2, tag, null);
plain = dec.decrypt(cipher, iv2, tag, null);
/* make sure decrypted is same as input */
if (Arrays.equals(rand2kBuf, plain)) {
results.add(0);
}
else {
/* not equal, error case */
results.add(1);
}
} catch (Exception e) {
e.printStackTrace();
results.add(1);
} finally {
enc.releaseNativeStruct();
dec.releaseNativeStruct();
latch.countDown();
}
}
});
}
/* wait for all threads to complete */
latch.await();
/* compare all digests, all should be the same across threads */
Iterator<Integer> listIterator = results.iterator();
while (listIterator.hasNext()) {
Integer cur = listIterator.next();
if (cur == 1) {
fail("Threading error in AES-GCM-192 thread test");
}
}
}
@Test
public void testThreadedAes256() throws InterruptedException {
int numThreads = 20;
ExecutorService service = Executors.newFixedThreadPool(numThreads);
final CountDownLatch latch = new CountDownLatch(numThreads);
final LinkedBlockingQueue<Integer> results = new LinkedBlockingQueue<>();
final byte[] rand2kBuf = new byte[2048];
/* skip test if AES-256 is not compiled in native library */
if (!FeatureDetect.Aes256Enabled()) {
return;
}
/* fill large input buffer with random bytes */
new Random().nextBytes(rand2kBuf);
/* encrypt / decrypt input data, make sure decrypted matches original */
for (int i = 0; i < numThreads; i++) {
service.submit(new Runnable() {
@Override public void run() {
int ret = 0;
AesGcm enc = new AesGcm(k1);
AesGcm dec = new AesGcm(k1);
byte[] cipher = new byte[2048];
byte[] plain = new byte[2048];
byte[] tag = new byte[t1.length];
try {
cipher = enc.encrypt(rand2kBuf, iv1, tag, null);
plain = dec.decrypt(cipher, iv1, tag, null);
/* make sure decrypted is same as input */
if (Arrays.equals(rand2kBuf, plain)) {
results.add(0);
}
else {
/* not equal, error case */
results.add(1);
}
} catch (Exception e) {
e.printStackTrace();
results.add(1);
} finally {
enc.releaseNativeStruct();
dec.releaseNativeStruct();
latch.countDown();
}
}
});
}
/* wait for all threads to complete */
latch.await();
/* compare all digests, all should be the same across threads */
Iterator<Integer> listIterator = results.iterator();
while (listIterator.hasNext()) {
Integer cur = listIterator.next();
if (cur == 1) {
fail("Threading error in AES-GCM-256 thread test");
}
}
}
}

View File

@ -28,6 +28,7 @@ import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({
AesTest.class,
AesGcmTest.class,
Des3Test.class,
ChachaTest.class,
Md5Test.class,