Class HybridKexContext

java.lang.Object
zeroecho.sdk.hybrid.kex.HybridKexContext
All Implemented Interfaces:
Closeable, AutoCloseable, AgreementContext, CryptoContext, MessageAgreementContext

public final class HybridKexContext extends Object implements MessageAgreementContext
Hybrid key exchange (KEX) context that combines a classic agreement and a post-quantum KEM-style agreement.

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

Thread safety

Instances are mutable and not thread-safe; use one instance per handshake/session and thread.

Since:
1.0
  • 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 length
      classic - classic agreement context (must not be null)
      pqc - PQC message agreement context (must not be null)
      Throws:
      NullPointerException - if any argument is null
      Since:
      1.0
  • Method Details

    • algorithm

      public CryptoAlgorithm 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:
      algorithm in interface CryptoContext
      Returns:
      representative algorithm (classic leg)
      Since:
      1.0
    • key

      public Key key()
      Returns the representative key of this context.

      This is delegated to the classic leg to satisfy the CryptoContext contract. The hybrid KEX also depends on the PQC leg keys; callers should store those keys separately if needed by the application.

      Specified by:
      key in interface CryptoContext
      Returns:
      representative key (classic leg)
      Since:
      1.0
    • setPeerPublic

      public void setPeerPublic(PublicKey peer)
      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:
      setPeerPublic in interface AgreementContext
      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 implements MessageAgreementContext; otherwise it is ignored.

      Passing null resets the peer-message related state of both legs (best-effort).

      Specified by:
      setPeerMessage in interface MessageAgreementContext
      Parameters:
      message - hybrid message, or null to 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:
      getPeerMessage in interface MessageAgreementContext
      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 info parameter (HybridKexProfile.hkdfInfo()).

      Specified by:
      deriveSecret in interface AgreementContext
      Returns:
      derived keying material (OKM) of length HybridKexProfile.outLenBytes()
      Throws:
      IllegalStateException - if HKDF fails or underlying contexts are not configured
      Since:
      1.0
    • close

      public void close() throws IOException
      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:
      close in interface AutoCloseable
      Specified by:
      close in interface Closeable
      Specified by:
      close in interface CryptoContext
      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 null if none has been produced or set
      Since:
      1.0