CVE-2025-12194
Description
Uncontrolled Resource Consumption vulnerability in Legion of the Bouncy Castle Inc. Bouncy Castle for Java FIPS bc-fips on All (API modules), Legion of the Bouncy Castle Inc. Bouncy Castle for Java LTS bcprov-lts8on on All (API modules) allows Excessive Allocation. This vulnerability is associated with program files core/src/main/jdk1.9/org/bouncycastle/crypto/fips/AESNativeCFB.Java, core/src/main/jdk1.9/org/bouncycastle/crypto/fips/AESNativeGCM.Java, core/src/main/jdk1.9/org/bouncycastle/crypto/fips/SHA256NativeDigest.Java, core/src/main/jdk1.9/org/bouncycastle/crypto/fips/AESNativeEngine.Java, core/src/main/jdk1.9/org/bouncycastle/crypto/fips/AESNativeCBC.Java, core/src/main/jdk1.9/org/bouncycastle/crypto/fips/AESNativeCTR.Java, core/src/main/jdk1.9/org/bouncycastle/crypto/engines/AESNativeCFB.Java, core/src/main/jdk1.9/org/bouncycastle/crypto/engines/AESNativeGCM.Java, core/src/main/jdk1.9/org/bouncycastle/crypto/engines/AESNativeEngine.Java, core/src/main/jdk1.9/org/bouncycastle/crypto/engines/AESNativeCBC.Java, core/src/main/jdk1.9/org/bouncycastle/crypto/engines/AESNativeGCMSIV.Java, core/src/main/jdk1.9/org/bouncycastle/crypto/engines/AESNativeCCM.Java, core/src/main/jdk1.9/org/bouncycastle/crypto/engines/AESNativeCTR.Java, core/src/main/jdk1.9/org/bouncycastle/crypto/digests/SHA256NativeDigest.Java, core/src/main/jdk1.9/org/bouncycastle/crypto/digests/SHA224NativeDigest.Java, core/src/main/jdk1.9/org/bouncycastle/crypto/digests/SHA3NativeDigest.Java, core/src/main/jdk1.9/org/bouncycastle/crypto/digests/SHAKENativeDigest.Java, core/src/main/jdk1.9/org/bouncycastle/crypto/digests/SHA512NativeDigest.Java, core/src/main/jdk1.9/org/bouncycastle/crypto/digests/SHA384NativeDigest.Java.
This issue affects Bouncy Castle for Java FIPS: from 2.1.0 through 2.1.1; Bouncy Castle for Java LTS: from 2.73.0 through 2.73.7.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.bouncycastle:bc-fipsMaven | >= 2.1.0, < 2.1.2 | 2.1.2 |
org.bouncycastle:bcprov-debug-lts8onMaven | >= 2.73.0, < 2.73.8 | 2.73.8 |
Affected products
1- Range: >= 2.73.0, <= 2.73.7
Patches
216 files changed · +4 −4
core/build.gradle+2 −2 modified@@ -10,7 +10,7 @@ jar.archiveBaseName = "bccore-lts$vm_range" sourceSets { java9 { java { - srcDirs = ['src/main/java9'] + srcDirs = ['src/main/jdk1.9'] } } } @@ -97,7 +97,7 @@ compileJava9Java { languageVersion = JavaLanguageVersion.of(11) } options.release = 9 - options.sourcepath = files(['src/main/java', 'src/main/java9']) + options.sourcepath = files(['src/main/java', 'src/main/jdk1.9']) }
core/src/main/jdk1.9/module-info.java+0 −0 renamedcore/src/main/jdk1.9/org/bouncycastle/crypto/digests/SHA224NativeDigest.java+0 −0 renamedcore/src/main/jdk1.9/org/bouncycastle/crypto/digests/SHA256NativeDigest.java+0 −0 renamedcore/src/main/jdk1.9/org/bouncycastle/crypto/digests/SHA384NativeDigest.java+0 −0 renamedcore/src/main/jdk1.9/org/bouncycastle/crypto/digests/SHA3NativeDigest.java+0 −0 renamedcore/src/main/jdk1.9/org/bouncycastle/crypto/digests/SHA512NativeDigest.java+0 −0 renamedcore/src/main/jdk1.9/org/bouncycastle/crypto/digests/SHAKENativeDigest.java+0 −0 renamedcore/src/main/jdk1.9/org/bouncycastle/crypto/engines/AESNativeCBC.java+0 −0 renamedcore/src/main/jdk1.9/org/bouncycastle/crypto/engines/AESNativeCCM.java+0 −0 renamedcore/src/main/jdk1.9/org/bouncycastle/crypto/engines/AESNativeCFB.java+0 −0 renamedcore/src/main/jdk1.9/org/bouncycastle/crypto/engines/AESNativeCTR.java+0 −0 renamedcore/src/main/jdk1.9/org/bouncycastle/crypto/engines/AESNativeEngine.java+0 −0 renamedcore/src/main/jdk1.9/org/bouncycastle/crypto/engines/AESNativeGCM.java+0 −0 renamedcore/src/main/jdk1.9/org/bouncycastle/crypto/engines/AESNativeGCMSIV.java+0 −0 renamedprov/build.gradle+2 −2 modified@@ -17,7 +17,7 @@ sourceSets { } java9 { java { - srcDirs = ['src/main/jdk1.9','../core/src/main/java9/org'] + srcDirs = ['src/main/jdk1.9','../core/src/main/jdk1.9/org'] } } java11 { @@ -64,7 +64,7 @@ compileJava { compileJava9Java { options.release = 9 options.debug = rootProject.ext.debugBuild - options.sourcepath = files(['../core/src/main/java', 'src/main/java', 'src/main/jdk1.9','../core/src/main/java9/org']) + options.sourcepath = files(['../core/src/main/java', 'src/main/java', 'src/main/jdk1.9','../core/src/main/jdk1.9/org']) } compileJava11Java {
33 files changed · +5612 −818
core/build.gradle+23 −0 modified@@ -6,8 +6,22 @@ plugins { jar.archiveBaseName = "bccore-lts$vm_range" + +sourceSets { + java9 { + java { + srcDirs = ['src/main/java9'] + } + } +} + + dependencies { testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.13.3' + + java9Implementation files([sourceSets.main.output.classesDirs]) { + builtBy compileJava + } } test { @@ -68,6 +82,15 @@ artifacts { } +compileJava9Java { + javaCompiler = javaToolchains.compilerFor { + languageVersion = JavaLanguageVersion.of(11) + } + options.release = 9 + options.sourcepath = files(['src/main/java', 'src/main/java9']) +} + + task cleanNative(type: Delete) { delete("$projectDir/src/main/resources/native/"); delete("$projectDir/src/main/resources/META-INF/DRIVERS");
core/src/main/java9/org/bouncycastle/crypto/digests/SHA224NativeDigest.java+269 −0 added@@ -0,0 +1,269 @@ +package org.bouncycastle.crypto.digests; + +import org.bouncycastle.crypto.CryptoServiceProperties; +import org.bouncycastle.crypto.CryptoServicePurpose; +import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.crypto.SavableDigest; +import org.bouncycastle.util.Memoable; +import org.bouncycastle.util.dispose.NativeDisposer; +import org.bouncycastle.util.dispose.NativeReference; + +import java.lang.ref.Reference; + +/** + * SHA224 implementation. + */ +class SHA224NativeDigest + implements SavableDigest +{ + private final CryptoServicePurpose purpose; + + protected DigestRefWrapper nativeRef = null; + + SHA224NativeDigest(CryptoServicePurpose purpose) + { + this.purpose = purpose; + nativeRef = new DigestRefWrapper(makeNative()); + reset(); + CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties()); + } + + SHA224NativeDigest() + { + this(CryptoServicePurpose.ANY); + } + + SHA224NativeDigest(SHA224NativeDigest src) + { + + this(CryptoServicePurpose.ANY); + + byte[] state = src.getEncodedState(); + + restoreFullState(nativeRef.getReference(), state, 0); + } + + // + // From BC-LTS, used for testing in FIPS api only. + // ----------------------- Start Testing only methods. + + SHA224NativeDigest restoreState(byte[] state, int offset) + { + restoreFullState(nativeRef.getReference(), state, offset); + return this; + } + + // + // ----------------------- End Testing only methods. + // + + @Override + public String getAlgorithmName() + { + return "SHA-224"; + } + + @Override + public int getDigestSize() + { + return getDigestSize(nativeRef.getReference()); + } + + + @Override + public void update(byte in) + { + try + { + update(nativeRef.getReference(), in); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public void update(byte[] input, int inOff, int len) + { + try + { + update(nativeRef.getReference(), input, inOff, len); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public int doFinal(byte[] output, int outOff) + { + try + { + return doFinal(nativeRef.getReference(), output, outOff); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public void reset() + { + try + { + reset(nativeRef.getReference()); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public int getByteLength() + { + try + { + return getByteLength(nativeRef.getReference()); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public Memoable copy() + { + try + { + return new SHA224NativeDigest(this); + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public void reset(Memoable other) + { + try + { + SHA224NativeDigest dig = (SHA224NativeDigest) other; + restoreFullState(nativeRef.getReference(), dig.getEncodedState(), 0); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + public byte[] getEncodedState() + { + try + { + int l = encodeFullState(nativeRef.getReference(), null, 0); + byte[] state = new byte[l]; + encodeFullState(nativeRef.getReference(), state, 0); + return state; + } + finally + { + Reference.reachabilityFence(this); + } + } + + + void restoreFullState(byte[] encoded, int offset) + { + try + { + restoreFullState(nativeRef.getReference(), encoded, offset); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public String toString() + { + return "SHA224[Native]()"; + } + + static native long makeNative(); + + static native void destroy(long nativeRef); + + static native int getDigestSize(long nativeRef); + + static native void update(long nativeRef, byte in); + + static native void update(long nativeRef, byte[] in, int inOff, int len); + + static native int doFinal(long nativeRef, byte[] out, int outOff); + + static native void reset(long nativeRef); + + static native int getByteLength(long nativeRef); + + static native int encodeFullState(long nativeRef, byte[] buffer, int offset); + + static native void restoreFullState(long reference, byte[] encoded, int offset); + + protected CryptoServiceProperties cryptoServiceProperties() + { + return Utils.getDefaultProperties(this, 224, purpose); + } + + + private static class Disposer + extends NativeDisposer + { + + Disposer(long ref) + { + super(ref); + } + + @Override + protected void dispose(long reference) + { + destroy(reference); + } + } + + private static class DigestRefWrapper + extends NativeReference + { + + public DigestRefWrapper(long reference) + { + super(reference, "SHA224"); + } + + @Override + public Runnable createAction() + { + return new Disposer(reference); + } + } +} + + + + + +
core/src/main/java9/org/bouncycastle/crypto/digests/SHA256NativeDigest.java+284 −0 added@@ -0,0 +1,284 @@ +package org.bouncycastle.crypto.digests; + +import org.bouncycastle.crypto.CryptoServiceProperties; +import org.bouncycastle.crypto.CryptoServicePurpose; +import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.crypto.SavableDigest; +import org.bouncycastle.util.Memoable; +import org.bouncycastle.util.dispose.NativeDisposer; +import org.bouncycastle.util.dispose.NativeReference; + +import java.lang.ref.Reference; + +/** + * SHA256 implementation. + */ +class SHA256NativeDigest + implements SavableDigest +{ + private final CryptoServicePurpose purpose; + + protected DigestRefWrapper nativeRef = null; + + SHA256NativeDigest(CryptoServicePurpose purpose) + { + this.purpose = purpose; + nativeRef = new DigestRefWrapper(makeNative()); + reset(); + CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties()); + } + + SHA256NativeDigest() + { + this(CryptoServicePurpose.ANY); + } + + SHA256NativeDigest(SHA256NativeDigest src) + { + + this(CryptoServicePurpose.ANY); + + byte[] state = src.getEncodedState(); + + restoreFullState(nativeRef.getReference(), state, 0); + } + + // + // From BC-LTS, used for testing in FIPS api only. + // ----------------------- Start Testing only methods. + + SHA256NativeDigest restoreState(byte[] state, int offset) + { + restoreFullState(nativeRef.getReference(), state, offset); + return this; + } + + // + // ----------------------- End Testing only methods. + // + + @Override + public String getAlgorithmName() + { + try + { + return "SHA-256"; + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public int getDigestSize() + { + try + { + return getDigestSize(nativeRef.getReference()); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public void update(byte in) + { + + try + { + update(nativeRef.getReference(), in); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public void update(byte[] input, int inOff, int len) + { + try + { + update(nativeRef.getReference(), input, inOff, len); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public int doFinal(byte[] output, int outOff) + { + try + { + return doFinal(nativeRef.getReference(), output, outOff); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public void reset() + { + try + { + reset(nativeRef.getReference()); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public int getByteLength() + { + try + { + return getByteLength(nativeRef.getReference()); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public Memoable copy() + { + try + { + return new SHA256NativeDigest(this); + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public void reset(Memoable other) + { + try + { + SHA256NativeDigest dig = (SHA256NativeDigest) other; + restoreFullState(nativeRef.getReference(), dig.getEncodedState(), 0); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + public byte[] getEncodedState() + { + try + { + int l = encodeFullState(nativeRef.getReference(), null, 0); + byte[] state = new byte[l]; + encodeFullState(nativeRef.getReference(), state, 0); + return state; + } + finally + { + Reference.reachabilityFence(this); + } + } + + + void restoreFullState(byte[] encoded, int offset) + { + try + { + restoreFullState(nativeRef.getReference(), encoded, offset); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public String toString() + { + return "SHA256[Native]()"; + } + + static native long makeNative(); + + static native void destroy(long nativeRef); + + static native int getDigestSize(long nativeRef); + + static native void update(long nativeRef, byte in); + + static native void update(long nativeRef, byte[] in, int inOff, int len); + + static native int doFinal(long nativeRef, byte[] out, int outOff); + + static native void reset(long nativeRef); + + static native int getByteLength(long nativeRef); + + static native int encodeFullState(long nativeRef, byte[] buffer, int offset); + + static native void restoreFullState(long reference, byte[] encoded, int offset); + + protected CryptoServiceProperties cryptoServiceProperties() + { + return Utils.getDefaultProperties(this, 256, purpose); + } + + + private static class Disposer + extends NativeDisposer + { + + Disposer(long ref) + { + super(ref); + } + + @Override + protected void dispose(long reference) + { + destroy(reference); + } + } + + private static class DigestRefWrapper + extends NativeReference + { + + public DigestRefWrapper(long reference) + { + super(reference, "SHA256"); + } + + @Override + public Runnable createAction() + { + return new Disposer(reference); + } + } +} + + + + + +
core/src/main/java9/org/bouncycastle/crypto/digests/SHA384NativeDigest.java+259 −0 added@@ -0,0 +1,259 @@ +package org.bouncycastle.crypto.digests; + +import org.bouncycastle.crypto.CryptoServiceProperties; +import org.bouncycastle.crypto.CryptoServicePurpose; +import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.crypto.SavableDigest; +import org.bouncycastle.util.Memoable; +import org.bouncycastle.util.dispose.NativeDisposer; +import org.bouncycastle.util.dispose.NativeReference; + +import java.lang.ref.Reference; + +/** + * SHA384 implementation. + */ +class SHA384NativeDigest + implements SavableDigest +{ + private final CryptoServicePurpose purpose; + + protected DigestRefWrapper nativeRef = null; + + SHA384NativeDigest(CryptoServicePurpose purpose) + { + this.purpose = purpose; + nativeRef = new DigestRefWrapper(makeNative()); + reset(); + CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties()); + } + + SHA384NativeDigest() + { + this(CryptoServicePurpose.ANY); + } + + SHA384NativeDigest(SHA384NativeDigest src) + { + + this(CryptoServicePurpose.ANY); + + byte[] state = src.getEncodedState(); + + restoreFullState(nativeRef.getReference(), state, 0); + } + + // + // From BC-LTS, used for testing in FIPS api only. + // ----------------------- Start Testing only methods. + + SHA384NativeDigest restoreState(byte[] state, int offset) + { + restoreFullState(nativeRef.getReference(), state, offset); + return this; + } + + // + // ----------------------- End Testing only methods. + // + + @Override + public String getAlgorithmName() + { + try { + return "SHA-384"; + } finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public int getDigestSize() + { + try { + return getDigestSize(nativeRef.getReference()); + } finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public void update(byte in) + { +try { + update(nativeRef.getReference(), in); +} finally +{ + Reference.reachabilityFence(this); +} + } + + + @Override + public void update(byte[] input, int inOff, int len) + { + try { + update(nativeRef.getReference(), input, inOff, len); + } finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public int doFinal(byte[] output, int outOff) + { + try { + return doFinal(nativeRef.getReference(), output, outOff); + } finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public void reset() + { + try { + reset(nativeRef.getReference()); + } finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public int getByteLength() + { + try { + return getByteLength(nativeRef.getReference()); + } finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public Memoable copy() + { + return new SHA384NativeDigest(this); + } + + @Override + public void reset(Memoable other) + { + try { + SHA384NativeDigest dig = (SHA384NativeDigest) other; + restoreFullState(nativeRef.getReference(), dig.getEncodedState(), 0); + } finally + { + Reference.reachabilityFence(this); + } + } + + + public byte[] getEncodedState() + { + try { + int l = encodeFullState(nativeRef.getReference(), null, 0); + byte[] state = new byte[l]; + encodeFullState(nativeRef.getReference(), state, 0); + return state; + } finally + { + Reference.reachabilityFence(this); + } + } + + + + + void restoreFullState(byte[] encoded, int offset) + { + try { + restoreFullState(nativeRef.getReference(), encoded, offset); + } finally + { + Reference.reachabilityFence(this); + } + } + + + + @Override + public String toString() + { + return "SHA384[Native]()"; + } + + static native long makeNative(); + + static native void destroy(long nativeRef); + + static native int getDigestSize(long nativeRef); + + static native void update(long nativeRef, byte in); + + static native void update(long nativeRef, byte[] in, int inOff, int len); + + static native int doFinal(long nativeRef, byte[] out, int outOff); + + static native void reset(long nativeRef); + + static native int getByteLength(long nativeRef); + + static native int encodeFullState(long nativeRef, byte[] buffer, int offset); + + static native void restoreFullState(long reference, byte[] encoded, int offset); + + protected CryptoServiceProperties cryptoServiceProperties() + { + return Utils.getDefaultProperties(this, 384, purpose); + } + + + private static class Disposer + extends NativeDisposer + { + + Disposer(long ref) + { + super(ref); + } + + @Override + protected void dispose(long reference) + { + destroy(reference); + } + } + + private static class DigestRefWrapper + extends NativeReference + { + + public DigestRefWrapper(long reference) + { + super(reference,"SHA384"); + } + + @Override + public Runnable createAction() + { + return new Disposer(reference); + } + } +} + + + + + +
core/src/main/java9/org/bouncycastle/crypto/digests/SHA3NativeDigest.java+316 −0 added@@ -0,0 +1,316 @@ +package org.bouncycastle.crypto.digests; + +import org.bouncycastle.crypto.*; +import org.bouncycastle.util.Memoable; +import org.bouncycastle.util.dispose.NativeDisposer; +import org.bouncycastle.util.dispose.NativeReference; + +import java.lang.ref.Reference; + +/** + * SHA3 implementation. + */ +public class SHA3NativeDigest + implements SavableDigest +{ + private final CryptoServicePurpose purpose; + + protected DigestRefWrapper nativeRef = null; + private int bitLen; + + + public SHA3NativeDigest(CryptoServicePurpose purpose) + { + this(256, purpose); + } + + public SHA3NativeDigest(int bitLen, CryptoServicePurpose purpose) + { + if (!CryptoServicesRegistrar.hasEnabledService(NativeServices.SHA3)) + { + throw new IllegalStateException("no native SHA3 support"); + } + + this.purpose = purpose; + this.bitLen = bitLen; + nativeRef = new DigestRefWrapper(makeNative(bitLen)); + reset(); + CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties()); + } + + public SHA3NativeDigest(int bitLen) + { + this(bitLen, CryptoServicePurpose.ANY); + } + + public SHA3NativeDigest() + { + this(CryptoServicePurpose.ANY); + } + + public SHA3NativeDigest(SHA3NativeDigest src) + { + + this(CryptoServicePurpose.ANY); + + byte[] state = src.getEncodedState(); + + restoreFullState(nativeRef.getReference(), state, 0); + } + + public SHA3NativeDigest(byte[] encoded, CryptoServicePurpose purpose) + { + this(purpose); + restoreFullState(nativeRef.getReference(), encoded, 0); + } + + public SHA3NativeDigest(byte[] encoded) + { + this(); + restoreFullState(nativeRef.getReference(), encoded, 0); + } + + + SHA3NativeDigest restoreState(byte[] state, int offset) + { + try + { + restoreFullState(nativeRef.getReference(), state, offset); + return this; + } + finally + { + Reference.reachabilityFence(this); + } + } + + // + // ----------------------- End Testing only methods. + // + + @Override + public String getAlgorithmName() + { + try + { + return "SHA3-" + bitLen; + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public int getDigestSize() + { + try + { + return getDigestSize(nativeRef.getReference()); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public void update(byte in) + { + try + { + update(nativeRef.getReference(), in); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public void update(byte[] input, int inOff, int len) + { + try + { + update(nativeRef.getReference(), input, inOff, len); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public int doFinal(byte[] output, int outOff) + { + try + { + int i = doFinal(nativeRef.getReference(), output, outOff); + reset(); + return i; + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public void reset() + { + try + { + reset(nativeRef.getReference()); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public int getByteLength() + { + try + { + return getByteLength(nativeRef.getReference()); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public Memoable copy() + { + try + { + return new SHA3NativeDigest(this); + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public void reset(Memoable other) + { + try + { + SHA3NativeDigest dig = (SHA3NativeDigest) other; + restoreFullState(nativeRef.getReference(), dig.getEncodedState(), 0); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + public byte[] getEncodedState() + { + try + { + int l = encodeFullState(nativeRef.getReference(), null, 0); + byte[] state = new byte[l]; + encodeFullState(nativeRef.getReference(), state, 0); + return state; + } + finally + { + Reference.reachabilityFence(this); + } + } + + + void restoreFullState(byte[] encoded, int offset) + { + try + { + restoreFullState(nativeRef.getReference(), encoded, offset); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public String toString() + { + return "SHA3[Native]()"; + } + + static native long makeNative(int bitLen); + + static native void destroy(long nativeRef); + + static native int getDigestSize(long nativeRef); + + static native void update(long nativeRef, byte in); + + static native void update(long nativeRef, byte[] in, int inOff, int len); + + static native int doFinal(long nativeRef, byte[] out, int outOff); + + static native void reset(long nativeRef); + + static native int getByteLength(long nativeRef); + + static native int encodeFullState(long nativeRef, byte[] buffer, int offset); + + static native void restoreFullState(long reference, byte[] encoded, int offset); + + protected CryptoServiceProperties cryptoServiceProperties() + { + return Utils.getDefaultProperties(this, bitLen, purpose); + } + + + private static class Disposer + extends NativeDisposer + { + + Disposer(long ref) + { + super(ref); + } + + @Override + protected void dispose(long reference) + { + destroy(reference); + } + } + + protected static class DigestRefWrapper + extends NativeReference + { + + public DigestRefWrapper(long reference) + { + super(reference, "SHA3"); + } + + @Override + public Runnable createAction() + { + return new Disposer(reference); + } + } +} + + + + + +
core/src/main/java9/org/bouncycastle/crypto/digests/SHA512NativeDigest.java+271 −0 added@@ -0,0 +1,271 @@ +package org.bouncycastle.crypto.digests; + +import org.bouncycastle.crypto.CryptoServiceProperties; +import org.bouncycastle.crypto.CryptoServicePurpose; +import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.crypto.SavableDigest; +import org.bouncycastle.util.Memoable; +import org.bouncycastle.util.dispose.NativeDisposer; +import org.bouncycastle.util.dispose.NativeReference; + +import java.lang.ref.Reference; + +/** + * SHA512 implementation. + */ +class SHA512NativeDigest + implements SavableDigest +{ + private final CryptoServicePurpose purpose; + + protected DigestRefWrapper nativeRef = null; + + SHA512NativeDigest(CryptoServicePurpose purpose) + { + this.purpose = purpose; + nativeRef = new DigestRefWrapper(makeNative()); + reset(); + CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties()); + } + + SHA512NativeDigest() + { + this(CryptoServicePurpose.ANY); + } + + SHA512NativeDigest(SHA512NativeDigest src) + { + + this(CryptoServicePurpose.ANY); + + byte[] state = src.getEncodedState(); + + restoreFullState(nativeRef.getReference(), state, 0); + } + + // + // From BC-LTS, used for testing in FIPS api only. + // ----------------------- Start Testing only methods. + + SHA512NativeDigest restoreState(byte[] state, int offset) + { + try + { + restoreFullState(nativeRef.getReference(), state, offset); + return this; + } + finally + { + Reference.reachabilityFence(this); + } + } + + // + // ----------------------- End Testing only methods. + // + + @Override + public String getAlgorithmName() + { + try + { + return "SHA-512"; + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public int getDigestSize() + { + try { + return getDigestSize(nativeRef.getReference()); + } finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public void update(byte in) + { + + try { + update(nativeRef.getReference(), in); + } finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public void update(byte[] input, int inOff, int len) + { + try { + update(nativeRef.getReference(), input, inOff, len); + } finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public int doFinal(byte[] output, int outOff) + { + try { + return doFinal(nativeRef.getReference(), output, outOff); + } finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public void reset() + { + try { + reset(nativeRef.getReference()); + } finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public int getByteLength() + { + try { + return getByteLength(nativeRef.getReference()); + } finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public Memoable copy() + { + try { + return new SHA512NativeDigest(this); + } finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public void reset(Memoable other) + { + try { + SHA512NativeDigest dig = (SHA512NativeDigest) other; + restoreFullState(nativeRef.getReference(), dig.getEncodedState(), 0); + } finally + { + Reference.reachabilityFence(this); + } + } + + + public byte[] getEncodedState() + { + try { + int l = encodeFullState(nativeRef.getReference(), null, 0); + byte[] state = new byte[l]; + encodeFullState(nativeRef.getReference(), state, 0); + return state; + } finally + { + Reference.reachabilityFence(this); + } + } + + + void restoreFullState(byte[] encoded, int offset) + { + try { + restoreFullState(nativeRef.getReference(), encoded, offset); + } finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public String toString() + { + return "SHA512[Native]()"; + } + + static native long makeNative(); + + static native void destroy(long nativeRef); + + static native int getDigestSize(long nativeRef); + + static native void update(long nativeRef, byte in); + + static native void update(long nativeRef, byte[] in, int inOff, int len); + + static native int doFinal(long nativeRef, byte[] out, int outOff); + + static native void reset(long nativeRef); + + static native int getByteLength(long nativeRef); + + static native int encodeFullState(long nativeRef, byte[] buffer, int offset); + + static native void restoreFullState(long reference, byte[] encoded, int offset); + + protected CryptoServiceProperties cryptoServiceProperties() + { + return Utils.getDefaultProperties(this, 512, purpose); + } + + + private static class Disposer + extends NativeDisposer + { + + Disposer(long ref) + { + super(ref); + } + + @Override + protected void dispose(long reference) + { + destroy(reference); + } + } + + private static class DigestRefWrapper + extends NativeReference + { + + public DigestRefWrapper(long reference) + { + super(reference, "SHA512"); + } + + @Override + public Runnable createAction() + { + return new Disposer(reference); + } + } +} + + + + + +
core/src/main/java9/org/bouncycastle/crypto/digests/SHAKENativeDigest.java+337 −0 added@@ -0,0 +1,337 @@ +package org.bouncycastle.crypto.digests; + +import org.bouncycastle.crypto.*; +import org.bouncycastle.util.Memoable; +import org.bouncycastle.util.dispose.NativeDisposer; +import org.bouncycastle.util.dispose.NativeReference; + +import java.lang.ref.Reference; + +/** + * SHAKE implementation. + */ +public class SHAKENativeDigest + implements SavableDigestXof +{ + private final CryptoServicePurpose purpose; + + protected DigestRefWrapper nativeRef = null; + private int bitLen; + + + public SHAKENativeDigest(CryptoServicePurpose purpose) + { + this(128, purpose); + } + + public SHAKENativeDigest(int bitLen, CryptoServicePurpose purpose) + { + if (!CryptoServicesRegistrar.hasEnabledService(NativeServices.SHA3)) + { + throw new IllegalStateException("no native SHAKE support"); + } + + this.purpose = purpose; + this.bitLen = bitLen; + nativeRef = new DigestRefWrapper(makeNative(bitLen)); + reset(); + CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties()); + } + + public SHAKENativeDigest(int bitLen) + { + this(bitLen, CryptoServicePurpose.ANY); + } + + public SHAKENativeDigest() + { + this(CryptoServicePurpose.ANY); + } + + public SHAKENativeDigest(SHAKENativeDigest src) + { + this(CryptoServicePurpose.ANY); + byte[] state = src.getEncodedState(); + restoreFullState(nativeRef.getReference(), state, 0); + } + + public SHAKENativeDigest(byte[] encoded, CryptoServicePurpose purpose) + { + this(purpose); + restoreFullState(nativeRef.getReference(), encoded, 0); + } + + public SHAKENativeDigest(byte[] encoded) + { + this(); + restoreFullState(nativeRef.getReference(), encoded, 0); + } + + + SHAKENativeDigest restoreState(byte[] state, int offset) + { + try + { + restoreFullState(nativeRef.getReference(), state, offset); + return this; + } + finally + { + Reference.reachabilityFence(this); + } + } + + // + // ----------------------- End Testing only methods. + // + + @Override + public String getAlgorithmName() + { + + return "SHAKE" + bitLen; + } + + @Override + public int getDigestSize() + { + try + { + return getDigestSize(nativeRef.getReference()); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public void update(byte in) + { + try + { + update(nativeRef.getReference(), in); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public void update(byte[] input, int inOff, int len) + { + try + { + update(nativeRef.getReference(), input, inOff, len); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public int doFinal(byte[] output, int outOff) + { + try + { + int i = doFinal(nativeRef.getReference(), output, outOff); + return i; + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public int doFinal(byte[] out, int outOff, int outLen) + { + try + { + int i = doFinal(nativeRef.getReference(), out, outOff, outLen); + return i; + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public int doOutput(byte[] out, int outOff, int outLen) + { + try + { + return doOutput(nativeRef.getReference(), out, outOff, outLen); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public void reset() + { + try + { + reset(nativeRef.getReference()); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public int getByteLength() + { + try + { + return getByteLength(nativeRef.getReference()); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public Memoable copy() + { + try + { + return new SHAKENativeDigest(this); + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public void reset(Memoable other) + { + try + { + SHAKENativeDigest dig = (SHAKENativeDigest) other; + restoreFullState(nativeRef.getReference(), dig.getEncodedState(), 0); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + public byte[] getEncodedState() + { + try + { + int l = encodeFullState(nativeRef.getReference(), null, 0); + byte[] state = new byte[l]; + encodeFullState(nativeRef.getReference(), state, 0); + return state; + } + finally + { + Reference.reachabilityFence(this); + } + } + + + void restoreFullState(byte[] encoded, int offset) + { + try + { + restoreFullState(nativeRef.getReference(), encoded, offset); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public String toString() + { + return "SHAKE[Native]()"; + } + + static native long makeNative(int bitLen); + + static native void destroy(long nativeRef); + + static native int getDigestSize(long nativeRef); + + static native void update(long nativeRef, byte in); + + static native void update(long nativeRef, byte[] in, int inOff, int len); + + static native int doFinal(long nativeRef, byte[] out, int outOff, int len); + + static native int doFinal(long nativeRef, byte[] out, int outOff); + + static native int doOutput(long nativeRef, byte[] out, int outOff, int len); + + static native void reset(long nativeRef); + + static native int getByteLength(long nativeRef); + + static native int encodeFullState(long nativeRef, byte[] buffer, int offset); + + static native void restoreFullState(long reference, byte[] encoded, int offset); + + protected CryptoServiceProperties cryptoServiceProperties() + { + return Utils.getDefaultProperties(this, bitLen, purpose); + } + + + private static class Disposer + extends NativeDisposer + { + + Disposer(long ref) + { + super(ref); + } + + @Override + protected void dispose(long reference) + { + destroy(reference); + } + } + + protected static class DigestRefWrapper + extends NativeReference + { + + public DigestRefWrapper(long reference) + { + super(reference, "SHAKE"); + } + + @Override + public Runnable createAction() + { + return new Disposer(reference); + } + } +} + + + + + +
core/src/main/java9/org/bouncycastle/crypto/engines/AESNativeCBC.java+306 −0 added@@ -0,0 +1,306 @@ +package org.bouncycastle.crypto.engines; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.MultiBlockCipher; +import org.bouncycastle.crypto.modes.CBCModeCipher; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.dispose.NativeDisposer; +import org.bouncycastle.util.dispose.NativeReference; + +import java.lang.ref.Reference; + +class AESNativeCBC + implements CBCModeCipher +{ + private CBCRefWrapper referenceWrapper; + + byte[] IV = new byte[16]; + int keySize; + + private boolean encrypting; + + @Override + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException + { + try + { + boolean oldEncrypting = this.encrypting; + + this.encrypting = forEncryption; + + if (params instanceof ParametersWithIV) + { + ParametersWithIV ivParam = (ParametersWithIV) params; + byte[] iv = ivParam.getIV(); + + if (iv.length != getBlockSize()) + { + throw new IllegalArgumentException("initialisation vector must be the same length as block size"); + } + + System.arraycopy(iv, 0, IV, 0, iv.length); + + reset(); + + // if null it's an IV changed only. + if (ivParam.getParameters() != null) + { + init((KeyParameter) ivParam.getParameters()); + // cipher.init(encrypting, ivParam.getParameters()); + } + else + { + // The key parameter was null which inidicates that they + // IV is being changed. + + if (oldEncrypting != encrypting) + { + throw new IllegalArgumentException("cannot change encrypting state without providing key"); + } + + if (referenceWrapper == null) + { + throw new IllegalStateException("IV change attempted but not previously initialized with a key"); + } + + // We need to use the old key because + // the native layer requires a both key and iv each time. + init(new KeyParameter(referenceWrapper.oldKey)); + + } + } + else + { + reset(); + + // if it's null, key is to be reused. + if (params != null) + { + init((KeyParameter) params); + // cipher.init(encrypting, params); + } + else + { + if (oldEncrypting != encrypting) + { + throw new IllegalArgumentException("cannot change encrypting state without providing key."); + } + + if (referenceWrapper == null) + { + throw new IllegalStateException("IV change attempted but not previously initialized with a key"); + } + + // We need to use the old key because the + // native layer requires a both key and iv each time. + init(new KeyParameter(referenceWrapper.oldKey)); + + } + } + + } + finally + { + Reference.reachabilityFence(this); + } + } + + private void init(KeyParameter parameters) + { + + try + { + byte[] key = ((KeyParameter) parameters).getKey(); + + + switch (key.length) + { + case 16: + case 24: + case 32: + break; + default: + throw new IllegalArgumentException("key must be only 16,24,or 32 bytes long."); + } + + referenceWrapper = new CBCRefWrapper(makeNative(key.length, encrypting), Arrays.clone(key)); + + if (referenceWrapper.getReference() == 0) + { + throw new IllegalStateException("Native CBC native instance returned a null pointer."); + } + + init(referenceWrapper.getReference(), key, IV); + keySize = key.length * 8; + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public String getAlgorithmName() + { + return "AES/CBC"; + } + + @Override + public int getBlockSize() + { + return getBlockSize(0); + } + + + @Override + public int processBlock(byte[] in, int inOff, byte[] out, int outOff) + throws DataLengthException, IllegalStateException + { + + try + { + if (referenceWrapper == null) + { + throw new IllegalStateException("not initialized"); + } + + return process(referenceWrapper.getReference(), in, inOff, 1, out, outOff); + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public void reset() + { + try + { + // skip over spurious resets that may occur before init is called. + if (referenceWrapper == null) + { + return; + } + + reset(referenceWrapper.getReference()); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public int getMultiBlockSize() + { + try + { + return getMultiBlockSize(0); + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public int processBlocks(byte[] in, int inOff, int blockCount, byte[] out, int outOff) + throws DataLengthException, IllegalStateException + { + try + { + + if (referenceWrapper == null) + { + throw new IllegalStateException("not initialized"); + } + + return process(referenceWrapper.getReference(), in, inOff, blockCount, out, outOff); + } + finally + { + Reference.reachabilityFence(this); + } + } + + private static native int process(long ref, byte[] in, int inOff, int blockCount, byte[] out, int outOff); + + private static native int getMultiBlockSize(long ref); + + private static native int getBlockSize(long ref); + + static native long makeNative(int keyLen, boolean encryption); + + native void init(long nativeRef, byte[] key, byte[] iv); + + static native void dispose(long ref); + + private static native void reset(long nativeRef); + + @Override + public BlockCipher getUnderlyingCipher() + { + try + { + MultiBlockCipher eng = AESEngine.newInstance(); + eng.init(encrypting, new KeyParameter(referenceWrapper.oldKey)); + return eng; + } finally + { + Reference.reachabilityFence(this); + } + } + + + private static class Disposer + extends NativeDisposer + { + private final byte[] oldKey; + + Disposer(long ref, byte[] oldKey) + { + super(ref); + this.oldKey = oldKey; + } + + @Override + protected void dispose(long reference) + { + Arrays.clear(this.oldKey); + AESNativeCBC.dispose(reference); + } + } + + private class CBCRefWrapper + extends NativeReference + { + private final byte[] oldKey; + + public CBCRefWrapper(long reference, byte[] oldKey) + { + super(reference, "CBC"); + this.oldKey = oldKey; + } + + @Override + public Runnable createAction() + { + return new Disposer(reference, this.oldKey); + } + } + + @Override + public String toString() + { + return "CBC[Native](AES[Native](" + keySize + ")"; + } + +}
core/src/main/java9/org/bouncycastle/crypto/engines/AESNativeCCM.java+473 −0 added@@ -0,0 +1,473 @@ +package org.bouncycastle.crypto.engines; + +import org.bouncycastle.crypto.*; +import org.bouncycastle.crypto.modes.CCMModeCipher; +import org.bouncycastle.crypto.params.AEADParameters; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.dispose.NativeDisposer; +import org.bouncycastle.util.dispose.NativeReference; + +import java.io.ByteArrayOutputStream; +import java.lang.ref.Reference; + +class AESNativeCCM + implements CCMModeCipher +{ + private CCMRefWrapper refWrapper; + private boolean forEncryption = false; + private boolean initialised = false; + private final ExposedByteArrayOutputStream associatedText = new ExposedByteArrayOutputStream(); + private final ExposedByteArrayOutputStream data = new ExposedByteArrayOutputStream(); + + @Override + public BlockCipher getUnderlyingCipher() + { + try + { + BlockCipher engine = AESEngine.newInstance(); + + if (refWrapper != null && refWrapper.key != null) + { + engine.init(true, new KeyParameter(refWrapper.key)); + } + + return engine; + } + finally + { + Reference.reachabilityFence(this); + } + } + + + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException + { + try + { + this.forEncryption = forEncryption; + CipherParameters cipherParameters; + KeyParameter keyParam = null; + + byte[] nonce; + byte[] initialAssociatedText; + int macSize; + if (params instanceof AEADParameters) + { + AEADParameters param = (AEADParameters) params; + + nonce = param.getNonce(); + initialAssociatedText = param.getAssociatedText(); + macSize = getMacSize(forEncryption, param.getMacSize()); + cipherParameters = param.getKey(); + } + else if (params instanceof ParametersWithIV) + { + ParametersWithIV param = (ParametersWithIV) params; + + nonce = param.getIV(); + initialAssociatedText = null; + macSize = getMacSize(forEncryption, 64); + cipherParameters = param.getParameters(); + } + else + { + throw new IllegalArgumentException("invalid parameters passed to CCM"); + } + + // NOTE: Very basic support for key re-use, but no performance gain from it + if (cipherParameters != null) + { + keyParam = (KeyParameter) cipherParameters; + } + + if (keyParam != null) + { + byte[] key = keyParam.getKey(); + if (key == null) + { + throw new IllegalArgumentException("key was null"); + } + initRef(key); + } + + assert refWrapper != null; + + int iatLen = initialAssociatedText != null ? initialAssociatedText.length : 0; + initNative( + refWrapper.getReference(), + forEncryption, refWrapper.getKey(), + nonce, initialAssociatedText, iatLen, macSize * 8); + reset(); + initialised = true; + } + finally + { + Reference.reachabilityFence(this); + } + } + + + private void initRef(byte[] key) + { + refWrapper = new CCMRefWrapper(makeInstance(), Arrays.clone(key)); + } + + + @Override + public String getAlgorithmName() + { + return "AES/CCM"; + } + + @Override + public void processAADByte(byte in) + { + + associatedText.write(in); + } + + + @Override + public void processAADBytes(byte[] in, int inOff, int len) + { + + if (inOff < 0) + { + throw new IllegalArgumentException("offset is negative"); + } + if (len < 0) + { + throw new IllegalArgumentException("len is negative"); + } + if (in.length < inOff + len) + { + throw new IllegalArgumentException("array too short for offset + len"); + } + associatedText.write(in, inOff, len); + + } + + + @Override + public int processByte(byte in, byte[] out, int outOff) + throws DataLengthException + { + if (outOff < 0) + { + throw new IllegalArgumentException("offset is negative"); + } + + if (out != null && out.length < outOff) + { + throw new DataLengthException("offset past end"); + } + data.write(in); + return 0; + } + + + @Override + public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) + throws DataLengthException + { + if (inOff < 0 || outOff < 0) + { + throw new IllegalArgumentException("offset is negative"); + } + if (len < 0) + { + throw new IllegalArgumentException("len is negative"); + } + if (in == null) + { + throw new NullPointerException("input was null"); + } + if (in.length < (inOff + len)) + { + throw new DataLengthException("array too short for offset + len"); + } + + data.write(in, inOff, len); + + + return 0; + } + + + @Override + public int doFinal(byte[] out, int outOff) + throws IllegalStateException, InvalidCipherTextException + { + try + { + int len; + try + { + checkStatus(); + if (out == null) + { + throw new NullPointerException("output was null"); + } + if (outOff < 0) + { + throw new IllegalArgumentException("offset is negative"); + } + + if (getOutputSize(0) > out.length - outOff) + { + throw new OutputLengthException("output buffer too short"); + } + len = processPacket(refWrapper.getReference(), data.getBuffer(), 0, data.size(), associatedText.getBuffer(), 0, associatedText.size(), out, outOff); + resetKeepMac(); + // + // BlockCipherTest, testing ShortTagException. + // + } + catch (IllegalStateException e) + { + reset(); + throw e; + } + + return len; + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public byte[] getMac() + { + try + { + return getMac(refWrapper.getReference()); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public int getUpdateOutputSize(int len) + { + return 0; // Not relevant in CCM. + } + + + @Override + public int getOutputSize(int len) + { + try + { + return getOutputSize(refWrapper.getReference(), len + data.size()); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public void reset() + { + try + { + if (refWrapper == null) + { + // deal with reset being called before init. + return; + } + associatedText.reset(); + data.reset(); + reset(refWrapper.getReference(), false); + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public int processPacket(byte[] inBuf, int inOff, int length, byte[] outBuf, int outOff) + throws InvalidCipherTextException + { + try + { + int result = processPacket(refWrapper.getReference(), inBuf, inOff, length, associatedText.getBuffer(), 0, associatedText.size(), outBuf, outOff); + reset(); + return result; + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public byte[] processPacket(byte[] input, int inOff, int length) + throws InvalidCipherTextException + { + try + { + byte[] out = new byte[getOutputSize(length)]; + processPacket(input, inOff, length, out, 0); + reset(); + return out; + } + finally + { + Reference.reachabilityFence(this); + } + } + + + private void resetKeepMac() + { + try + { + if (refWrapper == null) + { + // deal with reset being called before init. + return; + } + associatedText.reset(); + data.reset(); + reset(refWrapper.getReference(), true); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + private void checkStatus() + { + if (!initialised) + { + if (forEncryption) + { + throw new IllegalStateException("CCM cipher cannot be reused for encryption"); + } + throw new IllegalStateException("CCM cipher needs to be initialised"); + } + } + + + private native void reset(long ref, boolean keepMac); + + static native void initNative( + long reference, + boolean forEncryption, + byte[] keyParam, + byte[] nonce, + byte[] initialAssociatedText, + int initialAssociatedTextLen, + int macSizeBits); + + static native long makeInstance(); + + static native void dispose(long nativeRef); + + + static native int getOutputSize(long ref, int len); + + static native byte[] getMac(long ref); + + static native int processPacket(long ref, byte[] in, int inOff, int inLen, byte[] aad, int aadOff, int aadlen, + byte[] out, int outOff); + + + private static class CCMRefWrapper + extends NativeReference + { + + private final byte[] key; + + public CCMRefWrapper(long reference, byte[] key) + { + super(reference, "CCM"); + this.key = key; + } + + @Override + public Runnable createAction() + { + return new Disposer(reference, key); + } + + + public byte[] getKey() + { + return key; + } + } + + + private static class Disposer + extends NativeDisposer + { + + private final byte[] key; + + + public Disposer(long reference, byte[] key) + { + super(reference); + this.key = key; + + } + + + @Override + protected void dispose(long reference) + { + Arrays.clear(key); + AESNativeCCM.dispose(reference); + } + } + + @Override + public String toString() + { + if (refWrapper != null && refWrapper.key != null) + { + return "CCM[Native](AES[Native](" + (refWrapper.key.length * 8) + "))"; + } + return "CCM[Native](AES[Native](not initialized))"; + } + + + private int getMacSize(boolean forEncryption, int requestedMacBits) + { + if (forEncryption && (requestedMacBits < 32 || requestedMacBits > 128 || 0 != (requestedMacBits & 15))) + { + throw new IllegalArgumentException("invalid value for MAC size"); + } + + return requestedMacBits >>> 3; + } + + private static class ExposedByteArrayOutputStream + extends ByteArrayOutputStream + { + public ExposedByteArrayOutputStream() + { + super(); + } + + public byte[] getBuffer() + { + return this.buf; + } + } +}
core/src/main/java9/org/bouncycastle/crypto/engines/AESNativeCFB.java+311 −0 added@@ -0,0 +1,311 @@ +package org.bouncycastle.crypto.engines; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.modes.CFBModeCipher; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.dispose.NativeDisposer; +import org.bouncycastle.util.dispose.NativeReference; + +import java.lang.ref.Reference; + +class AESNativeCFB + implements CFBModeCipher +{ + private final int bitSize; + private CFBRefWrapper referenceWrapper; + + private byte[] oldIv; + private boolean encrypting; + + public AESNativeCFB() + { + this(128); + } + + public AESNativeCFB(int bitSize) + { + this.bitSize = bitSize; + switch (bitSize) + { + case 128: + break; + default: + throw new IllegalArgumentException("native feedback bit size can only be 128"); + } + } + + @Override + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException + { + try + { + boolean oldEncrypting = this.encrypting; + + this.encrypting = forEncryption; + + byte[] key = null; + byte[] iv = null; + + if (params instanceof ParametersWithIV) + { + ParametersWithIV ivParam = (ParametersWithIV) params; + iv = ivParam.getIV(); + + if (iv.length > getBlockSize() || iv.length < 1) + { + throw new IllegalArgumentException("initialisation vector must be between one and block size length"); + } + + if (iv.length < getBlockSize()) + { + byte[] newIv = new byte[getBlockSize()]; + System.arraycopy(iv, 0, newIv, newIv.length - iv.length, iv.length); + iv = newIv; + } + + oldIv = Arrays.clone(iv); + + if (ivParam.getParameters() != null) + { + key = Arrays.clone(((KeyParameter) ivParam.getParameters()).getKey()); + } + + if (key != null) + { + oldEncrypting = encrypting; // Can change because key is supplied. + key = Arrays.clone(key); + } + else + { + // Use old key, it may be null but that is tested later. + key = referenceWrapper != null ? referenceWrapper.getKey() : null; + } + } + else + { + // + // Change of key. + // + + if (params instanceof KeyParameter) + { + key = Arrays.clone(((KeyParameter) params).getKey()); + iv = oldIv; + } + + } + + if (key == null && oldEncrypting != encrypting) + { + throw new IllegalArgumentException("cannot change encrypting state without providing key."); + } + + if (iv == null) + { + throw new IllegalArgumentException("iv is null"); + } + + + referenceWrapper = new CFBRefWrapper(makeNative(encrypting, key.length), key); + init(referenceWrapper.getReference(), key, iv); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public String getAlgorithmName() + { + return "AES/CFB"; + } + + @Override + public byte returnByte(byte in) + { + try + { + return processByte(referenceWrapper.getReference(), in); + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) + throws DataLengthException + { + try + { + if (referenceWrapper == null) + { + throw new IllegalStateException("not initialized"); + } + + + return processBytes(referenceWrapper.getReference(), in, inOff, len, out, outOff); + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public int getBlockSize() + { + return bitSize / 8; + } + + + @Override + public int processBlock(byte[] in, int inOff, byte[] out, int outOff) + throws DataLengthException, IllegalStateException + { + + try + { + if (referenceWrapper == null) + { + throw new IllegalStateException("not initialized"); + } + + return processBytes(referenceWrapper.getReference(), in, inOff, getBlockSize(), out, outOff); + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public void reset() + { + try + { + // skip over spurious resets that may occur before init is called. + if (referenceWrapper == null) + { + return; + } + + reset(referenceWrapper.getReference()); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public int getMultiBlockSize() + { + return getNativeMultiBlockSize(); + } + + @Override + public int processBlocks(byte[] in, int inOff, int blockCount, byte[] out, int outOff) + throws DataLengthException, IllegalStateException + { + try + { + + if (referenceWrapper == null) + { + throw new IllegalStateException("CFB engine not initialized"); + } + + return processBytes(in, inOff, blockCount * getBlockSize(), out, outOff); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + private static native byte processByte(long ref, byte in); + + private static native int processBytes(long ref, byte[] in, int inOff, int len, byte[] out, int outOff) + throws DataLengthException; + + static native long makeNative(boolean encrypting, int keyLen); + + native void init(long nativeRef, byte[] key, byte[] iv); + + static native void dispose(long ref); + + static native int getNativeMultiBlockSize(); + + private static native void reset(long nativeRef); + + + private static class Disposer + extends NativeDisposer + { + private final byte[] key; + + Disposer(long ref, byte[] key) + { + super(ref); + this.key = key; + } + + @Override + protected void dispose(long reference) + { + Arrays.clear(key); + AESNativeCFB.dispose(reference); + } + } + + private static class CFBRefWrapper + extends NativeReference + { + + private final byte[] key; + + public CFBRefWrapper(long reference, byte[] key) + { + super(reference, "CFB"); + this.key = key; + } + + public byte[] getKey() + { + return key; + } + + @Override + public Runnable createAction() + { + return new Disposer(reference, key); + } + } + + public String toString() + { + try + { + if (referenceWrapper != null && referenceWrapper.getKey() != null) + { + return "CFB[Native](AES[Native](" + (referenceWrapper.getKey().length * 8) + "))"; + } + return "CFB[Native](AES[Native](not initialized))"; + } + finally + { + Reference.reachabilityFence(this); + } + } + +}
core/src/main/java9/org/bouncycastle/crypto/engines/AESNativeCTR.java+357 −0 added@@ -0,0 +1,357 @@ +package org.bouncycastle.crypto.engines; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.modes.CTRModeCipher; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.dispose.NativeDisposer; +import org.bouncycastle.util.dispose.NativeReference; + +import java.lang.ref.Reference; + +public class AESNativeCTR + implements CTRModeCipher +{ + + private CTRRefWrapper referenceWrapper = null; + private int keyLen; + + + public AESNativeCTR() + { + } + + + public BlockCipher getUnderlyingCipher() + { + try + { + BlockCipher engine = AESEngine.newInstance(); + if (referenceWrapper != null) + { + byte[] k = referenceWrapper.getKey(); + if (k != null) + { + engine.init(true, new KeyParameter(referenceWrapper.getKey())); + } + } + return engine; + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public int getBlockSize() + { + return 16; + } + + + @Override + public int processBlock(byte[] in, int inOff, byte[] out, int outOff) + throws DataLengthException, IllegalStateException + { + + try + { + if (referenceWrapper == null) + { + throw new IllegalStateException("not initialized"); + } + + return processBytes(referenceWrapper.getReference(), in, inOff, getBlockSize(), out, outOff); + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public int getMultiBlockSize() + { + return getMultiBlockSize(0); + } + + @Override + public int processBlocks(byte[] in, int inOff, int blockCount, byte[] out, int outOff) + throws DataLengthException, IllegalStateException + { + try + { + int extent = getBlockSize() * blockCount; + + if (referenceWrapper == null) + { + throw new IllegalStateException("not initialized"); + } + + return processBytes(referenceWrapper.getReference(), in, inOff, extent, out, outOff); + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public long skip(long numberOfBytes) + { + try + { + if (referenceWrapper == null) + { + throw new IllegalStateException("not initialized"); + } + return skip(referenceWrapper.getReference(), numberOfBytes); + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public long seekTo(long position) + { + try + { + if (referenceWrapper == null) + { + throw new IllegalStateException("not initialized"); + } + return seekTo(referenceWrapper.getReference(), position); + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public long getPosition() + { + try + { + if (referenceWrapper == null) + { + throw new IllegalStateException("not initialized"); + } + return getPosition(referenceWrapper.getReference()); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException + { + try + { + if (params instanceof ParametersWithIV) + { + ParametersWithIV ivParam = (ParametersWithIV) params; + byte[] iv = ivParam.getIV(); + + int blockSize = getBlockSize(); + + int maxCounterSize = (8 > blockSize / 2) ? blockSize / 2 : 8; + + if (blockSize - iv.length > maxCounterSize) + { + throw new IllegalArgumentException("CTR mode requires IV of at least: " + (blockSize - maxCounterSize) + " bytes."); + } + + // + // if null it's an IV changed only. + if (ivParam.getParameters() == null) + { + if (referenceWrapper == null) + { + referenceWrapper = new CTRRefWrapper(makeCTRInstance(), null); + } + init(referenceWrapper.getReference(), referenceWrapper.getKey(), iv); + } + else + { + byte[] key = ((KeyParameter) ivParam.getParameters()).getKey(); + + switch (key.length) + { + case 16: + case 24: + case 32: + break; + default: + throw new IllegalArgumentException("invalid key length, key must be 16,24 or 32 bytes"); + } + + + keyLen = key.length * 8; + + referenceWrapper = new CTRRefWrapper(makeCTRInstance(), key); + init(referenceWrapper.getReference(), referenceWrapper.getKey(), iv); + + } + + reset(); + } + else + { + throw new IllegalArgumentException("CTR mode requires ParametersWithIV"); + } + } + finally + { + Reference.reachabilityFence(this); + } + } + + static native long makeCTRInstance(); + + @Override + public String getAlgorithmName() + { + return "AES/CTR"; + } + + @Override + public byte returnByte(byte in) + { + try + { + if (referenceWrapper == null) + { + throw new IllegalStateException("not initialized"); + } + return returnByte(referenceWrapper.getReference(), in); + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) + throws DataLengthException + { + try + { + if (referenceWrapper == null) + { + throw new IllegalStateException("not initialized"); + } + + return processBytes(referenceWrapper.getReference(), in, inOff, len, out, outOff); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public void reset() + { + try + { + if (referenceWrapper == null) + { + return; + } + + reset(referenceWrapper.getReference()); + } finally + { + Reference.reachabilityFence(this); + } + } + + private static native long getPosition(long reference); + + private static native int getMultiBlockSize(long ref); + + private static native long skip(long ref, long numberOfByte); + + private static native long seekTo(long ref, long position); + + static native void init(long ref, byte[] key, byte[] iv); + + private static native byte returnByte(long ref, byte b); + + private static native int processBytes(long ref, byte[] in, int inOff, int len, byte[] out, int outOff); + + private static native void reset(long ref); + + + native static void dispose(long ref); + + + private static class CTRRefWrapper + extends NativeReference + { + private final byte[] key; + + public CTRRefWrapper(long reference, byte[] key) + { + super(reference, "CTR"); + this.key = key; + } + + public byte[] getKey() + { + return key; + } + + @Override + public Runnable createAction() + { + return new Disposer(reference, key); + } + + + } + + + private static class Disposer + extends NativeDisposer + { + private final byte[] key; + + Disposer(long ref, byte[] key) + { + super(ref); + this.key = key; + } + + @Override + protected void dispose(long reference) + { + Arrays.clear(key); + AESNativeCTR.dispose(reference); + } + } + + public String toString() + { + if (keyLen > 0) + { + return "CTR[Native](AES[Native](" + keyLen + "))"; + } + return "CTR[Native](AES[Native](not initialized))"; + } + +}
core/src/main/java9/org/bouncycastle/crypto/engines/AESNativeEngine.java+281 −0 added@@ -0,0 +1,281 @@ +package org.bouncycastle.crypto.engines; + +import org.bouncycastle.crypto.*; +import org.bouncycastle.crypto.constraints.DefaultServiceProperties; +import org.bouncycastle.crypto.modes.*; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.util.dispose.NativeDisposer; +import org.bouncycastle.util.dispose.NativeReference; + +import java.lang.ref.Reference; + +class AESNativeEngine + extends DefaultMultiBlockCipher + implements NativeBlockCipherProvider, NativeCCMProvider, NativeEAXProvider, NativeOCBProvider, NativeGCMSIVProvider +{ + protected NativeReference wrapper = null; + private int keyLen = 0; + + AESNativeEngine() + { + CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(getAlgorithmName(), 256)); + } + + @Override + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException + { + try + { + if (params instanceof KeyParameter) + { + byte[] key = ((KeyParameter) params).getKey(); + + switch (key.length) + { + case 16: + case 24: + case 32: + wrapper = new ECBNativeRef(makeInstance(key.length, forEncryption)); + break; + + default: + throw new IllegalArgumentException("key must be 16, 24 or 32 bytes"); + } + + CryptoServicesRegistrar.checkConstraints( + new DefaultServiceProperties( + getAlgorithmName(), + key.length * 8, + params, + forEncryption ? CryptoServicePurpose.ENCRYPTION : CryptoServicePurpose.DECRYPTION + )); + + + init(wrapper.getReference(), key); + + keyLen = key.length * 8; + + return; + + } + + + throw new IllegalArgumentException("invalid parameter passed to AES init - " + params.getClass().getName()); + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public String getAlgorithmName() + { + return "AES"; + } + + @Override + public int getBlockSize() + { + return getBlockSize(0); + } + + @Override + public int processBlock(byte[] in, int inOff, byte[] out, int outOff) + throws DataLengthException, IllegalStateException + { + try + { + if (wrapper == null) + { + throw new IllegalStateException("not initialized"); + } + + return process(wrapper.getReference(), in, inOff, 1, out, outOff); + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public int getMultiBlockSize() + { + return getMultiBlockSize(0); + } + + + @Override + public int processBlocks(byte[] in, int inOff, int blockCount, byte[] out, int outOff) + throws DataLengthException, IllegalStateException + { + + try + { + if (wrapper == null) + { + throw new IllegalStateException("not initialized"); + } + + + return process(wrapper.getReference(), in, inOff, blockCount, out, outOff); + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public void reset() + { + try + { + // skip over spurious resets that may occur before init is called. + if (wrapper == null) + { + return; + } + reset(wrapper.getReference()); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public GCMModeCipher createGCM() + { + if (CryptoServicesRegistrar.hasEnabledService(NativeServices.AES_GCM)) + { + return new AESNativeGCM(); + } + + return new GCMBlockCipher(new AESEngine()); + } + + @Override + public GCMSIVModeCipher createGCMSIV() + { + if (CryptoServicesRegistrar.hasEnabledService(NativeServices.AES_GCMSIV)) + { + return new AESNativeGCMSIV(); + } + return new GCMSIVBlockCipher(new AESNativeEngine()); + } + + @Override + public CBCModeCipher createCBC() + { + if (CryptoServicesRegistrar.hasEnabledService(NativeServices.AES_CBC)) + { + return new AESNativeCBC(); + } + return new CBCBlockCipher(new AESNativeEngine()); + } + + @Override + public CFBModeCipher createCFB(int bitSize) + { + if (bitSize % 8 != 0 || bitSize == 0 || bitSize > 128) + { + throw new IllegalArgumentException("invalid CFB bitsize: " + bitSize); + } + + if (CryptoServicesRegistrar.hasEnabledService(NativeServices.AES_CFB)) + { + return new AESNativeCFB(bitSize); + } + + return new CFBBlockCipher(new AESNativeEngine(), bitSize); + } + + + @Override + public CTRModeCipher createCTR() + { + if (CryptoServicesRegistrar.hasEnabledService(NativeServices.AES_CTR)) + { + return new AESNativeCTR(); + } + + return new SICBlockCipher(AESEngine.newInstance()); + } + + @Override + public CCMModeCipher createCCM() + { + if (CryptoServicesRegistrar.hasEnabledService(NativeServices.AES_CCM)) + { + return new AESNativeCCM(); + } + + return new CCMBlockCipher(AESEngine.newInstance()); + } + + @Override + public EAXModeCipher createEAX() + { + return new EAXBlockCipher(AESEngine.newInstance()); + } + + @Override + public OCBModeCipher createOCB() + { + return new OCBBlockCipher(AESEngine.newInstance(), AESEngine.newInstance()); + } + + private static class Disposer + extends NativeDisposer + { + Disposer(long ref) + { + super(ref); + } + + @Override + protected void dispose(long reference) + { + AESNativeEngine.dispose(reference); + } + } + + private static class ECBNativeRef + extends NativeReference + { + + public ECBNativeRef(long reference) + { + super(reference, "ECB"); + } + + @Override + protected Runnable createAction() + { + return new Disposer(reference); + } + } + + public String toString() + { + return "AES[Native](" + keyLen + ")"; + } + + private static native void reset(long ref); + + private static native int process(long ref, byte[] in, int inOff, int blocks, byte[] out, int outOff); + + private static native int getMultiBlockSize(long nativeRef); + + private static native int getBlockSize(long ref); + + static native long makeInstance(int length, boolean forEncryption); + + static native void dispose(long ref); + + static native void init(long nativeRef, byte[] key); +}
core/src/main/java9/org/bouncycastle/crypto/engines/AESNativeGCM.java+477 −0 added@@ -0,0 +1,477 @@ +package org.bouncycastle.crypto.engines; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.modes.GCMModeCipher; +import org.bouncycastle.crypto.params.AEADParameters; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.dispose.NativeDisposer; +import org.bouncycastle.util.dispose.NativeReference; + +import java.lang.ref.Reference; + +class AESNativeGCM + implements GCMModeCipher +{ + private GCMRefWrapper refWrapper; + private byte[] oldNonce; + private boolean forEncryption = false; + private boolean initialised = false; + private byte[] keptMac = null; + + + @Override + public BlockCipher getUnderlyingCipher() + { + try + { + BlockCipher engine = AESEngine.newInstance(); + if (refWrapper != null && refWrapper.key != null) + { + engine.init(true, new KeyParameter(refWrapper.key)); + } + return engine; + } + finally + { + Reference.reachabilityFence(this); + } + } + + + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException + { + try + { + this.forEncryption = forEncryption; + KeyParameter keyParam; + byte[] newNonce = null; + keptMac = null; + int macSize; + byte[] initialAssociatedText; + + if (params instanceof AEADParameters) + { + AEADParameters param = (AEADParameters) params; + + newNonce = param.getNonce(); + initialAssociatedText = param.getAssociatedText(); + + int macSizeBits = param.getMacSize(); + if (macSizeBits < 32 || macSizeBits > 128 || macSizeBits % 8 != 0) + { + throw new IllegalArgumentException("invalid value for MAC size: " + macSizeBits); + } + + macSize = macSizeBits; + keyParam = param.getKey(); + } + else if (params instanceof ParametersWithIV) + { + ParametersWithIV param = (ParametersWithIV) params; + + newNonce = param.getIV(); + initialAssociatedText = null; + macSize = 128; + keyParam = (KeyParameter) param.getParameters(); + } + else + { + throw new IllegalArgumentException("invalid parameters passed to GCM"); + } + + if (newNonce == null || newNonce.length < 12) + { + throw new IllegalArgumentException("IV must be at least 12 bytes"); + } + + byte[] key = null; + + if (forEncryption) + { + if (oldNonce != null && Arrays.areEqual(oldNonce, newNonce)) + { + if (keyParam == null) + { + throw new IllegalArgumentException("cannot reuse nonce for GCM encryption"); + } + + if (refWrapper != null && refWrapper.key != null && Arrays.areEqual(refWrapper.key, keyParam.getKey())) + { + // same nonce, same key + throw new IllegalArgumentException("cannot reuse nonce for GCM encryption"); + } + + if (refWrapper != null && refWrapper.key != null) + { + key = Arrays.clone(refWrapper.key); // Case keyParam is null + } + } + } + + oldNonce = newNonce; + + if (keyParam != null) + { + key = keyParam.getKey(); + switch (key.length) + { + case 16: + case 24: + case 32: + break; + default: + throw new IllegalStateException("key must be only 16,24,or 32 bytes long."); + } + } + + initRef(key); + + initNative( + refWrapper.getReference(), + forEncryption, key, + oldNonce, initialAssociatedText, macSize); + + + initialised = true; + } + finally + { + Reference.reachabilityFence(this); + } + } + + + private void initRef(byte[] key) + { + try + { + refWrapper = new GCMRefWrapper(makeInstance(key.length, forEncryption), key); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public String getAlgorithmName() + { + return "AES/GCM"; + } + + @Override + public void processAADByte(byte in) + { + try + { + processAADByte(refWrapper.getReference(), in); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public void processAADBytes(byte[] in, int inOff, int len) + { + + try + { + if (refWrapper == null) + { + throw new IllegalStateException("GCM is uninitialized"); + } + + processAADBytes(refWrapper.getReference(), in, inOff, len); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public int processByte(byte in, byte[] out, int outOff) + throws DataLengthException + { + + try + { + if (refWrapper == null) + { + throw new IllegalStateException("GCM is uninitialized"); + } + + return processByte(refWrapper.getReference(), in, out, outOff); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) + throws DataLengthException + { + try + { + if (refWrapper == null) + { + throw new IllegalStateException("GCM is uninitialized"); + } + + return processBytes(refWrapper.getReference(), in, inOff, len, out, outOff); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public int doFinal(byte[] out, int outOff) + throws IllegalStateException, InvalidCipherTextException + { + + try + { + checkStatus(); + + + int len = doFinal(refWrapper.getReference(), out, outOff); + + // + // BlockCipherTest, testing ShortTagException. + // + + resetKeepMac(); + return len; + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public byte[] getMac() + { + try + { + if (keptMac != null) + { + return Arrays.clone(keptMac); + } + return getMac(refWrapper.getReference()); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public int getUpdateOutputSize(int len) + { + try + { + return getUpdateOutputSize(refWrapper.getReference(), len); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public int getOutputSize(int len) + { + try + { + return getOutputSize(refWrapper.getReference(), len); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public void reset() + { + try + { + if (refWrapper == null) + { + // deal with reset being called before init. + return; + } + + reset(refWrapper.getReference()); + initialised = false; + } + finally + { + Reference.reachabilityFence(this); + } + } + + private void resetKeepMac() + { + try + { + if (refWrapper == null) + { + // deal with reset being called before init. + return; + } + + keptMac = getMac(); + reset(refWrapper.getReference()); + initialised = false; + } + finally + { + Reference.reachabilityFence(this); + } + } + + + private void checkStatus() + { + if (!initialised) + { + if (forEncryption) + { + throw new IllegalStateException("GCM cipher cannot be reused for encryption"); + } + throw new IllegalStateException("GCM cipher needs to be initialised"); + } + } + + private native void reset(long ref); + + static native void initNative( + long reference, + boolean forEncryption, + byte[] keyParam, + byte[] nonce, + byte[] initialAssociatedText, + int macSizeBits); + + static native long makeInstance(int keySize, boolean forEncryption); + + static native void dispose(long nativeRef); + + private static native void processAADByte(long ref, byte in); + + private static native void processAADBytes(long ref, byte[] in, int inOff, int len); + + private static native int processByte(long ref, byte in, byte[] out, int outOff); + + private static native int processBytes(long ref, byte[] in, int inOff, int len, byte[] out, int outOff); + + private static native int doFinal(long ref, byte[] out, int outOff); + + private static native int getUpdateOutputSize(long ref, int len); + + private static native int getOutputSize(long ref, int len); + + public static native byte[] getMac(long ref); + + /** + * Set blocks remaining but only to a lesser value and only if the transformation has processed no data. + * Functionality limited to within the module only. + * + * @param value the step value. + */ + void setBlocksRemainingDown(long value) + { + try + { + setBlocksRemainingDown(refWrapper.getReference(), value); + } finally + { + Reference.reachabilityFence(this); + } + } + + // Set the blocks remaining, but only to a lesser value. + // This is intended for testing only and will throw from the native side if the + // transformation has processed any data. + private native void setBlocksRemainingDown(long nativeRef, long value); + + + private static class GCMRefWrapper + extends NativeReference + { + private final byte[] key; + + public GCMRefWrapper(long reference, byte[] key) + { + super(reference, "GCM"); + this.key = key; + } + + @Override + public Runnable createAction() + { + return new Disposer(reference, key); + } + + } + + + private static class Disposer + extends NativeDisposer + { + private final byte[] key; + + Disposer(long ref, byte[] key) + { + super(ref); + this.key = key; + } + + @Override + protected void dispose(long reference) + { + Arrays.clear(key); + AESNativeGCM.dispose(reference); + } + } + + @Override + public String toString() + { + try + { + if (refWrapper.key != null) + { + return "GCM[Native](AES[Native](" + (refWrapper.key.length * 8) + "))"; + } + return "GCM[Native](AES[Native](not initialized))"; + } + finally + { + Reference.reachabilityFence(this); + } + } +}
core/src/main/java9/org/bouncycastle/crypto/engines/AESNativeGCMSIV.java+423 −0 added@@ -0,0 +1,423 @@ +package org.bouncycastle.crypto.engines; + +import org.bouncycastle.crypto.*; +import org.bouncycastle.crypto.modes.GCMSIVModeCipher; +import org.bouncycastle.crypto.params.AEADParameters; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.dispose.NativeDisposer; +import org.bouncycastle.util.dispose.NativeReference; + +import java.io.ByteArrayOutputStream; +import java.lang.ref.Reference; + +public class AESNativeGCMSIV + implements GCMSIVModeCipher +{ + private GCMSIVRefWrapper refWrapper; + private byte[] keptMac; + + /** + * The encryptedDataStream + */ + private final GCMSIVCache theEncData = new GCMSIVCache(); + + /** + * Are we encrypting? + */ + private boolean forEncryption; + + + /** + * The nonce. + */ + private byte[] theNonce; + private byte[] theInitialAEAD; + + + @Override + public BlockCipher getUnderlyingCipher() + { + try + { + BlockCipher engine = AESEngine.newInstance(); + if (refWrapper != null && refWrapper.key != null) + { + engine.init(true, new KeyParameter(refWrapper.key)); + } + return engine; + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public void init(boolean forEncryption, CipherParameters cipherParameters) + throws IllegalArgumentException + { + try + { + this.forEncryption = forEncryption; + keptMac = null; + theEncData.reset(); + + /* Set defaults */ + byte[] myInitialAEAD = null; + byte[] myNonce; + KeyParameter myKey; + + /* Access parameters */ + if (cipherParameters instanceof AEADParameters) + { + final AEADParameters myAEAD = (AEADParameters) cipherParameters; + myInitialAEAD = myAEAD.getAssociatedText(); + myNonce = myAEAD.getNonce(); + myKey = myAEAD.getKey(); + } + else if (cipherParameters instanceof ParametersWithIV) + { + final ParametersWithIV myParms = (ParametersWithIV) cipherParameters; + myNonce = myParms.getIV(); + myKey = (KeyParameter) myParms.getParameters(); + } + else + { + throw new IllegalArgumentException("invalid parameters passed to GCM-SIV"); + } + + /* Reset details */ + /** + * The initialAEAD. + */ + theInitialAEAD = myInitialAEAD; + theNonce = myNonce; + byte[] keyBytes = myKey.getKey(); + switch (keyBytes.length) + { + case 16: + case 24: + case 32: + break; + default: + throw new IllegalStateException(ExceptionMessages.AES_KEY_LENGTH); + } + + initRef(keyBytes); + + + initNative( + refWrapper.getReference(), + forEncryption, refWrapper.key, + theNonce, theInitialAEAD); + } + finally + { + Reference.reachabilityFence(this); + } + } + + private void initRef(byte[] key) + { + refWrapper = new GCMSIVRefWrapper(makeInstance(), key); + } + + @Override + public String getAlgorithmName() + { + return "AES/GCM-SIV"; + } + + @Override + public void processAADByte(byte in) + { + try + { + if (refWrapper == null) + { + throw new IllegalStateException(ExceptionMessages.GCM_SIV_UNINITIALIZED); + } + processAADByte(refWrapper.getReference(), in); + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public void processAADBytes(byte[] in, int inOff, int len) + { + try + { + if (refWrapper == null) + { + throw new IllegalStateException(ExceptionMessages.GCM_SIV_UNINITIALIZED); + } + processAADBytes(refWrapper.getReference(), in, inOff, len); + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public int processByte(byte in, byte[] out, int outOff) + throws DataLengthException + { + try + { + if (refWrapper == null) + { + throw new IllegalStateException(ExceptionMessages.GCM_SIV_UNINITIALIZED); + } + theEncData.write(in); + return 0; + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) + throws DataLengthException + { + try + { + if (refWrapper == null) + { + throw new IllegalStateException(ExceptionMessages.GCM_SIV_UNINITIALIZED); + } + theEncData.write(in, inOff, len); + return 0; + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public int doFinal(byte[] out, int outOff) + throws IllegalStateException, InvalidCipherTextException + { + + try + { + int len = doFinal(refWrapper.getReference(), theEncData.getBuffer(), theEncData.size(), out, outOff); + //resetKeepMac + keptMac = getMac(); + reset(); + return len; + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public byte[] getMac() + { + try + { + if (refWrapper == null) + { + throw new IllegalStateException(ExceptionMessages.GCM_SIV_UNINITIALIZED); + } + + if (keptMac != null) + { + return Arrays.clone(keptMac); + } + return getMac(refWrapper.getReference()); + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public int getUpdateOutputSize(int len) + { + try + { + if (refWrapper == null) + { + throw new IllegalStateException(ExceptionMessages.GCM_SIV_UNINITIALIZED); + } + return getUpdateOutputSize(refWrapper.getReference(), len, theEncData.size()); + } + finally + { + Reference.reachabilityFence(this); + } + } + + @Override + public int getOutputSize(int len) + { + try + { + if (refWrapper == null) + { + throw new IllegalStateException(ExceptionMessages.GCM_SIV_UNINITIALIZED); + } + return getOutputSize(refWrapper.getReference(), len); + } + finally + { + Reference.reachabilityFence(this); + } + } + + + @Override + public void reset() + { + try + { + theEncData.clearBuffer(); + if (refWrapper == null) + { + // deal with reset being called before init. + return; + } + reset(refWrapper.getReference()); + initNative( + refWrapper.getReference(), + forEncryption, refWrapper.key, + theNonce, theInitialAEAD); + } + finally + { + Reference.reachabilityFence(this); + } + } + + public String toString() + { + try + { + if (refWrapper != null && refWrapper.key != null) + { + return "GCMSIV[Native](AES[Native](" + (refWrapper.key.length * 8) + "))"; + } + return "GCMSIV[Native](AES[Native](not initialized))"; + } + finally + { + Reference.reachabilityFence(this); + } + } + + private static class GCMSIVRefWrapper + extends NativeReference + { + private final byte[] key; + + public GCMSIVRefWrapper(long reference, byte[] key) + { + super(reference, "GCM-SIV"); + this.key = key; + } + + @Override + public Runnable createAction() + { + return new Disposer(reference, key); + } + + } + + private static class Disposer + extends NativeDisposer + { + private final byte[] key; + + Disposer(long ref, byte[] key) + { + super(ref); + this.key = key; + } + + @Override + protected void dispose(long reference) + { + Arrays.clear(key); + AESNativeGCMSIV.dispose(reference); + } + } + + private native void reset(long ref); + + static native void initNative( + long reference, + boolean forEncryption, + byte[] keyParam, + byte[] nonce, + byte[] initialAssociatedText); + + static native long makeInstance(); + + static native void dispose(long nativeRef); + + static native void processAADByte(long ref, byte in); + + static native void processAADBytes(long ref, byte[] in, int inOff, int len); + + static native int doFinal(long ref, byte[] input, int inputLen, byte[] out, int outOff); + + static native int getUpdateOutputSize(long ref, int len, int streamLen); + + static native int getOutputSize(long ref, int len); + + static native byte[] getMac(long ref); + + /** + * Test method, you have ABSOLUTELY no reason to call this in normal use. + * max_dl is the maximum amount of data the implementation will process. + */ + static native void test_set_max_dl(long ref, long value); + + + /** + * GCMSIVCache. + */ + private static class GCMSIVCache + extends ByteArrayOutputStream + { + /** + * Constructor. + */ + GCMSIVCache() + { + } + + /** + * Obtain the buffer. + * + * @return the buffer + */ + byte[] getBuffer() + { + return this.buf; + } + + /** + * Clear the buffer. + */ + void clearBuffer() + { + Arrays.fill(getBuffer(), (byte) 0); + reset(); + } + } + +}
core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java+6 −6 modified@@ -243,12 +243,12 @@ public int processBytes( len -= gapLen; } - if (in == out && Arrays.segmentsOverlap(inOff, len, outOff, length)) - { - in = new byte[len]; - System.arraycopy(out, inOff, in, 0, len); - inOff = 0; - } +// if (in == out && Arrays.segmentsOverlap(inOff, len, outOff, length)) +// { +// in = new byte[len]; +// System.arraycopy(out, inOff, in, 0, len); +// inOff = 0; +// } // if bufOff non-zero buffer must now be full if (bufOff != 0)
core/src/main/java/org/bouncycastle/crypto/digests/SHA224NativeDigest.java+46 −17 modified@@ -47,8 +47,11 @@ class SHA224NativeDigest SHA224NativeDigest restoreState(byte[] state, int offset) { - restoreFullState(nativeRef.getReference(), state, offset); - return this; + synchronized (this) + { + restoreFullState(nativeRef.getReference(), state, offset); + return this; + } } // @@ -64,43 +67,60 @@ public String getAlgorithmName() @Override public int getDigestSize() { - return getDigestSize(nativeRef.getReference()); + synchronized (this) + { + return getDigestSize(nativeRef.getReference()); + } } @Override public void update(byte in) { - - update(nativeRef.getReference(), in); + synchronized (this) + { + update(nativeRef.getReference(), in); + } } @Override public void update(byte[] input, int inOff, int len) { - update(nativeRef.getReference(), input, inOff, len); + synchronized (this) + { + update(nativeRef.getReference(), input, inOff, len); + } } @Override public int doFinal(byte[] output, int outOff) { - return doFinal(nativeRef.getReference(), output, outOff); + synchronized (this) + { + return doFinal(nativeRef.getReference(), output, outOff); + } } @Override public void reset() { - reset(nativeRef.getReference()); + synchronized (this) + { + reset(nativeRef.getReference()); + } } @Override public int getByteLength() { - return getByteLength(nativeRef.getReference()); + synchronized (this) + { + return getByteLength(nativeRef.getReference()); + } } @@ -113,23 +133,32 @@ public Memoable copy() @Override public void reset(Memoable other) { - SHA224NativeDigest dig = (SHA224NativeDigest) other; - restoreFullState(nativeRef.getReference(), dig.getEncodedState(), 0); + synchronized (this) + { + SHA224NativeDigest dig = (SHA224NativeDigest) other; + restoreFullState(nativeRef.getReference(), dig.getEncodedState(), 0); + } } public byte[] getEncodedState() { - int l = encodeFullState(nativeRef.getReference(), null, 0); - byte[] state = new byte[l]; - encodeFullState(nativeRef.getReference(), state, 0); - return state; + synchronized (this) + { + int l = encodeFullState(nativeRef.getReference(), null, 0); + byte[] state = new byte[l]; + encodeFullState(nativeRef.getReference(), state, 0); + return state; + } } void restoreFullState(byte[] encoded, int offset) { - restoreFullState(nativeRef.getReference(), encoded, offset); + synchronized (this) + { + restoreFullState(nativeRef.getReference(), encoded, offset); + } } @@ -187,7 +216,7 @@ private static class DigestRefWrapper public DigestRefWrapper(long reference) { - super(reference,"SHA224"); + super(reference, "SHA224"); } @Override
core/src/main/java/org/bouncycastle/crypto/digests/SHA256NativeDigest.java+51 −22 modified@@ -16,7 +16,7 @@ class SHA256NativeDigest { private final CryptoServicePurpose purpose; - protected DigestRefWrapper nativeRef = null; + private DigestRefWrapper nativeRef = null; SHA256NativeDigest(CryptoServicePurpose purpose) { @@ -47,8 +47,11 @@ class SHA256NativeDigest SHA256NativeDigest restoreState(byte[] state, int offset) { - restoreFullState(nativeRef.getReference(), state, offset); - return this; + synchronized (this) + { + restoreFullState(nativeRef.getReference(), state, offset); + return this; + } } // @@ -64,78 +67,104 @@ public String getAlgorithmName() @Override public int getDigestSize() { - return getDigestSize(nativeRef.getReference()); + synchronized (this) + { + return getDigestSize(nativeRef.getReference()); + } } @Override public void update(byte in) { - - update(nativeRef.getReference(), in); + synchronized (this) + { + update(nativeRef.getReference(), in); + } } @Override public void update(byte[] input, int inOff, int len) { - update(nativeRef.getReference(), input, inOff, len); + synchronized (this) + { + update(nativeRef.getReference(), input, inOff, len); + } } @Override public int doFinal(byte[] output, int outOff) { - return doFinal(nativeRef.getReference(), output, outOff); + synchronized (this) + { + return doFinal(nativeRef.getReference(), output, outOff); + } } @Override public void reset() { - reset(nativeRef.getReference()); + synchronized (this) + { + reset(nativeRef.getReference()); + } } @Override public int getByteLength() { - return getByteLength(nativeRef.getReference()); + synchronized (this) + { + return getByteLength(nativeRef.getReference()); + } } @Override public Memoable copy() { - return new SHA256NativeDigest(this); + synchronized (this) + { + return new SHA256NativeDigest(this); + } } @Override public void reset(Memoable other) { - SHA256NativeDigest dig = (SHA256NativeDigest) other; - restoreFullState(nativeRef.getReference(), dig.getEncodedState(), 0); + synchronized (this) + { + SHA256NativeDigest dig = (SHA256NativeDigest) other; + restoreFullState(nativeRef.getReference(), dig.getEncodedState(), 0); + } } public byte[] getEncodedState() { - int l = encodeFullState(nativeRef.getReference(), null, 0); - byte[] state = new byte[l]; - encodeFullState(nativeRef.getReference(), state, 0); - return state; + synchronized (this) + { + int l = encodeFullState(nativeRef.getReference(), null, 0); + byte[] state = new byte[l]; + encodeFullState(nativeRef.getReference(), state, 0); + return state; + } } - - void restoreFullState(byte[] encoded, int offset) { - restoreFullState(nativeRef.getReference(), encoded, offset); + synchronized (this) + { + restoreFullState(nativeRef.getReference(), encoded, offset); + } } - @Override public String toString() { @@ -190,7 +219,7 @@ private static class DigestRefWrapper public DigestRefWrapper(long reference) { - super(reference,"SHA256"); + super(reference, "SHA256"); } @Override
core/src/main/java/org/bouncycastle/crypto/digests/SHA384NativeDigest.java+50 −21 modified@@ -47,8 +47,11 @@ class SHA384NativeDigest SHA384NativeDigest restoreState(byte[] state, int offset) { - restoreFullState(nativeRef.getReference(), state, offset); - return this; + synchronized (this) + { + restoreFullState(nativeRef.getReference(), state, offset); + return this; + } } // @@ -64,78 +67,104 @@ public String getAlgorithmName() @Override public int getDigestSize() { - return getDigestSize(nativeRef.getReference()); + synchronized (this) + { + return getDigestSize(nativeRef.getReference()); + } } @Override public void update(byte in) { - - update(nativeRef.getReference(), in); + synchronized (this) + { + update(nativeRef.getReference(), in); + } } @Override public void update(byte[] input, int inOff, int len) { - update(nativeRef.getReference(), input, inOff, len); + synchronized (this) + { + update(nativeRef.getReference(), input, inOff, len); + } } @Override public int doFinal(byte[] output, int outOff) { - return doFinal(nativeRef.getReference(), output, outOff); + synchronized (this) + { + return doFinal(nativeRef.getReference(), output, outOff); + } } @Override public void reset() { - reset(nativeRef.getReference()); + synchronized (this) + { + reset(nativeRef.getReference()); + } } @Override public int getByteLength() { - return getByteLength(nativeRef.getReference()); + synchronized (this) + { + return getByteLength(nativeRef.getReference()); + } } @Override public Memoable copy() { - return new SHA384NativeDigest(this); + synchronized (this) + { + return new SHA384NativeDigest(this); + } } @Override public void reset(Memoable other) { - SHA384NativeDigest dig = (SHA384NativeDigest) other; - restoreFullState(nativeRef.getReference(), dig.getEncodedState(), 0); + synchronized (this) + { + SHA384NativeDigest dig = (SHA384NativeDigest) other; + restoreFullState(nativeRef.getReference(), dig.getEncodedState(), 0); + } } public byte[] getEncodedState() { - int l = encodeFullState(nativeRef.getReference(), null, 0); - byte[] state = new byte[l]; - encodeFullState(nativeRef.getReference(), state, 0); - return state; + synchronized (this) + { + int l = encodeFullState(nativeRef.getReference(), null, 0); + byte[] state = new byte[l]; + encodeFullState(nativeRef.getReference(), state, 0); + return state; + } } - - void restoreFullState(byte[] encoded, int offset) { - restoreFullState(nativeRef.getReference(), encoded, offset); + synchronized (this) + { + restoreFullState(nativeRef.getReference(), encoded, offset); + } } - @Override public String toString() { @@ -190,7 +219,7 @@ private static class DigestRefWrapper public DigestRefWrapper(long reference) { - super(reference,"SHA384"); + super(reference, "SHA384"); } @Override
core/src/main/java/org/bouncycastle/crypto/digests/SHA3NativeDigest.java+49 −19 modified@@ -17,12 +17,12 @@ public class SHA3NativeDigest private int bitLen; - public SHA3NativeDigest(CryptoServicePurpose purpose) + public SHA3NativeDigest(CryptoServicePurpose purpose) { this(256, purpose); } - public SHA3NativeDigest(int bitLen, CryptoServicePurpose purpose) + public SHA3NativeDigest(int bitLen, CryptoServicePurpose purpose) { if (!CryptoServicesRegistrar.hasEnabledService(NativeServices.SHA3)) { @@ -71,8 +71,11 @@ public SHA3NativeDigest(byte[] encoded) SHA3NativeDigest restoreState(byte[] state, int offset) { - restoreFullState(nativeRef.getReference(), state, offset); - return this; + synchronized (this) + { + restoreFullState(nativeRef.getReference(), state, offset); + return this; + } } // @@ -89,44 +92,62 @@ public String getAlgorithmName() @Override public int getDigestSize() { - return getDigestSize(nativeRef.getReference()); + synchronized (this) + { + return getDigestSize(nativeRef.getReference()); + } } @Override public void update(byte in) { - update(nativeRef.getReference(), in); + synchronized (this) + { + update(nativeRef.getReference(), in); + } } @Override public void update(byte[] input, int inOff, int len) { - update(nativeRef.getReference(), input, inOff, len); + synchronized (this) + { + update(nativeRef.getReference(), input, inOff, len); + } } @Override public int doFinal(byte[] output, int outOff) { - int i = doFinal(nativeRef.getReference(), output, outOff); - reset(); - return i; + synchronized (this) + { + int i = doFinal(nativeRef.getReference(), output, outOff); + reset(); + return i; + } } @Override public void reset() { - reset(nativeRef.getReference()); + synchronized (this) + { + reset(nativeRef.getReference()); + } } @Override public int getByteLength() { - return getByteLength(nativeRef.getReference()); + synchronized (this) + { + return getByteLength(nativeRef.getReference()); + } } @@ -139,23 +160,32 @@ public Memoable copy() @Override public void reset(Memoable other) { - SHA3NativeDigest dig = (SHA3NativeDigest) other; - restoreFullState(nativeRef.getReference(), dig.getEncodedState(), 0); + synchronized (this) + { + SHA3NativeDigest dig = (SHA3NativeDigest) other; + restoreFullState(nativeRef.getReference(), dig.getEncodedState(), 0); + } } public byte[] getEncodedState() { - int l = encodeFullState(nativeRef.getReference(), null, 0); - byte[] state = new byte[l]; - encodeFullState(nativeRef.getReference(), state, 0); - return state; + synchronized (this) + { + int l = encodeFullState(nativeRef.getReference(), null, 0); + byte[] state = new byte[l]; + encodeFullState(nativeRef.getReference(), state, 0); + return state; + } } void restoreFullState(byte[] encoded, int offset) { - restoreFullState(nativeRef.getReference(), encoded, offset); + synchronized (this) + { + restoreFullState(nativeRef.getReference(), encoded, offset); + } }
core/src/main/java/org/bouncycastle/crypto/digests/SHA512NativeDigest.java+50 −21 modified@@ -47,8 +47,11 @@ class SHA512NativeDigest SHA512NativeDigest restoreState(byte[] state, int offset) { - restoreFullState(nativeRef.getReference(), state, offset); - return this; + synchronized (this) + { + restoreFullState(nativeRef.getReference(), state, offset); + return this; + } } // @@ -64,78 +67,104 @@ public String getAlgorithmName() @Override public int getDigestSize() { - return getDigestSize(nativeRef.getReference()); + synchronized (this) + { + return getDigestSize(nativeRef.getReference()); + } } @Override public void update(byte in) { - - update(nativeRef.getReference(), in); + synchronized (this) + { + update(nativeRef.getReference(), in); + } } @Override public void update(byte[] input, int inOff, int len) { - update(nativeRef.getReference(), input, inOff, len); + synchronized (this) + { + update(nativeRef.getReference(), input, inOff, len); + } } @Override public int doFinal(byte[] output, int outOff) { - return doFinal(nativeRef.getReference(), output, outOff); + synchronized (this) + { + return doFinal(nativeRef.getReference(), output, outOff); + } } @Override public void reset() { - reset(nativeRef.getReference()); + synchronized (this) + { + reset(nativeRef.getReference()); + } } @Override public int getByteLength() { - return getByteLength(nativeRef.getReference()); + synchronized (this) + { + return getByteLength(nativeRef.getReference()); + } } @Override public Memoable copy() { - return new SHA512NativeDigest(this); + synchronized (this) + { + return new SHA512NativeDigest(this); + } } @Override public void reset(Memoable other) { - SHA512NativeDigest dig = (SHA512NativeDigest) other; - restoreFullState(nativeRef.getReference(), dig.getEncodedState(), 0); + synchronized (this) + { + SHA512NativeDigest dig = (SHA512NativeDigest) other; + restoreFullState(nativeRef.getReference(), dig.getEncodedState(), 0); + } } public byte[] getEncodedState() { - int l = encodeFullState(nativeRef.getReference(), null, 0); - byte[] state = new byte[l]; - encodeFullState(nativeRef.getReference(), state, 0); - return state; + synchronized (this) + { + int l = encodeFullState(nativeRef.getReference(), null, 0); + byte[] state = new byte[l]; + encodeFullState(nativeRef.getReference(), state, 0); + return state; + } } - - void restoreFullState(byte[] encoded, int offset) { - restoreFullState(nativeRef.getReference(), encoded, offset); + synchronized (this) + { + restoreFullState(nativeRef.getReference(), encoded, offset); + } } - @Override public String toString() { @@ -190,7 +219,7 @@ private static class DigestRefWrapper public DigestRefWrapper(long reference) { - super(reference,"SHA512"); + super(reference, "SHA512"); } @Override
core/src/main/java/org/bouncycastle/crypto/digests/SHAKENativeDigest.java+59 −20 modified@@ -69,8 +69,11 @@ public SHAKENativeDigest(byte[] encoded) SHAKENativeDigest restoreState(byte[] state, int offset) { - restoreFullState(nativeRef.getReference(), state, offset); - return this; + synchronized (this) + { + restoreFullState(nativeRef.getReference(), state, offset); + return this; + } } // @@ -87,85 +90,121 @@ public String getAlgorithmName() @Override public int getDigestSize() { - return getDigestSize(nativeRef.getReference()); + synchronized (this) + { + return getDigestSize(nativeRef.getReference()); + } } @Override public void update(byte in) { - update(nativeRef.getReference(), in); + synchronized (this) + { + update(nativeRef.getReference(), in); + } } @Override public void update(byte[] input, int inOff, int len) { - update(nativeRef.getReference(), input, inOff, len); + synchronized (this) + { + update(nativeRef.getReference(), input, inOff, len); + } } @Override public int doFinal(byte[] output, int outOff) { - int i = doFinal(nativeRef.getReference(), output, outOff); - return i; + synchronized (this) + { + int i = doFinal(nativeRef.getReference(), output, outOff); + return i; + } } @Override public int doFinal(byte[] out, int outOff, int outLen) { - int i = doFinal(nativeRef.getReference(),out,outOff,outLen); - return i; + synchronized (this) + { + int i = doFinal(nativeRef.getReference(), out, outOff, outLen); + return i; + } } @Override public int doOutput(byte[] out, int outOff, int outLen) { - return doOutput(nativeRef.getReference(),out,outOff,outLen); + synchronized (this) + { + return doOutput(nativeRef.getReference(), out, outOff, outLen); + } } @Override public void reset() { - reset(nativeRef.getReference()); + synchronized (this) + { + reset(nativeRef.getReference()); + } } @Override public int getByteLength() { - return getByteLength(nativeRef.getReference()); + synchronized (this) + { + return getByteLength(nativeRef.getReference()); + } } @Override public Memoable copy() { - return new SHAKENativeDigest(this); + synchronized (this) + { + return new SHAKENativeDigest(this); + } } @Override public void reset(Memoable other) { - SHAKENativeDigest dig = (SHAKENativeDigest) other; - restoreFullState(nativeRef.getReference(), dig.getEncodedState(), 0); + synchronized (this) + { + SHAKENativeDigest dig = (SHAKENativeDigest) other; + restoreFullState(nativeRef.getReference(), dig.getEncodedState(), 0); + } } public byte[] getEncodedState() { - int l = encodeFullState(nativeRef.getReference(), null, 0); - byte[] state = new byte[l]; - encodeFullState(nativeRef.getReference(), state, 0); - return state; + synchronized (this) + { + int l = encodeFullState(nativeRef.getReference(), null, 0); + byte[] state = new byte[l]; + encodeFullState(nativeRef.getReference(), state, 0); + return state; + } } void restoreFullState(byte[] encoded, int offset) { - restoreFullState(nativeRef.getReference(), encoded, offset); + synchronized (this) + { + restoreFullState(nativeRef.getReference(), encoded, offset); + } }
core/src/main/java/org/bouncycastle/crypto/engines/AESNativeCBC.java+101 −85 modified@@ -25,107 +25,113 @@ class AESNativeCBC public void init(boolean forEncryption, CipherParameters params) throws IllegalArgumentException { - boolean oldEncrypting = this.encrypting; - this.encrypting = forEncryption; - - if (params instanceof ParametersWithIV) + synchronized (this) { - ParametersWithIV ivParam = (ParametersWithIV)params; - byte[] iv = ivParam.getIV(); - - if (iv.length != getBlockSize()) - { - throw new IllegalArgumentException("initialisation vector must be the same length as block size"); - } - - System.arraycopy(iv, 0, IV, 0, iv.length); + boolean oldEncrypting = this.encrypting; - reset(); + this.encrypting = forEncryption; - // if null it's an IV changed only. - if (ivParam.getParameters() != null) - { - init((KeyParameter)ivParam.getParameters()); - // cipher.init(encrypting, ivParam.getParameters()); - } - else + if (params instanceof ParametersWithIV) { - // The key parameter was null which inidicates that they - // IV is being changed. + ParametersWithIV ivParam = (ParametersWithIV) params; + byte[] iv = ivParam.getIV(); - if (oldEncrypting != encrypting) + if (iv.length != getBlockSize()) { - throw new IllegalArgumentException("cannot change encrypting state without providing key"); + throw new IllegalArgumentException("initialisation vector must be the same length as block size"); } - if (referenceWrapper == null) + System.arraycopy(iv, 0, IV, 0, iv.length); + + reset(); + + // if null it's an IV changed only. + if (ivParam.getParameters() != null) { - throw new IllegalStateException("IV change attempted but not previously initialized with a key"); + init((KeyParameter) ivParam.getParameters()); + // cipher.init(encrypting, ivParam.getParameters()); } + else + { + // The key parameter was null which inidicates that they + // IV is being changed. - // We need to use the old key because - // the native layer requires a both key and iv each time. - init(new KeyParameter(referenceWrapper.oldKey)); + if (oldEncrypting != encrypting) + { + throw new IllegalArgumentException("cannot change encrypting state without providing key"); + } - } - } - else - { - reset(); + if (referenceWrapper == null) + { + throw new IllegalStateException("IV change attempted but not previously initialized with a key"); + } - // if it's null, key is to be reused. - if (params != null) - { - init((KeyParameter)params); - // cipher.init(encrypting, params); + // We need to use the old key because + // the native layer requires a both key and iv each time. + init(new KeyParameter(referenceWrapper.oldKey)); + + } } else { - if (oldEncrypting != encrypting) - { - throw new IllegalArgumentException("cannot change encrypting state without providing key."); - } + reset(); - if (referenceWrapper == null) + // if it's null, key is to be reused. + if (params != null) { - throw new IllegalStateException("IV change attempted but not previously initialized with a key"); + init((KeyParameter) params); + // cipher.init(encrypting, params); } + else + { + if (oldEncrypting != encrypting) + { + throw new IllegalArgumentException("cannot change encrypting state without providing key."); + } + + if (referenceWrapper == null) + { + throw new IllegalStateException("IV change attempted but not previously initialized with a key"); + } - // We need to use the old key because the - // native layer requires a both key and iv each time. - init(new KeyParameter(referenceWrapper.oldKey)); + // We need to use the old key because the + // native layer requires a both key and iv each time. + init(new KeyParameter(referenceWrapper.oldKey)); + } } } - } private void init(KeyParameter parameters) { - byte[] key = ((KeyParameter)parameters).getKey(); + synchronized (this) + { + byte[] key = ((KeyParameter) parameters).getKey(); + + switch (key.length) + { + case 16: + case 24: + case 32: + break; + default: + throw new IllegalArgumentException("key must be only 16,24,or 32 bytes long."); + } - switch (key.length) - { - case 16: - case 24: - case 32: - break; - default: - throw new IllegalArgumentException("key must be only 16,24,or 32 bytes long."); - } + referenceWrapper = new CBCRefWrapper(makeNative(key.length, encrypting), Arrays.clone(key)); - referenceWrapper = new CBCRefWrapper(makeNative(key.length, encrypting), Arrays.clone(key)); + if (referenceWrapper.getReference() == 0) + { + throw new IllegalStateException("Native CBC native instance returned a null pointer."); + } - if (referenceWrapper.getReference() == 0) - { - throw new IllegalStateException("Native CBC native instance returned a null pointer."); + init(referenceWrapper.getReference(), key, IV); + keySize = key.length * 8; } - - init(referenceWrapper.getReference(), key, IV); - keySize = key.length * 8; } @@ -147,25 +153,30 @@ public int processBlock(byte[] in, int inOff, byte[] out, int outOff) throws DataLengthException, IllegalStateException { - if (referenceWrapper == null) + synchronized (this) { - throw new IllegalStateException("not initialized"); - } + if (referenceWrapper == null) + { + throw new IllegalStateException("not initialized"); + } - return process(referenceWrapper.getReference(), in, inOff, 1, out, outOff); + return process(referenceWrapper.getReference(), in, inOff, 1, out, outOff); + } } @Override public void reset() { - // skip over spurious resets that may occur before init is called. - if (referenceWrapper == null) + synchronized (this) { - return; - } - - reset(referenceWrapper.getReference()); + // skip over spurious resets that may occur before init is called. + if (referenceWrapper == null) + { + return; + } + reset(referenceWrapper.getReference()); + } } @@ -179,14 +190,16 @@ public int getMultiBlockSize() public int processBlocks(byte[] in, int inOff, int blockCount, byte[] out, int outOff) throws DataLengthException, IllegalStateException { + synchronized (this) + { + if (referenceWrapper == null) + { + throw new IllegalStateException("not initialized"); + } - if (referenceWrapper == null) - { - throw new IllegalStateException("not initialized"); + return process(referenceWrapper.getReference(), in, inOff, blockCount, out, outOff); } - - return process(referenceWrapper.getReference(), in, inOff, blockCount, out, outOff); } private static native int process(long ref, byte[] in, int inOff, int blockCount, byte[] out, int outOff); @@ -206,9 +219,12 @@ public int processBlocks(byte[] in, int inOff, int blockCount, byte[] out, int o @Override public BlockCipher getUnderlyingCipher() { - MultiBlockCipher eng = AESEngine.newInstance(); - eng.init(encrypting, new KeyParameter(referenceWrapper.oldKey)); - return eng; + synchronized (this) + { + MultiBlockCipher eng = AESEngine.newInstance(); + eng.init(encrypting, new KeyParameter(referenceWrapper.oldKey)); + return eng; + } }
core/src/main/java/org/bouncycastle/crypto/engines/AESNativeCCM.java+129 −104 modified@@ -27,75 +27,81 @@ class AESNativeCCM @Override public BlockCipher getUnderlyingCipher() { - BlockCipher engine = AESEngine.newInstance(); - - if (refWrapper != null && refWrapper.key != null) + synchronized (this) { - engine.init(true, new KeyParameter(refWrapper.key)); - } + BlockCipher engine = AESEngine.newInstance(); - return engine; + if (refWrapper != null && refWrapper.key != null) + { + engine.init(true, new KeyParameter(refWrapper.key)); + } + + return engine; + } } public void init(boolean forEncryption, CipherParameters params) throws IllegalArgumentException { - this.forEncryption = forEncryption; - CipherParameters cipherParameters; - KeyParameter keyParam = null; - - byte[] nonce; - byte[] initialAssociatedText; - int macSize; - if (params instanceof AEADParameters) + synchronized (this) { - AEADParameters param = (AEADParameters) params; + this.forEncryption = forEncryption; + CipherParameters cipherParameters; + KeyParameter keyParam = null; + + byte[] nonce; + byte[] initialAssociatedText; + int macSize; + if (params instanceof AEADParameters) + { + AEADParameters param = (AEADParameters) params; - nonce = param.getNonce(); - initialAssociatedText = param.getAssociatedText(); - macSize = getMacSize(forEncryption, param.getMacSize()); - cipherParameters = param.getKey(); - } - else if (params instanceof ParametersWithIV) - { - ParametersWithIV param = (ParametersWithIV) params; + nonce = param.getNonce(); + initialAssociatedText = param.getAssociatedText(); + macSize = getMacSize(forEncryption, param.getMacSize()); + cipherParameters = param.getKey(); + } + else if (params instanceof ParametersWithIV) + { + ParametersWithIV param = (ParametersWithIV) params; - nonce = param.getIV(); - initialAssociatedText = null; - macSize = getMacSize(forEncryption, 64); - cipherParameters = param.getParameters(); - } - else - { - throw new IllegalArgumentException("invalid parameters passed to CCM"); - } + nonce = param.getIV(); + initialAssociatedText = null; + macSize = getMacSize(forEncryption, 64); + cipherParameters = param.getParameters(); + } + else + { + throw new IllegalArgumentException("invalid parameters passed to CCM"); + } - // NOTE: Very basic support for key re-use, but no performance gain from it - if (cipherParameters != null) - { - keyParam = (KeyParameter) cipherParameters; - } + // NOTE: Very basic support for key re-use, but no performance gain from it + if (cipherParameters != null) + { + keyParam = (KeyParameter) cipherParameters; + } - if (keyParam != null) - { - byte[] key = keyParam.getKey(); - if (key == null) + if (keyParam != null) { - throw new IllegalArgumentException("key was null"); + byte[] key = keyParam.getKey(); + if (key == null) + { + throw new IllegalArgumentException("key was null"); + } + initRef(key); } - initRef(key); - } - assert refWrapper != null; + assert refWrapper != null; - int iatLen = initialAssociatedText != null ? initialAssociatedText.length : 0; - initNative( - refWrapper.getReference(), - forEncryption, refWrapper.getKey(), - nonce, initialAssociatedText, iatLen, macSize * 8); - reset(); - initialised = true; + int iatLen = initialAssociatedText != null ? initialAssociatedText.length : 0; + initNative( + refWrapper.getReference(), + forEncryption, refWrapper.key, + nonce, initialAssociatedText, iatLen, macSize * 8); + reset(); + initialised = true; + } } @@ -187,43 +193,49 @@ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) public int doFinal(byte[] out, int outOff) throws IllegalStateException, InvalidCipherTextException { - int len; - try + synchronized (this) { - checkStatus(); - if (out == null) + int len; + try { - throw new NullPointerException("output was null"); + checkStatus(); + if (out == null) + { + throw new NullPointerException("output was null"); + } + if (outOff < 0) + { + throw new IllegalArgumentException("offset is negative"); + } + + if (getOutputSize(0) > out.length - outOff) + { + throw new OutputLengthException("output buffer too short"); + } + len = processPacket(refWrapper.getReference(), data.getBuffer(), 0, data.size(), associatedText.getBuffer(), 0, associatedText.size(), out, outOff); + resetKeepMac(); + // + // BlockCipherTest, testing ShortTagException. + // } - if (outOff < 0) + catch (IllegalStateException e) { - throw new IllegalArgumentException("offset is negative"); + reset(); + throw e; } - if (getOutputSize(0) > out.length - outOff) - { - throw new OutputLengthException("output buffer too short"); - } - len = processPacket(refWrapper.getReference(), data.getBuffer(), 0, data.size(), associatedText.getBuffer(), 0, associatedText.size(), out, outOff); - resetKeepMac(); - // - // BlockCipherTest, testing ShortTagException. - // + return len; } - catch (IllegalStateException e) - { - reset(); - throw e; - } - - return len; } @Override public byte[] getMac() { - return getMac(refWrapper.getReference()); + synchronized (this) + { + return getMac(refWrapper.getReference()); + } } @@ -237,33 +249,42 @@ public int getUpdateOutputSize(int len) @Override public int getOutputSize(int len) { - return getOutputSize(refWrapper.getReference(), len + data.size()); + synchronized (this) + { + return getOutputSize(refWrapper.getReference(), len + data.size()); + } } @Override public void reset() { - if (refWrapper == null) + synchronized (this) { - // deal with reset being called before init. - return; + if (refWrapper == null) + { + // deal with reset being called before init. + return; + } + associatedText.reset(); + data.reset(); + reset(refWrapper.getReference(), false); } - associatedText.reset(); - data.reset(); - reset(refWrapper.getReference(), false); } private void resetKeepMac() { - if (refWrapper == null) + synchronized (this) { - // deal with reset being called before init. - return; + if (refWrapper == null) + { + // deal with reset being called before init. + return; + } + associatedText.reset(); + data.reset(); + reset(refWrapper.getReference(), true); } - associatedText.reset(); - data.reset(); - reset(refWrapper.getReference(), true); } @@ -306,19 +327,25 @@ static native int processPacket(long ref, byte[] in, int inOff, int inLen, byte[ public int processPacket(byte[] inBuf, int inOff, int length, byte[] outBuf, int outOff) throws InvalidCipherTextException { - int result = processPacket(refWrapper.getReference(), inBuf, inOff, length, associatedText.getBuffer(), 0, associatedText.size(), outBuf, outOff); - reset(); - return result; + synchronized (this) + { + int result = processPacket(refWrapper.getReference(), inBuf, inOff, length, associatedText.getBuffer(), 0, associatedText.size(), outBuf, outOff); + reset(); + return result; + } } @Override public byte[] processPacket(byte[] input, int inOff, int length) throws InvalidCipherTextException { - byte[] out = new byte[getOutputSize(length)]; - processPacket(input, inOff, length, out, 0); - reset(); - return out; + synchronized (this) + { + byte[] out = new byte[getOutputSize(length)]; + processPacket(input, inOff, length, out, 0); + reset(); + return out; + } } private static class CCMRefWrapper @@ -339,11 +366,6 @@ public Runnable createAction() return new Disposer(reference, key); } - - public byte[] getKey() - { - return key; - } } @@ -373,11 +395,14 @@ protected void dispose(long reference) @Override public String toString() { - if (refWrapper != null && refWrapper.key != null) + synchronized (this) { - return "CCM[Native](AES[Native](" + (refWrapper.key.length * 8) + "))"; + if (refWrapper != null && refWrapper.key != null) + { + return "CCM[Native](AES[Native](" + (refWrapper.key.length * 8) + "))"; + } + return "CCM[Native](AES[Native](not initialized))"; } - return "CCM[Native](AES[Native](not initialized))"; }
core/src/main/java/org/bouncycastle/crypto/engines/AESNativeCFB.java+93 −76 modified@@ -39,77 +39,78 @@ public AESNativeCFB(int bitSize) public void init(boolean forEncryption, CipherParameters params) throws IllegalArgumentException { + synchronized (this) + { + boolean oldEncrypting = this.encrypting; - boolean oldEncrypting = this.encrypting; - - this.encrypting = forEncryption; - - byte[] key = null; - byte[] iv = null; + this.encrypting = forEncryption; - if (params instanceof ParametersWithIV) - { - ParametersWithIV ivParam = (ParametersWithIV) params; - iv = ivParam.getIV(); + byte[] key = null; + byte[] iv = null; - if (iv.length > getBlockSize() || iv.length < 1) + if (params instanceof ParametersWithIV) { - throw new IllegalArgumentException("initialisation vector must be between one and block size length"); + ParametersWithIV ivParam = (ParametersWithIV) params; + iv = ivParam.getIV(); + + if (iv.length > getBlockSize() || iv.length < 1) + { + throw new IllegalArgumentException("initialisation vector must be between one and block size length"); + } + + if (iv.length < getBlockSize()) + { + byte[] newIv = new byte[getBlockSize()]; + System.arraycopy(iv, 0, newIv, newIv.length - iv.length, iv.length); + iv = newIv; + } + + oldIv = Arrays.clone(iv); + + if (ivParam.getParameters() != null) + { + key = Arrays.clone(((KeyParameter) ivParam.getParameters()).getKey()); + } + + if (key != null) + { + oldEncrypting = encrypting; // Can change because key is supplied. + key = Arrays.clone(key); + } + else + { + // Use old key, it may be null but that is tested later. + key = referenceWrapper != null ? referenceWrapper.getKey() : null; + } } - - if (iv.length < getBlockSize()) + else { - byte[] newIv = new byte[getBlockSize()]; - System.arraycopy(iv, 0, newIv, newIv.length - iv.length, iv.length); - iv = newIv; - } + // + // Change of key. + // - oldIv = Arrays.clone(iv); + if (params instanceof KeyParameter) + { + key = Arrays.clone(((KeyParameter) params).getKey()); + iv = oldIv; + } - if (ivParam.getParameters() != null) - { - key = Arrays.clone(((KeyParameter) ivParam.getParameters()).getKey()); } - if (key != null) - { - oldEncrypting = encrypting; // Can change because key is supplied. - key = Arrays.clone(key); - } - else + if (key == null && oldEncrypting != encrypting) { - // Use old key, it may be null but that is tested later. - key = referenceWrapper!=null? referenceWrapper.getKey():null; + throw new IllegalArgumentException("cannot change encrypting state without providing key."); } - } - else - { - // - // Change of key. - // - if (params instanceof KeyParameter) + if (iv == null) { - key = Arrays.clone(((KeyParameter) params).getKey()); - iv = oldIv; + throw new IllegalArgumentException("iv is null"); } - } - if (key == null && oldEncrypting != encrypting) - { - throw new IllegalArgumentException("cannot change encrypting state without providing key."); + referenceWrapper = new CFBRefWrapper(makeNative(encrypting, key.length), key); + init(referenceWrapper.getReference(), key, iv); } - - if (iv == null) - { - throw new IllegalArgumentException("iv is null"); - } - - - referenceWrapper = new CFBRefWrapper(makeNative(encrypting, key.length), key); - init(referenceWrapper.getReference(), key, iv); - } @@ -122,21 +123,27 @@ public String getAlgorithmName() @Override public byte returnByte(byte in) { - return processByte(referenceWrapper.getReference(), in); + synchronized (this) + { + return processByte(referenceWrapper.getReference(), in); + } } @Override public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) throws DataLengthException { - if (referenceWrapper == null) + synchronized (this) { - throw new IllegalStateException("not initialized"); - } + if (referenceWrapper == null) + { + throw new IllegalStateException("not initialized"); + } - return processBytes(referenceWrapper.getReference(), in, inOff, len, out, outOff); + return processBytes(referenceWrapper.getReference(), in, inOff, len, out, outOff); + } } @Override @@ -150,25 +157,30 @@ public int getBlockSize() public int processBlock(byte[] in, int inOff, byte[] out, int outOff) throws DataLengthException, IllegalStateException { - - if (referenceWrapper == null) + synchronized (this) { - throw new IllegalStateException("not initialized"); - } + if (referenceWrapper == null) + { + throw new IllegalStateException("not initialized"); + } - return processBytes(referenceWrapper.getReference(), in, inOff, getBlockSize(), out, outOff); + return processBytes(referenceWrapper.getReference(), in, inOff, getBlockSize(), out, outOff); + } } @Override public void reset() { - // skip over spurious resets that may occur before init is called. - if (referenceWrapper == null) + synchronized (this) { - return; - } + // skip over spurious resets that may occur before init is called. + if (referenceWrapper == null) + { + return; + } - reset(referenceWrapper.getReference()); + reset(referenceWrapper.getReference()); + } } @@ -184,13 +196,15 @@ public int processBlocks(byte[] in, int inOff, int blockCount, byte[] out, int o throws DataLengthException, IllegalStateException { - - if (referenceWrapper == null) + synchronized (this) { - throw new IllegalStateException("CFB engine not initialized"); - } + if (referenceWrapper == null) + { + throw new IllegalStateException("CFB engine not initialized"); + } - return processBytes(in, inOff, blockCount * getBlockSize(), out, outOff); + return processBytes(in, inOff, blockCount * getBlockSize(), out, outOff); + } } @@ -255,11 +269,14 @@ public Runnable createAction() public String toString() { - if (referenceWrapper != null && referenceWrapper.getKey() != null) + synchronized (this) { - return "CFB[Native](AES[Native](" + (referenceWrapper.getKey().length * 8) + "))"; + if (referenceWrapper != null && referenceWrapper.getKey() != null) + { + return "CFB[Native](AES[Native](" + (referenceWrapper.getKey().length * 8) + "))"; + } + return "CFB[Native](AES[Native](not initialized))"; } - return "CFB[Native](AES[Native](not initialized))"; } }
core/src/main/java/org/bouncycastle/crypto/engines/AESNativeCTR.java+110 −78 modified@@ -1,6 +1,8 @@ package org.bouncycastle.crypto.engines; -import org.bouncycastle.crypto.*; +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.modes.CTRModeCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; @@ -23,16 +25,19 @@ public AESNativeCTR() public BlockCipher getUnderlyingCipher() { - BlockCipher engine = AESEngine.newInstance(); - if (referenceWrapper != null) + synchronized (this) { - byte[] k = referenceWrapper.getKey(); - if (k != null) + BlockCipher engine = AESEngine.newInstance(); + if (referenceWrapper != null) { - engine.init(true, new KeyParameter(referenceWrapper.getKey())); + byte[] k = referenceWrapper.getKey(); + if (k != null) + { + engine.init(true, new KeyParameter(referenceWrapper.getKey())); + } } + return engine; } - return engine; } @@ -47,124 +52,143 @@ public int getBlockSize() public int processBlock(byte[] in, int inOff, byte[] out, int outOff) throws DataLengthException, IllegalStateException { - - if (referenceWrapper == null) + synchronized (this) { - throw new IllegalStateException("not initialized"); - } - - return processBytes(referenceWrapper.getReference(), in, inOff, getBlockSize(), out, outOff); + if (referenceWrapper == null) + { + throw new IllegalStateException("not initialized"); + } + return processBytes(referenceWrapper.getReference(), in, inOff, getBlockSize(), out, outOff); + } } @Override public int getMultiBlockSize() { - return getMultiBlockSize(0); + synchronized (this) + { + return getMultiBlockSize(0); + } } @Override public int processBlocks(byte[] in, int inOff, int blockCount, byte[] out, int outOff) throws DataLengthException, IllegalStateException { - int extent = getBlockSize() * blockCount; - - if (referenceWrapper == null) + synchronized (this) { - throw new IllegalStateException("not initialized"); - } + int extent = getBlockSize() * blockCount; + + if (referenceWrapper == null) + { + throw new IllegalStateException("not initialized"); + } - return processBytes(referenceWrapper.getReference(), in, inOff, extent, out, outOff); + return processBytes(referenceWrapper.getReference(), in, inOff, extent, out, outOff); + } } @Override public long skip(long numberOfBytes) { - if (referenceWrapper == null) + synchronized (this) { - throw new IllegalStateException("not initialized"); + if (referenceWrapper == null) + { + throw new IllegalStateException("not initialized"); + } + return skip(referenceWrapper.getReference(), numberOfBytes); } - return skip(referenceWrapper.getReference(), numberOfBytes); } @Override public long seekTo(long position) { - if (referenceWrapper == null) + synchronized (this) { - throw new IllegalStateException("not initialized"); + if (referenceWrapper == null) + { + throw new IllegalStateException("not initialized"); + } + return seekTo(referenceWrapper.getReference(), position); } - return seekTo(referenceWrapper.getReference(), position); } @Override public long getPosition() { - if (referenceWrapper == null) + synchronized (this) { - throw new IllegalStateException("not initialized"); + if (referenceWrapper == null) + { + throw new IllegalStateException("not initialized"); + } + return getPosition(referenceWrapper.getReference()); } - return getPosition(referenceWrapper.getReference()); } @Override public void init(boolean forEncryption, CipherParameters params) throws IllegalArgumentException { - if (params instanceof ParametersWithIV) + synchronized (this) { - ParametersWithIV ivParam = (ParametersWithIV) params; - byte[] iv = ivParam.getIV(); - - int blockSize = getBlockSize(); + if (params instanceof ParametersWithIV) + { + ParametersWithIV ivParam = (ParametersWithIV) params; + byte[] iv = ivParam.getIV(); - int maxCounterSize = (8 > blockSize / 2) ? blockSize / 2 : 8; + int blockSize = getBlockSize(); - if (blockSize - iv.length > maxCounterSize) - { - throw new IllegalArgumentException("CTR mode requires IV of at least: " + (blockSize - maxCounterSize) + " bytes."); - } + int maxCounterSize = (8 > blockSize / 2) ? blockSize / 2 : 8; - // - // if null it's an IV changed only. - if (ivParam.getParameters() == null) - { - if (referenceWrapper == null) + if (blockSize - iv.length > maxCounterSize) { - referenceWrapper = new CTRRefWrapper(makeCTRInstance(), null); + throw new IllegalArgumentException("CTR mode requires IV of at least: " + (blockSize - maxCounterSize) + " bytes."); } - init(referenceWrapper.getReference(), referenceWrapper.getKey(), iv); - } - else - { - byte[] key = ((KeyParameter) ivParam.getParameters()).getKey(); - switch (key.length) + // + // if null it's an IV changed only. + if (ivParam.getParameters() == null) { - case 16: - case 24: - case 32: - break; - default: - throw new IllegalArgumentException("invalid key length, key must be 16,24 or 32 bytes"); + if (referenceWrapper == null) + { + referenceWrapper = new CTRRefWrapper(makeCTRInstance(), null); + } + init(referenceWrapper.getReference(), referenceWrapper.getKey(), iv); } + else + { + byte[] key = ((KeyParameter) ivParam.getParameters()).getKey(); + switch (key.length) + { + case 16: + case 24: + case 32: + break; + default: + throw new IllegalArgumentException("invalid key length, key must be 16,24 or 32 bytes"); + } - keyLen = key.length * 8; - referenceWrapper = new CTRRefWrapper(makeCTRInstance(), key); - init(referenceWrapper.getReference(), referenceWrapper.getKey(), iv); + keyLen = key.length * 8; - } + referenceWrapper = new CTRRefWrapper(makeCTRInstance(), key); + init(referenceWrapper.getReference(), referenceWrapper.getKey(), iv); - reset(); - } - else - { - throw new IllegalArgumentException("CTR mode requires ParametersWithIV"); + } + + reset(); + } + else + { + throw new IllegalArgumentException("CTR mode requires ParametersWithIV"); + } } } @@ -179,36 +203,44 @@ public String getAlgorithmName() @Override public byte returnByte(byte in) { - if (referenceWrapper == null) + synchronized (this) { - throw new IllegalStateException("not initialized"); + if (referenceWrapper == null) + { + throw new IllegalStateException("not initialized"); + } + return returnByte(referenceWrapper.getReference(), in); } - return returnByte(referenceWrapper.getReference(), in); } @Override public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) throws DataLengthException { - - if (referenceWrapper == null) + synchronized (this) { - throw new IllegalStateException("not initialized"); - } + if (referenceWrapper == null) + { + throw new IllegalStateException("not initialized"); + } - return processBytes(referenceWrapper.getReference(), in, inOff, len, out, outOff); + return processBytes(referenceWrapper.getReference(), in, inOff, len, out, outOff); + } } @Override public void reset() { - if (referenceWrapper == null) + synchronized (this) { - return; - } + if (referenceWrapper == null) + { + return; + } - reset(referenceWrapper.getReference()); + reset(referenceWrapper.getReference()); + } } private static native long getPosition(long reference);
core/src/main/java/org/bouncycastle/crypto/engines/AESNativeEngine.java+66 −52 modified@@ -48,40 +48,43 @@ class AESNativeEngine public void init(boolean forEncryption, CipherParameters params) throws IllegalArgumentException { - if (params instanceof KeyParameter) + synchronized (this) { - byte[] key = ((KeyParameter)params).getKey(); - - switch (key.length) + if (params instanceof KeyParameter) { - case 16: - case 24: - case 32: - wrapper = new ECBNativeRef(makeInstance(key.length, forEncryption)); - break; - - default: - throw new IllegalArgumentException("key must be 16, 24 or 32 bytes"); - } + byte[] key = ((KeyParameter) params).getKey(); - CryptoServicesRegistrar.checkConstraints( - new DefaultServiceProperties( - getAlgorithmName(), - key.length * 8, - params, - forEncryption ? CryptoServicePurpose.ENCRYPTION : CryptoServicePurpose.DECRYPTION - )); + switch (key.length) + { + case 16: + case 24: + case 32: + wrapper = new ECBNativeRef(makeInstance(key.length, forEncryption)); + break; + default: + throw new IllegalArgumentException("key must be 16, 24 or 32 bytes"); + } - init(wrapper.getReference(), key); + CryptoServicesRegistrar.checkConstraints( + new DefaultServiceProperties( + getAlgorithmName(), + key.length * 8, + params, + forEncryption ? CryptoServicePurpose.ENCRYPTION : CryptoServicePurpose.DECRYPTION + )); - keyLen = key.length * 8; - return; - } + init(wrapper.getReference(), key); + keyLen = key.length * 8; - throw new IllegalArgumentException("invalid parameter passed to AES init - " + params.getClass().getName()); + return; + } + + + throw new IllegalArgumentException("invalid parameter passed to AES init - " + params.getClass().getName()); + } } @Override @@ -100,12 +103,16 @@ public int getBlockSize() public int processBlock(byte[] in, int inOff, byte[] out, int outOff) throws DataLengthException, IllegalStateException { - if (wrapper == null) + synchronized (this) { - throw new IllegalStateException("not initialized"); - } - return process(wrapper.getReference(), in, inOff, 1, out, outOff); + if (wrapper == null) + { + throw new IllegalStateException("not initialized"); + } + + return process(wrapper.getReference(), in, inOff, 1, out, outOff); + } } @Override @@ -119,42 +126,34 @@ public int getMultiBlockSize() public int processBlocks(byte[] in, int inOff, int blockCount, byte[] out, int outOff) throws DataLengthException, IllegalStateException { - - - if (wrapper == null) + synchronized (this) { - throw new IllegalStateException("not initialized"); - } + + if (wrapper == null) + { + throw new IllegalStateException("not initialized"); + } - return process(wrapper.getReference(), in, inOff, blockCount, out, outOff); + return process(wrapper.getReference(), in, inOff, blockCount, out, outOff); + } } @Override public void reset() { - // skip over spurious resets that may occur before init is called. - if (wrapper == null) + synchronized (this) { - return; + // skip over spurious resets that may occur before init is called. + if (wrapper == null) + { + return; + } + reset(wrapper.getReference()); } - reset(wrapper.getReference()); } - private static native void reset(long ref); - - private static native int process(long ref, byte[] in, int inOff, int blocks, byte[] out, int outOff); - - private static native int getMultiBlockSize(long nativeRef); - - private static native int getBlockSize(long ref); - - static native long makeInstance(int length, boolean forEncryption); - - static native void dispose(long ref); - - static native void init(long nativeRef, byte[] key); @Override public GCMModeCipher createGCM() @@ -273,4 +272,19 @@ public String toString() { return "AES[Native](" + keyLen + ")"; } + + private static native void reset(long ref); + + private static native int process(long ref, byte[] in, int inOff, int blocks, byte[] out, int outOff); + + private static native int getMultiBlockSize(long nativeRef); + + private static native int getBlockSize(long ref); + + static native long makeInstance(int length, boolean forEncryption); + + static native void dispose(long ref); + + static native void init(long nativeRef, byte[] key); + }
core/src/main/java/org/bouncycastle/crypto/engines/AESNativeGCM.java+205 −163 modified@@ -25,109 +25,115 @@ class AESNativeGCM @Override public BlockCipher getUnderlyingCipher() { - BlockCipher engine = AESEngine.newInstance(); - if (refWrapper != null && refWrapper.key != null) + synchronized (this) { - engine.init(true, new KeyParameter(refWrapper.key)); + BlockCipher engine = AESEngine.newInstance(); + if (refWrapper != null && refWrapper.key != null) + { + engine.init(true, new KeyParameter(refWrapper.key)); + } + return engine; } - return engine; } public void init(boolean forEncryption, CipherParameters params) throws IllegalArgumentException { - this.forEncryption = forEncryption; - KeyParameter keyParam; - byte[] newNonce = null; - keptMac = null; - int macSize; - byte[] initialAssociatedText; - - if (params instanceof AEADParameters) + synchronized (this) { - AEADParameters param = (AEADParameters) params; - - newNonce = param.getNonce(); - initialAssociatedText = param.getAssociatedText(); - - int macSizeBits = param.getMacSize(); - if (macSizeBits < 32 || macSizeBits > 128 || macSizeBits % 8 != 0) + this.forEncryption = forEncryption; + KeyParameter keyParam; + byte[] newNonce = null; + keptMac = null; + int macSize; + byte[] initialAssociatedText; + + if (params instanceof AEADParameters) { - throw new IllegalArgumentException("invalid value for MAC size: " + macSizeBits); - } + AEADParameters param = (AEADParameters) params; - macSize = macSizeBits; - keyParam = param.getKey(); - } - else if (params instanceof ParametersWithIV) - { - ParametersWithIV param = (ParametersWithIV) params; + newNonce = param.getNonce(); + initialAssociatedText = param.getAssociatedText(); - newNonce = param.getIV(); - initialAssociatedText = null; - macSize = 128; - keyParam = (KeyParameter) param.getParameters(); - } - else - { - throw new IllegalArgumentException("invalid parameters passed to GCM"); - } + int macSizeBits = param.getMacSize(); + if (macSizeBits < 32 || macSizeBits > 128 || macSizeBits % 8 != 0) + { + throw new IllegalArgumentException("invalid value for MAC size: " + macSizeBits); + } - if (newNonce == null || newNonce.length < 12) - { - throw new IllegalArgumentException("IV must be at least 12 bytes"); - } + macSize = macSizeBits; + keyParam = param.getKey(); + } + else if (params instanceof ParametersWithIV) + { + ParametersWithIV param = (ParametersWithIV) params; - byte[] key = null; + newNonce = param.getIV(); + initialAssociatedText = null; + macSize = 128; + keyParam = (KeyParameter) param.getParameters(); + } + else + { + throw new IllegalArgumentException("invalid parameters passed to GCM"); + } - if (forEncryption) - { - if (oldNonce != null && Arrays.areEqual(oldNonce, newNonce)) + if (newNonce == null || newNonce.length < 12) { - if (keyParam == null) - { - throw new IllegalArgumentException("cannot reuse nonce for GCM encryption"); - } + throw new IllegalArgumentException("IV must be at least 12 bytes"); + } - if (refWrapper != null && refWrapper.key != null && Arrays.areEqual(refWrapper.key, keyParam.getKey())) - { - // same nonce, same key - throw new IllegalArgumentException("cannot reuse nonce for GCM encryption"); - } + byte[] key = null; - if (refWrapper != null && refWrapper.key != null) + if (forEncryption) + { + if (oldNonce != null && Arrays.areEqual(oldNonce, newNonce)) { - key = Arrays.clone(refWrapper.key); // Case keyParam is null + if (keyParam == null) + { + throw new IllegalArgumentException("cannot reuse nonce for GCM encryption"); + } + + if (refWrapper != null && refWrapper.key != null && Arrays.areEqual(refWrapper.key, keyParam.getKey())) + { + // same nonce, same key + throw new IllegalArgumentException("cannot reuse nonce for GCM encryption"); + } + + if (refWrapper != null && refWrapper.key != null) + { + key = Arrays.clone(refWrapper.key); // Case keyParam is null + } } } - } - oldNonce = newNonce; + oldNonce = newNonce; - if (keyParam != null) - { - key = keyParam.getKey(); - switch (key.length) + if (keyParam != null) { - case 16: - case 24: - case 32: - break; - default: - throw new IllegalStateException("key must be only 16,24,or 32 bytes long."); + key = keyParam.getKey(); + switch (key.length) + { + case 16: + case 24: + case 32: + break; + default: + throw new IllegalStateException("key must be only 16,24,or 32 bytes long."); + } } - } - initRef(key); + initRef(key); - initNative( - refWrapper.getReference(), - forEncryption, key, - oldNonce, initialAssociatedText, macSize); + initNative( + refWrapper.getReference(), + forEncryption, key, + oldNonce, initialAssociatedText, macSize); - initialised = true; + initialised = true; + } } @@ -146,50 +152,59 @@ public String getAlgorithmName() @Override public void processAADByte(byte in) { - processAADByte(refWrapper.getReference(), in); + synchronized (this) + { + processAADByte(refWrapper.getReference(), in); + } } @Override public void processAADBytes(byte[] in, int inOff, int len) { - -// - if (refWrapper == null) + synchronized (this) { - throw new IllegalStateException("GCM is uninitialized"); - } +// + if (refWrapper == null) + { + throw new IllegalStateException("GCM is uninitialized"); + } - processAADBytes(refWrapper.getReference(), in, inOff, len); + processAADBytes(refWrapper.getReference(), in, inOff, len); + } } @Override public int processByte(byte in, byte[] out, int outOff) throws DataLengthException { + synchronized (this) + { + if (refWrapper == null) + { + throw new IllegalStateException("GCM is uninitialized"); + } - if (refWrapper == null) - { - throw new IllegalStateException("GCM is uninitialized"); + return processByte(refWrapper.getReference(), in, out, outOff); } - - return processByte(refWrapper.getReference(), in, out, outOff); } @Override public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) throws DataLengthException { - - if (refWrapper == null) + synchronized (this) { - throw new IllegalStateException("GCM is uninitialized"); - } + if (refWrapper == null) + { + throw new IllegalStateException("GCM is uninitialized"); + } - return processBytes(refWrapper.getReference(), in, inOff, len, out, outOff); + return processBytes(refWrapper.getReference(), in, inOff, len, out, outOff); + } } @@ -198,70 +213,88 @@ public int doFinal(byte[] out, int outOff) throws IllegalStateException, InvalidCipherTextException { - checkStatus(); + synchronized (this) + { + checkStatus(); - int len = doFinal(refWrapper.getReference(), out, outOff); + int len = doFinal(refWrapper.getReference(), out, outOff); - // - // BlockCipherTest, testing ShortTagException. - // + // + // BlockCipherTest, testing ShortTagException. + // - resetKeepMac(); - return len; + resetKeepMac(); + return len; + } } @Override public byte[] getMac() { - if (keptMac != null) + synchronized (this) { - return Arrays.clone(keptMac); + if (keptMac != null) + { + return Arrays.clone(keptMac); + } + return getMac(refWrapper.getReference()); } - return getMac(refWrapper.getReference()); } @Override public int getUpdateOutputSize(int len) { - return getUpdateOutputSize(refWrapper.getReference(), len); + synchronized (this) + { + return getUpdateOutputSize(refWrapper.getReference(), len); + } } @Override public int getOutputSize(int len) { - return getOutputSize(refWrapper.getReference(), len); + synchronized (this) + { + return getOutputSize(refWrapper.getReference(), len); + } } @Override public void reset() { - if (refWrapper == null) + synchronized (this) { - // deal with reset being called before init. - return; - } + if (refWrapper == null) + { + // deal with reset being called before init. + return; + } - reset(refWrapper.getReference()); - initialised = false; + reset(refWrapper.getReference()); + initialised = false; + } } private void resetKeepMac() { - if (refWrapper == null) + synchronized (this) { - // deal with reset being called before init. - return; - } + if (refWrapper == null) + { + // deal with reset being called before init. + return; + } - keptMac = getMac(); - reset(refWrapper.getReference()); - initialised = false; + keptMac = getMac(); + reset(refWrapper.getReference()); + initialised = false; + } } @@ -277,53 +310,21 @@ private void checkStatus() } } - private native void reset(long ref); - - static native void initNative( - long reference, - boolean forEncryption, - byte[] keyParam, - byte[] nonce, - byte[] initialAssociatedText, - int macSizeBits); - - static native long makeInstance(int keySize, boolean forEncryption); - static native void dispose(long nativeRef); - private static native void processAADByte(long ref, byte in); - - private static native void processAADBytes(long ref, byte[] in, int inOff, int len); - - private static native int processByte(long ref, byte in, byte[] out, int outOff); - - private static native int processBytes(long ref, byte[] in, int inOff, int len, byte[] out, int outOff); - - private static native int doFinal(long ref, byte[] out, int outOff); - - private static native int getUpdateOutputSize(long ref, int len); - - private static native int getOutputSize(long ref, int len); - - public static native byte[] getMac(long ref); - - /** - * Set blocks remaining but only to a lesser value and only if the transformation has processed no data. - * Functionality limited to within the module only. - * - * @param value the step value. - */ - void setBlocksRemainingDown(long value) + @Override + public String toString() { - setBlocksRemainingDown(refWrapper.getReference(), value); + synchronized (this) + { + if (refWrapper.key != null) + { + return "GCM[Native](AES[Native](" + (refWrapper.key.length * 8) + "))"; + } + return "GCM[Native](AES[Native](not initialized))"; + } } - // Set the blocks remaining, but only to a lesser value. - // This is intended for testing only and will throw from the native side if the - // transformation has processed any data. - private native void setBlocksRemainingDown(long nativeRef, long value); - - private static class GCMRefWrapper extends NativeReference { @@ -363,13 +364,54 @@ protected void dispose(long reference) } } - @Override - public String toString() + private native void reset(long ref); + + static native void initNative( + long reference, + boolean forEncryption, + byte[] keyParam, + byte[] nonce, + byte[] initialAssociatedText, + int macSizeBits); + + static native long makeInstance(int keySize, boolean forEncryption); + + static native void dispose(long nativeRef); + + private static native void processAADByte(long ref, byte in); + + private static native void processAADBytes(long ref, byte[] in, int inOff, int len); + + private static native int processByte(long ref, byte in, byte[] out, int outOff); + + private static native int processBytes(long ref, byte[] in, int inOff, int len, byte[] out, int outOff); + + private static native int doFinal(long ref, byte[] out, int outOff); + + private static native int getUpdateOutputSize(long ref, int len); + + private static native int getOutputSize(long ref, int len); + + public static native byte[] getMac(long ref); + + /** + * Set blocks remaining but only to a lesser value and only if the transformation has processed no data. + * Functionality limited to within the module only. + * + * @param value the step value. + */ + void setBlocksRemainingDown(long value) { - if (refWrapper.key != null) + synchronized (this) { - return "GCM[Native](AES[Native](" + (refWrapper.key.length * 8) + "))"; + setBlocksRemainingDown(refWrapper.getReference(), value); } - return "GCM[Native](AES[Native](not initialized))"; } + + // Set the blocks remaining, but only to a lesser value. + // This is intended for testing only and will throw from the native side if the + // transformation has processed any data. + private native void setBlocksRemainingDown(long nativeRef, long value); + + }
core/src/main/java/org/bouncycastle/crypto/engines/AESNativeGCMSIV.java+125 −99 modified@@ -1,12 +1,6 @@ package org.bouncycastle.crypto.engines; -import java.io.ByteArrayOutputStream; - -import org.bouncycastle.crypto.BlockCipher; -import org.bouncycastle.crypto.CipherParameters; -import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.ExceptionMessages; -import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.*; import org.bouncycastle.crypto.modes.GCMSIVModeCipher; import org.bouncycastle.crypto.params.AEADParameters; import org.bouncycastle.crypto.params.KeyParameter; @@ -15,6 +9,8 @@ import org.bouncycastle.util.dispose.NativeDisposer; import org.bouncycastle.util.dispose.NativeReference; +import java.io.ByteArrayOutputStream; + public class AESNativeGCMSIV implements GCMSIVModeCipher { @@ -46,68 +42,73 @@ public class AESNativeGCMSIV @Override public BlockCipher getUnderlyingCipher() { - BlockCipher engine = AESEngine.newInstance(); - if (lastKey != null) + synchronized (this) { - engine.init(true, new KeyParameter(lastKey)); + BlockCipher engine = AESEngine.newInstance(); + if (lastKey != null) + { + engine.init(true, new KeyParameter(lastKey)); + } + return engine; } - return engine; } @Override public void init(boolean forEncryption, CipherParameters cipherParameters) throws IllegalArgumentException { - this.forEncryption = forEncryption; - keptMac = null; - theEncData.reset(); - - /* Set defaults */ - byte[] myInitialAEAD = null; - byte[] myNonce; - KeyParameter myKey; - - /* Access parameters */ - if (cipherParameters instanceof AEADParameters) - { - final AEADParameters myAEAD = (AEADParameters) cipherParameters; - myInitialAEAD = myAEAD.getAssociatedText(); - myNonce = myAEAD.getNonce(); - myKey = myAEAD.getKey(); - } - else if (cipherParameters instanceof ParametersWithIV) - { - final ParametersWithIV myParms = (ParametersWithIV) cipherParameters; - myNonce = myParms.getIV(); - myKey = (KeyParameter) myParms.getParameters(); - } - else - { - throw new IllegalArgumentException("invalid parameters passed to GCM-SIV"); - } - - /* Reset details */ - theInitialAEAD = myInitialAEAD; - theNonce = myNonce; - lastKey = myKey.getKey(); - switch (lastKey.length) + synchronized (this) { - case 16: - case 24: - case 32: - break; - default: - throw new IllegalStateException(ExceptionMessages.AES_KEY_LENGTH); + this.forEncryption = forEncryption; + keptMac = null; + theEncData.reset(); + + /* Set defaults */ + byte[] myInitialAEAD = null; + byte[] myNonce; + KeyParameter myKey; + + /* Access parameters */ + if (cipherParameters instanceof AEADParameters) + { + final AEADParameters myAEAD = (AEADParameters) cipherParameters; + myInitialAEAD = myAEAD.getAssociatedText(); + myNonce = myAEAD.getNonce(); + myKey = myAEAD.getKey(); + } + else if (cipherParameters instanceof ParametersWithIV) + { + final ParametersWithIV myParms = (ParametersWithIV) cipherParameters; + myNonce = myParms.getIV(); + myKey = (KeyParameter) myParms.getParameters(); + } + else + { + throw new IllegalArgumentException("invalid parameters passed to GCM-SIV"); + } + + /* Reset details */ + theInitialAEAD = myInitialAEAD; + theNonce = myNonce; + lastKey = myKey.getKey(); + switch (lastKey.length) + { + case 16: + case 24: + case 32: + break; + default: + throw new IllegalStateException(ExceptionMessages.AES_KEY_LENGTH); + } + + initRef(); + + + initNative( + refWrapper.getReference(), + forEncryption, lastKey, + theNonce, theInitialAEAD); } - - initRef(); - - - initNative( - refWrapper.getReference(), - forEncryption, lastKey, - theNonce, theInitialAEAD); - } private void initRef() @@ -124,106 +125,131 @@ public String getAlgorithmName() @Override public void processAADByte(byte in) { - if (refWrapper == null) + synchronized (this) { - throw new IllegalStateException(ExceptionMessages.GCM_SIV_UNINITIALIZED); + if (refWrapper == null) + { + throw new IllegalStateException(ExceptionMessages.GCM_SIV_UNINITIALIZED); + } + processAADByte(refWrapper.getReference(), in); } - processAADByte(refWrapper.getReference(), in); } @Override public void processAADBytes(byte[] in, int inOff, int len) { - if (refWrapper == null) + synchronized (this) { - throw new IllegalStateException(ExceptionMessages.GCM_SIV_UNINITIALIZED); + if (refWrapper == null) + { + throw new IllegalStateException(ExceptionMessages.GCM_SIV_UNINITIALIZED); + } + processAADBytes(refWrapper.getReference(), in, inOff, len); } - processAADBytes(refWrapper.getReference(), in, inOff, len); } @Override public int processByte(byte in, byte[] out, int outOff) throws DataLengthException { - if (refWrapper == null) + synchronized (this) { - throw new IllegalStateException(ExceptionMessages.GCM_SIV_UNINITIALIZED); + if (refWrapper == null) + { + throw new IllegalStateException(ExceptionMessages.GCM_SIV_UNINITIALIZED); + } + theEncData.write(in); + return 0; } - theEncData.write(in); - return 0; } @Override public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) throws DataLengthException { - if (refWrapper == null) + synchronized (this) { - throw new IllegalStateException(ExceptionMessages.GCM_SIV_UNINITIALIZED); + if (refWrapper == null) + { + throw new IllegalStateException(ExceptionMessages.GCM_SIV_UNINITIALIZED); + } + theEncData.write(in, inOff, len); + return 0; } - theEncData.write(in, inOff, len); - return 0; } @Override public int doFinal(byte[] out, int outOff) throws IllegalStateException, InvalidCipherTextException { - - int len = doFinal(refWrapper.getReference(), theEncData.getBuffer(), theEncData.size(), out, outOff); - //resetKeepMac - keptMac = getMac(); - reset(); - return len; + synchronized (this) + { + int len = doFinal(refWrapper.getReference(), theEncData.getBuffer(), theEncData.size(), out, outOff); + //resetKeepMac + keptMac = getMac(); + reset(); + return len; + } } @Override public byte[] getMac() { - if (refWrapper == null) - { - throw new IllegalStateException(ExceptionMessages.GCM_SIV_UNINITIALIZED); - } - - if (keptMac != null) + synchronized (this) { - return Arrays.clone(keptMac); + if (refWrapper == null) + { + throw new IllegalStateException(ExceptionMessages.GCM_SIV_UNINITIALIZED); + } + + if (keptMac != null) + { + return Arrays.clone(keptMac); + } + return getMac(refWrapper.getReference()); } - return getMac(refWrapper.getReference()); } @Override public int getUpdateOutputSize(int len) { - if (refWrapper == null) + synchronized (this) { - throw new IllegalStateException(ExceptionMessages.GCM_SIV_UNINITIALIZED); + if (refWrapper == null) + { + throw new IllegalStateException(ExceptionMessages.GCM_SIV_UNINITIALIZED); + } + return getUpdateOutputSize(refWrapper.getReference(), len, theEncData.size()); } - return getUpdateOutputSize(refWrapper.getReference(), len, theEncData.size()); } @Override public int getOutputSize(int len) { - if (refWrapper == null) + synchronized (this) { - throw new IllegalStateException(ExceptionMessages.GCM_SIV_UNINITIALIZED); + if (refWrapper == null) + { + throw new IllegalStateException(ExceptionMessages.GCM_SIV_UNINITIALIZED); + } + return getOutputSize(refWrapper.getReference(), len); } - return getOutputSize(refWrapper.getReference(), len); } @Override public void reset() { - - theEncData.clearBuffer(); - if (refWrapper == null) + synchronized (this) { - // deal with reset being called before init. - return; + theEncData.clearBuffer(); + if (refWrapper == null) + { + // deal with reset being called before init. + return; + } + reset(refWrapper.getReference()); } - reset(refWrapper.getReference()); } public String toString()
core/src/main/java/org/bouncycastle/util/dispose/DisposalDaemon.java+50 −22 modified@@ -37,22 +37,39 @@ public class DisposalDaemon static { - cleanupDelay = Properties.asInteger(CLEANUP_DELAY_PROP, 5); + + String cleanupDelayProp = Properties.getPropertyValue(CLEANUP_DELAY_PROP, "0").trim(); + if (cleanupDelayProp.endsWith("ms")) + { + cleanupDelay = Math.max(Long.parseLong(cleanupDelayProp.replace("ms", "")), 0); + } + else + { + cleanupDelay = Math.max(Long.parseLong(cleanupDelayProp) * 1000L, 0); + } + // // Clean up executor accepts references that are no longer needed // and disposes of them in turn. // - cleanupExecutor = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() + if (cleanupDelay > 0) { - @Override - public Thread newThread(Runnable r) + cleanupExecutor = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { - Thread t = new Thread(r, "BC Cleanup Executor"); - t.setDaemon(true); - return t; - } - }); + @Override + public Thread newThread(Runnable r) + { + Thread t = new Thread(r, "BC Cleanup Executor"); + t.setDaemon(true); + return t; + } + }); + } + else + { + cleanupExecutor = null; + } // // Sets up the daemon thread that deals with items on the reference @@ -117,23 +134,34 @@ public void run() (ReferenceWrapperWithDisposerRunnable) referenceQueue.remove(); refs.remove(item); - // - // Delay in order to avoid freeing a reference that the GC has - // decided is unreachable concurrently with its last use. - // - cleanupExecutor.schedule(new Runnable() + + if (cleanupExecutor == null) + { + if (LOG.isLoggable(Level.FINE)) + { + LOG.fine("Disposed: " + item); + } + item.dispose(); + } + else { - @Override - public void run() + // + // Delay in order to avoid freeing a reference that the GC has + // decided is unreachable concurrently with its last use. + // + cleanupExecutor.schedule(new Runnable() { - if (LOG.isLoggable(Level.FINE)) + @Override + public void run() { - LOG.fine("Disposed: " + item); + if (LOG.isLoggable(Level.FINE)) + { + LOG.fine("Disposed: " + item); + } + item.dispose(); } - item.dispose(); - } - }, cleanupDelay, TimeUnit.SECONDS); - + }, cleanupDelay, TimeUnit.MILLISECONDS); + } } catch (InterruptedException iex)
core/src/main/java/org/bouncycastle/util/test/SimpleTest.java+4 −3 modified@@ -203,12 +203,12 @@ public TestResult perform() public abstract void performTest() throws Exception; - public static void runTest(Test test) + public static boolean runTest(Test test) { - runTest(test, System.out); + return runTest(test, System.out); } - public static void runTest(Test test, PrintStream out) + public static boolean runTest(Test test, PrintStream out) { TestResult result = test.perform(); @@ -218,6 +218,7 @@ public static void runTest(Test test, PrintStream out) } // -DM out.println out.println(result); + return result.isSuccessful(); } public static void runTests(Test[] tests)
prov/build.gradle+2 −2 modified@@ -17,7 +17,7 @@ sourceSets { } java9 { java { - srcDirs = ['src/main/jdk1.9'] + srcDirs = ['src/main/jdk1.9','../core/src/main/java9' ] } } java11 { @@ -62,7 +62,7 @@ compileJava { compileJava9Java { options.release = 9 - options.sourcepath = files(['../core/src/main/java', 'src/main/java', 'src/main/jdk1.9']) + options.sourcepath = files(['../core/src/main/java', 'src/main/java', 'src/main/jdk1.9','../core/src/main/java9']) } compileJava11Java {
prov/src/test/java/org/bouncycastle/jce/provider/test/SimpleTestTest.java+5 −4 modified@@ -1,13 +1,13 @@ package org.bouncycastle.jce.provider.test; -import java.security.Security; - import junit.framework.TestCase; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.test.SimpleTestResult; +import java.security.Security; + public class SimpleTestTest - extends TestCase + extends TestCase { public void testJCE() { @@ -22,14 +22,15 @@ public void testJCE() for (int i = 0; i != tests.length; i++) { - SimpleTestResult result = (SimpleTestResult)tests[i].perform(); + SimpleTestResult result = (SimpleTestResult) tests[i].perform(); if (!result.isSuccessful()) { if (result.getException() != null) { result.getException().printStackTrace(); } + System.out.println("Test failed: " + tests[i]); fail("index " + i + " " + result.toString()); } }
README.md+24 −4 modified@@ -228,10 +228,30 @@ a segfault. ### Properties -| Property | Values | Description | -|----------------------------------------|-----------------------------|------------------------------------------------------------------------------| -| org.bouncycastle.native.cpu_variant | avx, vaes, vaesf or neon-le | Specify a variant to use see "Selecting a specific variant" for warnings. | -| org.bouncycastle.packet_cipher_enabled | true or false | False by default, enable or disable use of packet ciphers where appropriate. | +| Property | Values | Description | +|-----------------------------------------|-----------------------------|------------------------------------------------------------------------------------------------------| +| org.bouncycastle.native.cpu_variant | avx, vaes, vaesf or neon-le | Specify a variant to use see "Selecting a specific variant" for warnings. | +| org.bouncycastle.packet_cipher_enabled | true or false | False by default, enable or disable use of packet ciphers where appropriate. | +| org.bouncycastle.native.cleanup_delay | 1000ms / 1 | Delays freeing of native allocations by the given time in milliseconds or seconds, the default is 0. | + + +### Disposal Daemon / Freeing native allocations +The library tacks classes and when they become available for garbage collection, and we free any underlying native allocations. + +Overly aggressive garbage collectors may signal that a class is available for collection while another thread is accessing +that class. On busy multicore machines this may occur during the last call to that class causing use after free situation. + +To deal with this we have employed either the reachability fence, applicable on java 9 and above or synchronized blocks for +java 8. + +If this proves to be unreliable users can also set a cleanup delay via the ```org.bouncycastle.native.cleanup_delay``` property. +For example: + +```-Dorg.bouncycastle.native.cleanup_delay=10ms``` would set a delay of 10 milliseconds, and +```-Dorg.bouncycastle.native.cleanup_delay=1``` would set a delay of 1 second. + +The default cleanup delay is zero and native allocations will be cleaned up immediately upon notification that the relevant +class is available to GC. # Things to watch out for
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4News mentions
0No linked articles in our index yet.