This is an automated email from the ASF dual-hosted git repository.
jiriondrusek pushed a commit to branch camel-main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git
The following commit(s) were added to refs/heads/camel-main by this push:
new 9ae444697e Pqc native support
9ae444697e is described below
commit 9ae444697eec4f5cfbbe967a2d73126203520ada
Author: Jiri Ondrusek <[email protected]>
AuthorDate: Thu Mar 26 15:44:26 2026 +0100
Pqc native support
---
docs/modules/ROOT/examples/components/pqc.yml | 6 +-
docs/modules/ROOT/examples/dataformats/pqc.yml | 6 +-
.../ROOT/pages/reference/extensions/pqc.adoc | 14 +-
extensions-jvm/pom.xml | 1 -
.../component/pqc/deployment/PqcProcessor.java | 46 -----
.../BouncyCastleAdditionalProviderBuildItem.java | 26 +--
.../deployment/BouncyCastleSupportProcessor.java | 14 +-
.../support/bouncycastle/BouncyCastleRecorder.java | 43 +++--
extensions/pom.xml | 1 +
.../pqc/deployment/pom.xml | 4 +
.../component/pqc/deployment/PqcProcessor.java | 81 +++++++++
{extensions-jvm => extensions}/pqc/pom.xml | 2 +-
{extensions-jvm => extensions}/pqc/runtime/pom.xml | 5 +
.../main/resources/META-INF/quarkus-extension.yaml | 1 -
integration-tests-jvm/pom.xml | 1 -
.../quarkus/component/pqc/it/PqcResource.java | 50 ------
integration-tests/pom.xml | 1 +
.../pqc/pom.xml | 36 ++++
.../quarkus/component/pqc/it/PqcProducers.java | 89 ++++++++++
.../quarkus/component/pqc/it/PqcResource.java | 187 +++++++++++++++++++++
.../camel/quarkus/component/pqc/it/PqcIT.java | 17 +-
.../camel/quarkus/component/pqc/it/PqcTest.java | 159 ++++++++++++++++++
tooling/scripts/test-categories.yaml | 1 +
23 files changed, 636 insertions(+), 155 deletions(-)
diff --git a/docs/modules/ROOT/examples/components/pqc.yml
b/docs/modules/ROOT/examples/components/pqc.yml
index 5d107de78c..723e0663d8 100644
--- a/docs/modules/ROOT/examples/components/pqc.yml
+++ b/docs/modules/ROOT/examples/components/pqc.yml
@@ -2,11 +2,11 @@
# This file was generated by
camel-quarkus-maven-plugin:update-extension-doc-page
cqArtifactId: camel-quarkus-pqc
cqArtifactIdBase: pqc
-cqNativeSupported: false
-cqStatus: Preview
+cqNativeSupported: true
+cqStatus: Stable
cqDeprecated: false
cqJvmSince: 3.24.0
-cqNativeSince: n/a
+cqNativeSince: 3.35.0
cqCamelPartName: pqc
cqCamelPartTitle: PQC Algorithms
cqCamelPartDescription: Post Quantum Cryptography Signature and Verification
component.
diff --git a/docs/modules/ROOT/examples/dataformats/pqc.yml
b/docs/modules/ROOT/examples/dataformats/pqc.yml
index 57e5f19113..62643f1ab3 100644
--- a/docs/modules/ROOT/examples/dataformats/pqc.yml
+++ b/docs/modules/ROOT/examples/dataformats/pqc.yml
@@ -2,11 +2,11 @@
# This file was generated by
camel-quarkus-maven-plugin:update-extension-doc-page
cqArtifactId: camel-quarkus-pqc
cqArtifactIdBase: pqc
-cqNativeSupported: false
-cqStatus: Preview
+cqNativeSupported: true
+cqStatus: Stable
cqDeprecated: false
cqJvmSince: 3.24.0
-cqNativeSince: n/a
+cqNativeSince: 3.35.0
cqCamelPartName: pqc
cqCamelPartTitle: PQC (Post-Quantum Cryptography)
cqCamelPartDescription: Encrypt and decrypt messages using Post-Quantum
Cryptography Key Encapsulation Mechanisms (KEM).
diff --git a/docs/modules/ROOT/pages/reference/extensions/pqc.adoc
b/docs/modules/ROOT/pages/reference/extensions/pqc.adoc
index 2e95efcd0c..1c17849e83 100644
--- a/docs/modules/ROOT/pages/reference/extensions/pqc.adoc
+++ b/docs/modules/ROOT/pages/reference/extensions/pqc.adoc
@@ -4,17 +4,17 @@
= PQC Algorithms
:linkattrs:
:cq-artifact-id: camel-quarkus-pqc
-:cq-native-supported: false
-:cq-status: Preview
-:cq-status-deprecation: Preview
+:cq-native-supported: true
+:cq-status: Stable
+:cq-status-deprecation: Stable
:cq-description: Post Quantum Computing Signature and Verification component.
:cq-deprecated: false
:cq-jvm-since: 3.24.0
-:cq-native-since: n/a
+:cq-native-since: 3.35.0
ifeval::[{doc-show-badges} == true]
[.badges]
-[.badge-key]##JVM since##[.badge-supported]##3.24.0##
[.badge-key]##Native##[.badge-unsupported]##unsupported##
+[.badge-key]##JVM since##[.badge-supported]##3.24.0## [.badge-key]##Native
since##[.badge-supported]##3.35.0##
endif::[]
Post Quantum Computing Signature and Verification component.
@@ -30,6 +30,10 @@ Please refer to the above links for usage and configuration
details.
[id="extensions-pqc-maven-coordinates"]
== Maven coordinates
+https://{link-quarkus-code-generator}/?extension-search=camel-quarkus-pqc[Create
a new project with this extension on {link-quarkus-code-generator},
window="_blank"]
+
+Or add the coordinates to your existing project:
+
[source,xml]
----
<dependency>
diff --git a/extensions-jvm/pom.xml b/extensions-jvm/pom.xml
index 7a000433fb..0251dc69e6 100644
--- a/extensions-jvm/pom.xml
+++ b/extensions-jvm/pom.xml
@@ -80,7 +80,6 @@
<module>lucene</module>
<module>mvel</module>
<module>opensearch</module>
- <module>pqc</module>
<module>printer</module>
<module>pulsar</module>
<module>python</module>
diff --git
a/extensions-jvm/pqc/deployment/src/main/java/org/apache/camel/quarkus/component/pqc/deployment/PqcProcessor.java
b/extensions-jvm/pqc/deployment/src/main/java/org/apache/camel/quarkus/component/pqc/deployment/PqcProcessor.java
deleted file mode 100644
index b4826f3f13..0000000000
---
a/extensions-jvm/pqc/deployment/src/main/java/org/apache/camel/quarkus/component/pqc/deployment/PqcProcessor.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.quarkus.component.pqc.deployment;
-
-import io.quarkus.deployment.annotations.BuildStep;
-import io.quarkus.deployment.annotations.ExecutionTime;
-import io.quarkus.deployment.annotations.Record;
-import io.quarkus.deployment.builditem.FeatureBuildItem;
-import io.quarkus.deployment.pkg.steps.NativeOrNativeSourcesBuild;
-import org.apache.camel.quarkus.core.JvmOnlyRecorder;
-import org.jboss.logging.Logger;
-
-class PqcProcessor {
-
- private static final Logger LOG = Logger.getLogger(PqcProcessor.class);
- private static final String FEATURE = "camel-pqc";
-
- @BuildStep
- FeatureBuildItem feature() {
- return new FeatureBuildItem(FEATURE);
- }
-
- /**
- * Remove this once this extension starts supporting the native mode.
- */
- @BuildStep(onlyIf = NativeOrNativeSourcesBuild.class)
- @Record(value = ExecutionTime.RUNTIME_INIT)
- void warnJvmInNative(JvmOnlyRecorder recorder) {
- JvmOnlyRecorder.warnJvmInNative(LOG, FEATURE); // warn at build time
- recorder.warnJvmInNative(FEATURE); // warn at runtime
- }
-}
diff --git
a/integration-tests-jvm/pqc/src/test/java/org/apache/camel/quarkus/component/pqc/it/PqcTest.java
b/extensions-support/bouncycastle/deployment/src/main/java/org/apache/camel/quarkus/support/bouncycastle/deployment/BouncyCastleAdditionalProviderBuildItem.java
similarity index 59%
copy from
integration-tests-jvm/pqc/src/test/java/org/apache/camel/quarkus/component/pqc/it/PqcTest.java
copy to
extensions-support/bouncycastle/deployment/src/main/java/org/apache/camel/quarkus/support/bouncycastle/deployment/BouncyCastleAdditionalProviderBuildItem.java
index ff955411d3..c004eca4cf 100644
---
a/integration-tests-jvm/pqc/src/test/java/org/apache/camel/quarkus/component/pqc/it/PqcTest.java
+++
b/extensions-support/bouncycastle/deployment/src/main/java/org/apache/camel/quarkus/support/bouncycastle/deployment/BouncyCastleAdditionalProviderBuildItem.java
@@ -14,21 +14,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.camel.quarkus.component.pqc.it;
+package org.apache.camel.quarkus.support.bouncycastle.deployment;
-import io.quarkus.test.junit.QuarkusTest;
-import io.restassured.RestAssured;
-import org.junit.jupiter.api.Test;
+import io.quarkus.builder.item.MultiBuildItem;
-@QuarkusTest
-class PqcTest {
+/**
+ * In case that non-default BC provider has to be registered, use this
buildItem.
+ * (provider available for registration is `BCPQC`)
+ */
+public final class BouncyCastleAdditionalProviderBuildItem extends
MultiBuildItem {
+
+ private final String proivderName;
- @Test
- public void loadComponentPqc() {
- /* A simple autogenerated test */
- RestAssured.get("/pqc/load/component/pqc")
- .then()
- .statusCode(200);
+ public BouncyCastleAdditionalProviderBuildItem(String providerNme) {
+ this.proivderName = providerNme;
}
+ public String getProviderName() {
+ return proivderName;
+ }
}
diff --git
a/extensions-support/bouncycastle/deployment/src/main/java/org/apache/camel/quarkus/support/bouncycastle/deployment/BouncyCastleSupportProcessor.java
b/extensions-support/bouncycastle/deployment/src/main/java/org/apache/camel/quarkus/support/bouncycastle/deployment/BouncyCastleSupportProcessor.java
index a6e47238aa..012b6e73b2 100644
---
a/extensions-support/bouncycastle/deployment/src/main/java/org/apache/camel/quarkus/support/bouncycastle/deployment/BouncyCastleSupportProcessor.java
+++
b/extensions-support/bouncycastle/deployment/src/main/java/org/apache/camel/quarkus/support/bouncycastle/deployment/BouncyCastleSupportProcessor.java
@@ -26,7 +26,6 @@ import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
-import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import
io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem;
import io.quarkus.security.deployment.BouncyCastleProviderBuildItem;
@@ -46,12 +45,17 @@ public class BouncyCastleSupportProcessor {
@BuildStep()
@Record(ExecutionTime.STATIC_INIT)
- public void
registerBouncyCastleProvider(List<CipherTransformationBuildItem>
cipherTransformations,
- BouncyCastleRecorder recorder,
- ShutdownContextBuildItem shutdownContextBuildItem) {
+ public void registerBouncyCastleProvider(
+ List<BouncyCastleAdditionalProviderBuildItem>
additionalProviderBuildItems,
+ List<CipherTransformationBuildItem> cipherTransformations,
+ BouncyCastleRecorder recorder) {
+
List<String> allCipherTransformations = cipherTransformations.stream()
.flatMap(c ->
c.getCipherTransformations().stream()).collect(Collectors.toList());
- recorder.registerBouncyCastleProvider(allCipherTransformations,
shutdownContextBuildItem);
+
+ recorder.registerBouncyCastleProvider(
+
additionalProviderBuildItems.stream().map(BouncyCastleAdditionalProviderBuildItem::getProviderName).toList(),
+ allCipherTransformations);
}
@BuildStep()
diff --git
a/extensions-support/bouncycastle/runtime/src/main/java/org/apache/camel/quarkus/support/bouncycastle/BouncyCastleRecorder.java
b/extensions-support/bouncycastle/runtime/src/main/java/org/apache/camel/quarkus/support/bouncycastle/BouncyCastleRecorder.java
index e559d1c496..b53a64f717 100644
---
a/extensions-support/bouncycastle/runtime/src/main/java/org/apache/camel/quarkus/support/bouncycastle/BouncyCastleRecorder.java
+++
b/extensions-support/bouncycastle/runtime/src/main/java/org/apache/camel/quarkus/support/bouncycastle/BouncyCastleRecorder.java
@@ -18,25 +18,33 @@ package org.apache.camel.quarkus.support.bouncycastle;
import java.lang.reflect.InvocationTargetException;
import java.security.Provider;
-import java.security.Security;
+import java.util.ArrayList;
import java.util.List;
import javax.crypto.Cipher;
-import io.quarkus.runtime.ShutdownContext;
import io.quarkus.runtime.annotations.Recorder;
import io.quarkus.security.runtime.SecurityProviderUtils;
+import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
import org.jboss.logging.Logger;
+import static java.security.Security.addProvider;
+import static java.security.Security.getProvider;
+
@Recorder
public class BouncyCastleRecorder {
private static final Logger LOG =
Logger.getLogger(BouncyCastleRecorder.class);
- public void registerBouncyCastleProvider(List<String>
cipherTransformations, ShutdownContext shutdownContext) {
- Provider provider =
Security.getProvider(SecurityProviderUtils.BOUNCYCASTLE_PROVIDER_NAME);
+ public static final String BOUNCYCASTLE_PCQ_PROVIDER_NAME = "BCPCQ";
+
+ public void registerBouncyCastleProvider(
+ List<String> additionalProviders,
+ List<String> cipherTransformations) {
+ List<Provider> registeredProviders = new ArrayList<>();
+ Provider provider =
getProvider(SecurityProviderUtils.BOUNCYCASTLE_PROVIDER_NAME);
if (provider == null) {
- provider =
Security.getProvider(SecurityProviderUtils.BOUNCYCASTLE_FIPS_PROVIDER_NAME);
+ provider =
getProvider(SecurityProviderUtils.BOUNCYCASTLE_FIPS_PROVIDER_NAME);
}
if (provider == null) {
// TODO: Fix BuildStep execution order so that this is not required
@@ -44,7 +52,7 @@ public class BouncyCastleRecorder {
try {
provider = (Provider)
Thread.currentThread().getContextClassLoader()
.loadClass(SecurityProviderUtils.BOUNCYCASTLE_PROVIDER_CLASS_NAME).getConstructor().newInstance();
- Security.addProvider(provider);
+ addProvider(provider);
} catch (ClassNotFoundException | InvocationTargetException |
InstantiationException | IllegalAccessException
| NoSuchMethodException e) {
try {
@@ -52,7 +60,7 @@ public class BouncyCastleRecorder {
provider = (Provider)
Thread.currentThread().getContextClassLoader()
.loadClass(SecurityProviderUtils.BOUNCYCASTLE_FIPS_PROVIDER_CLASS_NAME).getConstructor()
.newInstance();
- Security.addProvider(provider);
+ addProvider(provider);
} catch (ClassNotFoundException | InvocationTargetException |
InstantiationException | IllegalAccessException
| NoSuchMethodException e2) {
throw new RuntimeException("Neither BC nor BCFIPS provider
can be registered. \nBC: " + e.getMessage()
@@ -60,6 +68,7 @@ public class BouncyCastleRecorder {
}
}
}
+ registeredProviders.add(provider);
// Make it explicit to the static analysis that below security
services should be registered as they are reachable at runtime
for (String cipherTransformation : cipherTransformations) {
@@ -73,12 +82,20 @@ public class BouncyCastleRecorder {
}
}
- shutdownContext.addShutdownTask(new Runnable() {
- @Override
- public void run() {
-
Security.removeProvider(SecurityProviderUtils.BOUNCYCASTLE_PROVIDER_NAME);
- LOG.debug("Removed Bouncy Castle security provider");
+ if (additionalProviders.contains(BOUNCYCASTLE_PCQ_PROVIDER_NAME)) {
+ Provider pqcProvider = getProvider("BCPQC");
+ if (pqcProvider == null) {
+ pqcProvider = new BouncyCastlePQCProvider();
+ try {
+ addProvider(pqcProvider);
+ } catch (SecurityException e) {
+ throw new RuntimeException(e);
+ }
+ LOG.debugf("Registered BouncyCastlePQCProvider");
}
- });
+ registeredProviders.add(pqcProvider);
+
+ }
+
}
}
diff --git a/extensions/pom.xml b/extensions/pom.xml
index edcc530261..c5956445c9 100644
--- a/extensions/pom.xml
+++ b/extensions/pom.xml
@@ -233,6 +233,7 @@
<module>pgevent</module>
<module>pinecone</module>
<module>platform-http</module>
+ <module>pqc</module>
<module>protobuf</module>
<module>pubnub</module>
<module>qdrant</module>
diff --git a/extensions-jvm/pqc/deployment/pom.xml
b/extensions/pqc/deployment/pom.xml
similarity index 93%
rename from extensions-jvm/pqc/deployment/pom.xml
rename to extensions/pqc/deployment/pom.xml
index 1c046ba848..0c70adc24d 100644
--- a/extensions-jvm/pqc/deployment/pom.xml
+++ b/extensions/pqc/deployment/pom.xml
@@ -38,6 +38,10 @@
<groupId>org.apache.camel.quarkus</groupId>
<artifactId>camel-quarkus-pqc</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel.quarkus</groupId>
+
<artifactId>camel-quarkus-support-bouncycastle-deployment</artifactId>
+ </dependency>
</dependencies>
<build>
diff --git
a/extensions/pqc/deployment/src/main/java/org/apache/camel/quarkus/component/pqc/deployment/PqcProcessor.java
b/extensions/pqc/deployment/src/main/java/org/apache/camel/quarkus/component/pqc/deployment/PqcProcessor.java
new file mode 100644
index 0000000000..425d492964
--- /dev/null
+++
b/extensions/pqc/deployment/src/main/java/org/apache/camel/quarkus/component/pqc/deployment/PqcProcessor.java
@@ -0,0 +1,81 @@
+/*
+ * 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.quarkus.component.pqc.deployment;
+
+import io.quarkus.deployment.annotations.BuildProducer;
+import io.quarkus.deployment.annotations.BuildStep;
+import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
+import io.quarkus.deployment.builditem.FeatureBuildItem;
+import io.quarkus.deployment.builditem.IndexDependencyBuildItem;
+import
io.quarkus.deployment.builditem.nativeimage.NativeImageSecurityProviderBuildItem;
+import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
+import org.apache.camel.quarkus.support.bouncycastle.BouncyCastleRecorder;
+import
org.apache.camel.quarkus.support.bouncycastle.deployment.BouncyCastleAdditionalProviderBuildItem;
+import org.jboss.jandex.IndexView;
+import org.jboss.logging.Logger;
+
+class PqcProcessor {
+
+ private static final Logger LOG = Logger.getLogger(PqcProcessor.class);
+ private static final String FEATURE = "camel-pqc";
+
+ @BuildStep
+ FeatureBuildItem feature() {
+ return new FeatureBuildItem(FEATURE);
+ }
+
+ @BuildStep
+ NativeImageSecurityProviderBuildItem registerBcpqcSecurityProvider() {
+ return new
NativeImageSecurityProviderBuildItem("org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider");
+ }
+
+ @BuildStep
+ IndexDependencyBuildItem indexBouncyCastlePQC() {
+ return new IndexDependencyBuildItem("org.bouncycastle",
"bcprov-jdk18on");
+ }
+
+ @BuildStep
+ ReflectiveClassBuildItem
registerBouncyCastlePQCClasses(CombinedIndexBuildItem combinedIndex) {
+ IndexView index = combinedIndex.getIndex();
+
+ String[] pqcClasses = index.getKnownClasses().stream()
+ .map(ci -> ci.name().toString())
+ .filter(
+ n ->
(n.startsWith("org.bouncycastle.pqc.jcajce.provider")
+ && (n.endsWith("Spi") || n.contains("Spi$"))))
+ .toArray(String[]::new);
+
+ return
ReflectiveClassBuildItem.builder(pqcClasses).methods().fields().build();
+ }
+
+ @BuildStep
+ void registerCryptoClasses(BuildProducer<ReflectiveClassBuildItem>
reflectiveClasses) {
+ reflectiveClasses.produce(
+ ReflectiveClassBuildItem.builder(
+ java.security.KeyPairGenerator.class,
+ java.security.Signature.class,
+ java.security.KeyFactory.class,
+ javax.crypto.KeyGenerator.class,
+ javax.crypto.SecretKey.class).methods().build());
+ }
+
+ @BuildStep
+ BouncyCastleAdditionalProviderBuildItem registerBouncyCastlePQCProvider() {
+ return new
BouncyCastleAdditionalProviderBuildItem(BouncyCastleRecorder.BOUNCYCASTLE_PCQ_PROVIDER_NAME);
+ }
+
+}
diff --git a/extensions-jvm/pqc/pom.xml b/extensions/pqc/pom.xml
similarity index 96%
rename from extensions-jvm/pqc/pom.xml
rename to extensions/pqc/pom.xml
index 8b2520b670..7290384ab2 100644
--- a/extensions-jvm/pqc/pom.xml
+++ b/extensions/pqc/pom.xml
@@ -21,7 +21,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.camel.quarkus</groupId>
- <artifactId>camel-quarkus-extensions-jvm</artifactId>
+ <artifactId>camel-quarkus-extensions</artifactId>
<version>3.35.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
diff --git a/extensions-jvm/pqc/runtime/pom.xml b/extensions/pqc/runtime/pom.xml
similarity index 93%
rename from extensions-jvm/pqc/runtime/pom.xml
rename to extensions/pqc/runtime/pom.xml
index 191cfbef83..9c46a0a174 100644
--- a/extensions-jvm/pqc/runtime/pom.xml
+++ b/extensions/pqc/runtime/pom.xml
@@ -32,6 +32,7 @@
<properties>
<camel.quarkus.jvmSince>3.24.0</camel.quarkus.jvmSince>
+ <camel.quarkus.nativeSince>3.35.0</camel.quarkus.nativeSince>
</properties>
<dependencies>
@@ -43,6 +44,10 @@
<groupId>org.apache.camel</groupId>
<artifactId>camel-pqc</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel.quarkus</groupId>
+ <artifactId>camel-quarkus-support-bouncycastle</artifactId>
+ </dependency>
</dependencies>
<build>
diff --git
a/extensions-jvm/pqc/runtime/src/main/resources/META-INF/quarkus-extension.yaml
b/extensions/pqc/runtime/src/main/resources/META-INF/quarkus-extension.yaml
similarity index 98%
rename from
extensions-jvm/pqc/runtime/src/main/resources/META-INF/quarkus-extension.yaml
rename to
extensions/pqc/runtime/src/main/resources/META-INF/quarkus-extension.yaml
index 8dba5652fd..00f74799d4 100644
---
a/extensions-jvm/pqc/runtime/src/main/resources/META-INF/quarkus-extension.yaml
+++ b/extensions/pqc/runtime/src/main/resources/META-INF/quarkus-extension.yaml
@@ -26,7 +26,6 @@ description: "Post Quantum Computing Signature and
Verification component"
metadata:
icon-url:
"https://raw.githubusercontent.com/apache/camel-website/main/antora-ui-camel/src/img/logo-d.svg"
sponsor: "Apache Software Foundation"
- unlisted: true
guide:
"https://camel.apache.org/camel-quarkus/latest/reference/extensions/pqc.html"
categories:
- "integration"
diff --git a/integration-tests-jvm/pom.xml b/integration-tests-jvm/pom.xml
index a1f1237b89..33ae81867c 100644
--- a/integration-tests-jvm/pom.xml
+++ b/integration-tests-jvm/pom.xml
@@ -83,7 +83,6 @@
<module>main-devmode</module>
<module>mvel</module>
<module>opensearch</module>
- <module>pqc</module>
<module>printer</module>
<module>pulsar</module>
<module>python</module>
diff --git
a/integration-tests-jvm/pqc/src/main/java/org/apache/camel/quarkus/component/pqc/it/PqcResource.java
b/integration-tests-jvm/pqc/src/main/java/org/apache/camel/quarkus/component/pqc/it/PqcResource.java
deleted file mode 100644
index 3824d6ceba..0000000000
---
a/integration-tests-jvm/pqc/src/main/java/org/apache/camel/quarkus/component/pqc/it/PqcResource.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.quarkus.component.pqc.it;
-
-import jakarta.enterprise.context.ApplicationScoped;
-import jakarta.inject.Inject;
-import jakarta.ws.rs.GET;
-import jakarta.ws.rs.Path;
-import jakarta.ws.rs.Produces;
-import jakarta.ws.rs.core.MediaType;
-import jakarta.ws.rs.core.Response;
-import org.apache.camel.CamelContext;
-import org.jboss.logging.Logger;
-
-@Path("/pqc")
-@ApplicationScoped
-public class PqcResource {
-
- private static final Logger LOG = Logger.getLogger(PqcResource.class);
-
- private static final String COMPONENT_PQC = "pqc";
- @Inject
- CamelContext context;
-
- @Path("/load/component/pqc")
- @GET
- @Produces(MediaType.TEXT_PLAIN)
- public Response loadComponentPqc() throws Exception {
- /* This is an autogenerated test */
- if (context.getComponent(COMPONENT_PQC) != null) {
- return Response.ok().build();
- }
- LOG.warnf("Could not load [%s] from the Camel context", COMPONENT_PQC);
- return Response.status(500, COMPONENT_PQC + " could not be loaded from
the Camel context").build();
- }
-}
diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml
index 7f277bbdef..b9d959a23a 100644
--- a/integration-tests/pom.xml
+++ b/integration-tests/pom.xml
@@ -206,6 +206,7 @@
<module>platform-http</module>
<module>platform-http-proxy</module>
<module>platform-http-proxy-ssl</module>
+ <module>pqc</module>
<module>protobuf</module>
<module>pubnub</module>
<module>qdrant</module>
diff --git a/integration-tests-jvm/pqc/pom.xml b/integration-tests/pqc/pom.xml
similarity index 72%
rename from integration-tests-jvm/pqc/pom.xml
rename to integration-tests/pqc/pom.xml
index eb554db9df..aa6451218f 100644
--- a/integration-tests-jvm/pqc/pom.xml
+++ b/integration-tests/pqc/pom.xml
@@ -39,6 +39,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy</artifactId>
</dependency>
+ <dependency>
+ <groupId>io.quarkus</groupId>
+ <artifactId>quarkus-resteasy-jackson</artifactId>
+ </dependency>
<!-- test dependencies -->
<dependency>
@@ -51,6 +55,11 @@
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<profiles>
@@ -78,5 +87,32 @@
</dependency>
</dependencies>
</profile>
+ <profile>
+ <id>native</id>
+ <activation>
+ <property>
+ <name>native</name>
+ </property>
+ </activation>
+ <properties>
+ <quarkus.native.enabled>true</quarkus.native.enabled>
+ </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>integration-test</goal>
+ <goal>verify</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
</profiles>
</project>
diff --git
a/integration-tests/pqc/src/main/java/org/apache/camel/quarkus/component/pqc/it/PqcProducers.java
b/integration-tests/pqc/src/main/java/org/apache/camel/quarkus/component/pqc/it/PqcProducers.java
new file mode 100644
index 0000000000..076b329a68
--- /dev/null
+++
b/integration-tests/pqc/src/main/java/org/apache/camel/quarkus/component/pqc/it/PqcProducers.java
@@ -0,0 +1,89 @@
+/*
+ * 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.quarkus.component.pqc.it;
+
+import java.security.*;
+import java.security.spec.AlgorithmParameterSpec;
+
+import jakarta.enterprise.inject.Produces;
+import jakarta.inject.Named;
+import jakarta.inject.Singleton;
+import org.bouncycastle.pqc.crypto.lms.LMOtsParameters;
+import org.bouncycastle.pqc.crypto.lms.LMSigParameters;
+import org.bouncycastle.pqc.jcajce.spec.*;
+
+@Singleton
+public class PqcProducers {
+
+ //not-static to avoid buildtime initialization
+ private final SecureRandom secureRandom = new SecureRandom();
+
+ @Produces
+ @Singleton
+ @Named("dilithiumKeyPair")
+ KeyPair dilithiumKeyPair() throws Exception {
+ return generateKeyPair("Dilithium", DilithiumParameterSpec.dilithium2);
+ }
+
+ @Produces
+ @Singleton
+ @Named("falconKeyPair")
+ public KeyPair falconKeyPair() throws Exception {
+ return generateKeyPair("Falcon", FalconParameterSpec.falcon_512);
+ }
+
+ @Produces
+ @Singleton
+ @Named("sphincsKeyPair")
+ public KeyPair sphincsKeyPair() throws Exception {
+ return generateKeyPair("SPHINCSPlus",
SPHINCSPlusParameterSpec.sha2_128f);
+ }
+
+ @Produces
+ @Singleton
+ @Named("lmsKeyPair")
+ public KeyPair lmsKeyPair() throws Exception {
+ return generateKeyPair("LMS", new
LMSParameterSpec(LMSigParameters.lms_sha256_n32_h5,
LMOtsParameters.sha256_n32_w4));
+ }
+
+ @Produces
+ @Singleton
+ @Named("xmssKeyPair")
+ public KeyPair xmssKeyPair() throws Exception {
+ return generateKeyPair("XMSS", XMSSParameterSpec.SHA2_10_256);
+ }
+
+ @Produces
+ @Singleton
+ @Named("kyberKeyPair")
+ public KeyPair kyberKeyPair() throws Exception {
+ return generateKeyPair("Kyber", KyberParameterSpec.kyber512);
+ }
+
+ @Produces
+ @Singleton
+ @Named("kyberWrongKeyPair") //second keypair for negative scenario
+ public KeyPair kyberWrongKeyPair() throws Exception {
+ return generateKeyPair("Kyber", KyberParameterSpec.kyber512);
+ }
+
+ private KeyPair generateKeyPair(String algorithm, AlgorithmParameterSpec
spec) throws Exception {
+ KeyPairGenerator gen = KeyPairGenerator.getInstance(algorithm,
"BCPQC");
+ gen.initialize(spec, secureRandom);
+ return gen.generateKeyPair();
+ }
+}
diff --git
a/integration-tests/pqc/src/main/java/org/apache/camel/quarkus/component/pqc/it/PqcResource.java
b/integration-tests/pqc/src/main/java/org/apache/camel/quarkus/component/pqc/it/PqcResource.java
new file mode 100644
index 0000000000..5fdc95c8c0
--- /dev/null
+++
b/integration-tests/pqc/src/main/java/org/apache/camel/quarkus/component/pqc/it/PqcResource.java
@@ -0,0 +1,187 @@
+/*
+ * 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.quarkus.component.pqc.it;
+
+import java.nio.charset.StandardCharsets;
+import java.security.KeyPair;
+import java.security.Security;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.crypto.spec.SecretKeySpec;
+
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import jakarta.inject.Named;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.core.MediaType;
+import org.apache.camel.Exchange;
+import org.apache.camel.ProducerTemplate;
+import org.bouncycastle.jcajce.SecretKeyWithEncapsulation;
+import org.jboss.logging.Logger;
+
+@Path("/pqc")
+@ApplicationScoped
+public class PqcResource {
+
+ private static final Logger LOG = Logger.getLogger(PqcResource.class);;
+
+ @Inject
+ @Named("kyberKeyPair")
+ KeyPair kyberKeyPair;
+
+ @Inject
+ ProducerTemplate producerTemplate;
+
+ @Path("/sign/{algorithm}")
+ @POST
+ @Consumes(MediaType.TEXT_PLAIN)
+ @jakarta.ws.rs.Produces(MediaType.TEXT_PLAIN)
+ public String sign(String message, @PathParam("algorithm") String
algorithm) {
+ Exchange exchange = producerTemplate.request(
+
"pqc:sign?operation=sign&signatureAlgorithm=%s&keyPair=%s".formatted(algorithm,
toKeyPair(algorithm)),
+ ex ->
ex.getIn().setBody(message.getBytes(StandardCharsets.UTF_8)));
+
+ // The sign operation outputs signature in the HEADER, not the body
+ byte[] signature =
exchange.getMessage().getHeader("CamelPQCSignature", byte[].class);
+ return Base64.getEncoder().encodeToString(signature);
+ }
+
+ @Path("/verify/{algorithm}/{message}")
+ @POST
+ @Consumes(MediaType.TEXT_PLAIN)
+ @jakarta.ws.rs.Produces(MediaType.TEXT_PLAIN)
+ public boolean verify(String signature,
+ @PathParam("algorithm") String algorithm,
+ @PathParam("message") String message) {
+ byte[] signatureBytes = Base64.getDecoder().decode(signature);
+
+ Map<String, Object> headers = new HashMap<>();
+ headers.put("CamelPQCSignature", signatureBytes);
+
+ Exchange exchange = producerTemplate.request(
+
"pqc:verify?operation=verify&signatureAlgorithm=%s&keyPair=%s".formatted(algorithm,
toKeyPair(algorithm)),
+ ex -> {
+
ex.getIn().setBody(message.getBytes(StandardCharsets.UTF_8));
+ ex.getIn().setHeaders(headers);
+ });
+
+ Object verification =
exchange.getMessage().getHeader("CamelPQCVerification");
+ return Boolean.TRUE.equals(verification);
+ }
+
+ @Path("/kem/encapsulate/{algorithm}/{keyAlgorithm}/{length}")
+ @POST
+ @jakarta.ws.rs.Produces(MediaType.APPLICATION_JSON)
+ public Map<String, String> encapsulate(@PathParam("algorithm") String
algorithm,
+ @PathParam("keyAlgorithm") String keyAlgorithm,
+ @PathParam("length") int length) {
+ SecretKeyWithEncapsulation secretKeyWithEncapsulation =
producerTemplate.requestBody(
+
"pqc:encapsulate?operation=generateSecretKeyEncapsulation&keyEncapsulationAlgorithm=%s&symmetricKeyAlgorithm=%s&symmetricKeyLength=%s&keyPair=%s"
+ .formatted(algorithm, keyAlgorithm, length,
toKeyPair(algorithm)),
+ null,
+ SecretKeyWithEncapsulation.class);
+ String enc =
Base64.getEncoder().encodeToString(secretKeyWithEncapsulation.getEncapsulation());
+ String secret =
Base64.getEncoder().encodeToString(secretKeyWithEncapsulation.getEncoded());
+ return Map.of("enc", enc, "secret", secret);
+ }
+
+ @Path("/kem/extract/{algorithm}/{keyAlgorithm}/{length}")
+ @POST
+ @Consumes(MediaType.TEXT_PLAIN)
+ @jakarta.ws.rs.Produces(MediaType.TEXT_PLAIN)
+ public String extract(String enc,
+ @PathParam("algorithm") String algorithm,
+ @PathParam("keyAlgorithm") String keyAlgorithm,
+ @PathParam("length") int length) {
+
+ byte[] encapsulation = Base64.getDecoder().decode(enc);
+ //create SecretKeyWithEncapsulation from the private key
+ SecretKeyWithEncapsulation privateKeyWithEncapsulation = new
SecretKeyWithEncapsulation(
+ new SecretKeySpec(kyberKeyPair.getPrivate().getEncoded(),
algorithm), encapsulation);
+
+ SecretKeyWithEncapsulation result = producerTemplate.requestBody(
+
"pqc:extract?operation=extractSecretKeyEncapsulation&keyEncapsulationAlgorithm=%s&symmetricKeyAlgorithm=%s&symmetricKeyLength=%s&keyPair=%s"
+ .formatted(algorithm, keyAlgorithm, length,
toKeyPair(algorithm)),
+ privateKeyWithEncapsulation,
+ SecretKeyWithEncapsulation.class);
+ return Base64.getEncoder().encodeToString(result.getEncoded());
+ }
+
+ private String toKeyPair(String algorithm) {
+ return "SPHINCSPLUS".equals(algorithm) ? "#sphincsKeyPair" : "#" +
algorithm.toLowerCase() + "KeyPair";
+ }
+
+ // Body tests: binary data
+ @Path("/signBinaryData")
+ @POST
+ @jakarta.ws.rs.Produces(MediaType.TEXT_PLAIN)
+ public String signWithBinaryData(String message) {
+ byte[] binaryData = message.getBytes(StandardCharsets.UTF_8);
+
+ Exchange exchange = producerTemplate.request(
+
"pqc:sign?operation=sign&signatureAlgorithm=DILITHIUM&keyPair=#dilithiumKeyPair",
+ ex -> ex.getIn().setBody(binaryData));
+
+ byte[] signature =
exchange.getMessage().getHeader("CamelPQCSignature", byte[].class);
+ // Store binary data for verification
+ exchange.getIn().setHeader("CamelTestBinaryData", binaryData);
+ return Base64.getEncoder().encodeToString(signature);
+ }
+
+ @Path("/verifyBinaryData/{message}")
+ @POST
+ @Consumes(MediaType.TEXT_PLAIN)
+ @jakarta.ws.rs.Produces(MediaType.TEXT_PLAIN)
+ public boolean verifyWithBinaryData(String signature,
+ @PathParam("message") String message) {
+ byte[] signatureBytes = Base64.getDecoder().decode(signature);
+ byte[] binaryData = message.getBytes(StandardCharsets.UTF_8);
+
+ Map<String, Object> headers = new HashMap<>();
+ headers.put("CamelPQCSignature", signatureBytes);
+
+ Exchange exchange = producerTemplate.request(
+
"pqc:verify?operation=verify&signatureAlgorithm=DILITHIUM&keyPair=#dilithiumKeyPair",
+ ex -> {
+ ex.getIn().setBody(binaryData);
+ ex.getIn().setHeaders(headers);
+ });
+
+ Object verification =
exchange.getMessage().getHeader("CamelPQCVerification");
+ return Boolean.TRUE.equals(verification);
+ }
+
+ // Native mode test: check provider
+ @Path("/provider/check")
+ @POST
+ @jakarta.ws.rs.Produces(MediaType.TEXT_PLAIN)
+ public String checkProvider() {
+ java.security.Provider[] providers = Security.getProviders();
+ for (java.security.Provider provider : providers) {
+ if ("BCPQC".equals(provider.getName())) {
+ return "available";
+ }
+ }
+ return "unavailable";
+ }
+
+}
diff --git
a/integration-tests-jvm/pqc/src/test/java/org/apache/camel/quarkus/component/pqc/it/PqcTest.java
b/integration-tests/pqc/src/test/java/org/apache/camel/quarkus/component/pqc/it/PqcIT.java
similarity index 71%
rename from
integration-tests-jvm/pqc/src/test/java/org/apache/camel/quarkus/component/pqc/it/PqcTest.java
rename to
integration-tests/pqc/src/test/java/org/apache/camel/quarkus/component/pqc/it/PqcIT.java
index ff955411d3..e156d72964 100644
---
a/integration-tests-jvm/pqc/src/test/java/org/apache/camel/quarkus/component/pqc/it/PqcTest.java
+++
b/integration-tests/pqc/src/test/java/org/apache/camel/quarkus/component/pqc/it/PqcIT.java
@@ -16,19 +16,8 @@
*/
package org.apache.camel.quarkus.component.pqc.it;
-import io.quarkus.test.junit.QuarkusTest;
-import io.restassured.RestAssured;
-import org.junit.jupiter.api.Test;
-
-@QuarkusTest
-class PqcTest {
-
- @Test
- public void loadComponentPqc() {
- /* A simple autogenerated test */
- RestAssured.get("/pqc/load/component/pqc")
- .then()
- .statusCode(200);
- }
+import io.quarkus.test.junit.QuarkusIntegrationTest;
+@QuarkusIntegrationTest
+class PqcIT extends PqcTest {
}
diff --git
a/integration-tests/pqc/src/test/java/org/apache/camel/quarkus/component/pqc/it/PqcTest.java
b/integration-tests/pqc/src/test/java/org/apache/camel/quarkus/component/pqc/it/PqcTest.java
new file mode 100644
index 0000000000..3acece8d4d
--- /dev/null
+++
b/integration-tests/pqc/src/test/java/org/apache/camel/quarkus/component/pqc/it/PqcTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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.quarkus.component.pqc.it;
+
+import java.util.Map;
+import java.util.stream.Stream;
+
+import io.quarkus.test.junit.QuarkusTest;
+import io.restassured.RestAssured;
+import io.restassured.http.ContentType;
+import org.apache.camel.component.pqc.PQCKeyEncapsulationAlgorithms;
+import org.apache.camel.component.pqc.PQCSignatureAlgorithms;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.EnumSource;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+@QuarkusTest
+class PqcTest {
+
+ @ParameterizedTest
+ @EnumSource(value = PQCSignatureAlgorithms.class, names = { "FALCON",
"DILITHIUM", "SPHINCSPLUS", "XMSS", "LMS" })
+ public void testSignAndVerify(PQCSignatureAlgorithms signatureAlgorithm) {
+ // Sign operation using Falcon algorithm
+ String signature = RestAssured
+ .given()
+ .contentType(ContentType.TEXT)
+ .body("Hello")
+ .post("/pqc/sign/" + signatureAlgorithm.getAlgorithm())
+ .then()
+ .statusCode(200)
+ .extract()
+ .asString();
+
+ assertNotNull(signature);
+ assertFalse(signature.isEmpty());
+
+ // Verify operation using Falcon algorithm
+ RestAssured.given()
+ .contentType("text/plain")
+ .body(signature)
+ .post("/pqc/verify/" + signatureAlgorithm.getAlgorithm() +
"/Hello")
+ .then()
+ .statusCode(200)
+ .body(equalTo("true"));
+ }
+
+ @Test
+ public void testNegativeSignAndVerify() {
+ // Sign operation using Falcon algorithm
+ String signature = RestAssured
+ .given()
+ .contentType(ContentType.TEXT)
+ .body("Hello")
+ .post("/pqc/sign/" +
PQCSignatureAlgorithms.DILITHIUM.getAlgorithm())
+ .then()
+ .statusCode(200)
+ .extract()
+ .asString();
+
+ assertThat(signature).isNotBlank();
+
+ // Verify operation using Falcon algorithm
+ RestAssured.given()
+ .contentType("text/plain")
+ .body(signature)
+ .post("/pqc/verify/" +
PQCSignatureAlgorithms.DILITHIUM.getAlgorithm() + "/Wrong_text")
+ .then()
+ .statusCode(200)
+ .body(equalTo("false"));
+ }
+
+ static Stream<Arguments> encapsulationAlgorithms() {
+ return Stream.of(
+ Arguments.of(PQCKeyEncapsulationAlgorithms.KYBER, "AES", 128),
+ Arguments.of(PQCKeyEncapsulationAlgorithms.KYBER,
"CHACHA7539", 256));
+ }
+
+ @ParameterizedTest
+ @MethodSource("encapsulationAlgorithms")
+ public void testEncapsulationAndExtract(PQCKeyEncapsulationAlgorithms
encapsulationAlgorithm, String keyAlgorithm,
+ int length) {
+
+ // Generate encapsulation using Camel PQC component
+ Map<?, ?> map = RestAssured.given()
+ .contentType(ContentType.JSON)
+ .post("/pqc/kem/encapsulate/" +
encapsulationAlgorithm.getAlgorithm() + "/" + keyAlgorithm + "/" + length)
+ .then()
+ .statusCode(200)
+ .body(notNullValue())
+ .body("secret", notNullValue())
+ .body("enc", notNullValue())
+ .extract()
+ .as(Map.class);
+
+ // Extract secret key from encapsulation using Camel PQC component
+ RestAssured.given()
+ .contentType("text/plain")
+ .body(map.get("enc"))
+ .post("/pqc/kem/extract/" +
encapsulationAlgorithm.getAlgorithm() + "/" + keyAlgorithm + "/" + length)
+ .then()
+ .statusCode(200)
+ .body(equalTo(map.get("secret")));
+ }
+
+ @Test
+ public void testSignVerifyWithBinaryData() {
+ // Sign and verify with binary data
+ String signature = RestAssured.given()
+ .body("hello")
+ .post("/pqc/signBinaryData")
+ .then()
+ .statusCode(200)
+ .extract()
+ .asString();
+
+ assertNotNull(signature);
+
+ // Verify with same binary data
+ RestAssured.given()
+ .contentType("text/plain")
+ .body(signature)
+ .post("/pqc/verifyBinaryData/hello")
+ .then()
+ .statusCode(200)
+ .body(equalTo("true"));
+ }
+
+ @Test
+ public void testBouncyCastleProviderAvailable() {
+ // Verify BouncyCastlePQCProvider is registered in Security providers
+ RestAssured.post("/pqc/provider/check")
+ .then()
+ .statusCode(200)
+ .body(equalTo("available"));
+ }
+
+}
diff --git a/tooling/scripts/test-categories.yaml
b/tooling/scripts/test-categories.yaml
index 46575624b7..790327c7a6 100644
--- a/tooling/scripts/test-categories.yaml
+++ b/tooling/scripts/test-categories.yaml
@@ -153,6 +153,7 @@ group-08:
- master-file
- pdf
- pinecone
+ - pqc
- saxon
- syndication
- telegram