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)