This is an automated email from the ASF dual-hosted git repository.
oscerd pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new ad0273a2867d CAMEL-23842: camel-pqc - use authenticated encryption
(AEAD) in PQCDataFormat instead of ECB (#24348)
ad0273a2867d is described below
commit ad0273a2867d898abd29043ad8f99851c1a6b1d7
Author: Andrea Cosentino <[email protected]>
AuthorDate: Wed Jul 1 14:57:20 2026 +0200
CAMEL-23842: camel-pqc - use authenticated encryption (AEAD) in
PQCDataFormat instead of ECB (#24348)
* CAMEL-23842: camel-pqc - use authenticated encryption (AEAD) in
PQCDataFormat instead of ECB
The PQC data format encrypted payloads with a bare cipher name (e.g. "AES"),
which resolves to AES/ECB/PKCS5Padding: ECB leaks plaintext block structure
and
there is no IV or integrity protection, so the ciphertext is malleable and
the
DEM layer is not IND-CCA2 secure.
The symmetric layer now uses authenticated encryption: 128-bit block
ciphers are
encrypted with GCM and the ChaCha20 stream cipher with ChaCha20-Poly1305. A
random
12-byte nonce is written to the wire format and decryption verifies the
auth tag via
Cipher.doFinal (not CipherInputStream, which can silently swallow a tag
failure), so
tampered data is rejected. Only AEAD-capable symmetric algorithms are
accepted; the
non-AEAD legacy/stream ciphers (RC2, RC5, CAST5, GOST28147, DESEDE,
GRAIN128, HC128,
HC256, SALSA20) are rejected at startup. The DSL model enum, generated
metadata,
component docs and the 4.22 upgrade guide are updated accordingly.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Signed-off-by: Andrea Cosentino <[email protected]>
* CAMEL-23842: camel-pqc - remove dead bufferSize dataformat option and
regenerate metadata
Addresses review feedback on the AEAD change:
- Remove the now-dead `bufferSize` dataformat option (it only configured the
previous streaming implementation, which authenticated encryption
replaced).
Dropped from the component and core-model model (field, getter/setter,
Builder)
and from the hand-written reifier; regenerated the configurer,
catalog/model
metadata, XSD/YAML schemas and DSL writers/parsers. Documented in the
docs and
the 4.22 upgrade guide.
- Regenerate camel-spring.xsd / camel-xml-io.xsd, which the earlier
module-only
build had left stale (this was the CI failure).
- Upgrade guide: note that payloads are now processed in memory (the AEAD
tag is
verified before releasing plaintext) rather than streamed.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Signed-off-by: Andrea Cosentino <[email protected]>
---------
Signed-off-by: Andrea Cosentino <[email protected]>
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
---
.../org/apache/camel/catalog/dataformats/pqc.json | 7 +-
.../org/apache/camel/catalog/models/pqc.json | 7 +-
.../apache/camel/catalog/schemas/camel-spring.xsd | 13 +-
.../apache/camel/catalog/schemas/camel-xml-io.xsd | 13 +-
.../pqc/dataformat/PQCDataFormatConfigurer.java | 7 -
.../apache/camel/component/pqc/dataformat/pqc.json | 7 +-
.../camel-pqc/src/main/docs/pqc-dataformat.adoc | 68 +++----
.../component/pqc/dataformat/PQCDataFormat.java | 222 +++++++++++++--------
.../pqc/dataformat/PQCDataFormatAeadTest.java | 143 +++++++++++++
.../org/apache/camel/model/dataformat/pqc.json | 7 +-
.../camel/model/dataformat/PQCDataFormat.java | 41 +---
.../reifier/dataformat/PQCDataFormatReifier.java | 1 -
.../apache/camel/java/out/JavaDslModelWriter.java | 1 -
.../java/org/apache/camel/xml/in/ModelParser.java | 1 -
.../java/org/apache/camel/xml/out/ModelWriter.java | 1 -
.../org/apache/camel/yaml/out/YamlModelWriter.java | 1 -
.../ROOT/pages/camel-4x-upgrade-guide-4_22.adoc | 31 +++
.../modules/ROOT/pages/camel-4x-upgrade-guide.adoc | 1 +
.../dsl/yaml/deserializers/ModelDeserializers.java | 8 +-
.../resources/schema/camelYamlDsl-canonical.json | 10 +-
.../generated/resources/schema/camelYamlDsl.json | 10 +-
21 files changed, 365 insertions(+), 235 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/pqc.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/pqc.json
index fd2e8936db02..57e09380ad0d 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/pqc.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dataformats/pqc.json
@@ -18,11 +18,10 @@
"properties": {
"id": { "index": 0, "kind": "attribute", "displayName": "Id", "group":
"common", "required": false, "type": "string", "javaType": "java.lang.String",
"deprecated": false, "autowired": false, "secret": false, "description": "The
id of this node" },
"keyEncapsulationAlgorithm": { "index": 1, "kind": "attribute",
"displayName": "Key Encapsulation Algorithm", "group": "common", "required":
false, "type": "enum", "javaType": "java.lang.String", "enum": [ "MLKEM",
"BIKE", "HQC", "CMCE", "SABER", "FRODO", "NTRU", "NTRULPRime", "SNTRUPrime",
"KYBER" ], "deprecated": false, "autowired": false, "secret": false,
"defaultValue": "MLKEM", "description": "The Post-Quantum KEM algorithm to use
for key encapsulation." },
- "symmetricKeyAlgorithm": { "index": 2, "kind": "attribute", "displayName":
"Symmetric Key Algorithm", "group": "common", "required": false, "type":
"enum", "javaType": "java.lang.String", "enum": [ "AES", "ARIA", "RC2", "RC5",
"CAMELLIA", "CAST5", "CAST6", "CHACHA7539", "DSTU7624", "GOST28147",
"GOST3412_2015", "GRAIN128", "HC128", "HC256", "SALSA20", "SEED", "SM4",
"DESEDE" ], "deprecated": false, "autowired": false, "secret": false,
"defaultValue": "AES", "description": "The symmet [...]
+ "symmetricKeyAlgorithm": { "index": 2, "kind": "attribute", "displayName":
"Symmetric Key Algorithm", "group": "common", "required": false, "type":
"enum", "javaType": "java.lang.String", "enum": [ "AES", "ARIA", "CAMELLIA",
"CAST6", "DSTU7624", "GOST3412_2015", "SEED", "SM4", "CHACHA7539" ],
"deprecated": false, "autowired": false, "secret": false, "defaultValue":
"AES", "description": "The symmetric encryption algorithm to use with the
shared secret. Only algorithms that support au [...]
"symmetricKeyLength": { "index": 3, "kind": "attribute", "displayName":
"Symmetric Key Length", "group": "common", "required": false, "type":
"integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": 128, "description": "The length (in
bits) of the symmetric key." },
"keyPair": { "index": 4, "kind": "attribute", "displayName": "Key Pair",
"group": "common", "required": false, "type": "object", "javaType":
"java.security.KeyPair", "deprecated": false, "autowired": false, "secret":
false, "description": "Refers to the KeyPair to lookup from the registry to use
for KEM operations." },
- "bufferSize": { "index": 5, "kind": "attribute", "displayName": "Buffer
Size", "group": "advanced", "label": "advanced", "required": false, "type":
"integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": 4096, "description": "The size of the
buffer used for streaming encryption\/decryption." },
- "provider": { "index": 6, "kind": "attribute", "displayName": "Provider",
"group": "advanced", "label": "advanced", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The JCE security provider to use." },
- "keyGenerator": { "index": 7, "kind": "attribute", "displayName": "Key
Generator", "group": "advanced", "label": "advanced", "required": false,
"type": "object", "javaType": "javax.crypto.KeyGenerator", "deprecated": false,
"autowired": false, "secret": false, "description": "Refers to a custom
KeyGenerator to lookup from the registry for KEM operations." }
+ "provider": { "index": 5, "kind": "attribute", "displayName": "Provider",
"group": "advanced", "label": "advanced", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The JCE security provider to use." },
+ "keyGenerator": { "index": 6, "kind": "attribute", "displayName": "Key
Generator", "group": "advanced", "label": "advanced", "required": false,
"type": "object", "javaType": "javax.crypto.KeyGenerator", "deprecated": false,
"autowired": false, "secret": false, "description": "Refers to a custom
KeyGenerator to lookup from the registry for KEM operations." }
}
}
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/pqc.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/pqc.json
index 15e14666950a..bc3e555dd029 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/pqc.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/models/pqc.json
@@ -15,11 +15,10 @@
"properties": {
"id": { "index": 0, "kind": "attribute", "displayName": "Id", "group":
"common", "required": false, "type": "string", "javaType": "java.lang.String",
"deprecated": false, "autowired": false, "secret": false, "description": "The
id of this node" },
"keyEncapsulationAlgorithm": { "index": 1, "kind": "attribute",
"displayName": "Key Encapsulation Algorithm", "group": "common", "required":
false, "type": "enum", "javaType": "java.lang.String", "enum": [ "MLKEM",
"BIKE", "HQC", "CMCE", "SABER", "FRODO", "NTRU", "NTRULPRime", "SNTRUPrime",
"KYBER" ], "deprecated": false, "autowired": false, "secret": false,
"defaultValue": "MLKEM", "description": "The Post-Quantum KEM algorithm to use
for key encapsulation." },
- "symmetricKeyAlgorithm": { "index": 2, "kind": "attribute", "displayName":
"Symmetric Key Algorithm", "group": "common", "required": false, "type":
"enum", "javaType": "java.lang.String", "enum": [ "AES", "ARIA", "RC2", "RC5",
"CAMELLIA", "CAST5", "CAST6", "CHACHA7539", "DSTU7624", "GOST28147",
"GOST3412_2015", "GRAIN128", "HC128", "HC256", "SALSA20", "SEED", "SM4",
"DESEDE" ], "deprecated": false, "autowired": false, "secret": false,
"defaultValue": "AES", "description": "The symmet [...]
+ "symmetricKeyAlgorithm": { "index": 2, "kind": "attribute", "displayName":
"Symmetric Key Algorithm", "group": "common", "required": false, "type":
"enum", "javaType": "java.lang.String", "enum": [ "AES", "ARIA", "CAMELLIA",
"CAST6", "DSTU7624", "GOST3412_2015", "SEED", "SM4", "CHACHA7539" ],
"deprecated": false, "autowired": false, "secret": false, "defaultValue":
"AES", "description": "The symmetric encryption algorithm to use with the
shared secret. Only algorithms that support au [...]
"symmetricKeyLength": { "index": 3, "kind": "attribute", "displayName":
"Symmetric Key Length", "group": "common", "required": false, "type":
"integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": 128, "description": "The length (in
bits) of the symmetric key." },
"keyPair": { "index": 4, "kind": "attribute", "displayName": "Key Pair",
"group": "common", "required": false, "type": "object", "javaType":
"java.security.KeyPair", "deprecated": false, "autowired": false, "secret":
false, "description": "Refers to the KeyPair to lookup from the registry to use
for KEM operations." },
- "bufferSize": { "index": 5, "kind": "attribute", "displayName": "Buffer
Size", "group": "advanced", "label": "advanced", "required": false, "type":
"integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": 4096, "description": "The size of the
buffer used for streaming encryption\/decryption." },
- "provider": { "index": 6, "kind": "attribute", "displayName": "Provider",
"group": "advanced", "label": "advanced", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The JCE security provider to use." },
- "keyGenerator": { "index": 7, "kind": "attribute", "displayName": "Key
Generator", "group": "advanced", "label": "advanced", "required": false,
"type": "object", "javaType": "javax.crypto.KeyGenerator", "deprecated": false,
"autowired": false, "secret": false, "description": "Refers to a custom
KeyGenerator to lookup from the registry for KEM operations." }
+ "provider": { "index": 5, "kind": "attribute", "displayName": "Provider",
"group": "advanced", "label": "advanced", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The JCE security provider to use." },
+ "keyGenerator": { "index": 6, "kind": "attribute", "displayName": "Key
Generator", "group": "advanced", "label": "advanced", "required": false,
"type": "object", "javaType": "javax.crypto.KeyGenerator", "deprecated": false,
"autowired": false, "secret": false, "description": "Refers to a custom
KeyGenerator to lookup from the registry for KEM operations." }
}
}
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
index a3a4ab3e136c..d3685f4cf029 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-spring.xsd
@@ -9452,7 +9452,9 @@ The Post-Quantum KEM algorithm to use for key
encapsulation. Default value: MLKE
<xs:annotation>
<xs:documentation xml:lang="en">
<![CDATA[
-The symmetric encryption algorithm to use with the shared secret. Default
value: AES
+The symmetric encryption algorithm to use with the shared secret. Only
algorithms that support authenticated encryption
+(AEAD) are allowed: AES, ARIA, CAMELLIA, CAST6, DSTU7624, GOST3412_2015, SEED
and SM4 are encrypted with GCM, and
+CHACHA7539 with ChaCha20-Poly1305. Default value: AES
]]>
</xs:documentation>
</xs:annotation>
@@ -9471,15 +9473,6 @@ The length (in bits) of the symmetric key. Default
value: 128
<xs:documentation xml:lang="en">
<![CDATA[
Refers to the KeyPair to lookup from the registry to use for KEM operations.
-]]>
- </xs:documentation>
- </xs:annotation>
- </xs:attribute>
- <xs:attribute name="bufferSize" type="xs:string">
- <xs:annotation>
- <xs:documentation xml:lang="en">
-<![CDATA[
-The size of the buffer used for streaming encryption/decryption. Default
value: 4096
]]>
</xs:documentation>
</xs:annotation>
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-xml-io.xsd
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-xml-io.xsd
index ea145f0853cb..540634bc0072 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-xml-io.xsd
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camel-xml-io.xsd
@@ -8574,7 +8574,9 @@ The Post-Quantum KEM algorithm to use for key
encapsulation. Default value: MLKE
<xs:annotation>
<xs:documentation xml:lang="en">
<![CDATA[
-The symmetric encryption algorithm to use with the shared secret. Default
value: AES
+The symmetric encryption algorithm to use with the shared secret. Only
algorithms that support authenticated encryption
+(AEAD) are allowed: AES, ARIA, CAMELLIA, CAST6, DSTU7624, GOST3412_2015, SEED
and SM4 are encrypted with GCM, and
+CHACHA7539 with ChaCha20-Poly1305. Default value: AES
]]>
</xs:documentation>
</xs:annotation>
@@ -8593,15 +8595,6 @@ The length (in bits) of the symmetric key. Default
value: 128
<xs:documentation xml:lang="en">
<![CDATA[
Refers to the KeyPair to lookup from the registry to use for KEM operations.
-]]>
- </xs:documentation>
- </xs:annotation>
- </xs:attribute>
- <xs:attribute name="bufferSize" type="xs:string">
- <xs:annotation>
- <xs:documentation xml:lang="en">
-<![CDATA[
-The size of the buffer used for streaming encryption/decryption. Default
value: 4096
]]>
</xs:documentation>
</xs:annotation>
diff --git
a/components/camel-pqc/src/generated/java/org/apache/camel/component/pqc/dataformat/PQCDataFormatConfigurer.java
b/components/camel-pqc/src/generated/java/org/apache/camel/component/pqc/dataformat/PQCDataFormatConfigurer.java
index 6ecf5fbf2efc..03c3f1ebe4cf 100644
---
a/components/camel-pqc/src/generated/java/org/apache/camel/component/pqc/dataformat/PQCDataFormatConfigurer.java
+++
b/components/camel-pqc/src/generated/java/org/apache/camel/component/pqc/dataformat/PQCDataFormatConfigurer.java
@@ -22,7 +22,6 @@ public class PQCDataFormatConfigurer extends
org.apache.camel.support.component.
private static final Map<String, Object> ALL_OPTIONS;
static {
Map<String, Object> map = new CaseInsensitiveMap();
- map.put("BufferSize", int.class);
map.put("KeyEncapsulationAlgorithm", java.lang.String.class);
map.put("KeyGenerator", javax.crypto.KeyGenerator.class);
map.put("KeyPair", java.security.KeyPair.class);
@@ -36,8 +35,6 @@ public class PQCDataFormatConfigurer extends
org.apache.camel.support.component.
public boolean configure(CamelContext camelContext, Object obj, String
name, Object value, boolean ignoreCase) {
PQCDataFormat target = (PQCDataFormat) obj;
switch (ignoreCase ? name.toLowerCase() : name) {
- case "buffersize":
- case "bufferSize": target.setBufferSize(property(camelContext,
int.class, value)); return true;
case "keyencapsulationalgorithm":
case "keyEncapsulationAlgorithm":
target.setKeyEncapsulationAlgorithm(property(camelContext,
java.lang.String.class, value)); return true;
case "keygenerator":
@@ -61,8 +58,6 @@ public class PQCDataFormatConfigurer extends
org.apache.camel.support.component.
@Override
public Class<?> getOptionType(String name, boolean ignoreCase) {
switch (ignoreCase ? name.toLowerCase() : name) {
- case "buffersize":
- case "bufferSize": return int.class;
case "keyencapsulationalgorithm":
case "keyEncapsulationAlgorithm": return java.lang.String.class;
case "keygenerator":
@@ -82,8 +77,6 @@ public class PQCDataFormatConfigurer extends
org.apache.camel.support.component.
public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
PQCDataFormat target = (PQCDataFormat) obj;
switch (ignoreCase ? name.toLowerCase() : name) {
- case "buffersize":
- case "bufferSize": return target.getBufferSize();
case "keyencapsulationalgorithm":
case "keyEncapsulationAlgorithm": return
target.getKeyEncapsulationAlgorithm();
case "keygenerator":
diff --git
a/components/camel-pqc/src/generated/resources/META-INF/org/apache/camel/component/pqc/dataformat/pqc.json
b/components/camel-pqc/src/generated/resources/META-INF/org/apache/camel/component/pqc/dataformat/pqc.json
index fd2e8936db02..57e09380ad0d 100644
---
a/components/camel-pqc/src/generated/resources/META-INF/org/apache/camel/component/pqc/dataformat/pqc.json
+++
b/components/camel-pqc/src/generated/resources/META-INF/org/apache/camel/component/pqc/dataformat/pqc.json
@@ -18,11 +18,10 @@
"properties": {
"id": { "index": 0, "kind": "attribute", "displayName": "Id", "group":
"common", "required": false, "type": "string", "javaType": "java.lang.String",
"deprecated": false, "autowired": false, "secret": false, "description": "The
id of this node" },
"keyEncapsulationAlgorithm": { "index": 1, "kind": "attribute",
"displayName": "Key Encapsulation Algorithm", "group": "common", "required":
false, "type": "enum", "javaType": "java.lang.String", "enum": [ "MLKEM",
"BIKE", "HQC", "CMCE", "SABER", "FRODO", "NTRU", "NTRULPRime", "SNTRUPrime",
"KYBER" ], "deprecated": false, "autowired": false, "secret": false,
"defaultValue": "MLKEM", "description": "The Post-Quantum KEM algorithm to use
for key encapsulation." },
- "symmetricKeyAlgorithm": { "index": 2, "kind": "attribute", "displayName":
"Symmetric Key Algorithm", "group": "common", "required": false, "type":
"enum", "javaType": "java.lang.String", "enum": [ "AES", "ARIA", "RC2", "RC5",
"CAMELLIA", "CAST5", "CAST6", "CHACHA7539", "DSTU7624", "GOST28147",
"GOST3412_2015", "GRAIN128", "HC128", "HC256", "SALSA20", "SEED", "SM4",
"DESEDE" ], "deprecated": false, "autowired": false, "secret": false,
"defaultValue": "AES", "description": "The symmet [...]
+ "symmetricKeyAlgorithm": { "index": 2, "kind": "attribute", "displayName":
"Symmetric Key Algorithm", "group": "common", "required": false, "type":
"enum", "javaType": "java.lang.String", "enum": [ "AES", "ARIA", "CAMELLIA",
"CAST6", "DSTU7624", "GOST3412_2015", "SEED", "SM4", "CHACHA7539" ],
"deprecated": false, "autowired": false, "secret": false, "defaultValue":
"AES", "description": "The symmetric encryption algorithm to use with the
shared secret. Only algorithms that support au [...]
"symmetricKeyLength": { "index": 3, "kind": "attribute", "displayName":
"Symmetric Key Length", "group": "common", "required": false, "type":
"integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": 128, "description": "The length (in
bits) of the symmetric key." },
"keyPair": { "index": 4, "kind": "attribute", "displayName": "Key Pair",
"group": "common", "required": false, "type": "object", "javaType":
"java.security.KeyPair", "deprecated": false, "autowired": false, "secret":
false, "description": "Refers to the KeyPair to lookup from the registry to use
for KEM operations." },
- "bufferSize": { "index": 5, "kind": "attribute", "displayName": "Buffer
Size", "group": "advanced", "label": "advanced", "required": false, "type":
"integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": 4096, "description": "The size of the
buffer used for streaming encryption\/decryption." },
- "provider": { "index": 6, "kind": "attribute", "displayName": "Provider",
"group": "advanced", "label": "advanced", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The JCE security provider to use." },
- "keyGenerator": { "index": 7, "kind": "attribute", "displayName": "Key
Generator", "group": "advanced", "label": "advanced", "required": false,
"type": "object", "javaType": "javax.crypto.KeyGenerator", "deprecated": false,
"autowired": false, "secret": false, "description": "Refers to a custom
KeyGenerator to lookup from the registry for KEM operations." }
+ "provider": { "index": 5, "kind": "attribute", "displayName": "Provider",
"group": "advanced", "label": "advanced", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The JCE security provider to use." },
+ "keyGenerator": { "index": 6, "kind": "attribute", "displayName": "Key
Generator", "group": "advanced", "label": "advanced", "required": false,
"type": "object", "javaType": "javax.crypto.KeyGenerator", "deprecated": false,
"autowired": false, "secret": false, "description": "Refers to a custom
KeyGenerator to lookup from the registry for KEM operations." }
}
}
diff --git a/components/camel-pqc/src/main/docs/pqc-dataformat.adoc
b/components/camel-pqc/src/main/docs/pqc-dataformat.adoc
index b0220e07c798..7d7c4093a700 100644
--- a/components/camel-pqc/src/main/docs/pqc-dataformat.adoc
+++ b/components/camel-pqc/src/main/docs/pqc-dataformat.adoc
@@ -32,25 +32,26 @@ This approach provides **defense-in-depth**: even if the
symmetric algorithm is
== Benefits
* **Simplified API** - Single marshal/unmarshal operation instead of multiple
KEM steps
-* **Streaming Support** - Efficient encryption/decryption of large payloads
+* **Authenticated Encryption** - AEAD (GCM or ChaCha20-Poly1305) protects both
confidentiality and integrity
* **Header-based Configuration** - Dynamic algorithm and key selection per
message
* **Standards-based** - Uses NIST-standardized ML-KEM and other PQC algorithms
* **Drop-in Replacement** - Compatible with existing Camel DataFormat patterns
== Wire Format
-Since Camel 4.19, the PQC DataFormat uses wire format v2 which includes magic
bytes, version, and algorithm identifiers (see
xref:others:pqc-hybrid.adoc#_wire_format_v2_and_algorithm_identification[Wire
Format v2 and Algorithm Identification]). The v1 format `[4 bytes:
encapsulation length][N bytes: encapsulation][M bytes: encrypted data]` is
still accepted on input for backward compatibility.
-
-The v2 encrypted message format is:
+The encrypted message produced by the PQC DataFormat is:
[source,text]
----
-[2 bytes: magic "PQ"] [1 byte: version] [1 byte: format type]
-[2 bytes: KEM algorithm ID] [2 bytes: symmetric algorithm ID]
-[4 bytes: encapsulation length] [N bytes: encapsulation] [M bytes: encrypted
data]
+[4 bytes: encapsulation length] [N bytes: encapsulation] [12 bytes: AEAD
nonce] [M bytes: ciphertext + auth tag]
----
-This format allows the receiver to identify the algorithms used, extract the
encapsulation, decapsulate the shared secret using their private key, and
decrypt the data.
+The KEM encapsulation allows the receiver to decapsulate the shared secret
using their private key. The payload is then encrypted with an authenticated
cipher (AEAD): 128-bit block ciphers use GCM and the ChaCha20 stream cipher
uses ChaCha20-Poly1305, so both confidentiality and integrity are protected.
Decryption fails if the ciphertext or its authentication tag has been modified.
+
+[NOTE]
+====
+The authenticated wire format was introduced in Camel 4.22. Data encrypted by
earlier versions used unauthenticated ECB and did not include a nonce, and
cannot be decrypted by Camel 4.22 or later. See the Camel 4.x upgrade guide for
details.
+====
== Basic Usage
@@ -119,7 +120,7 @@ from("file:encrypted")
|symmetricKeyAlgorithm
|String
|AES
-|Symmetric encryption algorithm (AES, ARIA, RC2, RC5, CAMELLIA, CAST5, CAST6,
CHACHA7539, DSTU7624, GOST28147, GRAIN128, SEED, SM4, DESEDE)
+|Symmetric encryption algorithm. Only AEAD-capable algorithms are supported:
AES, ARIA, CAMELLIA, CAST6, DSTU7624, GOST3412-2015, SEED, SM4 (GCM) and
CHACHA7539 (ChaCha20-Poly1305)
|symmetricKeyLength
|int
@@ -131,11 +132,6 @@ from("file:encrypted")
|null
|KeyPair to use for encryption/decryption. Can also be provided via header.
-|bufferSize
-|int
-|4096
-|Buffer size for streaming encryption/decryption
-
|provider
|String
|null
@@ -221,18 +217,19 @@ from("direct:encrypt")
=== Symmetric Encryption Algorithms
+Only algorithms that support authenticated encryption (AEAD) are supported.
All except CHACHA7539 are encrypted with GCM; CHACHA7539 is encrypted with
ChaCha20-Poly1305 and requires a 256-bit key.
+
* AES - Advanced Encryption Standard (recommended)
* ARIA - Korean standard
-* RC2, RC5 - Rivest ciphers
* CAMELLIA - Japanese standard
-* CAST5, CAST6 - CAST ciphers
-* CHACHA7539 - ChaCha stream cipher
-* DSTU7624 - Ukrainian standard
-* GOST28147 - Russian standard
-* GRAIN128 - Stream cipher
+* CAST6 - CAST-256
+* DSTU7624 - Ukrainian standard (Kalyna)
+* GOST3412-2015 - Russian standard (Kuznyechik)
* SEED - Korean standard
* SM4 - Chinese standard
-* DESEDE - Triple DES
+* CHACHA7539 - ChaCha20 (ChaCha20-Poly1305)
+
+The legacy ciphers RC2, RC5, CAST5, GOST28147 and DESEDE, and the
unauthenticated stream ciphers GRAIN128, HC128, HC256 and SALSA20, cannot
provide AEAD and are no longer supported.
== Advanced Examples
@@ -401,28 +398,17 @@ The DataFormat approach is significantly simpler and
handles all KEM operations
**3. Symmetric Algorithm**
* **Recommended**: AES-256 or AES-128
-* **Avoid**: RC2, GOST28147 (legacy algorithms)
+* **Not supported**: legacy ciphers without AEAD (RC2, RC5, CAST5, GOST28147,
DESEDE) and unauthenticated stream ciphers (GRAIN128, HC128, HC256, SALSA20)
are rejected at startup
**4. Wire Format Security**
* The encapsulation is not secret and can be transmitted in the clear
* The encrypted data is protected by the symmetric key
-* Use authenticated encryption modes (GCM) when available
+* The symmetric layer always uses authenticated encryption (GCM or
ChaCha20-Poly1305), protecting integrity as well as confidentiality
== Performance Considerations
-**1. Buffer Size**
-
-Adjust bufferSize based on message size:
-
-._Java-only: configuring PQCDataFormat buffer size_
-[source,java]
-----
-PQCDataFormat pqcFormat = new PQCDataFormat();
-pqcFormat.setBufferSize(8192); // Larger buffer for big files
-----
-
-**2. Caching KeyGenerator**
+**1. Caching KeyGenerator**
Configure a KeyGenerator once to avoid repeated initialization:
@@ -438,11 +424,11 @@ PQCDataFormat pqcFormat = new PQCDataFormat();
pqcFormat.setKeyGenerator(kemKeyGenerator);
----
-**3. Streaming**
+**2. Large Payloads**
-The DataFormat supports streaming for large payloads, avoiding memory issues:
+To guarantee integrity, the authenticated (AEAD) cipher verifies the
authentication tag before releasing any plaintext, so the payload is processed
as a whole rather than incrementally. Enable stream caching to let Camel spill
large bodies to disk on the route:
-._Java-only: streaming large files with PQCDataFormat_
+._Java-only: stream caching large files with PQCDataFormat_
[source,java]
----
from("file:large-files?noop=true")
@@ -504,13 +490,11 @@ Security.addProvider(new BouncyCastlePQCProvider());
**Issue**: OutOfMemoryError with large files
-* **Solution**: Increase buffer size or enable stream caching:
+* **Solution**: Authenticated (AEAD) encryption processes the payload as a
whole; enable stream caching so Camel spills large bodies to disk:
+
-._Java-only: increasing buffer size or enabling stream caching_
+._Java-only: enabling stream caching for large files_
[source,java]
----
-pqcFormat.setBufferSize(16384);
-// or
from("file:large-files?noop=true").streamCache(true).marshal(pqcFormat)...
----
diff --git
a/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/dataformat/PQCDataFormat.java
b/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/dataformat/PQCDataFormat.java
index 32d4e68335e6..b12101dd9a61 100644
---
a/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/dataformat/PQCDataFormat.java
+++
b/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/dataformat/PQCDataFormat.java
@@ -23,11 +23,16 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.security.KeyPair;
import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Set;
+import javax.crypto.AEADBadTagException;
import javax.crypto.Cipher;
-import javax.crypto.CipherInputStream;
-import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
import org.apache.camel.Exchange;
import org.apache.camel.component.pqc.PQCKeyEncapsulationAlgorithms;
@@ -36,12 +41,11 @@ import org.apache.camel.spi.DataFormat;
import org.apache.camel.spi.DataFormatName;
import org.apache.camel.spi.annotations.Dataformat;
import org.apache.camel.support.ExchangeHelper;
-import org.apache.camel.support.builder.OutputStreamBuilder;
import org.apache.camel.support.service.ServiceSupport;
-import org.apache.camel.util.IOHelper;
import org.bouncycastle.jcajce.SecretKeyWithEncapsulation;
import org.bouncycastle.jcajce.spec.KEMExtractSpec;
import org.bouncycastle.jcajce.spec.KEMGenerateSpec;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -53,17 +57,24 @@ import org.slf4j.LoggerFactory;
* This DataFormat provides quantum-resistant encryption by:
* </p>
* <ul>
- * <li>Using KEM algorithms (ML-KEM, BIKE, etc.) to establish a shared
secret</li>
- * <li>Encrypting data with symmetric algorithms (AES, Camellia, etc.) using
the shared secret</li>
- * <li>Including the encapsulated key in the output stream for decryption</li>
+ * <li>Using KEM algorithms (ML-KEM, BIKE, etc.) to establish a fresh shared
secret per message</li>
+ * <li>Encrypting data with an <em>authenticated</em> symmetric cipher (AEAD)
using the shared secret</li>
+ * <li>Including the encapsulated key and the AEAD nonce in the output stream
for decryption</li>
* </ul>
*
* <p>
+ * The symmetric (data-encapsulation) layer always uses authenticated
encryption so that both confidentiality and
+ * integrity are protected: 128-bit block ciphers use GCM and the ChaCha20
stream cipher uses ChaCha20-Poly1305. Only
+ * AEAD-capable symmetric algorithms are supported (AES, ARIA, CAMELLIA,
CAST6, DSTU7624, GOST3412-2015, SEED, SM4 and
+ * CHACHA7539); configuring any other algorithm fails fast.
+ * </p>
+ *
+ * <p>
* The encrypted message format is:
* </p>
*
* <pre>
- * [4 bytes: encapsulation length] [N bytes: encapsulation] [M bytes:
encrypted data]
+ * [4 bytes: encapsulation length] [N bytes: encapsulation] [12 bytes: AEAD
nonce] [M bytes: ciphertext + auth tag]
* </pre>
*
* <p>
@@ -94,11 +105,28 @@ public class PQCDataFormat extends ServiceSupport
implements DataFormat, DataFor
private static final Logger LOG =
LoggerFactory.getLogger(PQCDataFormat.class);
+ /** Authentication tag length, in bits, used for GCM. */
+ private static final int GCM_TAG_LENGTH_BITS = 128;
+ /** Nonce length, in bytes, used for both GCM and ChaCha20-Poly1305. */
+ private static final int NONCE_LENGTH_BYTES = 12;
+ /** Upper bound for the encapsulation length read from untrusted input, to
guard against malformed data. */
+ private static final int MAX_ENCAPSULATION_LENGTH = 1024 * 1024;
+
+ private static final String CHACHA_ALGORITHM = "CHACHA7539";
+ private static final String CHACHA_POLY1305_TRANSFORMATION =
"CHACHA20-POLY1305";
+ private static final String CHACHA_KEY_ALGORITHM = "ChaCha20";
+
+ /**
+ * JCE names of the 128-bit block ciphers that provide a GCM AEAD mode and
can therefore be used as the symmetric
+ * (data-encapsulation) layer. The ChaCha20 stream cipher is handled
separately via ChaCha20-Poly1305.
+ */
+ private static final Set<String> GCM_CAPABLE_ALGORITHMS
+ = Set.of("AES", "ARIA", "CAMELLIA", "CAST6", "DSTU7624",
"GOST3412-2015", "SEED", "SM4");
+
private String keyEncapsulationAlgorithm = "MLKEM";
private String symmetricKeyAlgorithm = "AES";
private int symmetricKeyLength = 128;
private KeyPair keyPair;
- private int bufferSize = 4096;
private String provider;
private KeyGenerator keyGenerator;
@@ -128,42 +156,32 @@ public class PQCDataFormat extends ServiceSupport
implements DataFormat, DataFor
String kemAlg = getKemAlgorithm(exchange);
String symAlg = getSymmetricAlgorithm(exchange);
+ checkAeadSupported(symAlg);
- InputStream plaintextStream =
ExchangeHelper.convertToMandatoryType(exchange, InputStream.class, graph);
+ byte[] plaintext = ExchangeHelper.convertToMandatoryType(exchange,
byte[].class, graph);
try {
- // Generate KEM encapsulation and shared secret
- KeyGenerator kg = getOrCreateKeyGenerator(kemAlg);
- kg.init(
- new KEMGenerateSpec(kp.getPublic(), symAlg,
symmetricKeyLength),
- new SecureRandom());
+ SecureRandom random = new SecureRandom();
+ // Generate KEM encapsulation and the fresh shared secret
+ KeyGenerator kg = getOrCreateKeyGenerator(kemAlg);
+ kg.init(new KEMGenerateSpec(kp.getPublic(), symAlg,
symmetricKeyLength), random);
SecretKeyWithEncapsulation secretKey =
(SecretKeyWithEncapsulation) kg.generateKey();
byte[] encapsulation = secretKey.getEncapsulation();
- // Write encapsulation length and data
+ // Encrypt the payload with an authenticated cipher using a fresh
random nonce
+ byte[] nonce = new byte[NONCE_LENGTH_BYTES];
+ random.nextBytes(nonce);
+ Cipher cipher = initAeadCipher(Cipher.ENCRYPT_MODE, symAlg,
secretKey.getEncoded(), nonce);
+ byte[] ciphertext = cipher.doFinal(plaintext);
+
+ // Write encapsulation length, encapsulation, nonce and the
authenticated ciphertext
DataOutputStream dataOut = new DataOutputStream(outputStream);
dataOut.writeInt(encapsulation.length);
- outputStream.write(encapsulation);
- outputStream.flush();
-
- // Encrypt data with the shared secret
- Cipher cipher = Cipher.getInstance(symAlg);
- cipher.init(Cipher.ENCRYPT_MODE, secretKey);
-
- byte[] buffer = new byte[bufferSize];
- int read;
- CipherOutputStream cipherStream = null;
- try {
- cipherStream = new CipherOutputStream(outputStream, cipher);
- while ((read = plaintextStream.read(buffer)) > 0) {
- cipherStream.write(buffer, 0, read);
- cipherStream.flush();
- }
- } finally {
- IOHelper.close(cipherStream, "cipher", LOG);
- IOHelper.close(plaintextStream, "plaintext", LOG);
- }
+ dataOut.write(encapsulation);
+ dataOut.write(nonce);
+ dataOut.write(ciphertext);
+ dataOut.flush();
} catch (Exception e) {
throw new IOException("Failed to encrypt data using PQC KEM", e);
}
@@ -185,52 +203,53 @@ public class PQCDataFormat extends ServiceSupport
implements DataFormat, DataFor
String kemAlg = getKemAlgorithm(exchange);
String symAlg = getSymmetricAlgorithm(exchange);
+ checkAeadSupported(symAlg);
- CipherInputStream cipherStream = null;
- OutputStreamBuilder osb = null;
-
- try {
- // Read encapsulation from stream
- DataInputStream dataIn = new DataInputStream(encryptedStream);
+ try (DataInputStream dataIn = new DataInputStream(encryptedStream)) {
+ // Read encapsulation
int encapsulationLength = dataIn.readInt();
- byte[] encapsulation = new byte[encapsulationLength];
- int read = encryptedStream.read(encapsulation);
- if (read != encapsulationLength) {
- throw new IOException(
- String.format("Expected to read %d bytes of
encapsulation but got %d bytes",
- encapsulationLength, read));
+ if (encapsulationLength < 0 || encapsulationLength >
MAX_ENCAPSULATION_LENGTH) {
+ throw new IOException("Invalid PQC encapsulation length: " +
encapsulationLength);
}
+ byte[] encapsulation = new byte[encapsulationLength];
+ dataIn.readFully(encapsulation);
- // Extract secret key from encapsulation
- KeyGenerator kg = getOrCreateKeyGenerator(kemAlg);
- kg.init(
- new KEMExtractSpec(kp.getPrivate(), encapsulation, symAlg,
symmetricKeyLength),
- new SecureRandom());
-
- SecretKeyWithEncapsulation secretKey =
(SecretKeyWithEncapsulation) kg.generateKey();
+ // Read the AEAD nonce
+ byte[] nonce = new byte[NONCE_LENGTH_BYTES];
+ dataIn.readFully(nonce);
- // Decrypt data with the extracted secret
- Cipher cipher = Cipher.getInstance(symAlg);
- cipher.init(Cipher.DECRYPT_MODE, secretKey);
+ // The remainder is the authenticated ciphertext (including the
tag)
+ byte[] ciphertext = dataIn.readAllBytes();
- cipherStream = new CipherInputStream(encryptedStream, cipher);
- osb = OutputStreamBuilder.withExchange(exchange);
- byte[] buffer = new byte[bufferSize];
- while ((read = cipherStream.read(buffer)) >= 0) {
- osb.write(buffer, 0, read);
- }
+ // Extract the shared secret from the encapsulation
+ KeyGenerator kg = getOrCreateKeyGenerator(kemAlg);
+ kg.init(new KEMExtractSpec(kp.getPrivate(), encapsulation, symAlg,
symmetricKeyLength), new SecureRandom());
+ SecretKeyWithEncapsulation secretKey =
(SecretKeyWithEncapsulation) kg.generateKey();
- return osb.build();
+ // Decrypt. doFinal() verifies the authentication tag and throws
on tampering; this must not be done with
+ // CipherInputStream, which can silently swallow an AEAD tag
failure and return truncated plaintext.
+ Cipher cipher = initAeadCipher(Cipher.DECRYPT_MODE, symAlg,
secretKey.getEncoded(), nonce);
+ return cipher.doFinal(ciphertext);
+ } catch (AEADBadTagException e) {
+ throw new IOException(
+ "PQC decryption failed: authentication tag mismatch. The
encrypted data may have been tampered with, "
+ + "or the wrong key or algorithm was used.",
+ e);
+ } catch (IOException e) {
+ throw e;
} catch (Exception e) {
throw new IOException("Failed to decrypt data using PQC KEM", e);
- } finally {
- IOHelper.close(cipherStream, "cipher", LOG);
- IOHelper.close(osb, "plaintext", LOG);
}
}
@Override
protected void doStart() throws Exception {
+ // Fail fast if the configured symmetric algorithm cannot provide
authenticated encryption. Per-message
+ // overrides via the CamelPQCSymmetricAlgorithm header are validated
at marshal/unmarshal time.
+ if (symmetricKeyAlgorithm != null) {
+ checkAeadSupported(toJceAlgorithm(symmetricKeyAlgorithm));
+ }
+
if (keyEncapsulationAlgorithm != null && keyGenerator == null) {
PQCKeyEncapsulationAlgorithms kemEnum =
PQCKeyEncapsulationAlgorithms.valueOf(keyEncapsulationAlgorithm);
String algorithm = kemEnum.getAlgorithm();
@@ -251,6 +270,41 @@ public class PQCDataFormat extends ServiceSupport
implements DataFormat, DataFor
// noop
}
+ /**
+ * Builds and initialises an AEAD {@link Cipher} for the given symmetric
algorithm. 128-bit block ciphers use GCM;
+ * ChaCha20 uses ChaCha20-Poly1305. Both use a 12-byte nonce.
+ */
+ private Cipher initAeadCipher(int mode, String jceAlgorithm, byte[]
keyBytes, byte[] nonce) throws Exception {
+ boolean chacha = CHACHA_ALGORITHM.equalsIgnoreCase(jceAlgorithm);
+ String transformation = chacha ? CHACHA_POLY1305_TRANSFORMATION :
jceAlgorithm + "/GCM/NoPadding";
+ SecretKey key = new SecretKeySpec(keyBytes, chacha ?
CHACHA_KEY_ALGORITHM : jceAlgorithm);
+ AlgorithmParameterSpec spec
+ = chacha ? new IvParameterSpec(nonce) : new
GCMParameterSpec(GCM_TAG_LENGTH_BITS, nonce);
+
+ String cipherProvider = provider != null ? provider :
BouncyCastleProvider.PROVIDER_NAME;
+ LOG.debug("Using AEAD transformation {} (provider {})",
transformation, cipherProvider);
+
+ Cipher cipher = Cipher.getInstance(transformation, cipherProvider);
+ cipher.init(mode, key, spec);
+ return cipher;
+ }
+
+ /**
+ * Verifies that the given (JCE) symmetric algorithm supports
authenticated encryption (AEAD). PQCDataFormat only
+ * supports AEAD ciphers so that integrity is always protected.
+ */
+ private static void checkAeadSupported(String jceAlgorithm) {
+ if (CHACHA_ALGORITHM.equalsIgnoreCase(jceAlgorithm) ||
GCM_CAPABLE_ALGORITHMS.contains(jceAlgorithm)) {
+ return;
+ }
+ throw new IllegalArgumentException(
+ "Symmetric algorithm '" + jceAlgorithm
+ + "' does not support authenticated
encryption (AEAD) and cannot be used with PQCDataFormat, "
+ + "which requires an authenticated
cipher to protect both confidentiality and integrity. "
+ + "Use one of the AEAD-capable
algorithms: AES, ARIA, CAMELLIA, CAST6, DSTU7624, GOST3412-2015, "
+ + "SEED, SM4 (GCM) or CHACHA7539
(ChaCha20-Poly1305).");
+ }
+
private KeyPair getKeyPair(Exchange exchange) {
KeyPair kp = exchange.getIn().getHeader(KEY_PAIR, KeyPair.class);
if (kp != null) {
@@ -271,16 +325,20 @@ public class PQCDataFormat extends ServiceSupport
implements DataFormat, DataFor
private String getSymmetricAlgorithm(Exchange exchange) {
String alg = exchange.getIn().getHeader(SYMMETRIC_ALGORITHM,
String.class);
if (alg != null) {
- return alg;
+ return toJceAlgorithm(alg);
}
+ return toJceAlgorithm(this.symmetricKeyAlgorithm);
+ }
- // Convert to BC algorithm name if it's an enum value
+ /**
+ * Maps a {@link PQCSymmetricAlgorithms} enum name to its JCE algorithm
name, returning the input unchanged when it
+ * is not an enum constant (e.g. it is already a JCE name).
+ */
+ private static String toJceAlgorithm(String algorithm) {
try {
- PQCSymmetricAlgorithms symEnum =
PQCSymmetricAlgorithms.valueOf(this.symmetricKeyAlgorithm);
- return symEnum.getAlgorithm();
+ return PQCSymmetricAlgorithms.valueOf(algorithm).getAlgorithm();
} catch (IllegalArgumentException e) {
- // Not an enum, return as-is
- return this.symmetricKeyAlgorithm;
+ return algorithm;
}
}
@@ -322,8 +380,9 @@ public class PQCDataFormat extends ServiceSupport
implements DataFormat, DataFor
}
/**
- * Sets the symmetric encryption algorithm to use with the shared secret.
Supported values: AES, ARIA, RC2, RC5,
- * CAMELLIA, CAST5, CAST6, CHACHA7539, etc.
+ * Sets the symmetric encryption algorithm to use with the shared secret.
Only algorithms that support authenticated
+ * encryption (AEAD) are allowed: AES, ARIA, CAMELLIA, CAST6, DSTU7624,
GOST3412-2015, SEED, SM4 (encrypted with
+ * GCM) and CHACHA7539 (encrypted with ChaCha20-Poly1305). The default is
AES.
*/
public void setSymmetricKeyAlgorithm(String symmetricKeyAlgorithm) {
this.symmetricKeyAlgorithm = symmetricKeyAlgorithm;
@@ -352,17 +411,6 @@ public class PQCDataFormat extends ServiceSupport
implements DataFormat, DataFor
this.keyPair = keyPair;
}
- public int getBufferSize() {
- return bufferSize;
- }
-
- /**
- * Sets the buffer size for streaming encryption/decryption. Default is
4096 bytes.
- */
- public void setBufferSize(int bufferSize) {
- this.bufferSize = bufferSize;
- }
-
public String getProvider() {
return provider;
}
diff --git
a/components/camel-pqc/src/test/java/org/apache/camel/component/pqc/dataformat/PQCDataFormatAeadTest.java
b/components/camel-pqc/src/test/java/org/apache/camel/component/pqc/dataformat/PQCDataFormatAeadTest.java
new file mode 100644
index 000000000000..c0da08b8dbed
--- /dev/null
+++
b/components/camel-pqc/src/test/java/org/apache/camel/component/pqc/dataformat/PQCDataFormatAeadTest.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.pqc.dataformat;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.SecureRandom;
+import java.security.Security;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.component.pqc.PQCKeyEncapsulationAlgorithms;
+import org.apache.camel.support.DefaultExchange;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.bouncycastle.jcajce.spec.MLKEMParameterSpec;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Tests the authenticated-encryption (AEAD) behaviour of {@link
PQCDataFormat}: GCM and ChaCha20-Poly1305 round-trips,
+ * tamper detection, and rejection of non-AEAD symmetric algorithms.
+ */
+public class PQCDataFormatAeadTest extends CamelTestSupport {
+
+ private static final String ORIGINAL = "Authenticated post-quantum
encryption payload";
+
+ @BeforeAll
+ public static void startup() {
+ Security.addProvider(new BouncyCastleProvider());
+ Security.addProvider(new BouncyCastlePQCProvider());
+ }
+
+ private KeyPair mlkemKeyPair() throws Exception {
+ KeyPairGenerator kpg = KeyPairGenerator.getInstance(
+ PQCKeyEncapsulationAlgorithms.MLKEM.getAlgorithm(),
+ PQCKeyEncapsulationAlgorithms.MLKEM.getBcProvider());
+ kpg.initialize(MLKEMParameterSpec.ml_kem_768, new SecureRandom());
+ return kpg.generateKeyPair();
+ }
+
+ private byte[] marshal(PQCDataFormat df, String body) throws Exception {
+ Exchange exchange = new DefaultExchange(context);
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ df.marshal(exchange, body, bos);
+ return bos.toByteArray();
+ }
+
+ private String unmarshalToString(PQCDataFormat df, byte[] data) throws
Exception {
+ Exchange exchange = new DefaultExchange(context);
+ Object out = df.unmarshal(exchange, new ByteArrayInputStream(data));
+ assertNotNull(out);
+ return new String((byte[]) out, StandardCharsets.UTF_8);
+ }
+
+ @Test
+ void testAesGcmRoundTrip() throws Exception {
+ PQCDataFormat df = new PQCDataFormat("MLKEM", "AES", mlkemKeyPair());
+ df.setSymmetricKeyLength(256);
+ df.start();
+ byte[] enc = marshal(df, ORIGINAL);
+ assertEquals(ORIGINAL, unmarshalToString(df, enc));
+ }
+
+ @Test
+ void testAriaGcmRoundTrip() throws Exception {
+ PQCDataFormat df = new PQCDataFormat("MLKEM", "ARIA", mlkemKeyPair());
+ df.setSymmetricKeyLength(128);
+ df.start();
+ byte[] enc = marshal(df, ORIGINAL);
+ assertEquals(ORIGINAL, unmarshalToString(df, enc));
+ }
+
+ @Test
+ void testChaCha20Poly1305RoundTrip() throws Exception {
+ PQCDataFormat df = new PQCDataFormat("MLKEM", "CHACHA7539",
mlkemKeyPair());
+ // ChaCha20-Poly1305 requires a 256-bit key
+ df.setSymmetricKeyLength(256);
+ df.start();
+ byte[] enc = marshal(df, ORIGINAL);
+ assertEquals(ORIGINAL, unmarshalToString(df, enc));
+ }
+
+ @Test
+ void testTamperedCiphertextIsRejected() throws Exception {
+ PQCDataFormat df = new PQCDataFormat("MLKEM", "AES", mlkemKeyPair());
+ df.setSymmetricKeyLength(256);
+ df.start();
+ byte[] enc = marshal(df, ORIGINAL);
+
+ // Flip the last byte, which is part of the GCM authentication tag
+ enc[enc.length - 1] ^= 0x01;
+
+ Exchange exchange = new DefaultExchange(context);
+ ByteArrayInputStream in = new ByteArrayInputStream(enc);
+ IOException ex = assertThrows(IOException.class, () ->
df.unmarshal(exchange, in));
+ String msg = ex.getMessage().toLowerCase();
+ assertTrue(msg.contains("authentication") || msg.contains("tamper"),
+ "Expected an authentication/tamper error but got: " +
ex.getMessage());
+ }
+
+ @Test
+ void testNonAeadAlgorithmRejectedAtStart() throws Exception {
+ PQCDataFormat df = new PQCDataFormat("MLKEM", "DESEDE",
mlkemKeyPair());
+ df.setSymmetricKeyLength(192);
+ Exception ex = assertThrows(Exception.class, df::start);
+ String msg = ex.getMessage() != null
+ ? ex.getMessage()
+ : (ex.getCause() != null ? ex.getCause().getMessage() : "");
+ assertTrue(msg != null && msg.contains("AEAD"), "Expected an AEAD
rejection but got: " + ex);
+ }
+
+ @Test
+ void testNonAeadAlgorithmRejectedAtMarshal() throws Exception {
+ PQCDataFormat df = new PQCDataFormat("MLKEM", "RC2", mlkemKeyPair());
+ Exchange exchange = new DefaultExchange(context);
+ assertThrows(IllegalArgumentException.class,
+ () -> df.marshal(exchange, ORIGINAL, new
ByteArrayOutputStream()));
+ }
+}
diff --git
a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/dataformat/pqc.json
b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/dataformat/pqc.json
index 15e14666950a..bc3e555dd029 100644
---
a/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/dataformat/pqc.json
+++
b/core/camel-core-model/src/generated/resources/META-INF/org/apache/camel/model/dataformat/pqc.json
@@ -15,11 +15,10 @@
"properties": {
"id": { "index": 0, "kind": "attribute", "displayName": "Id", "group":
"common", "required": false, "type": "string", "javaType": "java.lang.String",
"deprecated": false, "autowired": false, "secret": false, "description": "The
id of this node" },
"keyEncapsulationAlgorithm": { "index": 1, "kind": "attribute",
"displayName": "Key Encapsulation Algorithm", "group": "common", "required":
false, "type": "enum", "javaType": "java.lang.String", "enum": [ "MLKEM",
"BIKE", "HQC", "CMCE", "SABER", "FRODO", "NTRU", "NTRULPRime", "SNTRUPrime",
"KYBER" ], "deprecated": false, "autowired": false, "secret": false,
"defaultValue": "MLKEM", "description": "The Post-Quantum KEM algorithm to use
for key encapsulation." },
- "symmetricKeyAlgorithm": { "index": 2, "kind": "attribute", "displayName":
"Symmetric Key Algorithm", "group": "common", "required": false, "type":
"enum", "javaType": "java.lang.String", "enum": [ "AES", "ARIA", "RC2", "RC5",
"CAMELLIA", "CAST5", "CAST6", "CHACHA7539", "DSTU7624", "GOST28147",
"GOST3412_2015", "GRAIN128", "HC128", "HC256", "SALSA20", "SEED", "SM4",
"DESEDE" ], "deprecated": false, "autowired": false, "secret": false,
"defaultValue": "AES", "description": "The symmet [...]
+ "symmetricKeyAlgorithm": { "index": 2, "kind": "attribute", "displayName":
"Symmetric Key Algorithm", "group": "common", "required": false, "type":
"enum", "javaType": "java.lang.String", "enum": [ "AES", "ARIA", "CAMELLIA",
"CAST6", "DSTU7624", "GOST3412_2015", "SEED", "SM4", "CHACHA7539" ],
"deprecated": false, "autowired": false, "secret": false, "defaultValue":
"AES", "description": "The symmetric encryption algorithm to use with the
shared secret. Only algorithms that support au [...]
"symmetricKeyLength": { "index": 3, "kind": "attribute", "displayName":
"Symmetric Key Length", "group": "common", "required": false, "type":
"integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": 128, "description": "The length (in
bits) of the symmetric key." },
"keyPair": { "index": 4, "kind": "attribute", "displayName": "Key Pair",
"group": "common", "required": false, "type": "object", "javaType":
"java.security.KeyPair", "deprecated": false, "autowired": false, "secret":
false, "description": "Refers to the KeyPair to lookup from the registry to use
for KEM operations." },
- "bufferSize": { "index": 5, "kind": "attribute", "displayName": "Buffer
Size", "group": "advanced", "label": "advanced", "required": false, "type":
"integer", "javaType": "java.lang.Integer", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": 4096, "description": "The size of the
buffer used for streaming encryption\/decryption." },
- "provider": { "index": 6, "kind": "attribute", "displayName": "Provider",
"group": "advanced", "label": "advanced", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The JCE security provider to use." },
- "keyGenerator": { "index": 7, "kind": "attribute", "displayName": "Key
Generator", "group": "advanced", "label": "advanced", "required": false,
"type": "object", "javaType": "javax.crypto.KeyGenerator", "deprecated": false,
"autowired": false, "secret": false, "description": "Refers to a custom
KeyGenerator to lookup from the registry for KEM operations." }
+ "provider": { "index": 5, "kind": "attribute", "displayName": "Provider",
"group": "advanced", "label": "advanced", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The JCE security provider to use." },
+ "keyGenerator": { "index": 6, "kind": "attribute", "displayName": "Key
Generator", "group": "advanced", "label": "advanced", "required": false,
"type": "object", "javaType": "javax.crypto.KeyGenerator", "deprecated": false,
"autowired": false, "secret": false, "description": "Refers to a custom
KeyGenerator to lookup from the registry for KEM operations." }
}
}
diff --git
a/core/camel-core-model/src/main/java/org/apache/camel/model/dataformat/PQCDataFormat.java
b/core/camel-core-model/src/main/java/org/apache/camel/model/dataformat/PQCDataFormat.java
index 071d4183d7e7..9ddd208b0040 100644
---
a/core/camel-core-model/src/main/java/org/apache/camel/model/dataformat/PQCDataFormat.java
+++
b/core/camel-core-model/src/main/java/org/apache/camel/model/dataformat/PQCDataFormat.java
@@ -41,8 +41,10 @@ public class PQCDataFormat extends DataFormatDefinition {
private String keyEncapsulationAlgorithm;
@XmlAttribute
@Metadata(defaultValue = "AES",
- enums =
"AES,ARIA,RC2,RC5,CAMELLIA,CAST5,CAST6,CHACHA7539,DSTU7624,GOST28147,GOST3412_2015,GRAIN128,HC128,HC256,SALSA20,SEED,SM4,DESEDE",
- description = "The symmetric encryption algorithm to use with
the shared secret.")
+ enums =
"AES,ARIA,CAMELLIA,CAST6,DSTU7624,GOST3412_2015,SEED,SM4,CHACHA7539",
+ description = "The symmetric encryption algorithm to use with
the shared secret. Only algorithms that support"
+ + " authenticated encryption (AEAD) are allowed:
AES, ARIA, CAMELLIA, CAST6, DSTU7624, GOST3412_2015,"
+ + " SEED and SM4 are encrypted with GCM, and
CHACHA7539 with ChaCha20-Poly1305.")
private String symmetricKeyAlgorithm;
@XmlAttribute
@Metadata(javaType = "java.lang.Integer", defaultValue = "128",
@@ -53,10 +55,6 @@ public class PQCDataFormat extends DataFormatDefinition {
description = "Refers to the KeyPair to lookup from the registry
to use for KEM operations.")
private String keyPair;
@XmlAttribute
- @Metadata(label = "advanced", javaType = "java.lang.Integer", defaultValue
= "4096",
- description = "The size of the buffer used for streaming
encryption/decryption.")
- private String bufferSize;
- @XmlAttribute
@Metadata(label = "advanced",
description = "The JCE security provider to use.")
private String provider;
@@ -75,7 +73,6 @@ public class PQCDataFormat extends DataFormatDefinition {
this.symmetricKeyAlgorithm = source.symmetricKeyAlgorithm;
this.symmetricKeyLength = source.symmetricKeyLength;
this.keyPair = source.keyPair;
- this.bufferSize = source.bufferSize;
this.provider = source.provider;
this.keyGenerator = source.keyGenerator;
}
@@ -86,7 +83,6 @@ public class PQCDataFormat extends DataFormatDefinition {
this.symmetricKeyAlgorithm = builder.symmetricKeyAlgorithm;
this.symmetricKeyLength = builder.symmetricKeyLength;
this.keyPair = builder.keyPair;
- this.bufferSize = builder.bufferSize;
this.provider = builder.provider;
this.keyGenerator = builder.keyGenerator;
}
@@ -128,14 +124,6 @@ public class PQCDataFormat extends DataFormatDefinition {
this.keyPair = keyPair;
}
- public String getBufferSize() {
- return bufferSize;
- }
-
- public void setBufferSize(String bufferSize) {
- this.bufferSize = bufferSize;
- }
-
public String getProvider() {
return provider;
}
@@ -162,7 +150,6 @@ public class PQCDataFormat extends DataFormatDefinition {
private String symmetricKeyAlgorithm = "AES";
private String symmetricKeyLength;
private String keyPair;
- private String bufferSize;
private String provider;
private String keyGenerator;
@@ -176,8 +163,8 @@ public class PQCDataFormat extends DataFormatDefinition {
}
/**
- * The symmetric encryption algorithm to use with the shared secret.
Supported values: AES, ARIA, RC2, RC5,
- * CAMELLIA, CAST5, CAST6, CHACHA7539, etc.
+ * The symmetric encryption algorithm to use with the shared secret.
Only AEAD-capable algorithms are allowed:
+ * AES, ARIA, CAMELLIA, CAST6, DSTU7624, GOST3412_2015, SEED, SM4
(GCM) and CHACHA7539 (ChaCha20-Poly1305).
*/
public Builder symmetricKeyAlgorithm(String symmetricKeyAlgorithm) {
this.symmetricKeyAlgorithm = symmetricKeyAlgorithm;
@@ -208,22 +195,6 @@ public class PQCDataFormat extends DataFormatDefinition {
return this;
}
- /**
- * The size of the buffer used for streaming encryption/decryption.
- */
- public Builder bufferSize(String bufferSize) {
- this.bufferSize = bufferSize;
- return this;
- }
-
- /**
- * The size of the buffer used for streaming encryption/decryption.
- */
- public Builder bufferSize(int bufferSize) {
- this.bufferSize = Integer.toString(bufferSize);
- return this;
- }
-
/**
* The JCE security provider to use.
*/
diff --git
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/dataformat/PQCDataFormatReifier.java
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/dataformat/PQCDataFormatReifier.java
index 44e1a806bcf8..bd91d04082cc 100644
---
a/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/dataformat/PQCDataFormatReifier.java
+++
b/core/camel-core-reifier/src/main/java/org/apache/camel/reifier/dataformat/PQCDataFormatReifier.java
@@ -34,7 +34,6 @@ public class PQCDataFormatReifier extends
DataFormatReifier<PQCDataFormat> {
properties.put("symmetricKeyAlgorithm",
definition.getSymmetricKeyAlgorithm());
properties.put("symmetricKeyLength",
definition.getSymmetricKeyLength());
properties.put("keyPair", asRef(definition.getKeyPair()));
- properties.put("bufferSize", definition.getBufferSize());
properties.put("provider", definition.getProvider());
properties.put("keyGenerator", asRef(definition.getKeyGenerator()));
}
diff --git
a/core/camel-java-io/src/generated/java/org/apache/camel/java/out/JavaDslModelWriter.java
b/core/camel-java-io/src/generated/java/org/apache/camel/java/out/JavaDslModelWriter.java
index 30fa1f0dada8..9640d635bbe7 100644
---
a/core/camel-java-io/src/generated/java/org/apache/camel/java/out/JavaDslModelWriter.java
+++
b/core/camel-java-io/src/generated/java/org/apache/camel/java/out/JavaDslModelWriter.java
@@ -3180,7 +3180,6 @@ public class JavaDslModelWriter extends
JavaDslModelWriterSupport {
doWriteAttribute(sb, "symmetricKeyAlgorithm",
def.getSymmetricKeyAlgorithm(), "AES");
doWriteAttribute(sb, "symmetricKeyLength",
def.getSymmetricKeyLength(), "128");
doWriteAttribute(sb, "keyPair", def.getKeyPair(), null);
- doWriteAttribute(sb, "bufferSize", def.getBufferSize(), "4096");
doWriteAttribute(sb, "provider", def.getProvider(), null);
doWriteAttribute(sb, "keyGenerator", def.getKeyGenerator(), null);
}
diff --git
a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
index 948b15ce67c0..abcef047096c 100644
---
a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
+++
b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/in/ModelParser.java
@@ -1951,7 +1951,6 @@ public class ModelParser extends BaseParser {
}
protected PQCDataFormat doParsePQCDataFormat() throws IOException,
XmlPullParserException {
return doParse(new PQCDataFormat(), (def, key, val) -> switch (key) {
- case "bufferSize": def.setBufferSize(val); yield true;
case "keyEncapsulationAlgorithm":
def.setKeyEncapsulationAlgorithm(val); yield true;
case "keyGenerator": def.setKeyGenerator(val); yield true;
case "keyPair": def.setKeyPair(val); yield true;
diff --git
a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
index ba02535775d3..0e15504b96da 100644
---
a/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
+++
b/core/camel-xml-io/src/generated/java/org/apache/camel/xml/out/ModelWriter.java
@@ -2563,7 +2563,6 @@ public class ModelWriter extends BaseWriter {
doWriteAttribute("symmetricKeyAlgorithm",
def.getSymmetricKeyAlgorithm(), "AES");
doWriteAttribute("symmetricKeyLength", def.getSymmetricKeyLength(),
"128");
doWriteAttribute("keyPair", def.getKeyPair(), null);
- doWriteAttribute("bufferSize", def.getBufferSize(), "4096");
doWriteAttribute("provider", def.getProvider(), null);
doWriteAttribute("keyGenerator", def.getKeyGenerator(), null);
endElement(name);
diff --git
a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/YamlModelWriter.java
b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/YamlModelWriter.java
index 191d45a6755d..8e9db27a681a 100644
---
a/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/YamlModelWriter.java
+++
b/core/camel-yaml-io/src/generated/java/org/apache/camel/yaml/out/YamlModelWriter.java
@@ -2561,7 +2561,6 @@ public class YamlModelWriter extends
YamlModelWriterSupport {
doWriteAttribute(jo, "symmetricKeyAlgorithm",
def.getSymmetricKeyAlgorithm(), "AES");
doWriteAttribute(jo, "symmetricKeyLength",
def.getSymmetricKeyLength(), "128");
doWriteAttribute(jo, "keyPair", def.getKeyPair(), null);
- doWriteAttribute(jo, "bufferSize", def.getBufferSize(), "4096");
doWriteAttribute(jo, "provider", def.getProvider(), null);
doWriteAttribute(jo, "keyGenerator", def.getKeyGenerator(), null);
return jo;
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
index 3564013b7878..3d5efab27be2 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
@@ -34,3 +34,34 @@ Result<String> chat(AiAgentBody<?> aiAgentBody, ToolProvider
toolProvider);
Due to new requirements from Apache Fory, when using Apache Fory Dataformat,
the JVM parameter `--add-opens java.base/java.lang.invoke=ALL-UNNAMED` must be
provided.
+=== camel-pqc - potential breaking change
+
+The `pqc` data format now encrypts the message payload with *authenticated
encryption (AEAD)* instead of the
+previous unauthenticated ECB mode. 128-bit block ciphers are encrypted with
GCM and the ChaCha20 stream cipher is
+encrypted with ChaCha20-Poly1305, so both the confidentiality and the
integrity of the data are now protected, and
+tampered or corrupted messages are detected and rejected on decryption.
+
+As a consequence:
+
+* The wire format changed. A 12-byte AEAD nonce is now written between the
encapsulation and the ciphertext, and the
+ciphertext carries an authentication tag:
++
+[source,text]
+----
+[4 bytes: encapsulation length] [N bytes: encapsulation] [12 bytes: AEAD
nonce] [M bytes: ciphertext + auth tag]
+----
++
+Data encrypted by Camel 4.21 or earlier (unauthenticated ECB, no nonce)
*cannot be decrypted* by Camel 4.22 or
+later. Re-encrypt any data at rest with the new version.
+
+* Only symmetric algorithms that support AEAD are accepted: `AES`, `ARIA`,
`CAMELLIA`, `CAST6`, `DSTU7624`,
+`GOST3412-2015`, `SEED`, `SM4` (GCM) and `CHACHA7539` (ChaCha20-Poly1305). The
previously accepted `RC2`, `RC5`,
+`CAST5`, `GOST28147`, `DESEDE` and the unauthenticated stream ciphers
`GRAIN128`, `HC128`, `HC256` and `SALSA20`
+are no longer supported and are rejected when the route starts. The default
(`AES`) is unchanged.
+
+* The message is now processed in memory rather than streamed: the
authenticated cipher must verify the authentication
+tag before releasing any plaintext, so the whole payload is held in memory
during marshal and unmarshal. For very large
+payloads, enable stream caching on the route.
+
+* The `bufferSize` option has been removed. It only configured the previous
streaming implementation and no longer has
+any effect with authenticated encryption.
diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide.adoc
index 7f4dcc8b8fd8..f9ed76dce6c0 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide.adoc
@@ -30,6 +30,7 @@ You can find the upgrade guide for each release in the
following pages:
- xref:camel-4x-upgrade-guide-4_19.adoc[Upgrade guide 4.18 -> 4.19]
- xref:camel-4x-upgrade-guide-4_20.adoc[Upgrade guide 4.19 -> 4.20]
- xref:camel-4x-upgrade-guide-4_21.adoc[Upgrade guide 4.20 -> 4.21]
+- xref:camel-4x-upgrade-guide-4_22.adoc[Upgrade guide 4.21 -> 4.22]
[NOTE]
====
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
index 4309da7aaf3a..03a1de268bb1 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-deserializers/src/generated/java/org/apache/camel/dsl/yaml/deserializers/ModelDeserializers.java
@@ -11257,13 +11257,12 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
description = "Encrypt and decrypt messages using Post-Quantum
Cryptography Key Encapsulation Mechanisms (KEM)",
deprecated = false,
properties = {
- @YamlProperty(name = "bufferSize", type = "number",
defaultValue = "4096", description = "The size of the buffer used for streaming
encryption/decryption.", displayName = "Buffer Size"),
@YamlProperty(name = "id", type = "string", description =
"The id of this node", displayName = "Id"),
@YamlProperty(name = "keyEncapsulationAlgorithm", type =
"enum:MLKEM,BIKE,HQC,CMCE,SABER,FRODO,NTRU,NTRULPRime,SNTRUPrime,KYBER",
defaultValue = "MLKEM", description = "The Post-Quantum KEM algorithm to use
for key encapsulation.", displayName = "Key Encapsulation Algorithm"),
@YamlProperty(name = "keyGenerator", type = "string",
description = "Refers to a custom KeyGenerator to lookup from the registry for
KEM operations.", displayName = "Key Generator"),
@YamlProperty(name = "keyPair", type = "string",
description = "Refers to the KeyPair to lookup from the registry to use for KEM
operations.", displayName = "Key Pair"),
@YamlProperty(name = "provider", type = "string",
description = "The JCE security provider to use.", displayName = "Provider"),
- @YamlProperty(name = "symmetricKeyAlgorithm", type =
"enum:AES,ARIA,RC2,RC5,CAMELLIA,CAST5,CAST6,CHACHA7539,DSTU7624,GOST28147,GOST3412_2015,GRAIN128,HC128,HC256,SALSA20,SEED,SM4,DESEDE",
defaultValue = "AES", description = "The symmetric encryption algorithm to use
with the shared secret.", displayName = "Symmetric Key Algorithm"),
+ @YamlProperty(name = "symmetricKeyAlgorithm", type =
"enum:AES,ARIA,CAMELLIA,CAST6,DSTU7624,GOST3412_2015,SEED,SM4,CHACHA7539",
defaultValue = "AES", description = "The symmetric encryption algorithm to use
with the shared secret. Only algorithms that support authenticated encryption
(AEAD) are allowed: AES, ARIA, CAMELLIA, CAST6, DSTU7624, GOST3412_2015, SEED
and SM4 are encrypted with GCM, and CHACHA7539 with ChaCha20-Poly1305.",
displayName = "Symmetric Key Algorithm"),
@YamlProperty(name = "symmetricKeyLength", type =
"number", defaultValue = "128", description = "The length (in bits) of the
symmetric key.", displayName = "Symmetric Key Length")
}
)
@@ -11282,11 +11281,6 @@ public final class ModelDeserializers extends
YamlDeserializerSupport {
Node node) {
propertyKey =
org.apache.camel.util.StringHelper.dashToCamelCase(propertyKey);
switch(propertyKey) {
- case "bufferSize": {
- String val = asText(node);
- target.setBufferSize(val);
- break;
- }
case "id": {
String val = asText(node);
target.setId(val);
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-canonical.json
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-canonical.json
index 9c00c6cab3a3..307f90a9553a 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-canonical.json
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-canonical.json
@@ -7972,12 +7972,6 @@
"type" : "object",
"additionalProperties" : false,
"properties" : {
- "bufferSize" : {
- "type" : "number",
- "title" : "Buffer Size",
- "description" : "The size of the buffer used for streaming
encryption/decryption.",
- "default" : 4096
- },
"id" : {
"type" : "string",
"title" : "Id",
@@ -8008,9 +8002,9 @@
"symmetricKeyAlgorithm" : {
"type" : "string",
"title" : "Symmetric Key Algorithm",
- "description" : "The symmetric encryption algorithm to use with
the shared secret.",
+ "description" : "The symmetric encryption algorithm to use with
the shared secret. Only algorithms that support authenticated encryption (AEAD)
are allowed: AES, ARIA, CAMELLIA, CAST6, DSTU7624, GOST3412_2015, SEED and SM4
are encrypted with GCM, and CHACHA7539 with ChaCha20-Poly1305.",
"default" : "AES",
- "enum" : [ "AES", "ARIA", "RC2", "RC5", "CAMELLIA", "CAST5",
"CAST6", "CHACHA7539", "DSTU7624", "GOST28147", "GOST3412_2015", "GRAIN128",
"HC128", "HC256", "SALSA20", "SEED", "SM4", "DESEDE" ]
+ "enum" : [ "AES", "ARIA", "CAMELLIA", "CAST6", "DSTU7624",
"GOST3412_2015", "SEED", "SM4", "CHACHA7539" ]
},
"symmetricKeyLength" : {
"type" : "number",
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
index 0415302419b0..08e2826ac762 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl.json
@@ -11407,12 +11407,6 @@
"type" : "object",
"additionalProperties" : false,
"properties" : {
- "bufferSize" : {
- "type" : "number",
- "title" : "Buffer Size",
- "description" : "The size of the buffer used for streaming
encryption/decryption.",
- "default" : 4096
- },
"id" : {
"type" : "string",
"title" : "Id",
@@ -11443,9 +11437,9 @@
"symmetricKeyAlgorithm" : {
"type" : "string",
"title" : "Symmetric Key Algorithm",
- "description" : "The symmetric encryption algorithm to use with
the shared secret.",
+ "description" : "The symmetric encryption algorithm to use with
the shared secret. Only algorithms that support authenticated encryption (AEAD)
are allowed: AES, ARIA, CAMELLIA, CAST6, DSTU7624, GOST3412_2015, SEED and SM4
are encrypted with GCM, and CHACHA7539 with ChaCha20-Poly1305.",
"default" : "AES",
- "enum" : [ "AES", "ARIA", "RC2", "RC5", "CAMELLIA", "CAST5",
"CAST6", "CHACHA7539", "DSTU7624", "GOST28147", "GOST3412_2015", "GRAIN128",
"HC128", "HC256", "SALSA20", "SEED", "SM4", "DESEDE" ]
+ "enum" : [ "AES", "ARIA", "CAMELLIA", "CAST6", "DSTU7624",
"GOST3412_2015", "SEED", "SM4", "CHACHA7539" ]
},
"symmetricKeyLength" : {
"type" : "number",