oscerd commented on code in PR #28099:
URL: https://github.com/apache/camel/pull/28099#discussion_r4314486930


##########
components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCProducer.java:
##########
@@ -1146,47 +1155,85 @@ private void checkStatefulKeyBeforeSign() {
     }
 
     /**
-     * Persists stateful key state after a signing operation through the 
KeyLifecycleManager, if configured. This
-     * ensures the key index is tracked across restarts.
+     * Records the use of the signing key through the KeyLifecycleManager, if 
one is configured and the exchange names a
+     * key.
+     * <p/>
+     * This advances {@code usageCount}, which is what the {@code maxKeyUsage} 
rotation signal of
+     * {@link org.apache.camel.component.pqc.lifecycle.KeyRotationScheduler} 
reads. It therefore runs for every signing
+     * key, not only the stateful ones: ML-DSA, SLH-DSA and Falcon keys would 
otherwise sit at a usage count of zero
+     * forever and never be rotated.
+     * <p/>
+     * The key encapsulation operations do not call this method, so a KEM key 
still does not advance its usage count and
+     * {@code maxKeyUsage} does not apply to it.
+     * <p/>
+     * For a stateful signature key the advanced private key is stored as 
well, so the one-time index survives a
+     * restart.
      */
-    private void persistStatefulKeyStateAfterSign(Exchange exchange) throws 
Exception {
+    private void recordKeyUsageAfterSign(Exchange exchange) throws Exception {
         if (keyPair == null || keyPair.getPrivate() == null) {
             return;
         }
 
         PrivateKey privateKey = keyPair.getPrivate();
-        long remaining = getStatefulKeyRemaining(privateKey);
-        if (remaining < 0) {
-            // Not a stateful key
-            return;
-        }
+        boolean stateful = getStatefulKeyRemaining(privateKey) >= 0;
 
         KeyLifecycleManager klm = getConfiguration().getKeyLifecycleManager();
         if (klm == null) {
+            warnKeyUsageNotRecorded(stateful, "no keyLifecycleManager is 
configured on the endpoint");

Review Comment:
   Agreed, and fixed in the rebased branch. A plain `pqc:sign` with no 
lifecycle management is an ordinary setup, and with no manager there is no 
`maxKeyUsage` to miss, so there is nothing useful to tell that user.
   
   The `klm == null` branch now warns only for a stateful key. The other two — 
manager configured but no `CamelPQCKeyId` header, or no stored metadata — warn 
for both kinds, with the wording matching the consequence: index reuse for a 
stateful key, a rotation threshold that is never reached for any other.
   
   _Claude Code on behalf of @oscerd_



##########
docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc:
##########
@@ -212,6 +212,35 @@ also applies to `pollEnrich` and 
`ConsumerTemplate.receive`, which take one file
 until the last file of the directory is received, where before the first 
received file deleted it. This applies to
 camel-ftp, camel-sftp, camel-smb and camel-azure-files as well.
 
+=== camel-pqc - the usage-count and PENDING_ROTATION key rotation signals now 
fire
+
+Two of the three documented rotation signals of `KeyRotationScheduler` 
previously never triggered. Both now work, so a
+deployment that already configured them will start rotating keys where before 
nothing happened.
+
+* `maxKeyUsage` (and `camel.component.pqc.key-rotation-max-usage`) read 
`usageCount`, which was only advanced for the
+  stateful signature algorithms (XMSS, XMSSMT, LMS, HSS). For ML-DSA, SLH-DSA, 
Falcon and the KEM keys the counter
+  stayed at zero forever. A signing operation now advances it for every key, 
provided a `keyLifecycleManager` is
+  configured and the exchange carries a `CamelPQCKeyId` header.
+* The scheduler's default key filter accepted only `ACTIVE` keys, so a key an 
operator had marked
+  `PENDING_ROTATION` was skipped before `needsRotation()` was consulted. The 
default filter now accepts `ACTIVE` and
+  `PENDING_ROTATION`. A custom filter set with `setKeyFilter` is unaffected.
+
+Review any configured `maxKeyUsage` threshold before upgrading: a long-running 
deployment may rotate on the first
+check once the counter starts advancing.
+
+=== camel-pqc - a stateful signature fails the exchange when its key index 
cannot be persisted
+
+Signing with one of the stateful hash-based algorithms (LMS, HSS, XMSS, 
XMSSMT) consumes a one-time index inside the
+private key. That advanced key is now persisted through the 
`keyLifecycleManager` *before* the signature is placed on
+the message, instead of after.
+
+An exchange whose key state cannot be stored therefore now fails, where before 
it returned a signature and only

Review Comment:
   You are right, and my sentence was simply wrong — `recordKeyUsageAfterSign` 
already threw out of `signature()`, so the exchange already failed. Thank you 
for catching it.
   
   Reworded in the rebased branch. The entry is now titled "a failed stateful 
signature no longer carries the signature headers" and says that what changes 
is the content of the failed message: `CamelPQCSignature` and the three hybrid 
headers are no longer on it, so a route using `onException(...).handled(true)` 
or a dead letter channel can no longer read the signature off the failed 
exchange and pass it on. I also dropped the claim about the crash case, which 
the reorder does not affect, and the last paragraph now covers every key rather 
than only the stateful ones.
   
   _Claude Code on behalf of @oscerd_



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to