This is an automated email from the ASF dual-hosted git repository. acosentino pushed a commit to branch kem-kyber in repository https://gitbox.apache.org/repos/asf/camel.git
commit 633d18b4013bdc0ee0169819a04e83109fd3cf75 Author: Andrea Cosentino <anco...@gmail.com> AuthorDate: Fri Aug 29 17:31:40 2025 +0200 Camel-PQC: Added KYBER KEM algorithm Signed-off-by: Andrea Cosentino <anco...@gmail.com> --- Jenkinsfile | 4 +- .../apache/camel/component/pqc/PQCComponent.java | 13 +-- .../pqc/PQCKeyEncapsulationAlgorithms.java | 3 +- .../pqc/crypto/kem/PQCDefaultKYBERMaterial.java | 63 ++++++++++++ ...BERGenerateEncapsulationAESNoAutowiredTest.java | 89 +++++++++++++++++ .../pqc/PQCKYBERGenerateEncapsulationAESTest.java | 107 +++++++++++++++++++++ 6 files changed, 268 insertions(+), 11 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 091f55d8b0c..2e98f17ea35 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -73,7 +73,7 @@ pipeline { exclude { axis { name 'JDK_NAME' - values 'jdk_21_latest' + values 'jdk_21_latest', 'ibm-semeru-21.0.2_13' } axis { name 'PLATFORM' @@ -83,7 +83,7 @@ pipeline { exclude { axis { name 'JDK_NAME' - values 'jdk_21_latest' + values 'jdk_21_latest', 'ibm-semeru-21.0.2_13' } axis { name 'PLATFORM' diff --git a/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCComponent.java b/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCComponent.java index d023c31df76..c434b939936 100644 --- a/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCComponent.java +++ b/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCComponent.java @@ -21,14 +21,7 @@ import java.util.Map; import org.apache.camel.CamelContext; import org.apache.camel.Endpoint; import org.apache.camel.component.pqc.crypto.*; -import org.apache.camel.component.pqc.crypto.kem.PQCDefaultBIKEMaterial; -import org.apache.camel.component.pqc.crypto.kem.PQCDefaultCMCEMaterial; -import org.apache.camel.component.pqc.crypto.kem.PQCDefaultFRODOMaterial; -import org.apache.camel.component.pqc.crypto.kem.PQCDefaultHQCMaterial; -import org.apache.camel.component.pqc.crypto.kem.PQCDefaultMLKEMMaterial; -import org.apache.camel.component.pqc.crypto.kem.PQCDefaultNTRULPRimeMaterial; -import org.apache.camel.component.pqc.crypto.kem.PQCDefaultNTRUMaterial; -import org.apache.camel.component.pqc.crypto.kem.PQCDefaultSABERMaterial; +import org.apache.camel.component.pqc.crypto.kem.*; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.annotations.Component; import org.apache.camel.support.HealthCheckComponent; @@ -144,6 +137,10 @@ public class PQCComponent extends HealthCheckComponent { configuration.setKeyGenerator(PQCDefaultNTRULPRimeMaterial.keyGenerator); configuration.setKeyPair(PQCDefaultNTRULPRimeMaterial.keyPair); break; + case "KYBER": + configuration.setKeyGenerator(PQCDefaultKYBERMaterial.keyGenerator); + configuration.setKeyPair(PQCDefaultKYBERMaterial.keyPair); + break; default: break; } diff --git a/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCKeyEncapsulationAlgorithms.java b/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCKeyEncapsulationAlgorithms.java index a05fd594d42..5be590ba90f 100644 --- a/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCKeyEncapsulationAlgorithms.java +++ b/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/PQCKeyEncapsulationAlgorithms.java @@ -28,7 +28,8 @@ public enum PQCKeyEncapsulationAlgorithms { SABER("SABER", "BCPQC"), FRODO("FRODO", "BCPQC"), NTRU("NTRU", "BCPQC"), - NTRULPRime("NTRULPRime", "BCPQC"); + NTRULPRime("NTRULPRime", "BCPQC"), + KYBER("KYBER", "BCPQC"); private final String algorithm; private final String bcProvider; diff --git a/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/crypto/kem/PQCDefaultKYBERMaterial.java b/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/crypto/kem/PQCDefaultKYBERMaterial.java new file mode 100644 index 00000000000..aabd580a30a --- /dev/null +++ b/components/camel-pqc/src/main/java/org/apache/camel/component/pqc/crypto/kem/PQCDefaultKYBERMaterial.java @@ -0,0 +1,63 @@ +/* + * 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.crypto.kem; + +import java.security.*; + +import javax.crypto.KeyGenerator; + +import org.apache.camel.component.pqc.PQCKeyEncapsulationAlgorithms; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider; +import org.bouncycastle.pqc.jcajce.spec.KyberParameterSpec; + +public class PQCDefaultKYBERMaterial { + + public static final KeyPair keyPair; + public static final KeyGenerator keyGenerator; + public static final KeyPairGenerator generator; + + static { + if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { + Security.addProvider(new BouncyCastleProvider()); + } + if (Security.getProvider(BouncyCastlePQCProvider.PROVIDER_NAME) == null) { + Security.addProvider(new BouncyCastlePQCProvider()); + } + try { + generator = prepareKeyPair(); + keyPair = generator.generateKeyPair(); + keyGenerator = prepareKeyGenerator(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + protected static KeyPairGenerator prepareKeyPair() + throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException { + KeyPairGenerator kpg = KeyPairGenerator.getInstance(PQCKeyEncapsulationAlgorithms.KYBER.getAlgorithm(), + PQCKeyEncapsulationAlgorithms.KYBER.getBcProvider()); + kpg.initialize(KyberParameterSpec.kyber1024, new SecureRandom()); + return kpg; + } + + protected static KeyGenerator prepareKeyGenerator() throws NoSuchAlgorithmException, NoSuchProviderException { + KeyGenerator kg = KeyGenerator.getInstance(PQCKeyEncapsulationAlgorithms.KYBER.getAlgorithm(), + PQCKeyEncapsulationAlgorithms.KYBER.getBcProvider()); + return kg; + } +} diff --git a/components/camel-pqc/src/test/java/org/apache/camel/component/pqc/PQCKYBERGenerateEncapsulationAESNoAutowiredTest.java b/components/camel-pqc/src/test/java/org/apache/camel/component/pqc/PQCKYBERGenerateEncapsulationAESNoAutowiredTest.java new file mode 100644 index 00000000000..9a3941a6f86 --- /dev/null +++ b/components/camel-pqc/src/test/java/org/apache/camel/component/pqc/PQCKYBERGenerateEncapsulationAESNoAutowiredTest.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.component.pqc; + +import java.security.NoSuchAlgorithmException; +import java.security.Security; + +import org.apache.camel.EndpointInject; +import org.apache.camel.Produce; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit5.CamelTestSupport; +import org.bouncycastle.jcajce.SecretKeyWithEncapsulation; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider; +import org.bouncycastle.util.Arrays; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class PQCKYBERGenerateEncapsulationAESNoAutowiredTest extends CamelTestSupport { + + @EndpointInject("mock:encapsulate") + protected MockEndpoint resultEncapsulate; + + @Produce("direct:encapsulate") + protected ProducerTemplate templateEncapsulate; + + @EndpointInject("mock:extract") + protected MockEndpoint resultExtract; + + public PQCKYBERGenerateEncapsulationAESNoAutowiredTest() throws NoSuchAlgorithmException { + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("direct:encapsulate").to( + "pqc:keyenc?operation=generateSecretKeyEncapsulation&symmetricKeyAlgorithm=AES&keyEncapsulationAlgorithm=KYBER") + .to("mock:encapsulate") + .to("pqc:keyenc?operation=extractSecretKeyEncapsulation&symmetricKeyAlgorithm=AES&keyEncapsulationAlgorithm=KYBER") + .to("mock:extract"); + } + }; + } + + @BeforeAll + public static void startup() throws Exception { + Security.addProvider(new BouncyCastleProvider()); + Security.addProvider(new BouncyCastlePQCProvider()); + } + + @Test + void testSignAndVerify() throws Exception { + resultEncapsulate.expectedMessageCount(1); + resultExtract.expectedMessageCount(1); + templateEncapsulate.sendBody("Hello"); + resultEncapsulate.assertIsSatisfied(); + assertNotNull(resultEncapsulate.getExchanges().get(0).getMessage().getBody(SecretKeyWithEncapsulation.class)); + assertEquals(PQCSymmetricAlgorithms.AES.getAlgorithm(), + resultEncapsulate.getExchanges().get(0).getMessage().getBody(SecretKeyWithEncapsulation.class).getAlgorithm()); + SecretKeyWithEncapsulation secEncrypted + = resultEncapsulate.getExchanges().get(0).getMessage().getBody(SecretKeyWithEncapsulation.class); + assertNotNull(resultExtract.getExchanges().get(0).getMessage().getBody(SecretKeyWithEncapsulation.class)); + assertEquals(PQCSymmetricAlgorithms.AES.getAlgorithm(), + resultExtract.getExchanges().get(0).getMessage().getBody(SecretKeyWithEncapsulation.class).getAlgorithm()); + SecretKeyWithEncapsulation secEncryptedExtracted + = resultExtract.getExchanges().get(0).getMessage().getBody(SecretKeyWithEncapsulation.class); + assertTrue(Arrays.areEqual(secEncrypted.getEncoded(), secEncryptedExtracted.getEncoded())); + } +} diff --git a/components/camel-pqc/src/test/java/org/apache/camel/component/pqc/PQCKYBERGenerateEncapsulationAESTest.java b/components/camel-pqc/src/test/java/org/apache/camel/component/pqc/PQCKYBERGenerateEncapsulationAESTest.java new file mode 100644 index 00000000000..4c9fd972c3e --- /dev/null +++ b/components/camel-pqc/src/test/java/org/apache/camel/component/pqc/PQCKYBERGenerateEncapsulationAESTest.java @@ -0,0 +1,107 @@ +/* + * 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; + +import java.security.*; + +import javax.crypto.KeyGenerator; + +import org.apache.camel.BindToRegistry; +import org.apache.camel.EndpointInject; +import org.apache.camel.Produce; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.component.mock.MockEndpoint; +import org.apache.camel.test.junit5.CamelTestSupport; +import org.bouncycastle.jcajce.SecretKeyWithEncapsulation; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider; +import org.bouncycastle.pqc.jcajce.spec.KyberParameterSpec; +import org.bouncycastle.util.Arrays; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class PQCKYBERGenerateEncapsulationAESTest extends CamelTestSupport { + + @EndpointInject("mock:sign") + protected MockEndpoint resultSign; + + @Produce("direct:sign") + protected ProducerTemplate templateSign; + + @EndpointInject("mock:verify") + protected MockEndpoint resultVerify; + + public PQCKYBERGenerateEncapsulationAESTest() throws NoSuchAlgorithmException { + } + + @Override + protected RouteBuilder createRouteBuilder() { + return new RouteBuilder() { + @Override + public void configure() { + from("direct:sign").to("pqc:keyenc?operation=generateSecretKeyEncapsulation&symmetricKeyAlgorithm=AES") + .to("mock:sign") + .to("pqc:keyenc?operation=extractSecretKeyEncapsulation&symmetricKeyAlgorithm=AES").to("mock:verify"); + } + }; + } + + @BeforeAll + public static void startup() throws Exception { + Security.addProvider(new BouncyCastleProvider()); + Security.addProvider(new BouncyCastlePQCProvider()); + } + + @Test + void testSignAndVerify() throws Exception { + resultSign.expectedMessageCount(1); + resultVerify.expectedMessageCount(1); + templateSign.sendBody("Hello"); + resultSign.assertIsSatisfied(); + assertNotNull(resultSign.getExchanges().get(0).getMessage().getBody(SecretKeyWithEncapsulation.class)); + assertEquals(PQCSymmetricAlgorithms.AES.getAlgorithm(), + resultSign.getExchanges().get(0).getMessage().getBody(SecretKeyWithEncapsulation.class).getAlgorithm()); + SecretKeyWithEncapsulation secEncrypted + = resultSign.getExchanges().get(0).getMessage().getBody(SecretKeyWithEncapsulation.class); + assertNotNull(resultVerify.getExchanges().get(0).getMessage().getBody(SecretKeyWithEncapsulation.class)); + assertEquals(PQCSymmetricAlgorithms.AES.getAlgorithm(), + resultVerify.getExchanges().get(0).getMessage().getBody(SecretKeyWithEncapsulation.class).getAlgorithm()); + SecretKeyWithEncapsulation secEncryptedExtracted + = resultVerify.getExchanges().get(0).getMessage().getBody(SecretKeyWithEncapsulation.class); + assertTrue(Arrays.areEqual(secEncrypted.getEncoded(), secEncryptedExtracted.getEncoded())); + } + + @BindToRegistry("Keypair") + public KeyPair setKeyPair() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException { + KeyPairGenerator kpg = KeyPairGenerator.getInstance(PQCKeyEncapsulationAlgorithms.KYBER.getAlgorithm(), + PQCKeyEncapsulationAlgorithms.KYBER.getBcProvider()); + kpg.initialize(KyberParameterSpec.kyber1024, new SecureRandom()); + KeyPair kp = kpg.generateKeyPair(); + return kp; + } + + @BindToRegistry("KeyGenerator") + public KeyGenerator setKeyGenerator() + throws NoSuchAlgorithmException, NoSuchProviderException { + KeyGenerator kg = KeyGenerator.getInstance(PQCKeyEncapsulationAlgorithms.KYBER.getAlgorithm(), + PQCKeyEncapsulationAlgorithms.KYBER.getBcProvider()); + return kg; + } +}