Class HybridKexContext
- All Implemented Interfaces:
Closeable, AutoCloseable, AgreementContext, CryptoContext, MessageAgreementContext
This context composes two independent shared secrets:
- Classic leg: a
AgreementContext(e.g. X25519, ECDH, DH) that derives a raw shared secret after the peer public key is configured. - PQC leg: a
MessageAgreementContext(typically ML-KEM exposed via core capabilities) whose "peer message" represents the encapsulation/decapsulation payload (e.g. KEM ciphertext).
Wire format
This class implements MessageAgreementContext to provide a single
hybrid message that can be transmitted between parties. The encoding is:
[int classicLen][classicBytes...][int pqcLen][pqcBytes...]
For typical pairings such as X25519 + ML-KEM, the classic part is
empty because the classic leg is configured using the peer public key
out-of-band. If the classic leg itself is message-capable, the classic part
may be populated.
Key derivation
The final keying material is derived using HKDF-SHA256 (RFC 5869):
classicSS = classic.deriveSecret()
pqcSS = pqc.deriveSecret()
IKM = classicSS || pqcSS
OKM = HKDF-SHA256(IKM, salt, info, outLen)
Intermediate raw secrets are treated as sensitive and are zeroized (best-effort) once HKDF completes.
CryptoContext identity
A hybrid exchange is inherently bound to multiple algorithms and keys. The
algorithm() and key() methods return representative values
from the classic leg to satisfy the
CryptoContext contract. Callers that require
full introspection should retain references to the underlying component
contexts.
Error handling
- Malformed hybrid messages cause
IllegalArgumentExceptioninsetPeerMessage(byte[]). - HKDF failures are surfaced as
IllegalStateExceptioninderiveSecret(). close()closes both component contexts and aggregatesIOExceptions via suppressed exceptions.
Thread safety
Instances are mutable and not thread-safe; use one instance per handshake/session and thread.
- Since:
- 1.0
-
Constructor Summary
ConstructorsConstructorDescriptionHybridKexContext(HybridKexProfile profile, AgreementContext classic, MessageAgreementContext pqc) Creates a hybrid KEX context by composing two underlying contexts. -
Method Summary
Modifier and TypeMethodDescriptionReturns the representative algorithm of this context.voidclose()Closes both underlying contexts.byte[]Derives the final hybrid shared secret (OKM) using HKDF-SHA256.byte[]Returns the hybrid message to be sent to the peer.key()Returns the representative key of this context.byte[]Returns the last hybrid peer message that was produced or set on this instance.voidsetPeerMessage(byte[] message) Supplies the hybrid peer message received from the remote party.voidsetPeerPublic(PublicKey peer) Sets the peer public key for the classic leg.
-
Constructor Details
-
HybridKexContext
public HybridKexContext(HybridKexProfile profile, AgreementContext classic, MessageAgreementContext pqc) Creates a hybrid KEX context by composing two underlying contexts.- Parameters:
profile- hybrid profile defining HKDF binding and output lengthclassic- classic agreement context (must not benull)pqc- PQC message agreement context (must not benull)- Throws:
NullPointerException- if any argument isnull- Since:
- 1.0
-
-
Method Details
-
algorithm
Returns the representative algorithm of this context.This is currently delegated to the classic leg. Hybrid constructions bind multiple algorithms; callers that need full visibility should track both legs explicitly.
- Specified by:
algorithmin interfaceCryptoContext- Returns:
- representative algorithm (classic leg)
- Since:
- 1.0
-
key
Returns the representative key of this context.This is delegated to the classic leg to satisfy the
CryptoContextcontract. The hybrid KEX also depends on the PQC leg keys; callers should store those keys separately if needed by the application.- Specified by:
keyin interfaceCryptoContext- Returns:
- representative key (classic leg)
- Since:
- 1.0
-
setPeerPublic
Sets the peer public key for the classic leg.This is the usual configuration step for classic DH-style agreements. The PQC leg typically does not use peer public keys through this method; however, the call is forwarded on a best-effort basis for implementations that accept it.
- Specified by:
setPeerPublicin interfaceAgreementContext- Parameters:
peer- peer public key for the classic agreement- Since:
- 1.0
-
setPeerMessage
public void setPeerMessage(byte[] message) Supplies the hybrid peer message received from the remote party.The message is decoded into its classic and PQC parts. The PQC part is always forwarded to the PQC leg via
MessageAgreementContext.setPeerMessage(byte[]). The classic part is forwarded only if the classic leg also implementsMessageAgreementContext; otherwise it is ignored.Passing
nullresets the peer-message related state of both legs (best-effort).- Specified by:
setPeerMessagein interfaceMessageAgreementContext- Parameters:
message- hybrid message, ornullto reset- Throws:
IllegalArgumentException- if the provided message does not conform to the hybrid encoding- Since:
- 1.0
-
getPeerMessage
public byte[] getPeerMessage()Returns the hybrid message to be sent to the peer.The PQC leg typically produces a non-empty message (encapsulation ciphertext) for initiator roles. The classic leg contributes an empty message unless it supports message mode.
- Specified by:
getPeerMessagein interfaceMessageAgreementContext- Returns:
- encoded hybrid peer message
- Throws:
IllegalStateException- if encoding fails or the PQC leg cannot produce a message in the current role- Since:
- 1.0
-
deriveSecret
public byte[] deriveSecret()Derives the final hybrid shared secret (OKM) using HKDF-SHA256.This method derives both component secrets and then performs HKDF over their concatenation. Higher-level protocols should additionally bind transcript/context data through the HKDF
infoparameter (HybridKexProfile.hkdfInfo()).- Specified by:
deriveSecretin interfaceAgreementContext- Returns:
- derived keying material (OKM) of length
HybridKexProfile.outLenBytes() - Throws:
IllegalStateException- if HKDF fails or underlying contexts are not configured- Since:
- 1.0
-
close
Closes both underlying contexts.If closing the second context fails after the first one already failed, the second failure is added as a suppressed exception on the first one.
- Specified by:
closein interfaceAutoCloseable- Specified by:
closein interfaceCloseable- Specified by:
closein interfaceCryptoContext- Throws:
IOException- if closing either leg fails- Since:
- 1.0
-
lastPeerMessageOrNull
public byte[] lastPeerMessageOrNull()Returns the last hybrid peer message that was produced or set on this instance.This is intended for diagnostics and testing. The returned array is a defensive copy.
- Returns:
- last hybrid message, or
nullif none has been produced or set - Since:
- 1.0
-