Merge pull request #255 from cconlon/fenrirAug11
Fenrir fixes for JNI digest bounds checks and CertManager callback lifecyclepull/252/merge
commit
216df069e0
|
|
@ -142,6 +142,36 @@ project located in the wolfcrypt-jni/IDE directory.
|
|||
This will ask for permissions to access the certificates in the /sdcard/
|
||||
directory and then print out the server certificate information on success.
|
||||
|
||||
## Gradle Dependency Verification
|
||||
|
||||
This project pins SHA-256 checksums for all remotely downloaded Gradle build
|
||||
dependencies in `gradle/verification-metadata.xml`. Gradle enforces these
|
||||
automatically on every build because the file is present. If an artifact
|
||||
downloaded from a repository does not match its pinned checksum, the build
|
||||
fails. The Gradle distribution itself is separately pinned via
|
||||
`distributionSha256Sum` in `gradle/wrapper/gradle-wrapper.properties`.
|
||||
|
||||
When changing the Android Gradle Plugin version or any dependency version, the
|
||||
metadata file must be regenerated from a trusted network environment:
|
||||
|
||||
```
|
||||
cd IDE/Android
|
||||
./gradlew -I gradle/update-verification-metadata.gradle \
|
||||
--write-verification-metadata sha256 help
|
||||
```
|
||||
|
||||
The `update-verification-metadata.gradle` init script captures artifacts that
|
||||
the Android Gradle Plugin only resolves while tasks execute (AAPT2 and the
|
||||
Unified Test Platform used by instrumented tests). These would otherwise be
|
||||
missing from the regenerated file and would fail verification during CI builds.
|
||||
The version coordinates inside that init script must be updated to match the
|
||||
new Android Gradle Plugin version, as described in the comments at the top of
|
||||
the script.
|
||||
|
||||
Review the diff of `gradle/verification-metadata.xml` before committing an
|
||||
update, and confirm new checksums come from a trusted build of the upstream
|
||||
artifacts.
|
||||
|
||||
## Support
|
||||
|
||||
Please contact wolfSSL support at support@wolfssl.com with any questions or
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* update-verification-metadata.gradle
|
||||
*
|
||||
* Helper init script used when regenerating the Gradle dependency
|
||||
* verification metadata file (gradle/verification-metadata.xml).
|
||||
*
|
||||
* The Android Gradle Plugin resolves some artifacts only while a task is
|
||||
* executing, not at configuration time. Those artifacts are missed by the
|
||||
* normal bootstrap command and would fail dependency verification later, for
|
||||
* example AAPT2 during assemble tasks and the Unified Test Platform (UTP)
|
||||
* stack during connectedDebugAndroidTest. This script declares those artifacts
|
||||
* in regular configurations so the bootstrap records them. All three AAPT2
|
||||
* platform classifiers are captured so Linux CI, macOS, and Windows developer
|
||||
* machines all pass verification.
|
||||
*
|
||||
* Usage, from the IDE/Android directory:
|
||||
*
|
||||
* ./gradlew -I gradle/update-verification-metadata.gradle \
|
||||
* --write-verification-metadata sha256 help
|
||||
*
|
||||
* Review the resulting gradle/verification-metadata.xml diff before
|
||||
* committing it.
|
||||
*
|
||||
* When the Android Gradle Plugin version changes, update the
|
||||
* versions below to match the new plugin:
|
||||
*
|
||||
* - aapt2: version is "<AGP version>-<build number>". Read it
|
||||
* from aapt2_version.properties inside the AGP jar:
|
||||
* unzip -p <gradle-X.Y.Z.jar> \
|
||||
* com/android/build/gradle/internal/res/aapt2_version.properties
|
||||
* - com.android.tools.utp artifacts: version is the AGP version
|
||||
* plus 23 in the major number (AGP 8.3.1 -> 31.3.1).
|
||||
* - com.google.testing.platform artifacts: version comes from the
|
||||
* UtpDependency class in the AGP jar. It also appears as the
|
||||
* core-proto version pulled in by the normal bootstrap.
|
||||
*/
|
||||
gradle.allprojects { project ->
|
||||
if (project.name != 'app') {
|
||||
return
|
||||
}
|
||||
def coords = [
|
||||
'com.android.tools.build:aapt2:8.3.1-10880808:linux',
|
||||
'com.android.tools.build:aapt2:8.3.1-10880808:osx',
|
||||
'com.android.tools.build:aapt2:8.3.1-10880808:windows',
|
||||
'com.google.testing.platform:launcher:0.0.9-alpha02',
|
||||
'com.google.testing.platform:core:0.0.9-alpha02',
|
||||
'com.google.testing.platform:android-driver-instrumentation:' +
|
||||
'0.0.9-alpha02',
|
||||
'com.google.testing.platform:android-test-plugin:0.0.9-alpha02',
|
||||
'com.android.tools.utp:android-device-provider-ddmlib:31.3.1',
|
||||
'com.android.tools.utp:android-device-provider-gradle:31.3.1',
|
||||
'com.android.tools.utp:android-test-plugin-host-device-info:31.3.1',
|
||||
'com.android.tools.utp:android-test-plugin-host-additional-' +
|
||||
'test-output:31.3.1',
|
||||
'com.android.tools.utp:android-test-plugin-host-apk-installer:31.3.1',
|
||||
'com.android.tools.utp:android-test-plugin-host-coverage:31.3.1',
|
||||
'com.android.tools.utp:android-test-plugin-host-logcat:31.3.1',
|
||||
'com.android.tools.utp:android-test-plugin-host-emulator-' +
|
||||
'control:31.3.1',
|
||||
'com.android.tools.utp:android-test-plugin-host-retention:31.3.1',
|
||||
'com.android.tools.utp:android-test-plugin-result-listener-' +
|
||||
'gradle:31.3.1',
|
||||
]
|
||||
coords.eachWithIndex { coord, idx ->
|
||||
def cfg = project.configurations.create("verifyMetadataCapture${idx}")
|
||||
cfg.canBeConsumed = false
|
||||
cfg.transitive = true
|
||||
project.dependencies.add(cfg.name, coord)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -190,8 +190,8 @@ Java_com_wolfssl_wolfcrypt_Md5_native_1update_1internal___3BII(
|
|||
data = getByteArray(env, data_buffer);
|
||||
dataSz = getByteArrayLength(env, data_buffer);
|
||||
|
||||
if (md5 == NULL || data == NULL ||
|
||||
((word32)(offset + len) > dataSz)) {
|
||||
if (md5 == NULL || data == NULL || offset < 0 || len < 0 ||
|
||||
(((jlong)offset + (jlong)len) > (jlong)dataSz)) {
|
||||
ret = BAD_FUNC_ARG;
|
||||
} else {
|
||||
ret = wc_Md5Update(md5, data + offset, len);
|
||||
|
|
|
|||
|
|
@ -319,7 +319,7 @@ Java_com_wolfssl_wolfcrypt_Sha_native_1update_1internal___3BII(
|
|||
dataSz = getByteArrayLength(env, data_buffer);
|
||||
|
||||
if (sha == NULL || data == NULL || offset < 0 || len < 0 ||
|
||||
(word32)(offset + len) > dataSz) {
|
||||
((jlong)offset + (jlong)len) > (jlong)dataSz) {
|
||||
ret = BAD_FUNC_ARG;
|
||||
}
|
||||
else {
|
||||
|
|
@ -536,7 +536,7 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha224_native_1update_1interna
|
|||
dataSz = getByteArrayLength(env, data_buffer);
|
||||
|
||||
if (sha == NULL || data == NULL || offset < 0 || len < 0 ||
|
||||
(word32)(offset + len) > dataSz) {
|
||||
((jlong)offset + (jlong)len) > (jlong)dataSz) {
|
||||
ret = BAD_FUNC_ARG;
|
||||
}
|
||||
else {
|
||||
|
|
@ -763,7 +763,7 @@ Java_com_wolfssl_wolfcrypt_Sha256_native_1update_1internal___3BII(
|
|||
dataSz = getByteArrayLength(env, data_buffer);
|
||||
|
||||
if (sha == NULL || data == NULL || offset < 0 || len < 0 ||
|
||||
(word32)(offset + len) > dataSz) {
|
||||
((jlong)offset + (jlong)len) > (jlong)dataSz) {
|
||||
ret = BAD_FUNC_ARG;
|
||||
}
|
||||
else {
|
||||
|
|
@ -969,7 +969,7 @@ Java_com_wolfssl_wolfcrypt_Sha384_native_1update_1internal___3BII(
|
|||
dataSz = getByteArrayLength(env, data_buffer);
|
||||
|
||||
if (sha == NULL || data == NULL || offset < 0 || len < 0 ||
|
||||
(word32)(offset + len) > dataSz) {
|
||||
((jlong)offset + (jlong)len) > (jlong)dataSz) {
|
||||
ret = BAD_FUNC_ARG;
|
||||
}
|
||||
else {
|
||||
|
|
@ -1176,7 +1176,7 @@ Java_com_wolfssl_wolfcrypt_Sha512_native_1update_1internal___3BII(
|
|||
dataSz = getByteArrayLength(env, data_buffer);
|
||||
|
||||
if (sha == NULL || data == NULL || offset < 0 || len < 0 ||
|
||||
(word32)(offset + len) > dataSz) {
|
||||
((jlong)offset + (jlong)len) > (jlong)dataSz) {
|
||||
ret = BAD_FUNC_ARG;
|
||||
}
|
||||
else {
|
||||
|
|
@ -1306,16 +1306,16 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha3_native_1init_1internal
|
|||
if (ret == 0) {
|
||||
switch (hashType) {
|
||||
case WC_HASH_TYPE_SHA3_224:
|
||||
ret = wc_InitSha3_224(sha, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
ret = wc_InitSha3_224(sha, NULL, INVALID_DEVID);
|
||||
break;
|
||||
case WC_HASH_TYPE_SHA3_256:
|
||||
ret = wc_InitSha3_256(sha, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
ret = wc_InitSha3_256(sha, NULL, INVALID_DEVID);
|
||||
break;
|
||||
case WC_HASH_TYPE_SHA3_384:
|
||||
ret = wc_InitSha3_384(sha, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
ret = wc_InitSha3_384(sha, NULL, INVALID_DEVID);
|
||||
break;
|
||||
case WC_HASH_TYPE_SHA3_512:
|
||||
ret = wc_InitSha3_512(sha, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
ret = wc_InitSha3_512(sha, NULL, INVALID_DEVID);
|
||||
break;
|
||||
default:
|
||||
ret = BAD_FUNC_ARG;
|
||||
|
|
@ -1471,7 +1471,7 @@ JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_Sha3_native_1update_1internal_
|
|||
dataSz = getByteArrayLength(env, data_buffer);
|
||||
|
||||
if (sha == NULL || data == NULL || offset < 0 || len < 0 ||
|
||||
(word32)(offset + len) > dataSz) {
|
||||
((jlong)offset + (jlong)len) > (jlong)dataSz) {
|
||||
ret = BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -138,6 +138,29 @@ static int addCallbackCtx(WOLFSSL_CERT_MANAGER* cm, VerifyCallbackCtx* ctx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Swap newCtx into the CallbackNode matching cm, in place.
|
||||
*
|
||||
* Caller must hold g_callbackMutex.
|
||||
*
|
||||
* Returns the displaced ctx, or NULL if no node matched. */
|
||||
static VerifyCallbackCtx* swapCallbackCtx(WOLFSSL_CERT_MANAGER* cm,
|
||||
VerifyCallbackCtx* newCtx)
|
||||
{
|
||||
CallbackNode* node = g_callbackList;
|
||||
VerifyCallbackCtx* oldCtx = NULL;
|
||||
|
||||
while (node != NULL) {
|
||||
if (node->cm == cm) {
|
||||
oldCtx = node->ctx;
|
||||
node->ctx = newCtx;
|
||||
return oldCtx;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Remove CallbackNode from global g_callbackList.
|
||||
*
|
||||
* Caller must hold g_callbackMutex. */
|
||||
|
|
@ -170,6 +193,35 @@ static void removeCallbackCtx(WOLFSSL_CERT_MANAGER* cm)
|
|||
}
|
||||
}
|
||||
|
||||
/* Remove and free the callback context registered for cm, deleting JNI global
|
||||
* reference. Used by CertManagerClearVerify and CertManagerFree.
|
||||
*
|
||||
* Returns 0 on success, BAD_MUTEX_E if the list mutex cannot be locked. */
|
||||
static int freeCallbackCtx(JNIEnv* env, WOLFSSL_CERT_MANAGER* cm)
|
||||
{
|
||||
VerifyCallbackCtx* ctx = NULL;
|
||||
|
||||
if (wc_LockMutex(&g_callbackMutex) != 0) {
|
||||
return BAD_MUTEX_E;
|
||||
}
|
||||
|
||||
ctx = findCallbackCtx(cm);
|
||||
if (ctx != NULL) {
|
||||
removeCallbackCtx(cm);
|
||||
}
|
||||
|
||||
wc_UnLockMutex(&g_callbackMutex);
|
||||
|
||||
if (ctx != NULL) {
|
||||
if (env != NULL && ctx->callback != NULL) {
|
||||
(*env)->DeleteGlobalRef(env, ctx->callback);
|
||||
}
|
||||
XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Extract cert DER bytes at given depth from WOLFSSL_X509_STORE_CTX into
|
||||
* a new jbyteArray. Returns NULL if cert not available at depth. */
|
||||
static jbyteArray getCertDerAtDepth(JNIEnv* jenv,
|
||||
|
|
@ -384,10 +436,25 @@ JNIEXPORT jlong JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManage
|
|||
JNIEXPORT void JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerFree
|
||||
(JNIEnv* env, jclass jcl, jlong cmPtr)
|
||||
{
|
||||
(void)env;
|
||||
WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)(uintptr_t)cmPtr;
|
||||
(void)jcl;
|
||||
|
||||
wolfSSL_CertManagerFree((WOLFSSL_CERT_MANAGER*)(uintptr_t)cmPtr);
|
||||
if (cm == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef NO_WOLFSSL_CM_VERIFY
|
||||
/* Reset cm callback to NULL in wolfSSL */
|
||||
wolfSSL_CertManagerSetVerify(cm, NULL);
|
||||
#endif
|
||||
|
||||
/* Remove callback context for this manager so the list node and JNI
|
||||
* global reference do not outlive it */
|
||||
if (freeCallbackCtx(env, cm) != 0) {
|
||||
LogStr("CertManagerFree: mutex lock failed, ctx may leak\n");
|
||||
}
|
||||
|
||||
wolfSSL_CertManagerFree(cm);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerLoadCA
|
||||
|
|
@ -915,6 +982,7 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManager
|
|||
int ret = 0;
|
||||
WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)(uintptr_t)cmPtr;
|
||||
VerifyCallbackCtx* ctx = NULL;
|
||||
VerifyCallbackCtx* oldCtx = NULL;
|
||||
JavaVM* jvm = NULL;
|
||||
(void)jcl;
|
||||
|
||||
|
|
@ -943,18 +1011,31 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManager
|
|||
}
|
||||
ctx->jvm = jvm;
|
||||
|
||||
/* Add context to global list */
|
||||
/* Swap into any existing entry for this cm so repeated SetVerify does
|
||||
* not leak the prior context */
|
||||
if (wc_LockMutex(&g_callbackMutex) != 0) {
|
||||
(*env)->DeleteGlobalRef(env, ctx->callback);
|
||||
XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
return BAD_MUTEX_E;
|
||||
}
|
||||
|
||||
ret = addCallbackCtx(cm, ctx);
|
||||
oldCtx = swapCallbackCtx(cm, ctx);
|
||||
if (oldCtx == NULL) {
|
||||
/* No existing entry, add new node */
|
||||
ret = addCallbackCtx(cm, ctx);
|
||||
}
|
||||
|
||||
wc_UnLockMutex(&g_callbackMutex);
|
||||
|
||||
if (oldCtx != NULL) {
|
||||
if (oldCtx->callback != NULL) {
|
||||
(*env)->DeleteGlobalRef(env, oldCtx->callback);
|
||||
}
|
||||
XFREE(oldCtx, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
/* Registration failed, no callback was registered */
|
||||
(*env)->DeleteGlobalRef(env, ctx->callback);
|
||||
XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
return ret;
|
||||
|
|
@ -979,7 +1060,6 @@ Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerClearVerify
|
|||
{
|
||||
#ifndef NO_WOLFSSL_CM_VERIFY
|
||||
WOLFSSL_CERT_MANAGER* cm = (WOLFSSL_CERT_MANAGER*)(uintptr_t)cmPtr;
|
||||
VerifyCallbackCtx* ctx = NULL;
|
||||
(void)jcl;
|
||||
|
||||
if (env == NULL || cm == NULL) {
|
||||
|
|
@ -989,30 +1069,10 @@ Java_com_wolfssl_wolfcrypt_WolfSSLCertManager_CertManagerClearVerify
|
|||
/* Clear callback in wolfSSL first */
|
||||
wolfSSL_CertManagerSetVerify(cm, NULL);
|
||||
|
||||
/* Lock mutex and find/remove callback context */
|
||||
if (wc_LockMutex(&g_callbackMutex) != 0) {
|
||||
if (freeCallbackCtx(env, cm) != 0) {
|
||||
return BAD_MUTEX_E;
|
||||
}
|
||||
|
||||
ctx = findCallbackCtx(cm);
|
||||
if (ctx != NULL) {
|
||||
/* Remove from global list */
|
||||
removeCallbackCtx(cm);
|
||||
}
|
||||
|
||||
wc_UnLockMutex(&g_callbackMutex);
|
||||
|
||||
/* Free context if it existed */
|
||||
if (ctx != NULL) {
|
||||
/* Delete global reference to callback object */
|
||||
if (ctx->callback != NULL) {
|
||||
(*env)->DeleteGlobalRef(env, ctx->callback);
|
||||
}
|
||||
|
||||
/* Free context structure */
|
||||
XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
}
|
||||
|
||||
return WOLFSSL_SUCCESS;
|
||||
#else
|
||||
(void)env;
|
||||
|
|
|
|||
|
|
@ -169,9 +169,8 @@ public abstract class MessageDigest extends NativeStruct {
|
|||
|
||||
checkStateAndInitialize();
|
||||
|
||||
if (((offset + len) > data.length) || offset < 0 || len < 0) {
|
||||
throw new RuntimeException(
|
||||
"Invalid offset or length");
|
||||
if (offset < 0 || len < 0 || len > (data.length - offset)) {
|
||||
throw new RuntimeException("Invalid offset or length");
|
||||
}
|
||||
|
||||
native_update(data, offset, len);
|
||||
|
|
|
|||
|
|
@ -807,6 +807,7 @@ public class WolfSSLCertManager extends WolfObject {
|
|||
/* free Java resources */
|
||||
this.active = false;
|
||||
this.cmPtr = 0;
|
||||
this.verifyCallback = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,6 +75,23 @@ public class Md5Test {
|
|||
assertEquals(NativeStruct.NULL, new Md5().getNativeStruct());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateWithWrappedOffsetAndLenShouldThrow() {
|
||||
Md5 md5 = new Md5();
|
||||
byte[] data = new byte[8];
|
||||
|
||||
/* offset + len wraps int arithmetic, update must reject it */
|
||||
try {
|
||||
md5.update(data, 1, Integer.MAX_VALUE);
|
||||
fail("update() should have thrown for wrapped offset + len");
|
||||
} catch (IllegalStateException e) {
|
||||
/* init failure is not the bounds rejection */
|
||||
throw e;
|
||||
} catch (RuntimeException e) {
|
||||
/* expected */
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashShouldMatchUsingByteBuffer() throws ShortBufferException {
|
||||
String[] dataVector = new String[] {
|
||||
|
|
|
|||
|
|
@ -85,6 +85,23 @@ public class Sha224Test {
|
|||
assertEquals(NativeStruct.NULL, new Sha224().getNativeStruct());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateWithWrappedOffsetAndLenShouldThrow() {
|
||||
Sha224 sha = new Sha224();
|
||||
byte[] data = new byte[8];
|
||||
|
||||
/* offset + len wraps int arithmetic, update must reject it */
|
||||
try {
|
||||
sha.update(data, 1, Integer.MAX_VALUE);
|
||||
fail("update() should have thrown for wrapped offset + len");
|
||||
} catch (IllegalStateException e) {
|
||||
/* init failure is not the bounds rejection */
|
||||
throw e;
|
||||
} catch (RuntimeException e) {
|
||||
/* expected */
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashShouldMatchUsingByteBuffer() throws ShortBufferException {
|
||||
|
||||
|
|
|
|||
|
|
@ -75,6 +75,23 @@ public class Sha256Test {
|
|||
assertEquals(NativeStruct.NULL, new Sha256().getNativeStruct());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateWithWrappedOffsetAndLenShouldThrow() {
|
||||
Sha256 sha = new Sha256();
|
||||
byte[] data = new byte[8];
|
||||
|
||||
/* offset + len wraps int arithmetic, update must reject it */
|
||||
try {
|
||||
sha.update(data, 1, Integer.MAX_VALUE);
|
||||
fail("update() should have thrown for wrapped offset + len");
|
||||
} catch (IllegalStateException e) {
|
||||
/* init failure is not the bounds rejection */
|
||||
throw e;
|
||||
} catch (RuntimeException e) {
|
||||
/* expected */
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashShouldMatchUsingByteBuffer() throws ShortBufferException {
|
||||
String[] dataVector = new String[] {
|
||||
|
|
|
|||
|
|
@ -75,6 +75,23 @@ public class Sha384Test {
|
|||
assertEquals(NativeStruct.NULL, new Sha384().getNativeStruct());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateWithWrappedOffsetAndLenShouldThrow() {
|
||||
Sha384 sha = new Sha384();
|
||||
byte[] data = new byte[8];
|
||||
|
||||
/* offset + len wraps int arithmetic, update must reject it */
|
||||
try {
|
||||
sha.update(data, 1, Integer.MAX_VALUE);
|
||||
fail("update() should have thrown for wrapped offset + len");
|
||||
} catch (IllegalStateException e) {
|
||||
/* init failure is not the bounds rejection */
|
||||
throw e;
|
||||
} catch (RuntimeException e) {
|
||||
/* expected */
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashShouldMatchUsingByteBuffer() throws ShortBufferException {
|
||||
String[] dataVector = new String[] { "", "c2edba56a6b82cc3",
|
||||
|
|
|
|||
|
|
@ -75,6 +75,23 @@ public class Sha3Test {
|
|||
new Sha3(Sha3.TYPE_SHA3_256).getNativeStruct());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateWithWrappedOffsetAndLenShouldThrow() {
|
||||
Sha3 sha = new Sha3(Sha3.TYPE_SHA3_256);
|
||||
byte[] data = new byte[8];
|
||||
|
||||
/* offset + len wraps int arithmetic, update must reject it */
|
||||
try {
|
||||
sha.update(data, 1, Integer.MAX_VALUE);
|
||||
fail("update() should have thrown for wrapped offset + len");
|
||||
} catch (IllegalStateException e) {
|
||||
/* init failure is not the bounds rejection */
|
||||
throw e;
|
||||
} catch (RuntimeException e) {
|
||||
/* expected */
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sha3_256HashShouldMatchUsingByteArray() {
|
||||
/* Test vectors from NIST FIPS 202 - SHA-3 Standard */
|
||||
|
|
|
|||
|
|
@ -75,6 +75,23 @@ public class Sha512Test {
|
|||
assertEquals(NativeStruct.NULL, new Sha512().getNativeStruct());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateWithWrappedOffsetAndLenShouldThrow() {
|
||||
Sha512 sha = new Sha512();
|
||||
byte[] data = new byte[8];
|
||||
|
||||
/* offset + len wraps int arithmetic, update must reject it */
|
||||
try {
|
||||
sha.update(data, 1, Integer.MAX_VALUE);
|
||||
fail("update() should have thrown for wrapped offset + len");
|
||||
} catch (IllegalStateException e) {
|
||||
/* init failure is not the bounds rejection */
|
||||
throw e;
|
||||
} catch (RuntimeException e) {
|
||||
/* expected */
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashShouldMatchUsingByteBuffer() throws ShortBufferException {
|
||||
String[] dataVector = new String[] { "", "20580a530f01e771",
|
||||
|
|
|
|||
|
|
@ -75,6 +75,23 @@ public class ShaTest {
|
|||
assertEquals(NativeStruct.NULL, new Sha().getNativeStruct());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateWithWrappedOffsetAndLenShouldThrow() {
|
||||
Sha sha = new Sha();
|
||||
byte[] data = new byte[8];
|
||||
|
||||
/* offset + len wraps int arithmetic, update must reject it */
|
||||
try {
|
||||
sha.update(data, 1, Integer.MAX_VALUE);
|
||||
fail("update() should have thrown for wrapped offset + len");
|
||||
} catch (IllegalStateException e) {
|
||||
/* init failure is not the bounds rejection */
|
||||
throw e;
|
||||
} catch (RuntimeException e) {
|
||||
/* expected */
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashShouldMatchUsingByteBuffer() throws ShortBufferException {
|
||||
String[] dataVector = new String[] {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import org.junit.runner.Description;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
|
|
@ -180,6 +181,138 @@ public class WolfSSLCertManagerVerifyCallbackTest {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test callback object is collectible after free(), which must release
|
||||
* the native global reference to it.
|
||||
*/
|
||||
@Test
|
||||
public void testVerifyCallbackCollectibleAfterFree() throws Exception {
|
||||
|
||||
WolfSSLCertManager cm = new WolfSSLCertManager();
|
||||
WolfSSLCertManagerVerifyCallback cb =
|
||||
new WolfSSLCertManagerVerifyCallback() {
|
||||
public int verify(int preverify, int error, int errorDepth) {
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
WeakReference<WolfSSLCertManagerVerifyCallback> ref =
|
||||
new WeakReference<WolfSSLCertManagerVerifyCallback>(cb);
|
||||
byte[] gcPressure = null;
|
||||
|
||||
try {
|
||||
cm.setVerifyCallback(cb);
|
||||
} finally {
|
||||
cm.free();
|
||||
}
|
||||
cm = null;
|
||||
cb = null;
|
||||
|
||||
/* Android ART does not reliably collect within the retry window
|
||||
* even after the native global reference is deleted. */
|
||||
if (!isAndroid()) {
|
||||
/* Retry GC up to ~500ms, small allocations encourage
|
||||
* collectors that treat System.gc() as advisory */
|
||||
for (int i = 0; i < 50 && ref.get() != null; i++) {
|
||||
gcPressure = new byte[4096];
|
||||
gcPressure[0] = (byte)i;
|
||||
System.gc();
|
||||
Thread.sleep(10);
|
||||
}
|
||||
|
||||
assertNull("callback not collected, native global reference " +
|
||||
"leaked", ref.get());
|
||||
}
|
||||
}
|
||||
|
||||
/* Register a first callback on cm then replace with a second.
|
||||
*
|
||||
* Return a WeakReference to the replaced first callback. */
|
||||
private static WeakReference<WolfSSLCertManagerVerifyCallback>
|
||||
registerThenReplaceCallback(WolfSSLCertManager cm,
|
||||
final AtomicBoolean firstInvoked,
|
||||
final AtomicBoolean secondInvoked) throws Exception {
|
||||
|
||||
WolfSSLCertManagerVerifyCallback first =
|
||||
new WolfSSLCertManagerVerifyCallback() {
|
||||
public int verify(int preverify, int error, int errorDepth) {
|
||||
firstInvoked.set(true);
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
WeakReference<WolfSSLCertManagerVerifyCallback> firstRef =
|
||||
new WeakReference<WolfSSLCertManagerVerifyCallback>(first);
|
||||
|
||||
cm.setVerifyCallback(first);
|
||||
|
||||
cm.setVerifyCallback(new WolfSSLCertManagerVerifyCallback() {
|
||||
public int verify(int preverify, int error, int errorDepth) {
|
||||
secondInvoked.set(true);
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
|
||||
return firstRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a second setVerifyCallback() replaces the first callback and
|
||||
* releases the native global reference to it.
|
||||
*/
|
||||
@Test
|
||||
public void testVerifyCallbackReplaceReleasesPrevious() throws Exception {
|
||||
|
||||
final AtomicBoolean firstInvoked = new AtomicBoolean(false);
|
||||
final AtomicBoolean secondInvoked = new AtomicBoolean(false);
|
||||
WolfSSLCertManager cm = new WolfSSLCertManager();
|
||||
byte[] gcPressure = null;
|
||||
WeakReference<WolfSSLCertManagerVerifyCallback> firstRef = null;
|
||||
|
||||
byte[] caDer = readFile(caCertDer);
|
||||
byte[] serverDer = readFile(serverCertDer);
|
||||
|
||||
try {
|
||||
cm.CertManagerLoadCABuffer(caDer, caDer.length,
|
||||
WolfCrypt.SSL_FILETYPE_ASN1);
|
||||
|
||||
firstRef = registerThenReplaceCallback(cm, firstInvoked,
|
||||
secondInvoked);
|
||||
|
||||
try {
|
||||
cm.CertManagerVerifyBuffer(serverDer, serverDer.length,
|
||||
WolfCrypt.SSL_FILETYPE_ASN1);
|
||||
} catch (WolfCryptException e) {
|
||||
/* Verification result itself is not what we assert here */
|
||||
}
|
||||
|
||||
assertTrue("second callback should have been invoked",
|
||||
secondInvoked.get());
|
||||
assertFalse("first callback should not have been invoked",
|
||||
firstInvoked.get());
|
||||
|
||||
/* Android ART does not reliably collect within the retry
|
||||
* window even after the native global reference is deleted,
|
||||
* so the collectibility assert runs on non-Android JVMs only.
|
||||
* The dispatch asserts above still run everywhere. */
|
||||
if (!isAndroid()) {
|
||||
/* Retry GC up to ~500ms, small allocations encourage
|
||||
* collectors that treat System.gc() as advisory */
|
||||
for (int i = 0; i < 50 && firstRef.get() != null; i++) {
|
||||
gcPressure = new byte[4096];
|
||||
gcPressure[0] = (byte)i;
|
||||
System.gc();
|
||||
Thread.sleep(10);
|
||||
}
|
||||
|
||||
assertNull("replaced callback not collected, native " +
|
||||
"global reference leaked", firstRef.get());
|
||||
}
|
||||
|
||||
} finally {
|
||||
cm.free();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test each CertManager verification invokes its own callback, not
|
||||
* the most recently registered one from another CertManager.
|
||||
|
|
|
|||
Loading…
Reference in New Issue