davsclaus commented on code in PR #28099:
URL: https://github.com/apache/camel/pull/28099#discussion_r4313842625
##########
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:
This now warns for every stateless signing endpoint without a
`keyLifecycleManager`, for example a plain
`pqc:sign?operation=sign&signatureAlgorithm=MLDSA`, which is the common case
with no rotation in use. The warning tells those users to configure a
`keyLifecycleManager` they do not need. With no manager there is no
`maxKeyUsage` to miss, so I would warn a stateless key only when a manager is
configured and the header or the metadata is missing. Keep the stateful
warnings as they are.
_Claude Code on behalf of Claus Ibsen_
##########
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:
Before this PR the exchange already failed: `recordKeyUsageAfterSign` threw
out of `signature()`. What is new is that the failed message carries no
signature header. A route with `onException(...).handled(true)` or a dead
letter channel could pass that signature on before. Please reword this sentence
to say so.
_Claude Code on behalf of Claus Ibsen_
--
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]