From 4da4547a469d10efdde469cb035534bb547221f9 Mon Sep 17 00:00:00 2001 From: Leo Galambos Date: Wed, 24 Dec 2025 23:39:32 +0100 Subject: [PATCH] fix: defensively copy secret and encapsulation before destroy() SecretWithEncapsulation may zeroize internal buffers on destroy(). Create defensive copies of the shared secret and ciphertext using Arrays.copyOf() before destroying the result object to ensure stable output. No cryptographic behavior changes; fixes a potential lifecycle bug. Signed-off-by: Leo Galambos --- lib/src/main/java/zeroecho/core/alg/bike/BikeKemContext.java | 5 +++-- lib/src/main/java/zeroecho/core/alg/cmce/CmceKemContext.java | 5 +++-- .../main/java/zeroecho/core/alg/frodo/FrodoKemContext.java | 5 +++-- lib/src/main/java/zeroecho/core/alg/hqc/HqcKemContext.java | 5 +++-- .../main/java/zeroecho/core/alg/kyber/KyberKemContext.java | 5 +++-- lib/src/main/java/zeroecho/core/alg/ntru/NtruKemContext.java | 5 +++-- .../zeroecho/core/alg/ntruprime/NtrulPrimeKemContext.java | 5 +++-- .../zeroecho/core/alg/ntruprime/SntruPrimeKemContext.java | 5 +++-- .../main/java/zeroecho/core/alg/saber/SaberKemContext.java | 5 +++-- 9 files changed, 27 insertions(+), 18 deletions(-) diff --git a/lib/src/main/java/zeroecho/core/alg/bike/BikeKemContext.java b/lib/src/main/java/zeroecho/core/alg/bike/BikeKemContext.java index dfc32c4..8ac48ce 100644 --- a/lib/src/main/java/zeroecho/core/alg/bike/BikeKemContext.java +++ b/lib/src/main/java/zeroecho/core/alg/bike/BikeKemContext.java @@ -39,6 +39,7 @@ import java.security.Key; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; +import java.util.Arrays; import java.util.Objects; import javax.security.auth.DestroyFailedException; @@ -158,8 +159,8 @@ public final class BikeKemContext implements KemContext { .createKey(key.getEncoded()); BIKEKEMGenerator gen = new BIKEKEMGenerator(new SecureRandom()); SecretWithEncapsulation res = gen.generateEncapsulated(keyParam); - byte[] secret = res.getSecret(); - byte[] ct = res.getEncapsulation(); + byte[] secret = Arrays.copyOf(res.getSecret(), res.getSecret().length); + byte[] ct = Arrays.copyOf(res.getEncapsulation(), res.getEncapsulation().length); res.destroy(); return new KemResult(ct, secret); } catch (DestroyFailedException e) { diff --git a/lib/src/main/java/zeroecho/core/alg/cmce/CmceKemContext.java b/lib/src/main/java/zeroecho/core/alg/cmce/CmceKemContext.java index 4180eb8..3af5518 100644 --- a/lib/src/main/java/zeroecho/core/alg/cmce/CmceKemContext.java +++ b/lib/src/main/java/zeroecho/core/alg/cmce/CmceKemContext.java @@ -39,6 +39,7 @@ import java.security.Key; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; +import java.util.Arrays; import java.util.Objects; import javax.security.auth.DestroyFailedException; @@ -190,8 +191,8 @@ public final class CmceKemContext implements KemContext { .createKey(key.getEncoded()); CMCEKEMGenerator gen = new CMCEKEMGenerator(new SecureRandom()); SecretWithEncapsulation res = gen.generateEncapsulated(keyParam); - byte[] secret = res.getSecret(); - byte[] ct = res.getEncapsulation(); + byte[] secret = Arrays.copyOf(res.getSecret(), res.getSecret().length); + byte[] ct = Arrays.copyOf(res.getEncapsulation(), res.getEncapsulation().length); res.destroy(); return new KemResult(ct, secret); } catch (DestroyFailedException e) { diff --git a/lib/src/main/java/zeroecho/core/alg/frodo/FrodoKemContext.java b/lib/src/main/java/zeroecho/core/alg/frodo/FrodoKemContext.java index 7a11bf4..68f2392 100644 --- a/lib/src/main/java/zeroecho/core/alg/frodo/FrodoKemContext.java +++ b/lib/src/main/java/zeroecho/core/alg/frodo/FrodoKemContext.java @@ -39,6 +39,7 @@ import java.security.Key; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; +import java.util.Arrays; import java.util.Objects; import javax.security.auth.DestroyFailedException; @@ -179,8 +180,8 @@ public final class FrodoKemContext implements KemContext { .createKey(key.getEncoded()); FrodoKEMGenerator gen = new FrodoKEMGenerator(new SecureRandom()); SecretWithEncapsulation res = gen.generateEncapsulated(keyParam); - byte[] secret = res.getSecret(); - byte[] ct = res.getEncapsulation(); + byte[] secret = Arrays.copyOf(res.getSecret(), res.getSecret().length); + byte[] ct = Arrays.copyOf(res.getEncapsulation(), res.getEncapsulation().length); res.destroy(); return new KemResult(ct, secret); } catch (DestroyFailedException e) { diff --git a/lib/src/main/java/zeroecho/core/alg/hqc/HqcKemContext.java b/lib/src/main/java/zeroecho/core/alg/hqc/HqcKemContext.java index fac759e..2dd3dee 100644 --- a/lib/src/main/java/zeroecho/core/alg/hqc/HqcKemContext.java +++ b/lib/src/main/java/zeroecho/core/alg/hqc/HqcKemContext.java @@ -39,6 +39,7 @@ import java.security.Key; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; +import java.util.Arrays; import java.util.Objects; import javax.security.auth.DestroyFailedException; @@ -179,8 +180,8 @@ public final class HqcKemContext implements KemContext { .createKey(key.getEncoded()); HQCKEMGenerator gen = new HQCKEMGenerator(new SecureRandom()); SecretWithEncapsulation res = gen.generateEncapsulated(keyParam); - byte[] secret = res.getSecret(); - byte[] ct = res.getEncapsulation(); + byte[] secret = Arrays.copyOf(res.getSecret(), res.getSecret().length); + byte[] ct = Arrays.copyOf(res.getEncapsulation(), res.getEncapsulation().length); res.destroy(); return new KemResult(ct, secret); } catch (DestroyFailedException e) { diff --git a/lib/src/main/java/zeroecho/core/alg/kyber/KyberKemContext.java b/lib/src/main/java/zeroecho/core/alg/kyber/KyberKemContext.java index ec3b878..221e067 100644 --- a/lib/src/main/java/zeroecho/core/alg/kyber/KyberKemContext.java +++ b/lib/src/main/java/zeroecho/core/alg/kyber/KyberKemContext.java @@ -39,6 +39,7 @@ import java.security.Key; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; +import java.util.Arrays; import java.util.Objects; import javax.security.auth.DestroyFailedException; @@ -182,8 +183,8 @@ public final class KyberKemContext implements KemContext { .createKey(key.getEncoded()); MLKEMGenerator gen = new MLKEMGenerator(new SecureRandom()); SecretWithEncapsulation res = gen.generateEncapsulated(keyParam); - byte[] secret = res.getSecret(); - byte[] ct = res.getEncapsulation(); + byte[] secret = Arrays.copyOf(res.getSecret(), res.getSecret().length); + byte[] ct = Arrays.copyOf(res.getEncapsulation(), res.getEncapsulation().length); res.destroy(); return new KemResult(ct, secret); } catch (DestroyFailedException e) { diff --git a/lib/src/main/java/zeroecho/core/alg/ntru/NtruKemContext.java b/lib/src/main/java/zeroecho/core/alg/ntru/NtruKemContext.java index 4192216..cd1bb43 100644 --- a/lib/src/main/java/zeroecho/core/alg/ntru/NtruKemContext.java +++ b/lib/src/main/java/zeroecho/core/alg/ntru/NtruKemContext.java @@ -39,6 +39,7 @@ import java.security.Key; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; +import java.util.Arrays; import java.util.Objects; import javax.security.auth.DestroyFailedException; @@ -213,8 +214,8 @@ public final class NtruKemContext implements KemContext { .createKey(key.getEncoded()); NTRUKEMGenerator gen = new NTRUKEMGenerator(new SecureRandom()); SecretWithEncapsulation res = gen.generateEncapsulated(keyParam); - byte[] secret = res.getSecret(); - byte[] ct = res.getEncapsulation(); + byte[] secret = Arrays.copyOf(res.getSecret(), res.getSecret().length); + byte[] ct = Arrays.copyOf(res.getEncapsulation(), res.getEncapsulation().length); res.destroy(); return new KemResult(ct, secret); } catch (DestroyFailedException e) { diff --git a/lib/src/main/java/zeroecho/core/alg/ntruprime/NtrulPrimeKemContext.java b/lib/src/main/java/zeroecho/core/alg/ntruprime/NtrulPrimeKemContext.java index 6533b0c..117dc17 100644 --- a/lib/src/main/java/zeroecho/core/alg/ntruprime/NtrulPrimeKemContext.java +++ b/lib/src/main/java/zeroecho/core/alg/ntruprime/NtrulPrimeKemContext.java @@ -39,6 +39,7 @@ import java.security.Key; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; +import java.util.Arrays; import java.util.Objects; import javax.security.auth.DestroyFailedException; @@ -175,8 +176,8 @@ public final class NtrulPrimeKemContext implements KemContext { .createKey(key.getEncoded()); NTRULPRimeKEMGenerator gen = new NTRULPRimeKEMGenerator(new SecureRandom()); SecretWithEncapsulation res = gen.generateEncapsulated(keyParam); - byte[] secret = res.getSecret(); - byte[] ct = res.getEncapsulation(); + byte[] secret = Arrays.copyOf(res.getSecret(), res.getSecret().length); + byte[] ct = Arrays.copyOf(res.getEncapsulation(), res.getEncapsulation().length); res.destroy(); return new KemResult(ct, secret); } catch (DestroyFailedException e) { diff --git a/lib/src/main/java/zeroecho/core/alg/ntruprime/SntruPrimeKemContext.java b/lib/src/main/java/zeroecho/core/alg/ntruprime/SntruPrimeKemContext.java index d118eb5..a498f89 100644 --- a/lib/src/main/java/zeroecho/core/alg/ntruprime/SntruPrimeKemContext.java +++ b/lib/src/main/java/zeroecho/core/alg/ntruprime/SntruPrimeKemContext.java @@ -39,6 +39,7 @@ import java.security.Key; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; +import java.util.Arrays; import java.util.Objects; import javax.security.auth.DestroyFailedException; @@ -181,8 +182,8 @@ public final class SntruPrimeKemContext implements KemContext { .createKey(key.getEncoded()); SNTRUPrimeKEMGenerator gen = new SNTRUPrimeKEMGenerator(new SecureRandom()); SecretWithEncapsulation res = gen.generateEncapsulated(keyParam); - byte[] secret = res.getSecret(); - byte[] ct = res.getEncapsulation(); + byte[] secret = Arrays.copyOf(res.getSecret(), res.getSecret().length); + byte[] ct = Arrays.copyOf(res.getEncapsulation(), res.getEncapsulation().length); res.destroy(); return new KemResult(ct, secret); } catch (DestroyFailedException e) { diff --git a/lib/src/main/java/zeroecho/core/alg/saber/SaberKemContext.java b/lib/src/main/java/zeroecho/core/alg/saber/SaberKemContext.java index 0fa67bf..482838c 100644 --- a/lib/src/main/java/zeroecho/core/alg/saber/SaberKemContext.java +++ b/lib/src/main/java/zeroecho/core/alg/saber/SaberKemContext.java @@ -39,6 +39,7 @@ import java.security.Key; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; +import java.util.Arrays; import java.util.Objects; import javax.security.auth.DestroyFailedException; @@ -182,8 +183,8 @@ public final class SaberKemContext implements KemContext { .createKey(key.getEncoded()); SABERKEMGenerator gen = new SABERKEMGenerator(new SecureRandom()); SecretWithEncapsulation res = gen.generateEncapsulated(keyParam); - byte[] secret = res.getSecret(); - byte[] ct = res.getEncapsulation(); + byte[] secret = Arrays.copyOf(res.getSecret(), res.getSecret().length); + byte[] ct = Arrays.copyOf(res.getEncapsulation(), res.getEncapsulation().length); res.destroy(); return new KemResult(ct, secret); } catch (DestroyFailedException e) {