Andrea Cosentino created CAMEL-25802:
----------------------------------------
Summary: camel-pqc - align the key material classes on
SecureRandom, provider names and documented defaults
Key: CAMEL-25802
URL: https://issues.apache.org/jira/browse/CAMEL-25802
Project: Camel
Issue Type: Improvement
Components: camel-pqc
Reporter: Andrea Cosentino
The twenty-one {{PQCDefault*Material}} classes were written by cloning each
other and have drifted. None of the items below is individually serious;
together they make the module's security-relevant defaults unpredictable and
undocumented.
h3. 1. Only 6 of 11 signature materials seed with {{SecureRandomHelper}}
Seeded: MLDSA, DILITHIUM, SLHDSA, SPHINCSPLUS, XMSS, XMSSMT. Not seeded —
Falcon, MAYO, SNOVA, LMS, HSS fall back to the provider default RNG.
{{PQCDefaultFalconMaterial.java:50-52}}:
{code:java}
KeyPairGenerator kpGen =
KeyPairGenerator.getInstance(PQCSignatureAlgorithms.FALCON.getAlgorithm(),
PQCSignatureAlgorithms.FALCON.getBcProvider());
kpGen.initialize(FalconParameterSpec.falcon_1024);
{code}
Same at {{PQCDefaultHSSMaterial.java:59}}, {{PQCDefaultLMSMaterial.java:54}},
{{PQCDefaultMAYOMaterial.java:52}}, {{PQCDefaultSNOVAMaterial.java:52}}. All
ten KEM materials do seed. {{SecureRandomHelper}} exists precisely to
centralise this choice. Note that {{PQCComponent.resolveKeyPair}} ({{:264}})
always seeds, so the same algorithm gets a different RNG depending on whether
{{parameterSpec}} was set.
h3. 2. Aliases of the same algorithm default to different security levels
{{MLKEM("ML-KEM", "BC")}} and {{KYBER("ML-KEM", "BC")}} are the same JCE
algorithm, yet {{PQCDefaultMLKEMMaterial.java:55}} uses
{{MLKEMParameterSpec.ml_kem_512}} (NIST level 1) while
{{PQCDefaultKYBERMaterial.java:55}} uses {{ml_kem_1024}} (level 5), and the
hybrid materials independently pick {{ml_kem_768}}. The production autowired
default for the flagship standardised KEM is therefore the weakest of the
three. The same split exists for {{MLDSA}} ({{ml_dsa_65}}) versus {{DILITHIUM}}
({{ml_dsa_87}}). None of these levels is documented anywhere.
Related drift across the family with no stated rationale:
{{CMCEParameterSpec.mceliece8192128f}} (highest),
{{SABERParameterSpec.lightsaberkem256r3}} and
{{NTRUParameterSpec.ntruhps2048509}} (lowest), versus {{bike192}} / {{hqc192}}
/ {{frodokem976aes}} (middle). {{PQCDefaultLMSMaterial}} uses
{{LMOtsParameters.sha256_n32_w1}} while {{PQCDefaultHSSMaterial.java:56}} uses
{{w2}} for the same underlying scheme, and the LMS default
{{lms_sha256_n32_h5}} is a height-5 tree, i.e. *32 signatures* before permanent
exhaustion.
h3. 3. Residual {{BCPQC}} provider strings
Twelve enum constants still name {{"BCPQC"}} —
{{PQCSignatureAlgorithms.java:26-27}}:
{code:java}
XMSS("XMSS", "BCPQC"),
XMSSMT("XMSSMT", "BCPQC"),
{code}
plus FALCON/SNOVA/MAYO and six KEM constants — mixed with {{"BC"}} for the
rest, with no principle distinguishing them. (They resolve identically in
Bouncy Castle 1.86, so this is cosmetic, but it is arbitrary.) There are also
six hardcoded {{"BC"}} literals in the hybrid materials, and
{{HybridKEM.java:293}} / {{PQCProducer.java:452,481}} resolve classical
primitives with no provider at all, so one hybrid exchange can mix
SunEC-produced keys with BC objects. No shared provider constant exists in
{{PQCConstants}}.
h3. 4. {{DILITHIUM}} and {{SPHINCSPLUS}} get a factually wrong warning
{{PQCProducer.java:96-103}} puts both in {{NON_FIPS_PQ_SIGNATURES}}, which
drives {{PQCProducer.java:1280}}:
{code:java}
LOG.warn("PQC signature algorithm {} is not part of NIST FIPS
204/205. Consider using "
+ "ML-DSA (FIPS 204) or SLH-DSA (FIPS 205) for
production hybrid deployments.",
{code}
But {{PQCSignatureAlgorithms.java:28,34}} are {{DILITHIUM("ML-DSA", "BC")}} and
{{SPHINCSPLUS("SLH-DSA", "BC")}} — they already *are* the FIPS 204/205
algorithms the warning recommends. The module knows this:
{{PQCParameterSpecResolver.java:117}} says "DILITHIUM and SPHINCSPLUS generate
ML-DSA and SLH-DSA keys". Either drop them from the set or warn that the name
is a deprecated alias.
h3. 5. {{parameterSpec}} is refused for MAYO and SNOVA on a premise that is not
true for BC 1.86
{{PQCParameterSpecResolver.java:43-45}}:
{code:java}
* algorithms (XMSS, XMSSMT, LMS, HSS) and MAYO/SNOVA have no name-addressable
BouncyCastle parameter spec and are
* therefore not supported here
{code}
Verified against {{bcprov-jdk18on-1.86.jar}}: both {{MayoParameterSpec}} and
{{SnovaParameterSpec}} declare {{public static ...
fromName(java.lang.String)}}. The claim is correct for XMSS/XMSSMT/LMS/HSS
({{XMSSParameterSpec}} has no {{fromName}}) but wrong for MAYO/SNOVA, which are
not stateful. {{PQCParameterSpecResolverTest.java:115}} enshrines the wrong
premise with {{assertFalse(PQCParameterSpecResolver.isSupported("MAYO"));}}.
h3. 6. {{label}} is a required URI path option that can never be bound
{{PQCConfiguration.java:36-38}}:
{code:java}
@UriPath(description = "Logical name")
@Metadata(required = true)
private String label;
{code}
There is no {{getLabel}} or {{setLabel}} anywhere in the module, and
{{PQCComponent.createEndpoint}} ({{:85-89}}) never uses its {{remaining}}
argument. The generated {{pqc.json}} emits it as {{"kind": "path", "required":
true}} and {{PQCEndpointUriFactory}} builds it, but nothing ever reads it.
h3. 7. Observability and Javadoc gaps
{{PQCProducer.java:233}} is the only {{recordOperation}} call site in the
module; {{PQCDataFormat}}, {{KeyRotationScheduler}} and all five
{{KeyLifecycleManager}} implementations have no metrics at all, so key
generation, rotation and storage are unobserved. Twenty-three public classes
have no class-level Javadoc (all 21 material classes, {{PQCConfiguration}},
{{KeyMetadataCodec}}) while every hybrid class, enum and lifecycle manager is
documented.
h3. Suggested direction
Most of this collapses if the clone-and-tweak material classes are replaced by
one parameterised factory driven by {{PQCParameterSpecResolver}}: that removes
the SecureRandom split, the undocumented level drift and the missing Javadoc in
one change, and it makes the "add a new algorithm" path a single registration
instead of a new file plus two switch arms.
----
_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)