-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Post-Quantum Cryptography (PQC) Algorithms for TLS 1.3 Key Exchange #1560
Changes from all commits
d7d7b2c
e0da08e
be62cbe
32997f7
431d8cc
889147e
7d587f0
e9eea59
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -102,6 +102,10 @@ public class NamedGroup | |
public static final int arbitrary_explicit_prime_curves = 0xFF01; | ||
public static final int arbitrary_explicit_char2_curves = 0xFF02; | ||
|
||
public static final int kyber512 = 0x023A; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned in #1578, the NIST round 3 implementation of Kyber is used in liboqs and FIPS implementation is used in BouncyCastle for the oids mentioned in this PR.
In liboqs, the FIPS implementation uses a separate OID and a separate name
Therefore to keep interoperability between BC and liboqs, we need to support both NIST3 and FIPS implementations for BC. Specially since both Cloudflare and Google chrome supports X25519Kyber768 which used NIST round 3 implementation of Kyber. It is important to support the older KEMs as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @hwupathum Note that that issue is stale. In 0.10.0, liboqs now includes draft ML-KEM in addition to round 3 Kyber. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, but as I mentioned the algorithm IDs are different between liboqs and BC. Therefore if I try to do a TLS handshake between a BC and liboqs with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @hwupathum Yes, I would expect the first to fail, because Round 3 Kyber and draft ML-DSA are different. The latter I would've expected to succeed. I'd suggest we should fix the compatibility for this case. I don't think support for Round 3 Kyber will remain once NIST publishes drafts. I'd imagine both Cloudflare and Google will update fairly quickly to the standardized (non-draft) ML-KEM implementation. |
||
public static final int kyber768 = 0x023C; | ||
public static final int kyber1024 = 0x023D; | ||
|
||
/* Names of the actual underlying elliptic curves (not necessarily matching the NamedGroup names). */ | ||
private static final String[] CURVE_NAMES = new String[] { "sect163k1", "sect163r1", "sect163r2", "sect193r1", | ||
"sect193r2", "sect233k1", "sect233r1", "sect239k1", "sect283k1", "sect283r1", "sect409k1", "sect409r1", | ||
|
@@ -130,7 +134,8 @@ public static boolean canBeNegotiated(int namedGroup, ProtocolVersion version) | |
else | ||
{ | ||
if ((namedGroup >= brainpoolP256r1tls13 && namedGroup <= brainpoolP512r1tls13) | ||
|| (namedGroup == curveSM2)) | ||
|| (namedGroup == curveSM2) | ||
|| (namedGroup == kyber512 || namedGroup == kyber768 || namedGroup == kyber1024)) | ||
{ | ||
return false; | ||
} | ||
|
@@ -260,6 +265,21 @@ public static String getFiniteFieldName(int namedGroup) | |
return null; | ||
} | ||
|
||
public static String getKEMName(int namedGroup) | ||
{ | ||
switch (namedGroup) | ||
{ | ||
case kyber512: | ||
return "kyber512"; | ||
case kyber768: | ||
return "kyber768"; | ||
case kyber1024: | ||
return "kyber1024"; | ||
default: | ||
return null; | ||
} | ||
} | ||
|
||
public static int getMaximumChar2CurveBits() | ||
{ | ||
return 571; | ||
|
@@ -344,6 +364,12 @@ public static String getStandardName(int namedGroup) | |
return finiteFieldName; | ||
} | ||
|
||
String kemName = getKEMName(namedGroup); | ||
if (null != kemName) | ||
{ | ||
return kemName; | ||
} | ||
|
||
return null; | ||
} | ||
|
||
|
@@ -412,9 +438,15 @@ public static boolean refersToASpecificFiniteField(int namedGroup) | |
return namedGroup >= ffdhe2048 && namedGroup <= ffdhe8192; | ||
} | ||
|
||
public static boolean refersToASpecificKEM(int namedGroup) | ||
{ | ||
return namedGroup == kyber512 || namedGroup == kyber768 || namedGroup == kyber1024; | ||
} | ||
|
||
public static boolean refersToASpecificGroup(int namedGroup) | ||
{ | ||
return refersToASpecificCurve(namedGroup) | ||
|| refersToASpecificFiniteField(namedGroup); | ||
|| refersToASpecificFiniteField(namedGroup) | ||
|| refersToASpecificKEM(namedGroup); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package org.bouncycastle.tls.crypto; | ||
|
||
public class TlsKEMConfig | ||
{ | ||
protected final int namedGroup; | ||
protected final TlsCryptoParameters cryptoParams; | ||
protected final int kemNamedGroup; | ||
|
||
public TlsKEMConfig(int namedGroup, TlsCryptoParameters cryptoParams) | ||
{ | ||
this.namedGroup = namedGroup; | ||
this.cryptoParams = cryptoParams; | ||
this.kemNamedGroup = getKEMNamedGroup(namedGroup); | ||
} | ||
|
||
public int getNamedGroup() | ||
{ | ||
return namedGroup; | ||
} | ||
|
||
public boolean isServer() | ||
{ | ||
return cryptoParams.isServer(); | ||
} | ||
|
||
public int getKEMNamedGroup() | ||
{ | ||
return kemNamedGroup; | ||
} | ||
|
||
private int getKEMNamedGroup(int namedGroup) | ||
{ | ||
return namedGroup; | ||
// switch (namedGroup) | ||
// { | ||
// case NamedGroup.kyber512: | ||
// case NamedGroup.secp256Kyber512: | ||
// case NamedGroup.x25519Kyber512: | ||
// return NamedGroup.kyber512; | ||
// case NamedGroup.kyber768: | ||
// case NamedGroup.secp384Kyber768: | ||
// case NamedGroup.x25519Kyber768: | ||
// case NamedGroup.x448Kyber768: | ||
// return NamedGroup.kyber768; | ||
// case NamedGroup.kyber1024: | ||
// case NamedGroup.secp521Kyber1024: | ||
// return NamedGroup.kyber1024; | ||
// default: | ||
// return namedGroup; | ||
// } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package org.bouncycastle.tls.crypto; | ||
|
||
public interface TlsKEMDomain | ||
{ | ||
TlsAgreement createKEM(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package org.bouncycastle.tls.crypto.impl.bc; | ||
|
||
import java.io.IOException; | ||
import org.bouncycastle.crypto.AsymmetricCipherKeyPair; | ||
import org.bouncycastle.crypto.SecretWithEncapsulation; | ||
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 BcTlsKyber implements TlsAgreement | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd rename this as well to BcTlsMlKem perhaps :-) (Comments apply below as well) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be good to drop a reference to https://datatracker.ietf.org/doc/draft-connolly-tls-mlkem-key-agreement/ as well. |
||
{ | ||
protected final BcTlsKyberDomain domain; | ||
|
||
protected AsymmetricCipherKeyPair localKeyPair; | ||
protected KyberPublicKeyParameters peerPublicKey; | ||
protected byte[] ciphertext; | ||
protected byte[] secret; | ||
|
||
public BcTlsKyber(BcTlsKyberDomain domain) | ||
{ | ||
this.domain = domain; | ||
} | ||
|
||
public byte[] generateEphemeral() throws IOException | ||
{ | ||
if (domain.getTlsKEMConfig().isServer()) | ||
{ | ||
return Arrays.clone(ciphertext); | ||
} | ||
else | ||
{ | ||
this.localKeyPair = domain.generateKeyPair(); | ||
return domain.encodePublicKey((KyberPublicKeyParameters)localKeyPair.getPublic()); | ||
} | ||
} | ||
|
||
public void receivePeerValue(byte[] peerValue) throws IOException | ||
{ | ||
if (domain.getTlsKEMConfig().isServer()) | ||
{ | ||
this.peerPublicKey = domain.decodePublicKey(peerValue); | ||
SecretWithEncapsulation encap = domain.enCap(peerPublicKey); | ||
ciphertext = encap.getEncapsulation(); | ||
secret = encap.getSecret(); | ||
} | ||
else | ||
{ | ||
this.ciphertext = Arrays.clone(peerValue); | ||
} | ||
} | ||
|
||
public TlsSecret calculateSecret() throws IOException | ||
{ | ||
if (domain.getTlsKEMConfig().isServer()) | ||
{ | ||
return domain.adoptLocalSecret(secret); | ||
} | ||
else | ||
{ | ||
return domain.adoptLocalSecret(domain.deCap((KyberPrivateKeyParameters)localKeyPair.getPrivate(), ciphertext)); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are incorrect. We only support ML-KEM (FIPS draft), not round 3 Kyber.