Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
5e8aadb
Add the DTLS 1.3 ack content type (RFC 9147 7.1), relates to github #…
mondain Sep 11, 2026
92a868c
Add DTLS 1.3 record number encryption mask primitive for AES and ChaC…
mondain Sep 11, 2026
db62603
Add DTLS 1.3 record protection to TlsAEADCipher via a new TlsDTLS13Ci…
mondain Sep 11, 2026
dda1d91
Add DTLS 1.3 unified header codec and sequence number reconstruction …
mondain Sep 11, 2026
d5a8bd2
Add DTLS 1.3 record layer send/receive paths and epoch switching (RFC…
mondain Sep 11, 2026
405626e
Use the "dtls13" HKDF-Expand-Label prefix for the DTLS 1.3 key schedu…
JonathanLennox Sep 14, 2026
5643cc6
Add the DTLS 1.3 reliable handshake and pack handshake flights into d…
mondain Sep 12, 2026
97c5666
Refuse to send a DTLS handshake message whose message_seq would wrap …
mondain Sep 20, 2026
0422371
Offer DTLS 1.3 from the DTLS client, relates to github #1468.
mondain Sep 12, 2026
2c8201a
Complete the client half of a DTLS 1.3 handshake, relates to github #…
mondain Sep 12, 2026
aef782f
Hash DTLS 1.3 handshake transcripts without the DTLS message header, …
mondain Sep 12, 2026
9ae9157
Fail loudly if a DTLS handshake transcript is read while undecided, r…
mondain Sep 12, 2026
a31400a
Complete the server half of a DTLS 1.3 handshake, relates to github #…
mondain Sep 12, 2026
c559800
Retain the DTLS 1.3 handshake epoch after the handshake completes, re…
mondain Sep 12, 2026
67c687d
Add an end-to-end DTLS 1.3 handshake test, relates to github #1468.
mondain Sep 12, 2026
79a6111
Bound the DTLS 1.3 epochs retained after the handshake, relates to gi…
mondain Sep 12, 2026
c49456e
Answer only a retransmission of the peer's final flight with an ACK, …
mondain Sep 12, 2026
c6b3583
Test the DTLS 1.3 retransmitted-ACK path with a reordered final fligh…
mondain Sep 12, 2026
2c9600a
Re-arm the DTLS 1.3 retained-epoch application data test, relates to …
mondain Sep 12, 2026
8135076
Add the DTLS 1.3 HelloRetryRequest and its cookie, relates to github …
mondain Sep 12, 2026
45d5c71
Refuse a DTLS 1.2 HelloVerifyRequest on a DTLS 1.3 handshake, relates…
mondain Sep 12, 2026
0939f56
Support DTLS 1.3 client authentication during the handshake, relates …
mondain Sep 12, 2026
13a2b77
Cover the DTLS 1.3 exporter, use_srtp and version fallback, relates t…
mondain Sep 12, 2026
7c7c568
Restore the DTLS 1.3 no-CertificateRequest and authenticated-flight c…
mondain Sep 12, 2026
b35d4b0
Drop the DTLS 1.3 authenticated flight deterministically and prove it…
mondain Sep 12, 2026
9148c41
Gate the DTLS secure renegotiation notification on the selected versi…
mondain Sep 12, 2026
90d77f7
Assert the HelloRetryRequest synthetic transcript bytes directly, rel…
mondain Sep 12, 2026
9b8d088
Derive the retained handshake epoch and document the DTLS 1.3 epoch r…
mondain Sep 12, 2026
c4418f4
Refuse to negotiate DTLS 1.3 behind a HelloVerifyRequest front end, r…
mondain Sep 12, 2026
4ce8020
Leave generate13EncryptedExtensions without an unused state parameter…
mondain Sep 12, 2026
d748767
Assert the server's own refusal and the second ClientHello's message_…
mondain Sep 12, 2026
831c940
Complete the handshake in the DTLS 1.3 record layer test harness, rel…
mondain Sep 12, 2026
3b6c77e
Let TlsCryptoParameters resolve security parameters after the handsha…
mondain Sep 12, 2026
2ab13cf
Route TlsCryptoParameters and Tls13NullCipher through their own acces…
mondain Sep 12, 2026
4eeedf7
Resolve DTLS 1.3 read epochs from one ordered live set, relates to gi…
mondain Sep 12, 2026
af57b4d
Hold a derived DTLS 1.3 write epoch before installing it, relates to …
mondain Sep 12, 2026
e0f920a
Own DTLS 1.3 post-handshake messages and acknowledgements, relates to…
mondain Sep 12, 2026
dc65c87
Install a new DTLS 1.3 read epoch on a peer KeyUpdate and retire the …
mondain Sep 12, 2026
36b651e
Gate the new DTLS 1.3 write epoch on the KeyUpdate acknowledgement, r…
mondain Sep 12, 2026
b7edf13
Drive a DTLS 1.3 key update through the real client and server, relat…
mondain Sep 12, 2026
e6bbaa1
Correct the peer-keyed epoch reasoning and apply the branch review, r…
mondain Sep 12, 2026
1d23aab
State the peer-keyed nonce reuse precisely and document the lock orde…
mondain Sep 12, 2026
c2790c3
Refuse to send a DTLS 1.3 KeyUpdate whose message_seq would wrap the …
mondain Sep 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions tls/src/main/java/org/bouncycastle/tls/AbstractTlsContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 +289,9 @@ else if (!TlsUtils.isValidUint16(context.length))
TlsHash exporterHash = getCrypto().createHash(cryptoHashAlgorithm);
byte[] emptyTranscriptHash = exporterHash.calculateHash();

TlsSecret exporterSecret = TlsUtils.deriveSecret(getSecurityParametersConnection(), secret, asciiLabel,
emptyTranscriptHash);
SecurityParameters sp = getSecurityParametersConnection();

TlsSecret exporterSecret = TlsUtils.deriveSecret(sp, secret, asciiLabel, emptyTranscriptHash);

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

// RFC 9147 5.9. DTLS 1.3 derives with the "dtls13" label prefix rather than TLS 1.3's "tls13 ".
boolean isDTLS = sp.getNegotiatedVersion().isDTLS();

return TlsCryptoUtils
.hkdfExpandLabel(exporterSecret, cryptoHashAlgorithm, "exporter", exporterContext, length).extract();
.hkdfExpandLabel(exporterSecret, cryptoHashAlgorithm, "exporter", exporterContext, length, isDTLS)
.extract();
}
catch (IOException e)
{
Expand Down
6 changes: 5 additions & 1 deletion tls/src/main/java/org/bouncycastle/tls/ContentType.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public class ContentType
public static final short application_data = 23;
public static final short heartbeat = 24;
public static final short tls12_cid = 25;
/** RFC 9147 7.1 */
public static final short ack = 26;

public static String getName(short contentType)
{
Expand All @@ -27,7 +29,9 @@ public static String getName(short contentType)
case heartbeat:
return "heartbeat";
case tls12_cid:
return "tls12_cid";
return "tls12_cid";
case ack:
return "ack";
default:
return "UNKNOWN";
}
Expand Down
162 changes: 162 additions & 0 deletions tls/src/main/java/org/bouncycastle/tls/DTLS13FlightTracker.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package org.bouncycastle.tls;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

/**
* RFC 9147 7.2. Tracks which record carried which handshake fragment of the current outbound flight, so
* that an ACK retires exactly the fragments it covers and a retransmission resends only what is left.
* <p>
* A fragment may be registered more than once, under a different record number each time it is sent. It
* is acknowledged as soon as any one of those records is acknowledged.
* </p>
*/
class DTLS13FlightTracker
{
/** One handshake fragment of the current outbound flight. */
static final class Fragment
{
private final int messageSeq;
private final int fragmentOffset;
private final int fragmentLength;

/*
* RFC 9147 5.8.1. The epoch the fragment was first sent at, which is the epoch it must be retransmitted
* at: a DTLS 1.3 flight straddles an epoch change, and once the handshake completes the write epoch has
* moved on past the keys a peer that is still retransmitting can read. Negative until a record number
* is registered, which means nothing was actually written.
*/
private int epoch = -1;

boolean acknowledged = false;

Fragment(int messageSeq, int fragmentOffset, int fragmentLength)
{
this.messageSeq = messageSeq;
this.fragmentOffset = fragmentOffset;
this.fragmentLength = fragmentLength;
}

int getEpoch()
{
return epoch;
}

int getMessageSeq()
{
return messageSeq;
}

int getFragmentOffset()
{
return fragmentOffset;
}

int getFragmentLength()
{
return fragmentLength;
}

private String key()
{
return messageSeq + ":" + fragmentOffset + ":" + fragmentLength;
}
}

// record number -> Fragment
private Hashtable carriers = new Hashtable();
// fragment key -> Fragment, so the same fragment sent twice is one entry
private Hashtable fragments = new Hashtable();
// fragments in registration order, for deterministic retransmission
private Vector order = new Vector();

void reset()
{
carriers = new Hashtable();
fragments = new Hashtable();
order = new Vector();
}

void register(DTLSRecordNumber recordNumber, int messageSeq, int fragmentOffset, int fragmentLength)
{
Fragment fragment = new Fragment(messageSeq, fragmentOffset, fragmentLength);
String key = fragment.key();

Fragment existing = (Fragment)fragments.get(key);
if (null == existing)
{
fragments.put(key, fragment);
order.addElement(fragment);
existing = fragment;
}

if (null != recordNumber)
{
carriers.put(recordNumber, existing);

if (existing.epoch < 0)
{
existing.epoch = (int)recordNumber.getEpoch();
}
}
}

void acknowledge(Vector recordNumbers)
{
for (int i = 0; i < recordNumbers.size(); ++i)
{
Fragment fragment = (Fragment)carriers.get(recordNumbers.elementAt(i));
if (null != fragment)
{
fragment.acknowledged = true;
}
}
}

boolean isEmpty()
{
return order.isEmpty();
}

/**
* @return true if fragments were registered and every one of them has been acknowledged.
*/
boolean isComplete()
{
if (order.isEmpty())
{
return false;
}

Enumeration e = order.elements();
while (e.hasMoreElements())
{
if (!((Fragment)e.nextElement()).acknowledged)
{
return false;
}
}
return true;
}

/**
* @return the fragments not yet acknowledged, in the order they were first registered.
*/
Vector getOutstanding()
{
Vector outstanding = new Vector();

Enumeration e = order.elements();
while (e.hasMoreElements())
{
Fragment fragment = (Fragment)e.nextElement();
if (!fragment.acknowledged)
{
outstanding.addElement(fragment);
}
}

return outstanding;
}
}
Loading