[ 
https://issues.apache.org/jira/browse/CAMEL-25796?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Claus Ibsen updated CAMEL-25796:
--------------------------------
    Fix Version/s: 4.24.0

> camel-pqc - persist the stateful signature key index before the signature is 
> emitted
> ------------------------------------------------------------------------------------
>
>                 Key: CAMEL-25796
>                 URL: https://issues.apache.org/jira/browse/CAMEL-25796
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-pqc
>            Reporter: Andrea Cosentino
>            Assignee: Andrea Cosentino
>            Priority: Major
>             Fix For: 4.24.0
>
>
> For the stateful hash-based signature schemes (LMS, HSS, XMSS, XMSSMT) each 
> private key index may be used exactly once. {{camel-pqc}} advances the index 
> by signing and persists the advanced key *afterwards*.
> {{PQCProducer.java:523-537}}:
> {code:java}
>     private void signature(Exchange exchange)
>             throws Exception {
>         checkStatefulKeyBeforeSign();
>         Signature signerForExchange = signerForExchange();
>         byte[] signature;
>         synchronized (signerForExchange) {
>             signerForExchange.initSign(keyPair.getPrivate());
>             updateSignatureFromBody(signerForExchange, exchange.getMessage());
>             signature = signerForExchange.sign();
>         }
>         exchange.getMessage().setHeader(PQCConstants.SIGNATURE, signature);
>         persistStatefulKeyStateAfterSign(exchange);
>     }
> {code}
> {{sign()}} consumes the index inside the Bouncy Castle private key and the 
> signature is published on the message. Only then is the advanced key written 
> to the store. Any failure in between — a crash, a {{kill -9}}, an exception 
> inside {{persistStatefulKeyStateAfterSign}} — leaves the emitted signature in 
> the world while the persisted key still sits at the old index. On restart the 
> same index is used again, which defeats the one-time-use property the whole 
> scheme rests on. The same ordering applies to hybrid signing at 
> {{PQCProducer.java:779}}.
> h3. Silent skips make the window wider
> {{persistStatefulKeyStateAfterSign}} ({{PQCProducer.java:1152-1190}}) returns 
> without any log when there is no {{KeyLifecycleManager}}:
> {code:java}
>         KeyLifecycleManager klm = getConfiguration().getKeyLifecycleManager();
>         if (klm == null) {
>             return;
>         }
> {code}
> and again when the {{CamelPQCKeyId}} header is absent ({{:1170-1172}}). Only 
> the {{metadata == null}} case warns. So a stateful route with no key id 
> header advances the index on every exchange and persists nothing, silently.
> h3. Not atomic with the exhaustion check either
> {{checkStatefulKeyBeforeSign()}} runs at {{:525}}, outside the 
> {{synchronized}} block, and the lock is on a *per-exchange* {{Signature}} 
> instance. Two concurrent exchanges can both observe {{remaining == 1}}.
> h3. Suggested fix
> Reserve and persist the next index before signing, then sign. Where the 
> backing store cannot do that atomically, at minimum persist before publishing 
> the signature on the exchange, make the two silent {{return}} paths log a 
> warning, and serialise the check-and-sign for stateful algorithms.
> h3. Test gap
> {{PQCStatefulKeyTrackingTest}} covers only sequential signing and two 
> hand-constructed {{StatefulKeyState}} objects. Nothing covers crash-restart 
> index resumption or concurrent signing.
> ----
> _Reported by Claude Code (AI agent) on behalf of [~oscerd]. Every code 
> reference was verified against commit {{7aae27315946}} on {{main}}._



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to