From fcca30e91ea474c2318cdf5454e4ae8475319e13 Mon Sep 17 00:00:00 2001 From: yuhh0328 Date: Mon, 8 Apr 2024 06:50:11 +0000 Subject: [PATCH 1/5] add ec-mlkem hybrid --- .../jsse/provider/NamedGroupInfo.java | 4 + .../java/org/bouncycastle/tls/NamedGroup.java | 17 +++- .../tls/crypto/impl/bc/BcTlsCrypto.java | 10 ++- .../tls/crypto/impl/bc/BcTlsECDomain.java | 13 +++ .../tls/crypto/impl/bc/BcTlsEcdhMlkem.java | 77 ++++++++++++++++++ .../crypto/impl/bc/BcTlsEcdhMlkemDomain.java | 63 +++++++++++++++ .../tls/crypto/impl/bc/BcTlsMLKem.java | 2 +- .../tls/crypto/impl/bc/BcTlsMLKemDomain.java | 8 +- .../tls/crypto/impl/jcajce/JcaTlsCrypto.java | 16 +++- .../crypto/impl/jcajce/JceTlsECDomain.java | 21 +++++ .../crypto/impl/jcajce/JceTlsEcdhMlkem.java | 79 +++++++++++++++++++ .../impl/jcajce/JceTlsEcdhMlkemDomain.java | 72 +++++++++++++++++ .../tls/crypto/impl/jcajce/JceTlsMLKem.java | 2 +- .../crypto/impl/jcajce/JceTlsMLKemDomain.java | 8 +- 14 files changed, 381 insertions(+), 11 deletions(-) create mode 100644 tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsEcdhMlkem.java create mode 100644 tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsEcdhMlkemDomain.java create mode 100644 tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsEcdhMlkem.java create mode 100644 tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsEcdhMlkemDomain.java diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/NamedGroupInfo.java b/tls/src/main/java/org/bouncycastle/jsse/provider/NamedGroupInfo.java index 18611a9294..c71e37d338 100644 --- a/tls/src/main/java/org/bouncycastle/jsse/provider/NamedGroupInfo.java +++ b/tls/src/main/java/org/bouncycastle/jsse/provider/NamedGroupInfo.java @@ -78,6 +78,10 @@ private enum All OQS_mlkem512(NamedGroup.OQS_mlkem512, "ML-KEM"), OQS_mlkem768(NamedGroup.OQS_mlkem768, "ML-KEM"), OQS_mlkem1024(NamedGroup.OQS_mlkem1024, "ML-KEM"), + OQS_secp256Mlkem512(NamedGroup.OQS_secp256Mlkem512, "ML-KEM"), + OQS_secp384Mlkem768(NamedGroup.OQS_secp384Mlkem768, "ML-KEM"), + OQS_secp521Mlkem1024(NamedGroup.OQS_secp521Mlkem1024, "ML-KEM"), + DRAFT_mlkem768(NamedGroup.DRAFT_mlkem768, "ML-KEM"), DRAFT_mlkem1024(NamedGroup.DRAFT_mlkem1024, "ML-KEM"); diff --git a/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java b/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java index 2e4bf4d510..e4c3d3fe4e 100644 --- a/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java +++ b/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java @@ -107,7 +107,13 @@ public class NamedGroup /** Experimental API (unstable): unofficial value from Open Quantum Safe project. */ public static final int OQS_mlkem768 = 0x0248; /** Experimental API (unstable): unofficial value from Open Quantum Safe project. */ - public static final int OQS_mlkem1024 = 0x0249; + public static final int OQS_mlkem1024 = 0x2F47; + /** Experimental API (unstable): unofficial value from Open Quantum Safe project. */ + public static final int OQS_secp256Mlkem512 = 0x0249; + /** Experimental API (unstable): unofficial value from Open Quantum Safe project. */ + public static final int OQS_secp384Mlkem768 = 0x2F48; + /** Experimental API (unstable): unofficial value from Open Quantum Safe project. */ + public static final int OQS_secp521Mlkem1024 = 0x2F49; /* * draft-connolly-tls-mlkem-key-agreement-01 @@ -310,6 +316,12 @@ public static String getKemName(int namedGroup) case OQS_mlkem1024: case DRAFT_mlkem1024: return "ML-KEM-1024"; + case OQS_secp256Mlkem512: + return "secp256-ML-KEM-512"; + case OQS_secp384Mlkem768: + return "secp384-ML-KEM-768"; + case OQS_secp521Mlkem1024: + return "secp521-ML-KEM-1024"; default: return null; } @@ -497,6 +509,9 @@ public static boolean refersToASpecificKem(int namedGroup) case OQS_mlkem512: case OQS_mlkem768: case OQS_mlkem1024: + case OQS_secp256Mlkem512: + case OQS_secp384Mlkem768: + case OQS_secp521Mlkem1024: case DRAFT_mlkem768: case DRAFT_mlkem1024: return true; diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java index 5cf7cd07d9..95b107fc2a 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java @@ -215,7 +215,15 @@ public TlsECDomain createECDomain(TlsECConfig ecConfig) public TlsKemDomain createKemDomain(TlsKemConfig kemConfig) { - return new BcTlsMLKemDomain(this, kemConfig); + switch (kemConfig.getNamedGroup()) + { + case NamedGroup.OQS_secp256Mlkem512: + case NamedGroup.OQS_secp384Mlkem768: + case NamedGroup.OQS_secp521Mlkem1024: + return new BcTlsEcdhMlkemDomain(this, kemConfig); + default: + return new BcTlsMLKemDomain(this, kemConfig); + } } public TlsNonceGenerator createNonceGenerator(byte[] additionalSeedMaterial) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsECDomain.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsECDomain.java index 0562963d06..ddd383018d 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsECDomain.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsECDomain.java @@ -27,6 +27,19 @@ */ public class BcTlsECDomain implements TlsECDomain { + public int getPublicKeyByteLength() + { + return (((domainParameters.getCurve().getFieldSize() + 7) / 8) * 2) + 1; + } + + public byte[] calculateECDHAgreementBytes(ECPrivateKeyParameters privateKey, ECPublicKeyParameters publicKey) + { + ECDHBasicAgreement basicAgreement = new ECDHBasicAgreement(); + basicAgreement.init(privateKey); + BigInteger agreementValue = basicAgreement.calculateAgreement(publicKey); + return BigIntegers.asUnsignedByteArray(basicAgreement.getFieldSize(), agreementValue); + } + public static BcTlsSecret calculateECDHAgreement(BcTlsCrypto crypto, ECPrivateKeyParameters privateKey, ECPublicKeyParameters publicKey) { diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsEcdhMlkem.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsEcdhMlkem.java new file mode 100644 index 0000000000..00427ee1ee --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsEcdhMlkem.java @@ -0,0 +1,77 @@ +package org.bouncycastle.tls.crypto.impl.bc; + +import java.io.IOException; + +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.SecretWithEncapsulation; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.pqc.crypto.crystals.kyber.KyberPrivateKeyParameters; +import org.bouncycastle.pqc.crypto.crystals.kyber.KyberPublicKeyParameters; +import org.bouncycastle.tls.crypto.TlsAgreement; +import org.bouncycastle.tls.crypto.TlsSecret; +import org.bouncycastle.util.Arrays; + +public class BcTlsEcdhMlkem implements TlsAgreement +{ + protected final BcTlsEcdhMlkemDomain domain; + + protected AsymmetricCipherKeyPair ecLocalKeyPair; + protected ECPublicKeyParameters ecPeerPublicKey; + protected AsymmetricCipherKeyPair kyberLocalKeyPair; + protected KyberPublicKeyParameters kyberPeerPublicKey; + protected byte[] kyberCiphertext; + protected byte[] kyberSecret; + protected TlsSecret secret; + + public BcTlsEcdhMlkem(BcTlsEcdhMlkemDomain domain) + { + this.domain = domain; + } + + public byte[] generateEphemeral() throws IOException + { + this.ecLocalKeyPair = domain.getEcDomain().generateKeyPair(); + byte[] ecPublickey = domain.getEcDomain().encodePublicKey((ECPublicKeyParameters)ecLocalKeyPair.getPublic()); + if (domain.isServer()) + { + return Arrays.concatenate(ecPublickey, kyberCiphertext); + } + else + { + this.kyberLocalKeyPair = domain.getMlkemDomain().generateKeyPair(); + byte[] kyberPublicKey = domain.getMlkemDomain().encodePublicKey((KyberPublicKeyParameters)kyberLocalKeyPair.getPublic()); + return Arrays.concatenate(ecPublickey, kyberPublicKey); + } + } + + public void receivePeerValue(byte[] peerValue) throws IOException + { + this.ecPeerPublicKey = domain.getEcDomain().decodePublicKey(Arrays.copyOf(peerValue, domain.getEcDomain().getPublicKeyByteLength())); + byte[] kyberValue = Arrays.copyOfRange(peerValue, domain.getEcDomain().getPublicKeyByteLength(), peerValue.length); + if (domain.isServer()) + { + this.kyberPeerPublicKey = domain.getMlkemDomain().decodePublicKey(kyberValue); + SecretWithEncapsulation encap = domain.getMlkemDomain().encapsulate(kyberPeerPublicKey); + kyberCiphertext = encap.getEncapsulation(); + kyberSecret = encap.getSecret(); + } + else + { + this.kyberCiphertext = Arrays.clone(kyberValue); + } + } + + public TlsSecret calculateSecret() throws IOException + { + byte[] ecSecret = domain.getEcDomain().calculateECDHAgreementBytes((ECPrivateKeyParameters)ecLocalKeyPair.getPrivate(), ecPeerPublicKey); + if (domain.isServer()) + { + } + else + { + kyberSecret = domain.getMlkemDomain().decapsulate((KyberPrivateKeyParameters) kyberLocalKeyPair.getPrivate(), kyberCiphertext); + } + return domain.adoptLocalSecret(Arrays.concatenate(ecSecret, kyberSecret)); + } +} diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsEcdhMlkemDomain.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsEcdhMlkemDomain.java new file mode 100644 index 0000000000..df44ec018a --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsEcdhMlkemDomain.java @@ -0,0 +1,63 @@ +package org.bouncycastle.tls.crypto.impl.bc; + +import org.bouncycastle.tls.NamedGroup; +import org.bouncycastle.tls.crypto.TlsAgreement; +import org.bouncycastle.tls.crypto.TlsECConfig; +import org.bouncycastle.tls.crypto.TlsKemConfig; +import org.bouncycastle.tls.crypto.TlsKemDomain; + +public class BcTlsEcdhMlkemDomain implements TlsKemDomain +{ + protected final BcTlsCrypto crypto; + protected final boolean isServer; + private final BcTlsECDomain ecDomain; + private final BcTlsMLKemDomain mlkemDomain; + + public BcTlsEcdhMlkemDomain(BcTlsCrypto crypto, TlsKemConfig kemConfig) + { + this.crypto = crypto; + this.ecDomain = getBcTlsECDomain(crypto, kemConfig); + this.mlkemDomain = new BcTlsMLKemDomain(crypto, kemConfig); + this.isServer = kemConfig.isServer(); + } + + public BcTlsSecret adoptLocalSecret(byte[] secret) + { + return crypto.adoptLocalSecret(secret); + } + + public TlsAgreement createKem() + { + return new BcTlsEcdhMlkem(this); + } + + public boolean isServer() + { + return isServer; + } + + public BcTlsECDomain getEcDomain() + { + return ecDomain; + } + + public BcTlsMLKemDomain getMlkemDomain() + { + return mlkemDomain; + } + + private BcTlsECDomain getBcTlsECDomain(BcTlsCrypto crypto, TlsKemConfig kemConfig) + { + switch (kemConfig.getNamedGroup()) + { + case NamedGroup.OQS_secp256Mlkem512: + return new BcTlsECDomain(crypto, new TlsECConfig(NamedGroup.secp256r1)); + case NamedGroup.OQS_secp384Mlkem768: + return new BcTlsECDomain(crypto, new TlsECConfig(NamedGroup.secp384r1)); + case NamedGroup.OQS_secp521Mlkem1024: + return new BcTlsECDomain(crypto, new TlsECConfig(NamedGroup.secp521r1)); + default: + return null; + } + } +} diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsMLKem.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsMLKem.java index 4d15220971..41d8a067f8 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsMLKem.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsMLKem.java @@ -47,7 +47,7 @@ public void receivePeerValue(byte[] peerValue) throws IOException } else { - this.secret = domain.decapsulate(privateKey, peerValue); + this.secret = domain.adoptLocalSecret(domain.decapsulate(privateKey, peerValue)); this.privateKey = null; } } diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsMLKemDomain.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsMLKemDomain.java index 2025e70b1a..7b5369eed2 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsMLKemDomain.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsMLKemDomain.java @@ -21,11 +21,14 @@ protected static KyberParameters getKyberParameters(int namedGroup) switch (namedGroup) { case NamedGroup.OQS_mlkem512: + case NamedGroup.OQS_secp256Mlkem512: return KyberParameters.kyber512; case NamedGroup.OQS_mlkem768: + case NamedGroup.OQS_secp384Mlkem768: case NamedGroup.DRAFT_mlkem768: return KyberParameters.kyber768; case NamedGroup.OQS_mlkem1024: + case NamedGroup.OQS_secp521Mlkem1024: case NamedGroup.DRAFT_mlkem1024: return KyberParameters.kyber1024; default: @@ -54,11 +57,10 @@ public TlsAgreement createKem() return new BcTlsMLKem(this); } - public BcTlsSecret decapsulate(KyberPrivateKeyParameters privateKey, byte[] ciphertext) + public byte[] decapsulate(KyberPrivateKeyParameters privateKey, byte[] ciphertext) { KyberKEMExtractor kemExtract = new KyberKEMExtractor(privateKey); - byte[] secret = kemExtract.extractSecret(ciphertext); - return adoptLocalSecret(secret); + return kemExtract.extractSecret(ciphertext); } public KyberPublicKeyParameters decodePublicKey(byte[] encoding) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java index 5c39e1a115..7347603b11 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java @@ -442,6 +442,12 @@ else if (NamedGroup.refersToASpecificKem(namedGroup)) { switch (namedGroup) { + case NamedGroup.OQS_secp256Mlkem512: + return ECUtil.getAlgorithmParameters(this, NamedGroup.getCurveName(NamedGroup.secp256r1)); + case NamedGroup.OQS_secp384Mlkem768: + return ECUtil.getAlgorithmParameters(this, NamedGroup.getCurveName(NamedGroup.secp384r1)); + case NamedGroup.OQS_secp521Mlkem1024: + return ECUtil.getAlgorithmParameters(this, NamedGroup.getCurveName(NamedGroup.secp521r1)); /* * TODO[tls-kem] Return AlgorithmParameters to check against disabled algorithms? */ @@ -848,7 +854,15 @@ public TlsECDomain createECDomain(TlsECConfig ecConfig) public TlsKemDomain createKemDomain(TlsKemConfig kemConfig) { - return new JceTlsMLKemDomain(this, kemConfig); + switch (kemConfig.getNamedGroup()) + { + case NamedGroup.OQS_secp256Mlkem512: + case NamedGroup.OQS_secp384Mlkem768: + case NamedGroup.OQS_secp521Mlkem1024: + return new JceTlsEcdhMlkemDomain(this, kemConfig); + default: + return new JceTlsMLKemDomain(this, kemConfig); + } } public TlsSecret hkdfInit(int cryptoHashAlgorithm) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsECDomain.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsECDomain.java index 8c332cbf04..5b683b04e8 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsECDomain.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsECDomain.java @@ -12,6 +12,9 @@ import java.security.spec.ECPublicKeySpec; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.crypto.agreement.ECDHBasicAgreement; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.tls.AlertDescription; @@ -21,6 +24,7 @@ import org.bouncycastle.tls.crypto.TlsCryptoException; import org.bouncycastle.tls.crypto.TlsECConfig; import org.bouncycastle.tls.crypto.TlsECDomain; +import org.bouncycastle.util.BigIntegers; /** * EC domain class for generating key pairs and performing key agreement. @@ -52,6 +56,23 @@ public JceTlsECDomain(JcaTlsCrypto crypto, TlsECConfig ecConfig) throw new IllegalArgumentException("NamedGroup not supported: " + NamedGroup.getText(namedGroup)); } + public int getPublicKeyByteLength() + { + return (((ecCurve.getFieldSize() + 7) / 8) * 2) + 1; + } + + public byte[] calculateECDHAgreementBytes(PrivateKey privateKey, PublicKey publicKey) throws IOException + { + try + { + return crypto.calculateKeyAgreement("ECDH", privateKey, publicKey, "TlsPremasterSecret"); + } + catch (GeneralSecurityException e) + { + throw new TlsCryptoException("cannot calculate secret", e); + } + } + public JceTlsSecret calculateECDHAgreement(PrivateKey privateKey, PublicKey publicKey) throws IOException { diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsEcdhMlkem.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsEcdhMlkem.java new file mode 100644 index 0000000000..1228ef48d3 --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsEcdhMlkem.java @@ -0,0 +1,79 @@ +package org.bouncycastle.tls.crypto.impl.jcajce; + +import java.io.IOException; +import java.security.KeyPair; +import java.security.PublicKey; + +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.SecretWithEncapsulation; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.pqc.crypto.crystals.kyber.KyberPrivateKeyParameters; +import org.bouncycastle.pqc.crypto.crystals.kyber.KyberPublicKeyParameters; +import org.bouncycastle.tls.crypto.TlsAgreement; +import org.bouncycastle.tls.crypto.TlsSecret; +import org.bouncycastle.util.Arrays; + +public class JceTlsEcdhMlkem implements TlsAgreement +{ + protected final JceTlsEcdhMlkemDomain domain; + + protected KeyPair ecLocalKeyPair; + protected PublicKey ecPeerPublicKey; + protected AsymmetricCipherKeyPair kyberLocalKeyPair; + protected KyberPublicKeyParameters kyberPeerPublicKey; + protected byte[] kyberCiphertext; + protected byte[] kyberSecret; + protected TlsSecret secret; + + public JceTlsEcdhMlkem(JceTlsEcdhMlkemDomain domain) + { + this.domain = domain; + } + + public byte[] generateEphemeral() throws IOException + { + this.ecLocalKeyPair = domain.getEcDomain().generateKeyPair(); + byte[] ecPublickey = domain.getEcDomain().encodePublicKey(ecLocalKeyPair.getPublic()); + if (domain.isServer()) + { + return Arrays.concatenate(ecPublickey, kyberCiphertext); + } + else + { + this.kyberLocalKeyPair = domain.getMlkemDomain().generateKeyPair(); + byte[] kyberPublicKey = domain.getMlkemDomain().encodePublicKey((KyberPublicKeyParameters)kyberLocalKeyPair.getPublic()); + return Arrays.concatenate(ecPublickey, kyberPublicKey); + } + } + + public void receivePeerValue(byte[] peerValue) throws IOException + { + this.ecPeerPublicKey = domain.getEcDomain().decodePublicKey(Arrays.copyOf(peerValue, domain.getEcDomain().getPublicKeyByteLength())); + byte[] kyberValue = Arrays.copyOfRange(peerValue, domain.getEcDomain().getPublicKeyByteLength(), peerValue.length); + if (domain.isServer()) + { + this.kyberPeerPublicKey = domain.getMlkemDomain().decodePublicKey(kyberValue); + SecretWithEncapsulation encap = domain.getMlkemDomain().encapsulate(kyberPeerPublicKey); + kyberCiphertext = encap.getEncapsulation(); + kyberSecret = encap.getSecret(); + } + else + { + this.kyberCiphertext = Arrays.clone(kyberValue); + } + } + + public TlsSecret calculateSecret() throws IOException + { + byte[] ecSecret = domain.getEcDomain().calculateECDHAgreementBytes(ecLocalKeyPair.getPrivate(), ecPeerPublicKey); + if (domain.isServer()) + { + } + else + { + kyberSecret = domain.getMlkemDomain().decapsulate((KyberPrivateKeyParameters) kyberLocalKeyPair.getPrivate(), kyberCiphertext); + } + return domain.adoptLocalSecret(Arrays.concatenate(ecSecret, kyberSecret)); + } +} diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsEcdhMlkemDomain.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsEcdhMlkemDomain.java new file mode 100644 index 0000000000..7fb19d1134 --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsEcdhMlkemDomain.java @@ -0,0 +1,72 @@ +package org.bouncycastle.tls.crypto.impl.jcajce; + +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.SecretWithEncapsulation; +import org.bouncycastle.pqc.crypto.crystals.kyber.KyberKEMExtractor; +import org.bouncycastle.pqc.crypto.crystals.kyber.KyberKEMGenerator; +import org.bouncycastle.pqc.crypto.crystals.kyber.KyberKeyGenerationParameters; +import org.bouncycastle.pqc.crypto.crystals.kyber.KyberKeyPairGenerator; +import org.bouncycastle.pqc.crypto.crystals.kyber.KyberParameters; +import org.bouncycastle.pqc.crypto.crystals.kyber.KyberPrivateKeyParameters; +import org.bouncycastle.pqc.crypto.crystals.kyber.KyberPublicKeyParameters; +import org.bouncycastle.tls.NamedGroup; +import org.bouncycastle.tls.crypto.TlsAgreement; +import org.bouncycastle.tls.crypto.TlsECConfig; +import org.bouncycastle.tls.crypto.TlsKemConfig; +import org.bouncycastle.tls.crypto.TlsKemDomain; + +public class JceTlsEcdhMlkemDomain implements TlsKemDomain +{ + protected final JcaTlsCrypto crypto; + protected final boolean isServer; + private final JceTlsECDomain ecDomain; + private final JceTlsMLKemDomain mlkemDomain; + + public JceTlsEcdhMlkemDomain(JcaTlsCrypto crypto, TlsKemConfig kemConfig) + { + this.crypto = crypto; + this.ecDomain = getJceTlsECDomain(crypto, kemConfig); + this.mlkemDomain = new JceTlsMLKemDomain(crypto, kemConfig); + this.isServer = kemConfig.isServer(); + } + + public JceTlsSecret adoptLocalSecret(byte[] secret) + { + return crypto.adoptLocalSecret(secret); + } + + public TlsAgreement createKem() + { + return new JceTlsEcdhMlkem(this); + } + + public boolean isServer() + { + return isServer; + } + + public JceTlsECDomain getEcDomain() + { + return ecDomain; + } + + public JceTlsMLKemDomain getMlkemDomain() + { + return mlkemDomain; + } + + private JceTlsECDomain getJceTlsECDomain(JcaTlsCrypto crypto, TlsKemConfig kemConfig) + { + switch (kemConfig.getNamedGroup()) + { + case NamedGroup.OQS_secp256Mlkem512: + return new JceTlsECDomain(crypto, new TlsECConfig(NamedGroup.secp256r1)); + case NamedGroup.OQS_secp384Mlkem768: + return new JceTlsECDomain(crypto, new TlsECConfig(NamedGroup.secp384r1)); + case NamedGroup.OQS_secp521Mlkem1024: + return new JceTlsECDomain(crypto, new TlsECConfig(NamedGroup.secp521r1)); + default: + return null; + } + } +} diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKem.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKem.java index 3a318e4f51..24d2176dcc 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKem.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKem.java @@ -47,7 +47,7 @@ public void receivePeerValue(byte[] peerValue) throws IOException } else { - this.secret = domain.decapsulate(privateKey, peerValue); + this.secret = domain.adoptLocalSecret(domain.decapsulate(privateKey, peerValue)); this.privateKey = null; } } diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKemDomain.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKemDomain.java index 7a67176231..85c4e8a6b4 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKemDomain.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKemDomain.java @@ -21,11 +21,14 @@ protected static KyberParameters getKyberParameters(int namedGroup) switch (namedGroup) { case NamedGroup.OQS_mlkem512: + case NamedGroup.OQS_secp256Mlkem512: return KyberParameters.kyber512; case NamedGroup.OQS_mlkem768: + case NamedGroup.OQS_secp384Mlkem768: case NamedGroup.DRAFT_mlkem768: return KyberParameters.kyber768; case NamedGroup.OQS_mlkem1024: + case NamedGroup.OQS_secp521Mlkem1024: case NamedGroup.DRAFT_mlkem1024: return KyberParameters.kyber1024; default: @@ -54,11 +57,10 @@ public TlsAgreement createKem() return new JceTlsMLKem(this); } - public JceTlsSecret decapsulate(KyberPrivateKeyParameters privateKey, byte[] ciphertext) + public byte[] decapsulate(KyberPrivateKeyParameters privateKey, byte[] ciphertext) { KyberKEMExtractor kemExtract = new KyberKEMExtractor(privateKey); - byte[] secret = kemExtract.extractSecret(ciphertext); - return adoptLocalSecret(secret); + return kemExtract.extractSecret(ciphertext); } public KyberPublicKeyParameters decodePublicKey(byte[] encoding) From eff0b751477a8ce7fbe45577571453aef29dcd00 Mon Sep 17 00:00:00 2001 From: yuhh0328 Date: Mon, 8 Apr 2024 06:57:37 +0000 Subject: [PATCH 2/5] add ec-mlkem hybrid --- tls/src/main/java/org/bouncycastle/tls/NamedGroup.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java b/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java index e4c3d3fe4e..18e4db324c 100644 --- a/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java +++ b/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java @@ -107,9 +107,9 @@ public class NamedGroup /** Experimental API (unstable): unofficial value from Open Quantum Safe project. */ public static final int OQS_mlkem768 = 0x0248; /** Experimental API (unstable): unofficial value from Open Quantum Safe project. */ - public static final int OQS_mlkem1024 = 0x2F47; + public static final int OQS_mlkem1024 = 0x0249; /** Experimental API (unstable): unofficial value from Open Quantum Safe project. */ - public static final int OQS_secp256Mlkem512 = 0x0249; + public static final int OQS_secp256Mlkem512 = 0x2F47; /** Experimental API (unstable): unofficial value from Open Quantum Safe project. */ public static final int OQS_secp384Mlkem768 = 0x2F48; /** Experimental API (unstable): unofficial value from Open Quantum Safe project. */ From 486c1eea8dbacf56eb9e3078eb7b37212d816b54 Mon Sep 17 00:00:00 2001 From: yuhh0328 Date: Mon, 8 Apr 2024 08:52:43 +0000 Subject: [PATCH 3/5] add ec-mlkem hybrid --- .../org/bouncycastle/tls/crypto/impl/bc/BcTlsEcdhMlkem.java | 5 +---- .../bouncycastle/tls/crypto/impl/jcajce/JceTlsEcdhMlkem.java | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsEcdhMlkem.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsEcdhMlkem.java index 00427ee1ee..6494c8acd6 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsEcdhMlkem.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsEcdhMlkem.java @@ -65,10 +65,7 @@ public void receivePeerValue(byte[] peerValue) throws IOException public TlsSecret calculateSecret() throws IOException { byte[] ecSecret = domain.getEcDomain().calculateECDHAgreementBytes((ECPrivateKeyParameters)ecLocalKeyPair.getPrivate(), ecPeerPublicKey); - if (domain.isServer()) - { - } - else + if (!domain.isServer()) { kyberSecret = domain.getMlkemDomain().decapsulate((KyberPrivateKeyParameters) kyberLocalKeyPair.getPrivate(), kyberCiphertext); } diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsEcdhMlkem.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsEcdhMlkem.java index 1228ef48d3..5b55c59030 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsEcdhMlkem.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsEcdhMlkem.java @@ -67,10 +67,7 @@ public void receivePeerValue(byte[] peerValue) throws IOException public TlsSecret calculateSecret() throws IOException { byte[] ecSecret = domain.getEcDomain().calculateECDHAgreementBytes(ecLocalKeyPair.getPrivate(), ecPeerPublicKey); - if (domain.isServer()) - { - } - else + if (!domain.isServer()) { kyberSecret = domain.getMlkemDomain().decapsulate((KyberPrivateKeyParameters) kyberLocalKeyPair.getPrivate(), kyberCiphertext); } From 8bbc2592295f607e1963bccc4ee2a3c793f25bb9 Mon Sep 17 00:00:00 2001 From: yuhh0328 Date: Mon, 8 Apr 2024 09:24:39 +0000 Subject: [PATCH 4/5] add ec-mlkem hybrid --- tls/src/main/java/org/bouncycastle/tls/NamedGroup.java | 8 +++++++- .../org/bouncycastle/jsse/provider/test/BasicTlsTest.java | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java b/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java index 18e4db324c..facf99faf1 100644 --- a/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java +++ b/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java @@ -388,7 +388,13 @@ public static String getName(int namedGroup) return "OQS_mlkem768"; case OQS_mlkem1024: return "OQS_mlkem1024"; - case DRAFT_mlkem768: + case OQS_secp256Mlkem512: + return "OQS_secp256Mlkem512"; + case OQS_secp384Mlkem768: + return "OQS_secp384Mlkem768"; + case OQS_secp521Mlkem1024: + return "OQS_secp521Mlkem1024"; + case DRAFT_mlkem768: return "DRAFT_mlkem768"; case DRAFT_mlkem1024: return "DRAFT_mlkem1024"; diff --git a/tls/src/test/java/org/bouncycastle/jsse/provider/test/BasicTlsTest.java b/tls/src/test/java/org/bouncycastle/jsse/provider/test/BasicTlsTest.java index 03f5bf58a7..341aeaed06 100644 --- a/tls/src/test/java/org/bouncycastle/jsse/provider/test/BasicTlsTest.java +++ b/tls/src/test/java/org/bouncycastle/jsse/provider/test/BasicTlsTest.java @@ -24,7 +24,7 @@ public class BasicTlsTest protected void setUp() { ProviderUtils.setupLowPriority(false); -// System.setProperty("jdk.tls.namedGroups", "kyber768"); + // System.setProperty("jdk.tls.namedGroups", "OQS_secp521Mlkem1024"); } private static final String HOST = "localhost"; From 4a4885efe4273821bb01561f5285bac10440e988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=ED=97=8C=ED=9D=AC/=EB=B3=B4=EC=95=88=EC=95=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=EC=A6=98Lab?= Date: Mon, 15 Apr 2024 08:52:23 +0900 Subject: [PATCH 5/5] remove unused import --- .../bouncycastle/tls/crypto/impl/jcajce/JceTlsECDomain.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsECDomain.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsECDomain.java index 5b683b04e8..d394618d2d 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsECDomain.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsECDomain.java @@ -12,9 +12,6 @@ import java.security.spec.ECPublicKeySpec; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; -import org.bouncycastle.crypto.agreement.ECDHBasicAgreement; -import org.bouncycastle.crypto.params.ECPrivateKeyParameters; -import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.tls.AlertDescription; @@ -24,7 +21,6 @@ import org.bouncycastle.tls.crypto.TlsCryptoException; import org.bouncycastle.tls.crypto.TlsECConfig; import org.bouncycastle.tls.crypto.TlsECDomain; -import org.bouncycastle.util.BigIntegers; /** * EC domain class for generating key pairs and performing key agreement.