refactor!: consolidate crypto architecture and security model

* make ZeroEchoSession the sole policy, audit, and runtime boundary
* replace combined key builders with operation-specific SPI and typed metadata
* remove obsolete pre-release compatibility APIs and global crypto operations
* finalize JCA agreement contexts and replace inheritance with composition
* harden secret lifecycle, key destruction, hybrid KEX, PBKDF2, and audit handling
* standardize PairSeq I/O and introduce immutable validated value types
* migrate app, ext, samples, and required pki integration points
* expand correctness, security, concurrency, and malformed-input coverage

BREAKING CHANGE: removes deprecated pre-release global configuration, legacy
context factories, combined key-builder contracts, String-based password APIs,
unchecked PairSeq writing, BlockGeometry public fields, and other compatibility
facades.
This commit is contained in:
2026-07-28 19:20:30 +02:00
parent 7319aca0db
commit 49dc080c65
298 changed files with 12802 additions and 8763 deletions

View File

@@ -50,7 +50,6 @@ import conflux.Ctx;
import conflux.CtxInterface;
import zeroecho.core.ConfluxKeys;
import zeroecho.core.CryptoAlgorithm;
import zeroecho.core.CryptoAlgorithms;
import zeroecho.core.KeyUsage;
import zeroecho.core.alg.aes.AesKeyGenSpec;
import zeroecho.core.alg.aes.AesSpec;
@@ -61,23 +60,16 @@ import zeroecho.sdk.builders.alg.AesDataContentBuilder;
import zeroecho.sdk.builders.core.DataContentChainBuilder;
import zeroecho.sdk.builders.core.PlainBytesBuilder;
import zeroecho.sdk.content.api.DataContent;
import zeroecho.sdk.ZeroEchoSession;
class AesTest {
private static final Logger LOG = Logger.getLogger(AesTest.class.getName());
private final ZeroEchoSession zeroEchoSession = new ZeroEchoSession();
SecretKey generateAesKey() throws GeneralSecurityException {
// Locate the AES algorithm in the catalog
CryptoAlgorithm aes = CryptoAlgorithms.require("AES");
SecretKey key = aes
// Retrieve the builder that works with AesKeyGenSpec - the specification for
// AES key generation
.symmetricKeyBuilder(AesKeyGenSpec.class)
// Generate a secret key according to the AES256 specification
.generateSecret(AesKeyGenSpec.aes256());
// Log the generated key (truncated to short hex for readability)
LOG.log(Level.INFO, "AES256 key generated: {0}", Strings.toShortHexString(key.getEncoded()));
// or just: CryptoAlgorithms.generateSecret("AES", AesKeyGenSpec.aes256())
SecretKey key = zeroEchoSession.keyBuilders().symmetric()
.generate("AES", AesKeyGenSpec.aes256());
LOG.log(Level.INFO, "AES256 key generated");
return key;
}
@@ -101,7 +93,7 @@ class AesTest {
SecretKey key = generateAesKey();
byte[] encrypted;
// Request an encryption context using the key and AES-GCM-128 specification
try (EncryptionContext enc = CryptoAlgorithms.create("AES", KeyUsage.ENCRYPT, key, spec)) {
try (EncryptionContext enc = zeroEchoSession.createContext("AES", KeyUsage.ENCRYPT, key, spec)) {
// This context implements ContextAware, allowing us to associate our session
((ContextAware) enc).setContext(session);
// Get an encrypted stream that processes the plaintext on-the-fly
@@ -132,7 +124,7 @@ class AesTest {
// Separate context again to hold IV and AAD values so we can inspect them later
CtxInterface session = Ctx.INSTANCE.getContext("aes-ctx-" + System.nanoTime());
AesDataContentBuilder aesBuilder = AesDataContentBuilder.builder()
AesDataContentBuilder aesBuilder = AesDataContentBuilder.builder(new zeroecho.sdk.ZeroEchoSession())
// Use the generated AES key
.importKeyRaw(key.getEncoded())
// Specify AES-GCM-128 mode
@@ -171,7 +163,7 @@ class AesTest {
// Sample message to encrypt
byte[] msg = randomBytes(100);
AesDataContentBuilder aesBuilder = AesDataContentBuilder.builder()
AesDataContentBuilder aesBuilder = AesDataContentBuilder.builder(new zeroecho.sdk.ZeroEchoSession())
// Automatically generate a 256-bit AES key
.generateKey(256)
// Store ad-hoc generated parameters (IV, AAD) in the stream header
@@ -210,7 +202,7 @@ class AesTest {
// Sample message to encrypt
byte[] msg = randomBytes(100);
AesDataContentBuilder aesBuilder = AesDataContentBuilder.builder().generateKey(256).modeGcm(128).withHeader();
AesDataContentBuilder aesBuilder = AesDataContentBuilder.builder(new zeroecho.sdk.ZeroEchoSession()).generateKey(256).modeGcm(128).withHeader();
// The builder stores generated IV and AAD inside the stream header
DataContent dccb = DataContentChainBuilder.encrypt().add(PlainBytesBuilder.builder().bytes(msg)).add(aesBuilder)
@@ -227,7 +219,7 @@ class AesTest {
dccb = DataContentChainBuilder.decrypt().add(PlainBytesBuilder.builder().bytes(encrypted))
// Use the same AES key for decryption; IV and AAD are restored from the header
.add(AesDataContentBuilder.builder().importKeyRaw(key.getEncoded()).modeGcm(128).withHeader())
.add(AesDataContentBuilder.builder(new zeroecho.sdk.ZeroEchoSession()).importKeyRaw(key.getEncoded()).modeGcm(128).withHeader())
// Build the pipeline
.build();
byte[] decrypted;

View File

@@ -52,6 +52,7 @@ import zeroecho.core.alg.xdh.XdhSpec;
import zeroecho.core.context.AgreementContext;
import zeroecho.core.context.MessageAgreementContext;
import zeroecho.core.spec.VoidSpec;
import zeroecho.sdk.ZeroEchoSession;
import zeroecho.core.util.Strings;
import zeroecho.sdk.util.BouncyCastleActivator;
@@ -107,6 +108,7 @@ import zeroecho.sdk.util.BouncyCastleActivator;
* </p>
*/
class AgreementVariantsTest {
private final ZeroEchoSession session = new ZeroEchoSession();
private static final Logger LOG = Logger.getLogger(AgreementVariantsTest.class.getName());
@@ -138,16 +140,17 @@ class AgreementVariantsTest {
void kemAdapter_mlKem_roundTrip() throws Exception {
LOG.info("kemAdapter_mlKem_roundTrip - KEM_ADAPTER (ML-KEM)");
KeyPair recipient = CryptoAlgorithms.generateKeyPair("ML-KEM", KyberKeyGenSpec.kyber1024());
KeyPair recipient = session.keyBuilders().asymmetric()
.generateKeyPair("ML-KEM", KyberKeyGenSpec.kyber1024());
MessageAgreementContext initiator = null;
MessageAgreementContext responder = null;
try {
// Initiator: constructed with recipient's public key (encapsulation side).
initiator = CryptoAlgorithms.create("ML-KEM", KeyUsage.AGREEMENT, recipient.getPublic(), VoidSpec.INSTANCE);
initiator = session.createContext("ML-KEM", KeyUsage.AGREEMENT, recipient.getPublic(), VoidSpec.INSTANCE);
// Responder: constructed with recipient's private key (decapsulation side).
responder = CryptoAlgorithms.create("ML-KEM", KeyUsage.AGREEMENT, recipient.getPrivate(),
responder = session.createContext("ML-KEM", KeyUsage.AGREEMENT, recipient.getPrivate(),
VoidSpec.INSTANCE);
// One-shot outbound message: KEM ciphertext / encapsulation payload.
@@ -193,17 +196,16 @@ class AgreementVariantsTest {
void classicAgreement_x25519_roundTrip() throws Exception {
LOG.info("classicAgreement_x25519_roundTrip - CLASSIC_AGREEMENT (X25519)");
CryptoAlgorithm xdh = CryptoAlgorithms.require("Xdh");
KeyPair alice = xdh.generateKeyPair();
KeyPair bob = xdh.generateKeyPair();
KeyPair alice = session.keyBuilders().asymmetric().generateKeyPair("Xdh", XdhSpec.X25519);
KeyPair bob = session.keyBuilders().asymmetric().generateKeyPair("Xdh", XdhSpec.X25519);
AgreementContext aCtx = null;
AgreementContext bCtx = null;
try {
// Both contexts are built from local private keys.
aCtx = CryptoAlgorithms.create("Xdh", KeyUsage.AGREEMENT, alice.getPrivate(), XdhSpec.X25519);
bCtx = CryptoAlgorithms.create("Xdh", KeyUsage.AGREEMENT, bob.getPrivate(), XdhSpec.X25519);
aCtx = session.createContext("Xdh", KeyUsage.AGREEMENT, alice.getPrivate(), XdhSpec.X25519);
bCtx = session.createContext("Xdh", KeyUsage.AGREEMENT, bob.getPrivate(), XdhSpec.X25519);
// The protocol layer provides peer public keys (here we use in-memory
// exchange).
@@ -254,9 +256,8 @@ class AgreementVariantsTest {
void pairMessage_x25519_roundTrip() throws Exception {
LOG.info("pairMessage_x25519_roundTrip - PAIR_MESSAGE (X25519)");
CryptoAlgorithm xdh = CryptoAlgorithms.require("Xdh");
KeyPair alice = xdh.generateKeyPair();
KeyPair bob = xdh.generateKeyPair();
KeyPair alice = session.keyBuilders().asymmetric().generateKeyPair("Xdh", XdhSpec.X25519);
KeyPair bob = session.keyBuilders().asymmetric().generateKeyPair("Xdh", XdhSpec.X25519);
// Wrapper is required because ZeroEcho capability dispatch uses Key (KeyPair is
// not a Key).
@@ -267,8 +268,8 @@ class AgreementVariantsTest {
MessageAgreementContext bCtx = null;
try {
aCtx = CryptoAlgorithms.create("Xdh", KeyUsage.AGREEMENT, aliceKey, XdhSpec.X25519);
bCtx = CryptoAlgorithms.create("Xdh", KeyUsage.AGREEMENT, bobKey, XdhSpec.X25519);
aCtx = session.createContext("Xdh", KeyUsage.AGREEMENT, aliceKey, XdhSpec.X25519);
bCtx = session.createContext("Xdh", KeyUsage.AGREEMENT, bobKey, XdhSpec.X25519);
// Outbound messages: SPKI encodings of local public keys.
byte[] aMsg = aCtx.getPeerMessage();

View File

@@ -46,7 +46,6 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import zeroecho.core.CryptoAlgorithms;
import zeroecho.core.KeyUsage;
import zeroecho.core.alg.elgamal.ElgamalParamSpec;
import zeroecho.core.alg.kyber.KyberKeyGenSpec;
@@ -58,11 +57,15 @@ import zeroecho.sdk.builders.core.PlainBytesBuilder;
import zeroecho.sdk.content.api.DataContent;
import zeroecho.sdk.guard.MultiRecipientDataSourceBuilder;
import zeroecho.sdk.guard.UnlockMaterial;
import zeroecho.sdk.Pbkdf2Limits;
import zeroecho.sdk.ZeroEchoSession;
import zeroecho.sdk.util.BouncyCastleActivator;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class CombinedDeliveryTest {
private static final Logger LOG = Logger.getLogger(CombinedDeliveryTest.class.getName());
private final ZeroEchoSession session = new ZeroEchoSession()
.withPbkdf2Limits(new Pbkdf2Limits(1_000_000, 1_000_000));
@BeforeAll
void setupProviders() {
@@ -70,31 +73,25 @@ class CombinedDeliveryTest {
}
KeyPair generateKyberKeys() throws GeneralSecurityException {
KeyPair kp = CryptoAlgorithms.generateKeyPair("ML-KEM", KyberKeyGenSpec.kyber1024());
LOG.log(Level.INFO, "ML-KEM key public={0} private={1}",
new Object[] { Strings.toShortHexString(kp.getPublic().getEncoded()),
Strings.toShortHexString(kp.getPrivate().getEncoded()) });
KeyPair kp = session.keyBuilders().asymmetric()
.generateKeyPair("ML-KEM", KyberKeyGenSpec.kyber1024());
LOG.log(Level.INFO, "ML-KEM key pair generated");
return kp;
}
KeyPair generateRsaKeys() throws GeneralSecurityException {
KeyPair kp = CryptoAlgorithms.generateKeyPair("RSA", RsaKeyGenSpec.rsa4096());
LOG.log(Level.INFO, "RSA key public={0} private={1}",
new Object[] { Strings.toShortHexString(kp.getPublic().getEncoded()),
Strings.toShortHexString(kp.getPrivate().getEncoded()) });
KeyPair kp = session.keyBuilders().asymmetric()
.generateKeyPair("RSA", RsaKeyGenSpec.rsa4096());
LOG.log(Level.INFO, "RSA key pair generated");
return kp;
}
KeyPair generateElGamalKeys() throws GeneralSecurityException {
KeyPair kp = CryptoAlgorithms.generateKeyPair("ElGamal", ElgamalParamSpec.ffdhe2048());
LOG.log(Level.INFO, "ElGamal key public={0} private={1}",
new Object[] { Strings.toShortHexString(kp.getPublic().getEncoded()),
Strings.toShortHexString(kp.getPrivate().getEncoded()) });
KeyPair kp = session.keyBuilders().asymmetric()
.generateKeyPair("ElGamal", ElgamalParamSpec.ffdhe2048());
LOG.log(Level.INFO, "ElGamal key pair generated");
return kp;
}
@@ -112,16 +109,16 @@ class CombinedDeliveryTest {
KeyPair decoy1rsa = generateRsaKeys();
KeyPair decoy2rsa = generateRsaKeys();
AesDataContentBuilder payload = AesDataContentBuilder.builder().modeCbcPkcs5().withHeader();
AesDataContentBuilder payload = AesDataContentBuilder.builder(session).modeCbcPkcs5().withHeader();
MultiRecipientDataSourceBuilder multi = MultiRecipientDataSourceBuilder.builder()
MultiRecipientDataSourceBuilder multi = MultiRecipientDataSourceBuilder.builder(session)
// AES-256 - 32 bytes of key material
.payloadKeyBytes(32).withAes(payload)
// add recipients - the context initialized with the public key
.addRecipient(CryptoAlgorithms.create("ElGamal", KeyUsage.ENCRYPT, elg.getPublic()))
.addRecipient(CryptoAlgorithms.create("RSA", KeyUsage.ENCRYPT, rsa.getPublic()))
.addRecipient(session.createContext("ElGamal", KeyUsage.ENCRYPT, elg.getPublic()))
.addRecipient(session.createContext("RSA", KeyUsage.ENCRYPT, rsa.getPublic()))
// ML-KEM PostQuantum uses AES for the inner payload
.addRecipient(CryptoAlgorithms.create("ML-KEM", KeyUsage.ENCAPSULATE, kem.getPublic()),
.addRecipient(session.createContext("ML-KEM", KeyUsage.ENCAPSULATE, kem.getPublic()),
/* AES256 key size */
32,
/* salt size in hkdf */
@@ -129,8 +126,8 @@ class CombinedDeliveryTest {
// Password (via KDF)
.addPasswordRecipient(password, /* iterations */ 200_000, /* saltLen */ 16, /* kekBytes */ 32)
// and some decoys
.addRecipientDecoy(CryptoAlgorithms.create("RSA", KeyUsage.ENCRYPT, decoy1rsa.getPublic()))
.addRecipientDecoy(CryptoAlgorithms.create("RSA", KeyUsage.ENCRYPT, decoy2rsa.getPublic()));
.addRecipientDecoy(session.createContext("RSA", KeyUsage.ENCRYPT, decoy1rsa.getPublic()))
.addRecipientDecoy(session.createContext("RSA", KeyUsage.ENCRYPT, decoy2rsa.getPublic()));
// shuffle all the recipients
multi.shuffle();
@@ -153,8 +150,8 @@ class CombinedDeliveryTest {
private void recipientProcessing(String method, byte[] msg, byte[] encrypted, UnlockMaterial unlock)
throws IOException {
AesDataContentBuilder payload = AesDataContentBuilder.builder().modeCbcPkcs5().withHeader();
MultiRecipientDataSourceBuilder multi = MultiRecipientDataSourceBuilder.builder()
AesDataContentBuilder payload = AesDataContentBuilder.builder(session).modeCbcPkcs5().withHeader();
MultiRecipientDataSourceBuilder multi = MultiRecipientDataSourceBuilder.builder(session)
// define our payload
.payloadKeyBytes(32).withAes(payload)
// one recipient

View File

@@ -44,7 +44,6 @@ import java.util.logging.Logger;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import zeroecho.core.CryptoAlgorithms;
import zeroecho.core.alg.kyber.KyberKeyGenSpec;
import zeroecho.core.alg.xdh.XdhSpec;
import zeroecho.sdk.builders.HybridKexBuilder;
@@ -56,6 +55,7 @@ import zeroecho.sdk.hybrid.derived.HybridDerived;
import zeroecho.sdk.hybrid.kex.HybridKexContext;
import zeroecho.sdk.hybrid.kex.HybridKexExporter;
import zeroecho.sdk.hybrid.kex.HybridKexProfile;
import zeroecho.sdk.ZeroEchoSession;
import zeroecho.sdk.hybrid.kex.HybridKexTranscript;
import zeroecho.sdk.util.BouncyCastleActivator;
@@ -78,6 +78,7 @@ import zeroecho.sdk.util.BouncyCastleActivator;
* </p>
*/
class HybridDerivedAesDemoTest {
private final ZeroEchoSession session = new ZeroEchoSession();
private static final Logger LOG = Logger.getLogger(HybridDerivedAesDemoTest.class.getName());
@@ -108,16 +109,17 @@ class HybridDerivedAesDemoTest {
"hybrid-derived-aes-gcm-condensed");
// ...Generate classic key pairs for X25519 (Xdh + XdhSpec.X25519).
KeyPair aliceClassic = CryptoAlgorithms.generateKeyPair("Xdh", XdhSpec.X25519);
KeyPair bobClassic = CryptoAlgorithms.generateKeyPair("Xdh", XdhSpec.X25519);
KeyPair aliceClassic = session.keyBuilders().asymmetric().generateKeyPair("Xdh", XdhSpec.X25519);
KeyPair bobClassic = session.keyBuilders().asymmetric().generateKeyPair("Xdh", XdhSpec.X25519);
// ...Generate PQC key pair for ML-KEM-768 (recipient; used by Bob side to
// decapsulate).
KeyPair bobPqc = CryptoAlgorithms.generateKeyPair("ML-KEM", KyberKeyGenSpec.kyber768());
KeyPair bobPqc = session.keyBuilders().asymmetric()
.generateKeyPair("ML-KEM", KyberKeyGenSpec.kyber768());
// ...Build Alice initiator: classic agreement (out-of-band peer pub) + PQC
// encapsulation.
HybridKexContext alice = HybridKexBuilder.builder()
HybridKexContext alice = HybridKexBuilder.builder(session)
// ...Set mandatory profile.
.profile(profile)
// ...Bind builder HKDF info to transcript.
@@ -142,7 +144,7 @@ class HybridDerivedAesDemoTest {
.buildInitiator();
// ...Build Bob responder: classic agreement + PQC decapsulation.
HybridKexContext bob = HybridKexBuilder.builder()
HybridKexContext bob = HybridKexBuilder.builder(session)
// ...Set mandatory profile.
.profile(profile)
// ...Bind builder HKDF info to transcript.
@@ -203,7 +205,7 @@ class HybridDerivedAesDemoTest {
// ...Inject explicit AAD.
.aad(aad)
// ...Apply derived key(256b) and IV(12B) to AES-GCM with header.
.applyToAesGcm(AesDataContentBuilder.builder()
.applyToAesGcm(AesDataContentBuilder.builder(session)
// ...Store IV in header for decrypt side.
.withHeader()
// ...Use AES-GCM with 128-bit authentication tag.
@@ -230,7 +232,7 @@ class HybridDerivedAesDemoTest {
// ...Same explicit AAD as encryption.
.aad(aad)
// ...Apply derived key and IV to AES-GCM with header.
.applyToAesGcm(AesDataContentBuilder.builder()
.applyToAesGcm(AesDataContentBuilder.builder(session)
// ...Parse IV from header.
.withHeader()
// ...Use AES-GCM with 128-bit authentication tag.
@@ -271,14 +273,15 @@ class HybridDerivedAesDemoTest {
"hybrid-derived-aes-gcm-expanded");
// ...Generate classic key pairs for X25519.
KeyPair aliceClassic = CryptoAlgorithms.generateKeyPair("Xdh", XdhSpec.X25519);
KeyPair bobClassic = CryptoAlgorithms.generateKeyPair("Xdh", XdhSpec.X25519);
KeyPair aliceClassic = session.keyBuilders().asymmetric().generateKeyPair("Xdh", XdhSpec.X25519);
KeyPair bobClassic = session.keyBuilders().asymmetric().generateKeyPair("Xdh", XdhSpec.X25519);
// ...Generate PQC key pair for ML-KEM-768.
KeyPair bobPqc = CryptoAlgorithms.generateKeyPair("ML-KEM", KyberKeyGenSpec.kyber768());
KeyPair bobPqc = session.keyBuilders().asymmetric()
.generateKeyPair("ML-KEM", KyberKeyGenSpec.kyber768());
// ...Build Alice initiator in a step-by-step manner.
HybridKexBuilder aliceBuilder = HybridKexBuilder.builder();
HybridKexBuilder aliceBuilder = HybridKexBuilder.builder(session);
// ...Set mandatory profile.
aliceBuilder.profile(profile);
// ...Bind builder HKDF info to transcript.
@@ -306,7 +309,7 @@ class HybridDerivedAesDemoTest {
HybridKexContext alice = alicePqcCfg.buildInitiator();
// ...Build Bob responder in a step-by-step manner.
HybridKexBuilder bobBuilder = HybridKexBuilder.builder();
HybridKexBuilder bobBuilder = HybridKexBuilder.builder(session);
// ...Set mandatory profile.
bobBuilder.profile(profile);
// ...Bind builder HKDF info to transcript.
@@ -356,7 +359,7 @@ class HybridDerivedAesDemoTest {
byte[] aad = "aad:demo:expanded".getBytes(StandardCharsets.UTF_8);
// ...Prepare AES builder for encryption.
AesDataContentBuilder aesEnc = AesDataContentBuilder.builder();
AesDataContentBuilder aesEnc = AesDataContentBuilder.builder(session);
// ...Store IV in header.
aesEnc.withHeader();
// ...Use AES-GCM with 128-bit authentication tag.
@@ -389,7 +392,7 @@ class HybridDerivedAesDemoTest {
System.out.println("...ciphertext " + lens(ciphertext) + " " + shortHex(ciphertext, 48));
// ...Prepare AES builder for decryption.
AesDataContentBuilder aesDec = AesDataContentBuilder.builder();
AesDataContentBuilder aesDec = AesDataContentBuilder.builder(session);
// ...Parse IV from header.
aesDec.withHeader();
// ...Use AES-GCM with 128-bit authentication tag.
@@ -453,13 +456,14 @@ class HybridDerivedAesDemoTest {
byte[] aad = "aad:local-self".getBytes(StandardCharsets.UTF_8);
// ...Generate classic identity keys (X25519).
KeyPair selfClassic = CryptoAlgorithms.generateKeyPair("Xdh", XdhSpec.X25519);
KeyPair selfClassic = session.keyBuilders().asymmetric().generateKeyPair("Xdh", XdhSpec.X25519);
// ...Generate PQC identity keys (ML-KEM-768).
KeyPair selfPqc = CryptoAlgorithms.generateKeyPair("ML-KEM", KyberKeyGenSpec.kyber768());
KeyPair selfPqc = session.keyBuilders().asymmetric()
.generateKeyPair("ML-KEM", KyberKeyGenSpec.kyber768());
// ...Build local initiator (encapsulation) against our own public keys.
HybridKexContext encKex = HybridKexBuilder.builder()
HybridKexContext encKex = HybridKexBuilder.builder(session)
// ...Set mandatory profile.
.profile(profile)
// ...Bind derivation to transcript.
@@ -509,7 +513,7 @@ class HybridDerivedAesDemoTest {
// ...Inject explicit AAD.
.aad(aad)
// ...Apply derived key(256b) and IV(12B) to AES-GCM with header.
.applyToAesGcm(AesDataContentBuilder.builder()
.applyToAesGcm(AesDataContentBuilder.builder(session)
// ...Store IV in header for decrypt side.
.withHeader()
// ...Use AES-GCM with 128-bit authentication tag.
@@ -525,7 +529,7 @@ class HybridDerivedAesDemoTest {
// ...Build local responder (decapsulation) using our own private keys and
// stored peer message.
HybridKexContext decKex = HybridKexBuilder.builder()
HybridKexContext decKex = HybridKexBuilder.builder(session)
// ...Set mandatory profile.
.profile(profile)
// ...Bind derivation to transcript.
@@ -577,7 +581,7 @@ class HybridDerivedAesDemoTest {
// ...Same explicit AAD.
.aad(aad)
// ...Apply derived key and IV to AES-GCM with header.
.applyToAesGcm(AesDataContentBuilder.builder()
.applyToAesGcm(AesDataContentBuilder.builder(session)
// ...Parse IV from header.
.withHeader()
// ...Use AES-GCM with 128-bit authentication tag.

View File

@@ -42,13 +42,13 @@ import java.util.logging.Logger;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import zeroecho.core.CryptoAlgorithms;
import zeroecho.core.alg.common.agreement.KeyPairKey;
import zeroecho.core.alg.kyber.KyberKeyGenSpec;
import zeroecho.core.alg.xdh.XdhSpec;
import zeroecho.sdk.hybrid.kex.HybridKexContext;
import zeroecho.sdk.hybrid.kex.HybridKexContexts;
import zeroecho.sdk.hybrid.kex.HybridKexProfile;
import zeroecho.sdk.ZeroEchoSession;
import zeroecho.sdk.util.BouncyCastleActivator;
/**
@@ -103,6 +103,7 @@ import zeroecho.sdk.util.BouncyCastleActivator;
* </p>
*/
class HybridKexDemoTest {
private final ZeroEchoSession session = new ZeroEchoSession();
private static final Logger LOG = Logger.getLogger(HybridKexDemoTest.class.getName());
@@ -121,11 +122,12 @@ class HybridKexDemoTest {
logBegin("CLASSIC_AGREEMENT + KEM_ADAPTER", "Xdh/X25519 + ML-KEM-768", "HKDF-SHA256", "OKM=32B");
// Classic leg keys (Xdh + XdhSpec.X25519).
KeyPair aliceClassic = CryptoAlgorithms.generateKeyPair("Xdh", XdhSpec.X25519);
KeyPair bobClassic = CryptoAlgorithms.generateKeyPair("Xdh", XdhSpec.X25519);
KeyPair aliceClassic = session.keyBuilders().asymmetric().generateKeyPair("Xdh", XdhSpec.X25519);
KeyPair bobClassic = session.keyBuilders().asymmetric().generateKeyPair("Xdh", XdhSpec.X25519);
// PQC leg: Bob is the KEM recipient (has ML-KEM keypair).
KeyPair bobPqc = CryptoAlgorithms.generateKeyPair("ML-KEM", KyberKeyGenSpec.kyber768());
KeyPair bobPqc = session.keyBuilders().asymmetric()
.generateKeyPair("ML-KEM", KyberKeyGenSpec.kyber768());
// Hybrid profile: default HKDF label, 32-byte output suitable for symmetric
// keys.
@@ -138,13 +140,13 @@ class HybridKexDemoTest {
// Alice (initiator): classic uses Alice private + Bob classic public
// (out-of-band).
// ...PQC uses Bob PQC public and will produce a KEM ciphertext.
alice = HybridKexContexts.initiator(profile, "Xdh", aliceClassic.getPrivate(), bobClassic.getPublic(),
alice = HybridKexContexts.initiator(session, profile, "Xdh", aliceClassic.getPrivate(), bobClassic.getPublic(),
XdhSpec.X25519, "ML-KEM", bobPqc.getPublic(), null);
// Bob (responder): classic uses Bob private + Alice classic public
// (out-of-band).
// ...PQC uses Bob PQC private and will consume Alice's ciphertext.
bob = HybridKexContexts.responder(profile, "Xdh", bobClassic.getPrivate(), aliceClassic.getPublic(),
bob = HybridKexContexts.responder(session, profile, "Xdh", bobClassic.getPrivate(), aliceClassic.getPublic(),
XdhSpec.X25519, "ML-KEM", bobPqc.getPrivate(), null);
// Alice -> Bob: hybrid message carries PQC ciphertext; classic part is empty.
@@ -175,11 +177,12 @@ class HybridKexDemoTest {
logBegin("PAIR_MESSAGE + KEM_ADAPTER", "Xdh/X25519 + ML-KEM-768", "HKDF-SHA256", "OKM=32B");
// Classic leg keys (Xdh + XdhSpec.X25519).
KeyPair aliceClassic = CryptoAlgorithms.generateKeyPair("Xdh", XdhSpec.X25519);
KeyPair bobClassic = CryptoAlgorithms.generateKeyPair("Xdh", XdhSpec.X25519);
KeyPair aliceClassic = session.keyBuilders().asymmetric().generateKeyPair("Xdh", XdhSpec.X25519);
KeyPair bobClassic = session.keyBuilders().asymmetric().generateKeyPair("Xdh", XdhSpec.X25519);
// PQC leg: Bob is the KEM recipient (has ML-KEM keypair).
KeyPair bobPqc = CryptoAlgorithms.generateKeyPair("ML-KEM", KyberKeyGenSpec.kyber768());
KeyPair bobPqc = session.keyBuilders().asymmetric()
.generateKeyPair("ML-KEM", KyberKeyGenSpec.kyber768());
HybridKexProfile profile = HybridKexProfile.defaultProfile(32);
@@ -190,14 +193,14 @@ class HybridKexDemoTest {
// Alice classic leg is message-based (PAIR_MESSAGE): it will emit her public
// key as SPKI bytes.
// ...PQC leg (KEM initiator) will emit ciphertext.
alice = HybridKexContexts.initiatorPairMessage(profile, "Xdh", new KeyPairKey(aliceClassic), XdhSpec.X25519,
alice = HybridKexContexts.initiatorPairMessage(session, profile, "Xdh", new KeyPairKey(aliceClassic), XdhSpec.X25519,
"ML-KEM", bobPqc.getPublic(), null);
// Bob classic leg is message-based (PAIR_MESSAGE): it will emit his public key
// as SPKI bytes.
// ...PQC leg (KEM responder) consumes ciphertext and typically does not emit a
// PQC message.
bob = HybridKexContexts.responderPairMessage(profile, "Xdh", new KeyPairKey(bobClassic), XdhSpec.X25519,
bob = HybridKexContexts.responderPairMessage(session, profile, "Xdh", new KeyPairKey(bobClassic), XdhSpec.X25519,
"ML-KEM", bobPqc.getPrivate(), null);
// Alice -> Bob: hybrid message carries classic SPKI + PQC ciphertext.
@@ -231,13 +234,13 @@ class HybridKexDemoTest {
logBegin("Builder", "CLASSIC_AGREEMENT + KEM_ADAPTER", "Xdh/X25519 + ML-KEM-768", "HKDF-SHA256", "OKM=32B");
// ...Generate classic leg keys (Xdh + XdhSpec.X25519).
java.security.KeyPair aliceClassic = zeroecho.core.CryptoAlgorithms.generateKeyPair("Xdh",
java.security.KeyPair aliceClassic = session.keyBuilders().asymmetric().generateKeyPair("Xdh",
zeroecho.core.alg.xdh.XdhSpec.X25519);
java.security.KeyPair bobClassic = zeroecho.core.CryptoAlgorithms.generateKeyPair("Xdh",
java.security.KeyPair bobClassic = session.keyBuilders().asymmetric().generateKeyPair("Xdh",
zeroecho.core.alg.xdh.XdhSpec.X25519);
// ...Generate PQC (recipient) keys (ML-KEM-768).
java.security.KeyPair bobPqc = zeroecho.core.CryptoAlgorithms.generateKeyPair("ML-KEM",
java.security.KeyPair bobPqc = session.keyBuilders().asymmetric().generateKeyPair("ML-KEM",
zeroecho.core.alg.kyber.KyberKeyGenSpec.kyber768());
// ...Create a profile for HKDF (output length 32 bytes).
@@ -252,7 +255,7 @@ class HybridKexDemoTest {
zeroecho.sdk.hybrid.kex.HybridKexPolicy policy = new zeroecho.sdk.hybrid.kex.HybridKexPolicy(128, 192, 32);
// ...Start the builder.
zeroecho.sdk.builders.HybridKexBuilder b = zeroecho.sdk.builders.HybridKexBuilder.builder()
zeroecho.sdk.builders.HybridKexBuilder b = zeroecho.sdk.builders.HybridKexBuilder.builder(session)
// ...Set HKDF profile (salt/info/outLen).
.profile(profile)
// ...Bind HKDF info to transcript (protocol context).
@@ -283,7 +286,7 @@ class HybridKexDemoTest {
// ...Build responder-side context (Bob) with symmetric configuration (note: PQC
// uses private key).
zeroecho.sdk.hybrid.kex.HybridKexContext bob = zeroecho.sdk.builders.HybridKexBuilder.builder()
zeroecho.sdk.hybrid.kex.HybridKexContext bob = zeroecho.sdk.builders.HybridKexBuilder.builder(session)
// ...Set the same HKDF profile to derive the same OKM.
.profile(profile)
// ...Bind the same transcript to ensure both sides derive the same OKM.
@@ -346,11 +349,12 @@ class HybridKexDemoTest {
logBegin("Builder", "PAIR_MESSAGE + KEM_ADAPTER", "Xdh/X25519 + ML-KEM-768", "HKDF-SHA256", "OKM=32B");
// ...Generate classic leg keys (Xdh + XdhSpec.X25519).
KeyPair aliceClassic = CryptoAlgorithms.generateKeyPair("Xdh", XdhSpec.X25519);
KeyPair bobClassic = CryptoAlgorithms.generateKeyPair("Xdh", XdhSpec.X25519);
KeyPair aliceClassic = session.keyBuilders().asymmetric().generateKeyPair("Xdh", XdhSpec.X25519);
KeyPair bobClassic = session.keyBuilders().asymmetric().generateKeyPair("Xdh", XdhSpec.X25519);
// ...Generate PQC (recipient) keys (ML-KEM-768).
KeyPair bobPqc = CryptoAlgorithms.generateKeyPair("ML-KEM", KyberKeyGenSpec.kyber768());
KeyPair bobPqc = session.keyBuilders().asymmetric()
.generateKeyPair("ML-KEM", KyberKeyGenSpec.kyber768());
// ...Create a profile for HKDF (output length 32 bytes).
HybridKexProfile profile = HybridKexProfile.defaultProfile(32);
@@ -365,7 +369,7 @@ class HybridKexDemoTest {
zeroecho.sdk.hybrid.kex.HybridKexPolicy policy = new zeroecho.sdk.hybrid.kex.HybridKexPolicy(128, 192, 32);
// ...Start the initiator builder.
zeroecho.sdk.builders.HybridKexBuilder initBuilder = zeroecho.sdk.builders.HybridKexBuilder.builder()
zeroecho.sdk.builders.HybridKexBuilder initBuilder = zeroecho.sdk.builders.HybridKexBuilder.builder(session)
// ...Set HKDF profile (salt/info/outLen).
.profile(profile)
// ...Bind HKDF info to transcript (protocol context).
@@ -395,7 +399,7 @@ class HybridKexDemoTest {
.buildInitiator();
// ...Start the responder builder.
zeroecho.sdk.builders.HybridKexBuilder respBuilder = zeroecho.sdk.builders.HybridKexBuilder.builder()
zeroecho.sdk.builders.HybridKexBuilder respBuilder = zeroecho.sdk.builders.HybridKexBuilder.builder(session)
// ...Set the same HKDF profile to derive the same OKM.
.profile(profile)
// ...Bind the same transcript to ensure both sides derive the same OKM.

View File

@@ -48,10 +48,11 @@ import javax.crypto.SecretKey;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import zeroecho.core.CryptoAlgorithm;
import zeroecho.core.CryptoAlgorithms;
import zeroecho.core.alg.ed25519.Ed25519KeyGenSpec;
import zeroecho.core.alg.sphincsplus.SphincsPlusKeyGenSpec;
import zeroecho.core.context.SignatureContext;
import zeroecho.sdk.builders.TagTrailerDataContentBuilder;
import zeroecho.sdk.ZeroEchoSession;
import zeroecho.sdk.builders.alg.AesDataContentBuilder;
import zeroecho.sdk.builders.core.DataContentChainBuilder;
import zeroecho.sdk.builders.core.PlainBytesBuilder;
@@ -99,7 +100,7 @@ class HybridSigningAesTest {
byte[] msg = randomBytes(100);
// AES-GCM with header, runtime params are stored in header
AesDataContentBuilder aesBuilder = AesDataContentBuilder.builder().generateKey(256).modeGcm(128).withHeader();
AesDataContentBuilder aesBuilder = AesDataContentBuilder.builder(new zeroecho.sdk.ZeroEchoSession()).generateKey(256).modeGcm(128).withHeader();
// Hybrid signature: Ed25519 + SPHINCS+ (AND)
HybridSignatureProfile profile = new HybridSignatureProfile("Ed25519", "SPHINCS+", null, null,
@@ -108,9 +109,9 @@ class HybridSigningAesTest {
KeyPair ed = generateKeyPair("Ed25519");
KeyPair spx = generateKeyPair("SPHINCS+");
SignatureContext tagEnc = HybridSignatureContexts.sign(profile, ed.getPrivate(), spx.getPrivate(),
SignatureContext tagEnc = HybridSignatureContexts.sign(new zeroecho.sdk.ZeroEchoSession(), profile, ed.getPrivate(), spx.getPrivate(),
2 * 1024 * 1024);
SignatureContext tagDec = HybridSignatureContexts.verify(profile, ed.getPublic(), spx.getPublic(),
SignatureContext tagDec = HybridSignatureContexts.verify(new zeroecho.sdk.ZeroEchoSession(), profile, ed.getPublic(), spx.getPublic(),
2 * 1024 * 1024);
// For verification, make mismatch behavior explicit (builder also supports
@@ -141,7 +142,7 @@ class HybridSigningAesTest {
// encrypted input
.add(PlainBytesBuilder.builder().bytes(ciphertext))
// AES-GCM decryption
.add(AesDataContentBuilder.builder().importKeyRaw(aesKey.getEncoded()).modeGcm(128).withHeader())
.add(AesDataContentBuilder.builder(new zeroecho.sdk.ZeroEchoSession()).importKeyRaw(aesKey.getEncoded()).modeGcm(128).withHeader())
// hybrid signature verification
.add(new TagTrailerDataContentBuilder<Signature>(tagDec).bufferSize(8192).throwOnMismatch()).build();
@@ -161,7 +162,7 @@ class HybridSigningAesTest {
byte[] msg = randomBytes(100);
// AES-GCM with header, runtime params are stored in header
AesDataContentBuilder aesBuilder = AesDataContentBuilder.builder().generateKey(256).modeGcm(128).withHeader();
AesDataContentBuilder aesBuilder = AesDataContentBuilder.builder(new zeroecho.sdk.ZeroEchoSession()).generateKey(256).modeGcm(128).withHeader();
// Hybrid signature: Ed25519 + SPHINCS+ (AND)
HybridSignatureProfile profile = new HybridSignatureProfile("Ed25519", "SPHINCS+", null, null,
@@ -170,9 +171,9 @@ class HybridSigningAesTest {
KeyPair ed = generateKeyPair("Ed25519");
KeyPair spx = generateKeyPair("SPHINCS+");
SignatureContext tagEnc = HybridSignatureContexts.sign(profile, ed.getPrivate(), spx.getPrivate(),
SignatureContext tagEnc = HybridSignatureContexts.sign(new zeroecho.sdk.ZeroEchoSession(), profile, ed.getPrivate(), spx.getPrivate(),
2 * 1024 * 1024);
SignatureContext tagDec = HybridSignatureContexts.verify(profile, ed.getPublic(), spx.getPublic(),
SignatureContext tagDec = HybridSignatureContexts.verify(new zeroecho.sdk.ZeroEchoSession(), profile, ed.getPublic(), spx.getPublic(),
2 * 1024 * 1024);
tagDec.setVerificationApproach(tagDec.getVerificationCore().getThrowOnMismatch());
@@ -205,7 +206,7 @@ class HybridSigningAesTest {
// hybrid signature verification
.add(new TagTrailerDataContentBuilder<Signature>(tagDec).bufferSize(8192).throwOnMismatch())
// AES-GCM decryption
.add(AesDataContentBuilder.builder().importKeyRaw(aesKey.getEncoded()).modeGcm(128).withHeader())
.add(AesDataContentBuilder.builder(new zeroecho.sdk.ZeroEchoSession()).importKeyRaw(aesKey.getEncoded()).modeGcm(128).withHeader())
.build();
byte[] decrypted;
@@ -219,8 +220,14 @@ class HybridSigningAesTest {
// helpers
private static KeyPair generateKeyPair(String algId) throws GeneralSecurityException {
CryptoAlgorithm alg = CryptoAlgorithms.require(algId);
return alg.generateKeyPair();
ZeroEchoSession session = new ZeroEchoSession();
return switch (algId) {
case "Ed25519" -> session.keyBuilders().asymmetric().generateKeyPair(algId,
Ed25519KeyGenSpec.defaultSpec());
case "SPHINCS+" -> session.keyBuilders().asymmetric().generateKeyPair(algId,
SphincsPlusKeyGenSpec.defaultSpec());
default -> throw new IllegalArgumentException("Unsupported sample signature algorithm: " + algId);
};
}
private static byte[] randomBytes(int len) {

View File

@@ -47,7 +47,6 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import zeroecho.core.CryptoAlgorithms;
import zeroecho.core.alg.kyber.KyberKeyGenSpec;
import zeroecho.core.util.Strings;
import zeroecho.sdk.builders.alg.AesDataContentBuilder;
@@ -56,9 +55,11 @@ import zeroecho.sdk.builders.core.DataContentChainBuilder;
import zeroecho.sdk.builders.core.PlainBytesBuilder;
import zeroecho.sdk.content.api.DataContent;
import zeroecho.sdk.util.BouncyCastleActivator;
import zeroecho.sdk.ZeroEchoSession;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class PostQuantumTest {
private final ZeroEchoSession session = new ZeroEchoSession();
private static final Logger LOG = Logger.getLogger(PostQuantumTest.class.getName());
@BeforeAll
@@ -67,7 +68,8 @@ class PostQuantumTest {
}
KeyPair generateKyberKeys() throws GeneralSecurityException {
KeyPair kp = CryptoAlgorithms.generateKeyPair("ML-KEM", KyberKeyGenSpec.kyber1024());
KeyPair kp = session.keyBuilders().asymmetric()
.generateKeyPair("ML-KEM", KyberKeyGenSpec.kyber1024());
LOG.log(Level.INFO, "ML-KEM key public={0} private={1}",
new Object[] { Strings.toShortHexString(kp.getPublic().getEncoded()),
@@ -87,7 +89,7 @@ class PostQuantumTest {
// Start the chain with a plain byte array as input
.add(PlainBytesBuilder.builder().bytes(msg))
// Add a KEM (Key Encapsulation Mechanism) stage
.add(KemDataContentBuilder.builder()
.add(KemDataContentBuilder.builder(new zeroecho.sdk.ZeroEchoSession())
// Select a KEM-capable algorithm: ML-KEM (Kyber variant)
.kem("ML-KEM")
// Encrypt to this recipients public key
@@ -95,7 +97,7 @@ class PostQuantumTest {
// Derive a shared secret and expand/obfuscate it using HKDF-SHA256
.hkdfSha256("KEM-demo".getBytes(StandardCharsets.US_ASCII))
// Use the derived secret as a key for AES
.withAes(AesDataContentBuilder.builder()
.withAes(AesDataContentBuilder.builder(new zeroecho.sdk.ZeroEchoSession())
// Configure AES in GCM mode with a 128-bit authentication tag
.modeGcm(128)
// Store auxiliary parameters (e.g., random IV) inside the stream header
@@ -113,7 +115,7 @@ class PostQuantumTest {
// Start with the encrypted byte array as input
.add(PlainBytesBuilder.builder().bytes(encrypted))
// Add the KEM stage again, matching the encryption parameters
.add(KemDataContentBuilder.builder()
.add(KemDataContentBuilder.builder(new zeroecho.sdk.ZeroEchoSession())
// Use the same KEM algorithm that was used for encryption
.kem("ML-KEM")
// Provide the recipients private key for decapsulation
@@ -121,7 +123,7 @@ class PostQuantumTest {
// Re-derive the shared secret with the same HKDF-SHA256 label
.hkdfSha256("KEM-demo".getBytes(StandardCharsets.US_ASCII))
// Use the secret to unlock AES payload decryption
.withAes(AesDataContentBuilder.builder()
.withAes(AesDataContentBuilder.builder(new zeroecho.sdk.ZeroEchoSession())
// AES in GCM mode with a 128-bit tag, consistent with encryption
.modeGcm(128)
// Read auxiliary parameters (IV, etc.) back from the header

View File

@@ -47,7 +47,6 @@ import javax.crypto.SecretKey;
import org.junit.jupiter.api.Test;
import zeroecho.core.CryptoAlgorithms;
import zeroecho.core.alg.rsa.RsaKeyGenSpec;
import zeroecho.core.alg.rsa.RsaSigSpec;
import zeroecho.core.tag.TagEngine;
@@ -58,12 +57,15 @@ import zeroecho.sdk.builders.alg.AesDataContentBuilder;
import zeroecho.sdk.builders.core.DataContentChainBuilder;
import zeroecho.sdk.builders.core.PlainBytesBuilder;
import zeroecho.sdk.content.api.DataContent;
import zeroecho.sdk.ZeroEchoSession;
class SigningAesTest {
private final ZeroEchoSession session = new ZeroEchoSession();
private static final Logger LOG = Logger.getLogger(SigningAesTest.class.getName());
KeyPair generateRsaKeys() throws GeneralSecurityException {
KeyPair kp = CryptoAlgorithms.generateKeyPair("RSA", RsaKeyGenSpec.rsa4096());
KeyPair kp = session.keyBuilders().asymmetric()
.generateKeyPair("RSA", RsaKeyGenSpec.rsa4096());
LOG.log(Level.INFO, "RSA key public={0} private={1}",
new Object[] { Strings.toShortHexString(kp.getPublic().getEncoded()),
@@ -81,7 +83,7 @@ class SigningAesTest {
// Configure AES in GCM mode with a 128-bit authentication tag. A fresh 256-bit
// AES key will be generated automatically, and runtime parameters (IV, AAD)
// will be written into the header.
AesDataContentBuilder aesBuilder = AesDataContentBuilder.builder().generateKey(256).modeGcm(128).withHeader();
AesDataContentBuilder aesBuilder = AesDataContentBuilder.builder(new zeroecho.sdk.ZeroEchoSession()).generateKey(256).modeGcm(128).withHeader();
// Generate RSA-4096 key pair (retrieved via algorithm registry for convenience)
KeyPair rsa = generateRsaKeys();
@@ -89,9 +91,9 @@ class SigningAesTest {
// Configure PSS signature parameters: SHA-256 hash, salt length = 32 bytes
RsaSigSpec pss = RsaSigSpec.pss(RsaSigSpec.Hash.SHA256, 32);
// Create signing engine (RSA-PSS with private key)
TagEngine<Signature> tagEnc = TagEngineBuilder.rsaSign(rsa.getPrivate(), pss).get();
TagEngine<Signature> tagEnc = TagEngineBuilder.rsaSign(new zeroecho.sdk.ZeroEchoSession(), rsa.getPrivate(), pss).get();
// Create verification engine (RSA-PSS with public key)
TagEngine<Signature> tagDec = TagEngineBuilder.rsaVerify(rsa.getPublic(), pss).get();
TagEngine<Signature> tagDec = TagEngineBuilder.rsaVerify(new zeroecho.sdk.ZeroEchoSession(), rsa.getPublic(), pss).get();
// Build the encryption pipeline
DataContent dccb = DataContentChainBuilder.encrypt()
@@ -119,7 +121,7 @@ class SigningAesTest {
.add(PlainBytesBuilder.builder().bytes(encrypted))
// AES-GCM decryption using the same key; IV and AAD are restored automatically
// from the header
.add(AesDataContentBuilder.builder().importKeyRaw(key.getEncoded()).modeGcm(128).withHeader())
.add(AesDataContentBuilder.builder(new zeroecho.sdk.ZeroEchoSession()).importKeyRaw(key.getEncoded()).modeGcm(128).withHeader())
// Verify the RSA-PSS signature trailer at the end of the stream (configured to
// throw on mismatch)
.add(new TagTrailerDataContentBuilder<>(tagDec).bufferSize(8192).throwOnMismatch())
@@ -142,15 +144,15 @@ class SigningAesTest {
// Create a random sample message to be encrypted
byte[] msg = randomBytes(100);
AesDataContentBuilder aesBuilder = AesDataContentBuilder.builder().generateKey(256).modeGcm(128).withHeader();
AesDataContentBuilder aesBuilder = AesDataContentBuilder.builder(new zeroecho.sdk.ZeroEchoSession()).generateKey(256).modeGcm(128).withHeader();
// Generate RSA-4096 key pair (retrieved via algorithm registry for convenience)
KeyPair rsa = generateRsaKeys();
// Configure PSS signature parameters: SHA-256 hash, salt length = 32 bytes
RsaSigSpec pss = RsaSigSpec.pss(RsaSigSpec.Hash.SHA256, 32);
TagEngine<Signature> tagEnc = TagEngineBuilder.rsaSign(rsa.getPrivate(), pss).get();
TagEngine<Signature> tagDec = TagEngineBuilder.rsaVerify(rsa.getPublic(), pss).get();
TagEngine<Signature> tagEnc = TagEngineBuilder.rsaSign(new zeroecho.sdk.ZeroEchoSession(), rsa.getPrivate(), pss).get();
TagEngine<Signature> tagDec = TagEngineBuilder.rsaVerify(new zeroecho.sdk.ZeroEchoSession(), rsa.getPublic(), pss).get();
// Build the encryption pipeline
DataContent dccb = DataContentChainBuilder.encrypt()
@@ -183,7 +185,7 @@ class SigningAesTest {
.add(new TagTrailerDataContentBuilder<>(tagDec).bufferSize(8192).throwOnMismatch())
// AES-GCM decryption using the same key; IV and AAD are restored automatically
// from the header
.add(AesDataContentBuilder.builder().importKeyRaw(key.getEncoded()).modeGcm(128).withHeader())
.add(AesDataContentBuilder.builder(new zeroecho.sdk.ZeroEchoSession()).importKeyRaw(key.getEncoded()).modeGcm(128).withHeader())
// Build the pipeline
.build();
byte[] decrypted;