Andrea Cosentino created CAMEL-25803:
----------------------------------------

             Summary: camel-pqc - the hybrid wire format v2 is documented but 
not implemented
                 Key: CAMEL-25803
                 URL: https://issues.apache.org/jira/browse/CAMEL-25803
             Project: Camel
          Issue Type: Bug
          Components: camel-pqc
            Reporter: Andrea Cosentino


{{src/main/docs/pqc-hybrid.adoc}} documents a self-describing "wire format v2" 
for hybrid signatures and encapsulations, in a dedicated hundred-line section. 
The code implements only v1.

h3. What the documentation claims

{{pqc-hybrid.adoc:104}}:
{noformat}
Since Camel 4.19, hybrid signatures use wire format v2 which includes magic 
bytes, version, and algorithm identifiers (see <<Wire Format v2 and Algorithm 
Identification>>). The v1 format `[4 bytes: classical sig length][classical 
sig][pqc sig]` is still accepted on input for backward compatibility.
{noformat}
The same sentence appears for encapsulations at {{:217}}, with the format laid 
out under {{== Wire Format v2 and Algorithm Identification}} ({{:289}}) and 
{{=== Wire Format v2 Layout}} ({{:364}}).

h3. What the code does

{{HybridSignature.java:73-74}}:
{code:java}
        // Combine: [4 bytes length][classical sig][pqc sig]
        return combineSignatures(classicalSignature, pqcSignature);
{code}
That is the v1 format. {{HybridKEM}} is the same. There are no magic bytes, no 
version byte and no algorithm identifiers anywhere in either class.

h3. {{PQCAlgorithmId}} is public, documented and referenced by nothing

{{PQCAlgorithmId.java:36}} declares a public enum of about fifty constants 
whose Javadoc says it is "used in the wire format header to make hybrid 
cryptographic outputs self-describing". A repository-wide grep finds *zero* 
references outside its own file — no production caller, no test, only the adoc. 
It is untested public API surface.

It is also internally inconsistent with the algorithm enums, so 
{{fromJcaName()}} returns {{UNKNOWN}} for the real JCE names. 
{{PQCAlgorithmId.java:77}}:
{code:java}
    FRODO(0x0406, "FRODO"),
{code}
versus {{PQCKeyEncapsulationAlgorithms.java:29}}:
{code:java}
    FRODO("FrodoKEM", "BC"),
{code}
Same for {{KYBER}} ({{"KYBER"}} vs {{"ML-KEM"}}), {{DILITHIUM}} and 
{{SPHINCSPLUS}}. The enum is also missing HSS and seven 
{{PQCSymmetricAlgorithms}} constants, and carries a {{PICNIC}} constant that 
exists in no other enum (Picnic was removed from Bouncy Castle).

h3. The documented API does not compile

{{pqc-hybrid.adoc:357}}:
{code:java}
PQCAlgorithmId classical = components.classicalAlgorithmId(); // e.g. 
SHA256_WITH_ECDSA
{code}
{{HybridSignature.java:216}}:
{code:java}
    public record HybridSignatureComponents(byte[] classicalSignature, byte[] 
pqcSignature) {
{code}
There is no {{classicalAlgorithmId()}} accessor. A user following the published 
snippet gets a compile error.

h3. Decision needed

Either implement v2 — magic bytes, version, algorithm ids, with v1 still 
accepted on input as the docs already promise — and give 
{{HybridSignatureComponents}} the documented accessors; or delete 
{{PQCAlgorithmId}} and the entire v2 section of the documentation and describe 
the v1 format that actually ships. Shipping a documented format that does not 
exist is worse than either.

If v2 is implemented, it would also be the natural place to fix the related gap 
in {{PQCDataFormat}} ({{:182-185}}), whose output carries neither the symmetric 
algorithm nor the key length even though both can be overridden per message via 
{{CamelPQCSymmetricAlgorithm}} — a mismatch currently surfaces as a misleading 
"may have been tampered with" at {{:240-243}}.

h3. Note on the combiner itself

For the record, the hybrid construction is sound in kind and should not change 
as part of this: both secrets are fed through HKDF ({{HybridKEM.java:260-266}}, 
not XOR and not single-secret), {{HybridSignature.verify}} correctly uses 
{{&&}} ({{:118}}), and the v1 wire format is length-prefixed and bounds-checked 
on parse. Two smaller hardening items worth folding in: the HKDF {{salt}} is 
{{null}} and {{info}} is only the cipher name, where the established combiners 
also bind the PQ ciphertext and the public keys; and {{info.getBytes()}} uses 
the platform default charset inside a key derivation.

h3. Test gap

The only negative hybrid-signature test tampers with the data, which 
invalidates *both* signatures — it would pass identically if the verification 
operator were {{||}}. A test that forges exactly one half (valid classical 
signature spliced onto a garbage PQC signature) is the highest-value missing 
test in this area.

----
_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)

Reply via email to