From 4871389f42744f6ba0e43981b843381ba1652e5d Mon Sep 17 00:00:00 2001 From: Pushkar Kulkarni Date: Mon, 22 Jun 2026 17:19:49 +0530 Subject: [PATCH 1/6] Add suport for ranges in engineEncapsulate/engineDecapsulate --- .../keyencapsulation/OpenSSLKEMRSA.java | 17 ++++++++-- src/test/java/KeyEncapsulationTest.java | 33 +++++++++++++++++++ 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/canonical/openssl/keyencapsulation/OpenSSLKEMRSA.java b/src/main/java/com/canonical/openssl/keyencapsulation/OpenSSLKEMRSA.java index 4e2116a..3a9b69a 100644 --- a/src/main/java/com/canonical/openssl/keyencapsulation/OpenSSLKEMRSA.java +++ b/src/main/java/com/canonical/openssl/keyencapsulation/OpenSSLKEMRSA.java @@ -104,8 +104,11 @@ public RSAKEMEncapsulator(PublicKey key) throws InvalidKeyException { } public KEM.Encapsulated engineEncapsulate(int from, int to, String algorithm) { - // TODO: ignoring from, to in the prototype int secretSize = engineSecretSize(); + if (from < 0 || from > to || to > secretSize) { + throw new IndexOutOfBoundsException( + "Invalid range [" + from + ", " + to + ") for secret of size " + secretSize); + } byte[] secretBytes = new byte[secretSize]; int encapsulationSize = engineEncapsulationSize(); @@ -113,7 +116,8 @@ public KEM.Encapsulated engineEncapsulate(int from, int to, String algorithm) { engineEncapsulate0(secretBytes, encapsulatedBytes); try { - SecretKey secretKey = new SecretKeySpec(secretBytes, algorithm); + SecretKey secretKey = + new SecretKeySpec(secretBytes, from, to - from, algorithm); return new KEM.Encapsulated(secretKey, encapsulatedBytes, null); } finally { Arrays.fill(secretBytes, (byte)0); @@ -177,9 +181,16 @@ public RSAKEMDecapsulator(PrivateKey key) throws InvalidKeyException { public SecretKey engineDecapsulate(byte[] encapsulation, int from, int to, String algorithm) throws DecapsulateException { + // The secret size is only known once the encapsulation has been + // unwrapped, so decapsulate first and then validate the range. byte[] secretBytes = engineDecapsulate0(encapsulation); try { - return new SecretKeySpec(secretBytes, algorithm); + if (from < 0 || from > to || to > secretBytes.length) { + throw new IndexOutOfBoundsException( + "Invalid range [" + from + ", " + to + ") for secret of size " + + secretBytes.length); + } + return new SecretKeySpec(secretBytes, from, to - from, algorithm); } finally { Arrays.fill(secretBytes, (byte)0); } diff --git a/src/test/java/KeyEncapsulationTest.java b/src/test/java/KeyEncapsulationTest.java index 6df09aa..c403808 100644 --- a/src/test/java/KeyEncapsulationTest.java +++ b/src/test/java/KeyEncapsulationTest.java @@ -61,4 +61,37 @@ public void testKEMRSA() throws Exception { assertTrue("Key Encapsulation with RSA test failed", aliceSecret.equals(bobSecret)); } + + @Test + public void testKEMRSAPartialRange() throws Exception { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); + kpg.initialize(4096); + + KeyPair aliceKeys = kpg.generateKeyPair(); + PublicKey alicePublicKey = aliceKeys.getPublic(); + PrivateKey alicePrivateKey = aliceKeys.getPrivate(); + + // Bob encapsulates only a sub-range of the shared secret + KEM bobKem = KEM.getInstance("RSA", "OpenSSLFIPSProvider"); + Encapsulator encapsulator = bobKem.newEncapsulator(alicePublicKey, null, null); + int secretSize = encapsulator.secretSize(); + int from = 8; + int to = secretSize / 2; + KEM.Encapsulated encapsulated = encapsulator.encapsulate(from, to, "AES"); + SecretKey bobSecret = encapsulated.key(); + + // The key must only contain the requested slice of the secret + assertTrue("Encapsulated key has wrong length for partial range", + bobSecret.getEncoded().length == to - from); + + // Alice decapsulates the same sub-range and must recover the same key + KEM aliceKem = KEM.getInstance("RSA", "OpenSSLFIPSProvider"); + Decapsulator decapsulator = aliceKem.newDecapsulator(alicePrivateKey, null); + byte[] encapsulationBytes = encapsulated.encapsulation(); + SecretKey aliceSecret = decapsulator.decapsulate(encapsulationBytes, from, to, "AES"); + + assertTrue("Decapsulated key has wrong length for partial range", + aliceSecret.getEncoded().length == to - from); + assertTrue("Partial range KEM with RSA test failed", aliceSecret.equals(bobSecret)); + } } From b47086716d117975466b439650cf5ed7860f1c31 Mon Sep 17 00:00:00 2001 From: Pushkar Kulkarni Date: Mon, 22 Jun 2026 22:55:46 +0530 Subject: [PATCH 2/6] Don't permit reuse of nonces --- .../openssl/cipher/OpenSSLCipher.java | 28 +++++++++++ src/test/java/CipherTest.java | 48 +++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/src/main/java/com/canonical/openssl/cipher/OpenSSLCipher.java b/src/main/java/com/canonical/openssl/cipher/OpenSSLCipher.java index ef3176e..6bcbd21 100644 --- a/src/main/java/com/canonical/openssl/cipher/OpenSSLCipher.java +++ b/src/main/java/com/canonical/openssl/cipher/OpenSSLCipher.java @@ -30,6 +30,7 @@ import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.GCMParameterSpec; import java.security.InvalidAlgorithmParameterException; +import java.security.MessageDigest; import java.security.SecureRandom; import javax.crypto.ShortBufferException; import javax.crypto.IllegalBlockSizeException; @@ -74,6 +75,10 @@ abstract public class OpenSSLCipher extends CipherSpi { boolean firstUpdate = true; private ClearableBuffer aeadDecryptBuffer; + // Last (key, IV) latched for AEAD encryption on this instance, used to reject GCM/CCM nonce reuse. + private byte[] lastEncKey; + private byte[] lastEncIv; + private static final class ClearableBuffer { private byte[] buf = new byte[256]; private int count; @@ -151,6 +156,9 @@ protected OpenSSLCipher(String nameKeySizeAndMode, String padding) { this.mode = parts[2]; this.padding = padding; this.cipherContext = createContext0(nameKeySizeAndMode, padding); + if (this.cipherContext == 0) { + throw new ProviderException("Failed to create cipher context for " + nameKeySizeAndMode); + } this.cipherState = new CipherState(this.cipherContext); cleanable = cleaner.register(this, cipherState); } @@ -271,10 +279,30 @@ protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params, Se if (newKeyBytes == null) { throw new InvalidKeyException("Key does not support encoding"); } + boolean encrypting = (opmode == Cipher.ENCRYPT_MODE || opmode == Cipher.WRAP_MODE); + boolean isAEAD = isModeGCM() || isModeCCM(); + // Reject reuse of the same key+IV for AEAD encryption: GCM/CCM nonce reuse is catastrophic. + if (encrypting && isAEAD && lastEncIv != null && lastEncKey != null + && MessageDigest.isEqual(specIv, lastEncIv) + && MessageDigest.isEqual(newKeyBytes, lastEncKey)) { + Arrays.fill(newKeyBytes, (byte) 0); + throw new InvalidAlgorithmParameterException( + "Cannot reuse the same key and IV for " + mode + " encryption (nonce reuse)"); + } resetStateForInit(opmode); this.keyBytes = newKeyBytes; this.iv = specIv; cipherState.setIV(this.iv); + if (encrypting && isAEAD) { + if (lastEncKey != null) { + Arrays.fill(lastEncKey, (byte) 0); + } + if (lastEncIv != null) { + Arrays.fill(lastEncIv, (byte) 0); + } + lastEncKey = newKeyBytes.clone(); + lastEncIv = specIv.clone(); + } if (!isModeCCM()) { doInit0(null, 0, 0, keyBytes, iv, this.opmode); Arrays.fill(keyBytes, (byte)0); diff --git a/src/test/java/CipherTest.java b/src/test/java/CipherTest.java index fb3d144..69c4e41 100644 --- a/src/test/java/CipherTest.java +++ b/src/test/java/CipherTest.java @@ -22,6 +22,7 @@ import java.security.SecureRandom; import javax.crypto.spec.IvParameterSpec; import java.security.spec.AlgorithmParameterSpec; +import java.security.InvalidAlgorithmParameterException; import com.canonical.openssl.provider.OpenSSLFIPSProvider; import org.junit.Test; @@ -443,6 +444,53 @@ private void runTestAEADDecryptMultipleUpdates(String nameKeySizeAndMode, String assertArrayEquals("AEAD multi-update decrypt failed for " + cipherName, plaintext, result); } + @Test + public void testGCMNonceReuseRejected() throws Exception { + for (String cipher : new String[]{"AES128/GCM", "AES192/GCM", "AES256/GCM"}) { + runTestGCMNonceReuseRejected(cipher); + } + } + + private void runTestGCMNonceReuseRejected(String nameKeySizeAndMode) throws Exception { + String cipherName = nameKeySizeAndMode + "/NONE"; + SecureRandom sr = SecureRandom.getInstance("NativePRNG"); + + int keyBytes = nameKeySizeAndMode.contains("256") ? 32 + : nameKeySizeAndMode.contains("192") ? 24 : 16; + byte[] key = new byte[keyBytes]; + sr.nextBytes(key); + SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); + + byte[] iv = new byte[12]; + sr.nextBytes(iv); + GCMParameterSpec gcmSpec = new GCMParameterSpec(128, iv); + + byte[] plaintext = new byte[32]; + sr.nextBytes(plaintext); + + Cipher enc = Cipher.getInstance(cipherName, "OpenSSLFIPSProvider"); + enc.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec, sr); + enc.doFinal(plaintext); + + // Re-initializing the same instance for encryption with the same key+IV must be rejected. + try { + enc.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec, sr); + fail("GCM nonce reuse for encryption must be rejected for " + cipherName); + } catch (InvalidAlgorithmParameterException expected) { + // expected + } + + // A fresh IV under the same key must be accepted. + byte[] iv2 = new byte[12]; + sr.nextBytes(iv2); + enc.init(Cipher.ENCRYPT_MODE, keySpec, new GCMParameterSpec(128, iv2), sr); + + // Decryption must never be blocked from reusing an IV. + Cipher dec = Cipher.getInstance(cipherName, "OpenSSLFIPSProvider"); + dec.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec, sr); + dec.init(Cipher.DECRYPT_MODE, keySpec, gcmSpec, sr); + } + @Test public void testDoFinalNoInputAfterUpdate() throws Exception { SecureRandom sr = SecureRandom.getInstance("NativePRNG"); From 2da62385189adb0db76f5011920cefca34db31ac Mon Sep 17 00:00:00 2001 From: Pushkar Kulkarni Date: Mon, 22 Jun 2026 22:57:24 +0530 Subject: [PATCH 3/6] Use utf-8 bytes for passwords in PBKDF2 --- .../canonical/openssl/kdf/OpenSSLPBKDF2.java | 40 ++++++++++++++++--- src/main/native/c/OpenSSLPBKDF2.c | 16 ++++---- src/main/native/include/jni/OpenSSLPBKDF2.h | 4 +- 3 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/canonical/openssl/kdf/OpenSSLPBKDF2.java b/src/main/java/com/canonical/openssl/kdf/OpenSSLPBKDF2.java index 006423a..e64e3a1 100644 --- a/src/main/java/com/canonical/openssl/kdf/OpenSSLPBKDF2.java +++ b/src/main/java/com/canonical/openssl/kdf/OpenSSLPBKDF2.java @@ -17,6 +17,9 @@ package com.canonical.openssl.kdf; import com.canonical.openssl.util.NativeLibraryLoader; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.StandardCharsets; import java.security.spec.KeySpec; import java.util.Arrays; import javax.crypto.SecretKey; @@ -142,10 +145,18 @@ protected SecretKey engineGenerateSecret(KeySpec keyspec) throws InvalidKeySpecE + " (FIPS SP 800-132)"); } int keyLengthBytes = resolveKeyLengthBytes(pbeKeySpec.getKeyLength()); - PBKDF2SecretKey secretKey = new PBKDF2SecretKey(pbeKeySpec.getPassword(), + char[] password = pbeKeySpec.getPassword(); + PBKDF2SecretKey secretKey = new PBKDF2SecretKey(password, pbeKeySpec.getSalt(), pbeKeySpec.getIterationCount()); - byte[] secretBytes = generateSecret0(pbeKeySpec.getPassword(), pbeKeySpec.getSalt(), + byte[] passwordBytes = encodePassword(password); + byte[] secretBytes; + try { + secretBytes = generateSecret0(passwordBytes, pbeKeySpec.getSalt(), pbeKeySpec.getIterationCount(), keyLengthBytes); + } finally { + Arrays.fill(passwordBytes, (byte) 0); + Arrays.fill(password, '\0'); + } if (secretBytes == null) { throw new InvalidKeySpecException("PBKDF2 derivation failed"); } @@ -222,10 +233,18 @@ protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException "Key length " + (keyLengthBytes * 8) + " bits exceeds the maximum supported " + (MAX_KEY_LENGTH_BYTES * 8) + " bits"); } - PBKDF2SecretKey secretKey = new PBKDF2SecretKey(pbeKey.getPassword(), pbeKey.getSalt(), + char[] password = pbeKey.getPassword(); + PBKDF2SecretKey secretKey = new PBKDF2SecretKey(password, pbeKey.getSalt(), pbeKey.getIterationCount()); - byte[] secretBytes = generateSecret0(pbeKey.getPassword(), pbeKey.getSalt(), + byte[] passwordBytes = encodePassword(password); + byte[] secretBytes; + try { + secretBytes = generateSecret0(passwordBytes, pbeKey.getSalt(), pbeKey.getIterationCount(), keyLengthBytes); + } finally { + Arrays.fill(passwordBytes, (byte) 0); + Arrays.fill(password, '\0'); + } if (secretBytes == null) { throw new InvalidKeyException("PBKDF2 derivation failed"); } @@ -237,6 +256,17 @@ protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException return secretKey; } - private native byte[] generateSecret0(char[] password, byte[] salt, int iterationCount, int keyLength); + // UTF-8 encode the password for portability + private static byte[] encodePassword(char[] password) { + ByteBuffer bb = StandardCharsets.UTF_8.encode(CharBuffer.wrap(password)); + byte[] bytes = new byte[bb.remaining()]; + bb.get(bytes); + if (bb.hasArray()) { + Arrays.fill(bb.array(), (byte) 0); + } + return bytes; + } + + private native byte[] generateSecret0(byte[] password, byte[] salt, int iterationCount, int keyLength); private static native int getMaxKeyLengthBytes0(); } diff --git a/src/main/native/c/OpenSSLPBKDF2.c b/src/main/native/c/OpenSSLPBKDF2.c index 681d3d0..369b88f 100644 --- a/src/main/native/c/OpenSSLPBKDF2.c +++ b/src/main/native/c/OpenSSLPBKDF2.c @@ -23,34 +23,34 @@ /* * Class: com_canonical_openssl_kdf_OpenSSLPBKDF2 * Method: generateSecret0 - * Signature: ([C[BII)[B + * Signature: ([B[BII)[B */ JNIEXPORT jbyteArray JNICALL Java_com_canonical_openssl_kdf_OpenSSLPBKDF2_generateSecret0 - (JNIEnv *env, jobject this, jcharArray password, jbyteArray salt, jint iteration_count, jint key_length) { + (JNIEnv *env, jobject this, jbyteArray password, jbyteArray salt, jint iteration_count, jint key_length) { if (key_length <= 0 || key_length > MAX_KEY_SIZE) { throwProviderException(env, "Invalid PBKDF2 key length"); return NULL; } - int password_length = (*env)->GetArrayLength(env, password); + int password_length = array_length(env, password); int salt_length = array_length(env, salt); byte output[MAX_KEY_SIZE] = {0}; jbyteArray result = NULL; - jchar *password_chars = (*env)->GetCharArrayElements(env, password, NULL); - if (password_chars == NULL) { + jbyte *password_bytes = (*env)->GetByteArrayElements(env, password, NULL); + if (password_bytes == NULL) { return NULL; } jbyte *salt_bytes = (*env)->GetByteArrayElements(env, salt, NULL); if (salt_bytes == NULL) { - (*env)->ReleaseCharArrayElements(env, password, password_chars, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, password, password_bytes, JNI_ABORT); return NULL; } - kdf_spec *spec = create_pbkdf_spec((byte *)password_chars, password_length * sizeof(jchar), + kdf_spec *spec = create_pbkdf_spec((byte *)password_bytes, password_length, (byte *)salt_bytes, salt_length, iteration_count); - (*env)->ReleaseCharArrayElements(env, password, password_chars, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, password, password_bytes, JNI_ABORT); (*env)->ReleaseByteArrayElements(env, salt, salt_bytes, JNI_ABORT); if (spec == NULL) { diff --git a/src/main/native/include/jni/OpenSSLPBKDF2.h b/src/main/native/include/jni/OpenSSLPBKDF2.h index 670ddf8..e8ccb36 100644 --- a/src/main/native/include/jni/OpenSSLPBKDF2.h +++ b/src/main/native/include/jni/OpenSSLPBKDF2.h @@ -26,10 +26,10 @@ extern "C" { /* * Class: com_canonical_openssl_kdf_OpenSSLPBKDF2 * Method: generateSecret0 - * Signature: ([C[BII)[B + * Signature: ([B[BII)[B */ JNIEXPORT jbyteArray JNICALL Java_com_canonical_openssl_kdf_OpenSSLPBKDF2_generateSecret0 - (JNIEnv *, jobject, jcharArray, jbyteArray, jint, jint); + (JNIEnv *, jobject, jbyteArray, jbyteArray, jint, jint); JNIEXPORT jint JNICALL Java_com_canonical_openssl_kdf_OpenSSLPBKDF2_getMaxKeyLengthBytes0 (JNIEnv *, jclass); From b92759d35117d12c80a8f9fbf682075751bc0e53 Mon Sep 17 00:00:00 2001 From: Pushkar Kulkarni Date: Mon, 22 Jun 2026 22:58:06 +0530 Subject: [PATCH 4/6] Add note on OPENSSL_CUSTOM_CONF --- src/main/native/c/init.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/native/c/init.c b/src/main/native/c/init.c index 9f69039..6fe8ef2 100644 --- a/src/main/native/c/init.c +++ b/src/main/native/c/init.c @@ -100,6 +100,26 @@ static void unload_global_libctx() { unload_libctx(ctx); } +/* + * Note on OPENSSL_CUSTOM_CONF and the config file: + * + * When the FIPS provider is not already available by default (i.e. outside the + * Ubuntu Pro auto-FIPS setup), we load OpenSSL's configuration from the file + * named by the OPENSSL_CUSTOM_CONF environment variable, or from + * /usr/local/ssl/openssl.cnf if that variable is not set. That config decides + * which provider gets loaded as "fips". + * + * Both the environment variable and the config file it points to are TRUSTED + * inputs. Anyone who can change either of them can choose which OpenSSL provider + * module is loaded into this process. We use secure_getenv, so the variable is + * ignored when the process is running with elevated privileges (setuid/setgid), + * but in every other case the caller is responsible for protecting these inputs. + * + * In deployments where FIPS compliance is required, make sure the config file + * (and the directory containing it) is owned by root and not writable by + * untrusted users, so that "fips" cannot be redirected to a non-validated + * module. + */ int JNI_OnLoad(JavaVM* vm, void *reserved) { const char *default_cnf = "/usr/local/ssl/openssl.cnf"; const char *custom_cnf = secure_getenv("OPENSSL_CUSTOM_CONF"); From c3fb6ea6d8b95691cec8ba4796f07932734bec34 Mon Sep 17 00:00:00 2001 From: Pushkar Kulkarni Date: Mon, 22 Jun 2026 22:58:42 +0530 Subject: [PATCH 5/6] Null check native handles before registering with Cleaners --- .../openssl/keyagreement/OpenSSLKeyAgreement.java | 3 +++ .../openssl/keyencapsulation/OpenSSLKEMRSA.java | 4 ++-- src/main/java/com/canonical/openssl/mac/OpenSSLMAC.java | 9 +++++++++ src/main/java/com/canonical/openssl/md/OpenSSLMD.java | 7 +++++++ .../canonical/openssl/signature/OpenSSLSignature.java | 6 ++++++ 5 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/canonical/openssl/keyagreement/OpenSSLKeyAgreement.java b/src/main/java/com/canonical/openssl/keyagreement/OpenSSLKeyAgreement.java index c1c1733..21252e6 100644 --- a/src/main/java/com/canonical/openssl/keyagreement/OpenSSLKeyAgreement.java +++ b/src/main/java/com/canonical/openssl/keyagreement/OpenSSLKeyAgreement.java @@ -129,6 +129,9 @@ protected void engineInit(Key key, SecureRandom random) throws InvalidKeyExcepti cleanable.clean(); } nativeHandle = initialize(key); + if (nativeHandle == 0) { + throw new InvalidKeyException("Failed to initialize key agreement"); + } cleanable = cleaner.register(this, new KeyAgreementState(nativeHandle)); state = State.INITIALIZED; } diff --git a/src/main/java/com/canonical/openssl/keyencapsulation/OpenSSLKEMRSA.java b/src/main/java/com/canonical/openssl/keyencapsulation/OpenSSLKEMRSA.java index 3a9b69a..36647e6 100644 --- a/src/main/java/com/canonical/openssl/keyencapsulation/OpenSSLKEMRSA.java +++ b/src/main/java/com/canonical/openssl/keyencapsulation/OpenSSLKEMRSA.java @@ -97,10 +97,10 @@ public RSAKEMEncapsulator(PublicKey key) throws InvalidKeyException { throw new InvalidKeyException("Key does not support encoding"); } nativeHandle = encapsulatorInit0(encoded); - cleanable = cleaner.register(this, new EncapsulatorState(nativeHandle)); if (nativeHandle == 0) { throw new InvalidKeyException("Failed to initialize RSA-KEM encapsulator"); } + cleanable = cleaner.register(this, new EncapsulatorState(nativeHandle)); } public KEM.Encapsulated engineEncapsulate(int from, int to, String algorithm) { @@ -173,10 +173,10 @@ public RSAKEMDecapsulator(PrivateKey key) throws InvalidKeyException { } finally { Arrays.fill(encoded, (byte) 0); } - cleanable = cleaner.register(this, new DecapsulatorState(nativeHandle)); if (nativeHandle == 0) { throw new InvalidKeyException("Failed to initialize RSA-KEM decapsulator"); } + cleanable = cleaner.register(this, new DecapsulatorState(nativeHandle)); } public SecretKey engineDecapsulate(byte[] encapsulation, int from, int to, String algorithm) diff --git a/src/main/java/com/canonical/openssl/mac/OpenSSLMAC.java b/src/main/java/com/canonical/openssl/mac/OpenSSLMAC.java index b3c16c3..48045c1 100644 --- a/src/main/java/com/canonical/openssl/mac/OpenSSLMAC.java +++ b/src/main/java/com/canonical/openssl/mac/OpenSSLMAC.java @@ -23,6 +23,7 @@ import java.nio.ByteBuffer; import java.security.InvalidKeyException; import java.security.Key; +import java.security.ProviderException; import java.util.Arrays; import javax.crypto.MacSpi; import java.security.InvalidAlgorithmParameterException; @@ -122,6 +123,11 @@ protected void engineInit(Key key, AlgorithmParameterSpec spec) throws InvalidKe this.keyBytes = newKeyBytes; this.cachedIV = iv; nativeHandle = doInit0(getAlgorithm(), getCipherType(), getDigestType(), iv, outputLength, keyBytes); + if (nativeHandle == 0) { + Arrays.fill(keyBytes, (byte) 0); + this.keyBytes = null; + throw new InvalidKeyException("Failed to initialize MAC"); + } macState = new MACState(nativeHandle); macState.setKeyBytes(keyBytes); cleanable = cleaner.register(this, macState); @@ -138,6 +144,9 @@ protected void engineReset() { cleanable.clean(); } nativeHandle = doInit0(getAlgorithm(), getCipherType(), getDigestType(), this.cachedIV, this.outputLength, keyBytes); + if (nativeHandle == 0) { + throw new ProviderException("Failed to reset MAC"); + } macState = new MACState(nativeHandle); macState.setKeyBytes(keyBytes); cleanable = cleaner.register(this, macState); diff --git a/src/main/java/com/canonical/openssl/md/OpenSSLMD.java b/src/main/java/com/canonical/openssl/md/OpenSSLMD.java index ca1fcde..1266446 100644 --- a/src/main/java/com/canonical/openssl/md/OpenSSLMD.java +++ b/src/main/java/com/canonical/openssl/md/OpenSSLMD.java @@ -24,6 +24,7 @@ import java.nio.ByteBuffer; import java.security.DigestException; import java.security.MessageDigestSpi; +import java.security.ProviderException; import java.util.Arrays; /* This implementation will be exercised by the user through the @@ -65,6 +66,9 @@ protected OpenSSLMD(String algorithm) { private void ensureInitialized() { if (!initialized) { nativeHandle = doInit0(mdName); + if (nativeHandle == 0) { + throw new ProviderException("Failed to initialize message digest " + mdName); + } cleanable = cleaner.register(this, new MDState(nativeHandle)); initialized = true; } @@ -94,6 +98,9 @@ protected void engineReset() { cleanable.clean(); } nativeHandle = doInit0(mdName); + if (nativeHandle == 0) { + throw new ProviderException("Failed to initialize message digest " + mdName); + } cleanable = cleaner.register(this, new MDState(nativeHandle)); initialized = true; } diff --git a/src/main/java/com/canonical/openssl/signature/OpenSSLSignature.java b/src/main/java/com/canonical/openssl/signature/OpenSSLSignature.java index b083026..0b5c751 100644 --- a/src/main/java/com/canonical/openssl/signature/OpenSSLSignature.java +++ b/src/main/java/com/canonical/openssl/signature/OpenSSLSignature.java @@ -137,6 +137,9 @@ protected void engineInitSign(PrivateKey key) throws InvalidKeyException { cleanable.clean(); } nativeHandle = engineInitSign0(getSignatureName(), privKey, params); + if (nativeHandle == 0) { + throw new InvalidKeyException("Failed to initialize signature for signing"); + } cleanable = cleaner.register(this, new SignatureState(nativeHandle)); } else { throw new InvalidKeyException ("Supplied PrivateKey is of type: " + key.getClass()); @@ -159,6 +162,9 @@ protected void engineInitVerify(PublicKey key) throws InvalidKeyException { cleanable.clean(); } nativeHandle = engineInitVerify0(getSignatureName(), pubKey, params); + if (nativeHandle == 0) { + throw new InvalidKeyException("Failed to initialize signature for verification"); + } cleanable = cleaner.register(this, new SignatureState(nativeHandle)); } else { throw new InvalidKeyException ("Supplied PublicKey is not OpenSSL-based"); From bde68b69e2e0d3c099c24362cbe8da2d6585e222 Mon Sep 17 00:00:00 2001 From: Pushkar Kulkarni Date: Mon, 22 Jun 2026 23:37:26 +0530 Subject: [PATCH 6/6] Prepare for tag 0.7.1 --- pom.xml | 2 +- src/test/consumer-snap/snapcraft.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 89bda67..eeca792 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ com.canonical.openssl openssl-fips-java - 0.7.0 + 0.7.1 17 diff --git a/src/test/consumer-snap/snapcraft.yaml b/src/test/consumer-snap/snapcraft.yaml index 808075a..82894ec 100644 --- a/src/test/consumer-snap/snapcraft.yaml +++ b/src/test/consumer-snap/snapcraft.yaml @@ -51,7 +51,7 @@ parts: apps: kem-test: - command: /usr/lib/jvm/java-21-openjdk-amd64/bin/java -cp $SNAP/bin/KEMTest.jar:$SNAP/imported-libs/jar/openssl-fips-java-0.7.0.jar KEMTest + command: /usr/lib/jvm/java-21-openjdk-amd64/bin/java -cp $SNAP/bin/KEMTest.jar:$SNAP/imported-libs/jar/openssl-fips-java-0.7.1.jar KEMTest environment: OPENSSL_MODULES: $SNAP/usr/local/lib64/ossl-modules/ OPENSSL_CUSTOM_CONF: $SNAP/usr/local/ssl/openssl.cnf