Skip to content

Commit d401e82

Browse files
committed
Merge branch 'dtls13': DTLS 1.3 (RFC 9147) support in the low-level TLS API, relates to github #1468 and #1487.
Implements the unified record header with encrypted record numbers, the ACK-driven reliable handshake with selective retransmission, the certificate handshake with HelloRetryRequest and client authentication, KeyUpdate in both directions, exporters and DTLS-SRTP. Opt-in: a DTLS peer offers or accepts DTLS 1.3 only when its supported versions include ProtocolVersion.DTLSv13. Not yet implemented: PSK and resumption, 0-RTT, connection IDs, post-handshake client authentication, and the stateless cookie exchange (DTLSVerifier remains DTLS 1.2 only). Handshake flights of every DTLS version are now packed into as few datagrams as the send limit allows; TlsPeer.allowRecordPacking() turns that off. Also accepts a cookie-only HelloRetryRequest in both the TLS 1.3 and DTLS 1.3 clients. Based on the series by Paul Gregoire (#2439-#2442), with review fixes.
2 parents ceda97c + 7d3e71d commit d401e82

78 files changed

Lines changed: 19134 additions & 351 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎CONTRIBUTORS.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ We also wish to acknowledge financial and collaborative support from [CISCO](htt
477477
- Steffen Heil \<https://github.com/steffen-heil\> - correction to error handling in Arrays.getLength().
478478
- Thomas D. \<https://github.com/thomdev\> - expansion of HPKE APIs, addition of user controlled ephemeral key to HPKE API.
479479
- Franz Fangmeyer \<https://github.com/ff-wl\> - exception handling for improper use of '=' in an X.500 RDN.
480-
- Jonathan Lennox \<https://github.com/JonathanLennox\> - fix DTLS retransmission bug with aggregated ChangeCipherSpec.
480+
- Jonathan Lennox \<https://github.com/JonathanLennox\> - fix DTLS retransmission bug with aggregated ChangeCipherSpec. Use of the "dtls13" HKDF-Expand-Label prefix for the DTLS 1.3 key schedule (RFC 9147 sec. 5.9), making its traffic, record number, Finished and exporter keys interoperable (PR #2439).
481481
- Thomas Devanneaux \<tdevanneaux&#064;apple.com\> - extensions to the HPKE API to support encryption/decryption from byte ranges, allow sender selected ephemeral key.
482482
- Norman Maurer \<norman_maurer&#064;apple.com\> - extensions to the HPKE API to support encryption/decryption from byte ranges, allow sender selected ephemeral key.
483483
- Bing Shi \<roadicing&#064;gmail.com\> - addition of F2m bounds checking for imported EC F2m curves.
@@ -558,3 +558,4 @@ We also wish to acknowledge financial and collaborative support from [CISCO](htt
558558
- Kaya Emre Arikan \<https://github.com/kemrec\> - analysis of rfc822Name name constraint processing in PKIXCertPathReviewer, which found the subject DN e-mail address check missing and improved the accuracy of the certification path reviewer.
559559
- Tom Tervoort (Bureau Veritas Cybersecurity) \<tom.tervoort&#064;bureauveritas.com\> - analysis of RFC 3161 signature time-stamps, which led to the SigningCertificateVerifier and the CAdESSignatureTimestampUtil overload binding the signing certificate into time-stamp validation.
560560
- cipheraenys \<https://github.com/cipheraenys\> - initial implementation of same-Commit member replacement in MLS, letting an Add proposal keep the signature key and credential of a member a Remove in the same proposal list takes out of the tree, with the regression test behind it (PR #2505).
561+
- Paul Gregoire \<https://github.com/mondain\> - initial implementation of DTLS 1.3 (RFC 9147) in the low-level TLS API: the record layer with record number encryption, the ACK-driven reliable handshake with datagram packing, the client and server handshakes with HelloRetryRequest and client authentication, and the post-handshake KeyUpdate and acknowledgement machinery (PRs #2439, #2440, #2441, #2442).

‎ant/bc+-build.xml‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,10 @@
773773
<fileset dir="${src.dir}">
774774
<exclude name="**/*Test.java" />
775775
<exclude name="**/*Tests.java" />
776+
<!-- test helpers that live in the org.bouncycastle.tls package to reach package-private
777+
members (the DTLS 1.3 *TestSupport classes); they reference excluded *Test classes
778+
and must not be swept into the provider jar -->
779+
<exclude name="**/*TestSupport.java" />
776780
<exclude name="**/test/*.java" />
777781
<exclude name="**/test/**" />
778782
<include name="org/bouncycastle/tls/**/*.java" />
@@ -1017,6 +1021,9 @@
10171021
<fileset dir="${src.dir}" includes="**/crypto/*/*Test.java" />
10181022
<fileset dir="${src.dir}" includes="**/crypto/agreement/owl/*Test.java" />
10191023
<fileset dir="${src.dir}" includes="org/bouncycastle/tls/SpreadCertificateStatusTest.java" />
1024+
<!-- package-level helper the tls.test DTLS 1.3 tests need (reaches package-private members);
1025+
the bctls copy excludes every *TestSupport.java, so it has to come from here -->
1026+
<fileset dir="${src.dir}" includes="org/bouncycastle/tls/DTLS13KeyUpdateTestSupport.java" />
10201027
<fileset dir="${src.dir}" includes="org/bouncycastle/cert/dane/fetcher/JndiDANEFetcherFactoryTest.java" />
10211028
<!-- main-namespace test fixture the bcpg copy now excludes; the openpgp.api tests resolve it from here -->
10221029
<fileset dir="${src.dir}" includes="org/bouncycastle/openpgp/OpenPGPTestKeys.java" />

‎ant/jdk15+.xml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@
8989
<exclude name="**/tls/test/TlsClientRawKeysTest.java"/>
9090
<exclude name="**/tls/test/TlsServerRawKeysTest.java"/>
9191
</fileset>
92+
<!-- The **/tls/*.java exclude above drops the package-level test helpers; this one is needed by
93+
tls/test/DTLS13ProtocolTest (bc+-build.xml's build-test includes it in bctest). -->
94+
<fileset dir="tls/src/test/java" includes="org/bouncycastle/tls/DTLS13KeyUpdateTestSupport.java" />
9295

9396
<fileset dir="pkix/src/main/java" includes="**/*.java" />
9497
<fileset dir="pkix/src/main/javadoc" includes="**/*.html" />

‎docs/claude/build-jdk14.md‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,18 @@ compiled. The contamination half does *not* fire here — the `build-tls` copy i
203203
already carries `<exclude name="**/*Test.java"/>`, so unlike `bcprov` these never leak into the
204204
shipped `bctls` jar (`unzip -l bctls-jdk14-*.jar | grep -cE 'Test\.class'` should stay 0).
205205

206+
**Test *helpers* in that package are the other half of the trap**, and the `*Test.java` exclude does
207+
not catch them. The DTLS 1.3 work added `DTLS13HandshakeTestSupport`, `DTLSRecordLayer13TestSupport`
208+
and `DTLS13KeyUpdateTestSupport` to `org.bouncycastle.tls` (they reach package-private members), none
209+
named `*Test`. The jdk1.4 and jdk1.3 builds stage the whole tls test tree, so the `build-tls` copy
210+
swept all three into `bctls` — and the first two reference excluded `*Test` classes, which breaks the
211+
provider compile outright. `build-tls` now also excludes `**/*TestSupport.java`; name any such helper
212+
that way. The one helper a `tls.test` class needs, `DTLS13KeyUpdateTestSupport` (for
213+
`DTLS13ProtocolTest`), gets a targeted `build-test` include next to the `SpreadCertificateStatusTest`
214+
one, and `ant/jdk15+.xml` — whose staging excludes `**/tls/*.java` wholesale — stages it with its own
215+
fileset, or `bctest-jdk15to18` fails to compile `DTLS13ProtocolTest`. A helper included this way must
216+
compile against `bctls` alone: no references to other test classes.
217+
206218
Adding the include compiles the class but does **not** run it: the `test` target's batchtest runs
207219
`**/AllTests.java` out of the staged bctest source tree, and `ant/jdk14.xml` (around line 283)
208220
excludes `**/tls/AllTests.java` — the only suite referencing these classes. So a targeted include is

‎docs/releasenotes.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ Date: 2026, TBD
215215
- On Java 9 and later the provider's DEFAULT and NONCEANDIV SecureRandom implementations accept java.security.DrbgParameters: SecureRandom.getInstance() given a DrbgParameters.Instantiation creates a DRBG of its own with the requested security strength (rounded up to 112, 128, 192 or 256 bits, 256 being the default and the maximum), capability and personalization string, nextBytes() takes a DrbgParameters.NextBytes carrying additional input and a request for prediction resistance, reseed() takes a DrbgParameters.Reseed, and getParameters() reports how an instance was instantiated. An instance created without parameters uses the shared DRBG as before. DEFAULT is also registered under the alias DRBG, the name Java 9 gives an SP 800-90A DRBG, so with BC installed ahead of the SUN provider SecureRandom.getInstance("DRBG") returns BC's. Lightweight users gain SP800SecureRandom.nextBytes(byte[], byte[]) for passing additional input to the DRBG.
216216

217217
- TLS: Added CertificateStatusType.getName and getText, and TlsUtils.hasExtension.
218+
- The low-level TLS API (org.bouncycastle.tls) now implements DTLS 1.3 (RFC 9147): the unified record header with encrypted record numbers, the ACK-driven reliable handshake with selective retransmission, the certificate-authenticated handshake with HelloRetryRequest and client authentication, KeyUpdate in both directions (including the automatic update at the AEAD's key usage limit), and exporters and DTLS-SRTP; NewSessionTicket messages are accepted but, with no resumption yet, not used. It is opt-in: a DTLS client or server offers or accepts it only when its supported versions include ProtocolVersion.DTLSv13, and nothing lists that by default, so existing applications are unaffected. Not yet implemented for DTLS 1.3 are pre-shared keys and resumption, 0-RTT, connection IDs, post-handshake client authentication, and the RFC 9147 sec. 5.1 stateless cookie exchange: DTLSVerifier still implements only the DTLS 1.2 HelloVerifyRequest exchange, and a server offering DTLS 1.3 cannot be placed behind it (the handshake fails at the ServerHello with an internal_error alert rather than leaving the client to abort). Handshake flights of every DTLS version are now also packed into as few datagrams as the transport's send limit allows, where each record went in its own datagram before. (github #1468, github #1487)
218219
- The provider's PKIX CertPathValidator now applies a freshness bound to an OCSP response that states no nextUpdate, accepting it only while its thisUpdate is within an hour of the validation date. RFC 6960 sec. 3.2 leaves it to the client to judge whether thisUpdate is sufficiently recent, and a response without a nextUpdate gives no other indication of how long it may be relied on; this matches the JDK's own OCSP client, and OpenSSL's -status_age option. The bound is set by the new property org.bouncycastle.ocsp.max_response_age (Properties.OCSP_MAX_RESPONSE_AGE, in seconds), so deployments whose responders pre-produce responses without a nextUpdate can widen it. Responses that state a nextUpdate are judged by it as before. Note a response without one that is older than the bound, which was previously accepted, now fails the check.
219220

220221
- org.bouncycastle.util.Pack.longToBigEndian() now writes the eight bytes of the long directly rather than through two intToBigEndian() calls, which the JIT of JDK 25 compiles to a single byte-swapped store. On JDK 23 and later longToLittleEndian() does the same, from a copy of Pack the bcprov and bccore multi-release jars carry under META-INF/versions/23, since JDK 23 is the first whose JIT merges those stores and the form is slower than the existing one on earlier JDKs. Packing longs big-endian measured about 5 times as fast on JDK 25 and about 10% faster on JDK 8 to 23, and little-endian about 2.2 to 2.7 times as fast on JDK 23 and 25, with SHAKE squeezing about 6% and SHA-512 about 4% faster on JDK 25 (github #2474).

‎tls/src/main/java/org/bouncycastle/tls/AbstractTlsContext.java‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -289,8 +289,9 @@ else if (!TlsUtils.isValidUint16(context.length))
289289
TlsHash exporterHash = getCrypto().createHash(cryptoHashAlgorithm);
290290
byte[] emptyTranscriptHash = exporterHash.calculateHash();
291291

292-
TlsSecret exporterSecret = TlsUtils.deriveSecret(getSecurityParametersConnection(), secret, asciiLabel,
293-
emptyTranscriptHash);
292+
SecurityParameters sp = getSecurityParametersConnection();
293+
294+
TlsSecret exporterSecret = TlsUtils.deriveSecret(sp, secret, asciiLabel, emptyTranscriptHash);
294295

295296
byte[] exporterContext = emptyTranscriptHash;
296297
if (context.length > 0)
@@ -299,8 +300,12 @@ else if (!TlsUtils.isValidUint16(context.length))
299300
exporterContext = exporterHash.calculateHash();
300301
}
301302

303+
// RFC 9147 5.9. DTLS 1.3 derives with the "dtls13" label prefix rather than TLS 1.3's "tls13 ".
304+
boolean isDTLS = sp.getNegotiatedVersion().isDTLS();
305+
302306
return TlsCryptoUtils
303-
.hkdfExpandLabel(exporterSecret, cryptoHashAlgorithm, "exporter", exporterContext, length).extract();
307+
.hkdfExpandLabel(exporterSecret, cryptoHashAlgorithm, "exporter", exporterContext, length, isDTLS)
308+
.extract();
304309
}
305310
catch (IOException e)
306311
{

‎tls/src/main/java/org/bouncycastle/tls/AbstractTlsPeer.java‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ public int getHandshakeResendTimeMillis()
6767
return 1000;
6868
}
6969

70+
public boolean allowRecordPacking()
71+
{
72+
return true;
73+
}
74+
7075
public boolean allowLegacyResumption()
7176
{
7277
return false;

‎tls/src/main/java/org/bouncycastle/tls/ContentType.java‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ public class ContentType
1111
public static final short application_data = 23;
1212
public static final short heartbeat = 24;
1313
public static final short tls12_cid = 25;
14+
/** RFC 9147 7.1 */
15+
public static final short ack = 26;
1416

1517
public static String getName(short contentType)
1618
{
@@ -27,7 +29,9 @@ public static String getName(short contentType)
2729
case heartbeat:
2830
return "heartbeat";
2931
case tls12_cid:
30-
return "tls12_cid";
32+
return "tls12_cid";
33+
case ack:
34+
return "ack";
3135
default:
3236
return "UNKNOWN";
3337
}

0 commit comments

Comments
 (0)