You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@camel.apache.org by "Claus Ibsen (Jira)" <ji...@apache.org> on 2026/10/21 12:48:00 UTC

[jira] [Resolved] (CAMEL-25797) camel-pqc - the usage-count and PENDING_ROTATION key rotation signals never fire

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

Claus Ibsen resolved CAMEL-25797.
---------------------------------
    Resolution: Fixed

Fixed by https://github.com/apache/camel/pull/28094 on main (4.24.0).

_Claude Code on behalf of Claus Ibsen_

> camel-pqc - the usage-count and PENDING_ROTATION key rotation signals never fire
> --------------------------------------------------------------------------------
>
>                 Key: CAMEL-25797
>                 URL: https://issues.apache.org/jira/browse/CAMEL-25797
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-pqc
>            Reporter: Andrea Cosentino
>            Assignee: Andrea Cosentino
>            Priority: Major
>             Fix For: 4.24.0
>
>
> {{KeyRotationScheduler}} documents three rotation signals: key age, usage count, and a {{PENDING_ROTATION}} status on the key metadata. Two of the three cannot trigger.
> h3. 1. {{usageCount}} is only ever advanced for stateful keys
> {{KeyMetadata.updateLastUsed()}} is the only thing that increments the counter — {{KeyMetadata.java:81-84}}:
> {code:java}
>     public void updateLastUsed() {
>         this.lastUsedAt = Instant.now();
>         this.usageCount++;
> {code}
> It has exactly one production caller, {{PQCProducer.java:1185}}, inside {{persistStatefulKeyStateAfterSign}}, which returns early for anything that is not XMSS/XMSSMT/LMS/HSS — {{PQCProducer.java:1159-1162}}:
> {code:java}
>         long remaining = getStatefulKeyRemaining(privateKey);
>         if (remaining < 0) {
>             // Not a stateful key
>             return;
>         }
> {code}
> So for ML-DSA, SLH-DSA, Falcon and every KEM key — the overwhelming majority — {{usageCount}} stays {{0}} forever, while all five managers gate rotation on it, e.g. {{InMemoryKeyLifecycleManager.java:158}}:
> {code:java}
>         return maxUsage > 0 && keyMetadata.getUsageCount() >= maxUsage;
> {code}
> {{KeyRotationScheduler.setMaxKeyUsage}} and the documented {{camel.component.pqc.key-rotation-max-usage}} property ({{pqc-key-lifecycle.adoc:975}}, "rotate keys used more than 1,000,000 times") are therefore inert.
> h3. 2. {{PENDING_ROTATION}} is filtered out before it is consulted
> {{KeyRotationScheduler.java:79}}:
> {code:java}
>     private Predicate<KeyMetadata> keyFilter = metadata -> metadata.getStatus() == KeyMetadata.KeyStatus.ACTIVE;
> {code}
> {{KeyRotationScheduler.java:130-137}}:
> {code:java}
>             for (KeyMetadata metadata : keys) {
>                 if (metadata == null || !keyFilter.test(metadata)) {
>                     continue;
>                 }
>                 String keyId = metadata.getKeyId();
>                 try {
>                     if (keyManager.needsRotation(keyId, maxKeyAge, maxKeyUsage)) {
> {code}
> {{KeyMetadata.needsRotation()}} ({{:130-132}}) does check the status:
> {code:java}
>     public boolean needsRotation() {
>         return status == KeyStatus.PENDING_ROTATION
>                 || (nextRotationAt != null && Instant.now().isAfter(nextRotationAt));
> {code}
> but a key whose status an operator set to {{PENDING_ROTATION}} is no longer {{ACTIVE}}, so it is {{continue}}d at {{:132}} before {{needsRotation}} ever runs. Only {{nextRotationAt}} on an otherwise-ACTIVE key works today.
> The scheduler javadoc ({{:62-64}}) and {{pqc-key-lifecycle.adoc:929-931}} both advertise {{PENDING_ROTATION}} as a supported signal.
> h3. Suggested fix
> Increment {{usageCount}} on every successful sign/verify/KEM operation, not just the stateful path. Widen the default {{keyFilter}} to {{ACTIVE}} or {{PENDING_ROTATION}}, or test {{needsRotation()}} before applying the filter.
> h3. Test gap
> {{KeyRotationSchedulerTest}} increments {{usageCount}} by hand ({{:87}}), which hides that production never does, and never asserts that a {{PENDING_ROTATION}} key rotates.
> ----
> _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
(v8.20.10#820010)