diff --git a/lib/build.gradle b/lib/build.gradle index 067cb5e..9f37360 100644 --- a/lib/build.gradle +++ b/lib/build.gradle @@ -5,9 +5,20 @@ plugins { group='org.egothor' +configurations { + mockitoAgent +} + dependencies { api 'org.bouncycastle:bcpkix-jdk18on' implementation 'org.egothor:conflux' + + testImplementation "org.mockito:mockito-core:5.23.0" + testImplementation "org.mockito:mockito-inline:5.2.0" + + mockitoAgent("org.mockito:mockito-core:5.23.0") { + transitive = false + } } @@ -53,3 +64,8 @@ javadoc { options.links("https://www.egothor.org/javadoc/conflux") // options.overview = file("src/main/javadoc/overview.html") } + +test { + useJUnitPlatform() + jvmArgs("-javaagent:${configurations.mockitoAgent.singleFile}") +} diff --git a/lib/src/main/java/zeroecho/core/CryptoAlgorithms.java b/lib/src/main/java/zeroecho/core/CryptoAlgorithms.java index 1b578ae..1773bab 100644 --- a/lib/src/main/java/zeroecho/core/CryptoAlgorithms.java +++ b/lib/src/main/java/zeroecho/core/CryptoAlgorithms.java @@ -49,6 +49,7 @@ import javax.crypto.SecretKey; import zeroecho.core.audit.AuditListener; import zeroecho.core.audit.AuditedContexts; +import zeroecho.core.context.AgreementContext; import zeroecho.core.context.CryptoContext; import zeroecho.core.context.DigestContext; import zeroecho.core.context.EncryptionContext; @@ -335,33 +336,39 @@ public final class CryptoAlgorithms { if (AUDIT_MODE == AuditMode.WRAP) { final AuditListener listener = AUDIT; // pass through the global listener - if (ctx instanceof SignatureContext) { - @SuppressWarnings("unchecked") - C out = (C) AuditedContexts.wrap(ctx, listener, role); - return out; - } else if (ctx instanceof EncryptionContext) { - @SuppressWarnings("unchecked") - C out = (C) AuditedContexts.wrap(ctx, listener, role); - return out; - } else if (ctx instanceof KemContext) { - @SuppressWarnings("unchecked") - C out = (C) AuditedContexts.wrap(ctx, listener, role); - return out; - } else if (ctx instanceof DigestContext) { - @SuppressWarnings("unchecked") - C out = (C) AuditedContexts.wrap(ctx, listener, role); - return out; - } else if (ctx instanceof MacContext) { - @SuppressWarnings("unchecked") - C out = (C) AuditedContexts.wrap(ctx, listener, role); - return out; - } - // Unknown context type: return as-is (no wrapping). + return switch (ctx) { + case SignatureContext signatureContext -> wrapForAudit(signatureContext, listener, role); + case EncryptionContext encryptionContext -> wrapForAudit(encryptionContext, listener, role); + case KemContext kemContext -> wrapForAudit(kemContext, listener, role); + case DigestContext digestContext -> wrapForAudit(digestContext, listener, role); + case MacContext macContext -> wrapForAudit(macContext, listener, role); + case AgreementContext agreementContext -> wrapForAudit(agreementContext, listener, role); + }; } return ctx; } + /** + * Returns the audited wrapper for the supplied context. + * + *
+ * The returned context remains owned by the caller of the factory method. This + * helper does not acquire an additional resource requiring local cleanup. + *
+ * + * @param+ * These tests focus on the internal audit wrapping path used by + * {@link CryptoAlgorithms#wrapForAudit(CryptoContext, zeroecho.core.audit.AuditListener, KeyUsage)}. + * They verify that representative context types are wrapped as audited JDK + * proxies and that the resulting wrapper preserves the expected basic + * delegation behavior. + *
+ */ +class CryptoAlgorithmsAuditWrapTest { + + @AfterEach + void restoreAuditConfiguration() { + CryptoAlgorithms.setAuditListener(AuditListener.noop()); + CryptoAlgorithms.setAuditMode(AuditMode.OFF); + } + + @Test + void wrapForAuditDigestContextReturnsProxy() { + System.out.println("wrapForAuditDigestContextReturnsProxy"); + + DigestContext context = mock(DigestContext.class); + DigestContext wrapped = CryptoAlgorithms.wrapForAudit(context, AuditListener.noop(), KeyUsage.DIGEST); + + System.out.println("...ctxClass=" + wrapped.getClass().getName()); + assertTrue(Proxy.isProxyClass(wrapped.getClass()), "Digest context should be wrapped as JDK proxy"); + + System.out.println("wrapForAuditDigestContextReturnsProxy...ok"); + } + + @Test + void wrapForAuditAgreementContextReturnsProxy() { + System.out.println("wrapForAuditAgreementContextReturnsProxy"); + + AgreementContext context = mock(AgreementContext.class); + AgreementContext wrapped = CryptoAlgorithms.wrapForAudit(context, AuditListener.noop(), KeyUsage.AGREEMENT); + + System.out.println("...ctxClass=" + wrapped.getClass().getName()); + assertTrue(Proxy.isProxyClass(wrapped.getClass()), "Agreement context should be wrapped as JDK proxy"); + + System.out.println("wrapForAuditAgreementContextReturnsProxy...ok"); + } + + @Test + void wrapForAuditDigestContextCloseDelegatesToWrappedContext() throws Exception { + System.out.println("wrapForAuditDigestContextCloseDelegatesToWrappedContext"); + + DigestContext context = mock(DigestContext.class); + DigestContext wrapped = CryptoAlgorithms.wrapForAudit(context, AuditListener.noop(), KeyUsage.DIGEST); + + wrapped.close(); + + verify(context).close(); + System.out.println("...wrappedCloseDelegated=true"); + System.out.println("wrapForAuditDigestContextCloseDelegatesToWrappedContext...ok"); + } +} \ No newline at end of file