Andrea Cosentino created CAMEL-25603:
----------------------------------------

             Summary: camel-pqc - key encapsulation operations share one 
KeyGenerator and KeyAgreement instance across concurrent exchanges without 
synchronization
                 Key: CAMEL-25603
                 URL: https://issues.apache.org/jira/browse/CAMEL-25603
             Project: Camel
          Issue Type: Bug
          Components: camel-pqc
            Reporter: Andrea Cosentino
            Assignee: Andrea Cosentino
             Fix For: 4.24.0, 4.18.6, 4.22.2


{{PQCProducer}} holds a single {{javax.crypto.KeyGenerator}} for the KEM 
operations. It comes from the {{keyGenerator}} option (which is autowired), 
from the static {{PQCDefault*Material.keyGenerator}} instances that 
{{PQCComponent}} assigns when no key material is configured, or from 
{{doStart()}}:

{code:java}
private KeyGenerator keyGenerator;
...
keyGenerator = getEndpoint().getConfiguration().getKeyGenerator();
if (ObjectHelper.isEmpty(keyGenerator)) {
    keyGenerator = KeyGenerator.getInstance(kemAlg.getAlgorithm(), 
kemAlg.getBcProvider());
}
{code}

{{generateEncapsulation()}} and {{extractEncapsulation()}} then run 
{{init(spec)}} followed by {{generateKey()}} on that object with no 
synchronization:

{code:java}
keyGenerator.init(new KEMExtractSpec(keyPair.getPrivate(), 
payload.getEncapsulation(), ...), SecureRandomHelper.getSecureRandom());
SecretKeyWithEncapsulation secEnc2 = (SecretKeyWithEncapsulation) 
keyGenerator.generateKey();
{code}

The same pattern is used in:
* {{HybridKEM.encapsulate()}} / {{HybridKEM.extract()}}: {{init}} + 
{{generateKey}} on the PQC {{KeyGenerator}}, and {{init}} / {{doPhase}} / 
{{generateSecret}} on the classical {{KeyAgreement}}. Both come from the 
configuration (autowired), from the static {{PQCDefaultX25519MLKEMMaterial}} / 
{{PQCDefaultECDHMLKEMMaterial}} instances, or are created once at producer 
start.
* {{PQCDataFormat.marshal()}} / {{unmarshal()}}: {{doStart()}} caches one 
{{KeyGenerator}} in the {{keyGenerator}} field whenever 
{{keyEncapsulationAlgorithm}} is set (the default is MLKEM), so every marshal 
and unmarshal call on a started instance uses the same object, also when no 
KeyGenerator is configured. As a side effect a started data format ignores the 
per-message {{CamelPQCKemAlgorithm}} header.

{{KeyGenerator}} and {{KeyAgreement}} keep the parameters passed to {{init()}} 
/ {{doPhase()}} in the object until {{generateKey()}} / {{generateSecret()}} 
reads them, and they are not thread-safe, while producers and data formats are 
invoked concurrently. Interleaved calls can produce a result computed from 
another invocation's parameters, or fail with an exception. The docs currently 
recommend sharing one instance: the ML-KEM example registers a single 
KeyGenerator bean used by both the generate and the extract endpoint, and 
pqc-dataformat.adoc has a "Caching KeyGenerator" tip.

Proposal, following CAMEL-24445:
* create the {{KeyGenerator}} / {{KeyAgreement}} per invocation where Camel 
creates it (producer and data format start);
* serialize each init-to-result sequence on a configured or otherwise shared 
instance;
* stop assigning the static {{PQCDefault*Material}} key generators and key 
agreements to endpoint configurations;
* update the docs, and add concurrency tests for the KEM, hybrid KEM and data 
format paths.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to