Skip to content

Commit 405626e

Browse files
JonathanLennoxmondain
authored andcommitted
Use the "dtls13" HKDF-Expand-Label prefix for the DTLS 1.3 key schedule (RFC 9147 section 5.9) instead of TLS 1.3's "tls13 ", so DTLS 1.3 traffic keys, record number keys, Finished keys and exporters interoperate with other implementations, relates to github #1468.
1 parent d5a8bd2 commit 405626e

9 files changed

Lines changed: 205 additions & 33 deletions

File tree

‎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/OfferedPsks.java‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,12 @@ public void encode(OutputStream output) throws IOException
144144
}
145145
}
146146

147+
/**
148+
* @param isDTLS whether the binders are for a DTLS 1.3 ClientHello, which selects the "dtls13" HKDF label prefix
149+
* (RFC 9147 5.9) rather than TLS 1.3's "tls13 ".
150+
*/
147151
static void encodeBinders(OutputStream output, TlsCrypto crypto, TlsHandshakeHash handshakeHash,
148-
BindersConfig bindersConfig) throws IOException
152+
BindersConfig bindersConfig, boolean isDTLS) throws IOException
149153
{
150154
TlsPSK[] psks = bindersConfig.psks;
151155
TlsSecret[] earlySecrets = bindersConfig.earlySecrets;
@@ -170,7 +174,7 @@ static void encodeBinders(OutputStream output, TlsCrypto crypto, TlsHandshakeHas
170174
byte[] transcriptHash = hash.calculateHash();
171175

172176
byte[] binder = TlsUtils.calculatePSKBinder(crypto, isExternalPSK, pskCryptoHashAlgorithm, earlySecret,
173-
transcriptHash);
177+
transcriptHash, isDTLS);
174178

175179
lengthOfBindersList += 1 + binder.length;
176180
TlsUtils.writeOpaque8(binder, output);

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2008,7 +2008,8 @@ protected void sendClientHelloMessage() throws IOException
20082008

20092009
if (null != clientBinders)
20102010
{
2011-
OfferedPsks.encodeBinders(message, tlsClientContext.getCrypto(), handshakeHash, clientBinders);
2011+
// TLS over TCP, so the binders use the TLS 1.3 label prefix
2012+
OfferedPsks.encodeBinders(message, tlsClientContext.getCrypto(), handshakeHash, clientBinders, false);
20122013
}
20132014

20142015
message.sendClientHello(this, handshakeHash, clientHello.getBindersSize());

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

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1776,15 +1776,16 @@ private static byte[] calculateFinishedHMAC(SecurityParameters securityParameter
17761776
{
17771777
int prfCryptoHashAlgorithm = securityParameters.getPRFCryptoHashAlgorithm();
17781778
int prfHashLength = securityParameters.getPRFHashLength();
1779+
boolean isDTLS = isDTLS(securityParameters);
17791780

1780-
return calculateFinishedHMAC(prfCryptoHashAlgorithm, prfHashLength, baseKey, transcriptHash);
1781+
return calculateFinishedHMAC(prfCryptoHashAlgorithm, prfHashLength, baseKey, transcriptHash, isDTLS);
17811782
}
17821783

17831784
private static byte[] calculateFinishedHMAC(int prfCryptoHashAlgorithm, int prfHashLength, TlsSecret baseKey,
1784-
byte[] transcriptHash) throws IOException
1785+
byte[] transcriptHash, boolean isDTLS) throws IOException
17851786
{
17861787
TlsSecret finishedKey = TlsCryptoUtils.hkdfExpandLabel(baseKey, prfCryptoHashAlgorithm, "finished", EMPTY_BYTES,
1787-
prfHashLength);
1788+
prfHashLength, isDTLS);
17881789

17891790
try
17901791
{
@@ -1817,19 +1818,19 @@ static TlsSecret calculateMasterSecret(TlsContext context, TlsSecret preMasterSe
18171818
}
18181819

18191820
static byte[] calculatePSKBinder(TlsCrypto crypto, boolean isExternalPSK, int pskCryptoHashAlgorithm,
1820-
TlsSecret earlySecret, byte[] transcriptHash) throws IOException
1821+
TlsSecret earlySecret, byte[] transcriptHash, boolean isDTLS) throws IOException
18211822
{
18221823
int prfHashLength = TlsCryptoUtils.getHashOutputSize(pskCryptoHashAlgorithm);
18231824

18241825
String label = isExternalPSK ? "ext binder" : "res binder";
18251826
byte[] emptyTranscriptHash = crypto.createHash(pskCryptoHashAlgorithm).calculateHash();
18261827

18271828
TlsSecret binderKey = deriveSecret(pskCryptoHashAlgorithm, prfHashLength, earlySecret, label,
1828-
emptyTranscriptHash);
1829+
emptyTranscriptHash, isDTLS);
18291830

18301831
try
18311832
{
1832-
return calculateFinishedHMAC(pskCryptoHashAlgorithm, prfHashLength, binderKey, transcriptHash);
1833+
return calculateFinishedHMAC(pskCryptoHashAlgorithm, prfHashLength, binderKey, transcriptHash, isDTLS);
18331834
}
18341835
finally
18351836
{
@@ -2012,7 +2013,21 @@ private static void update13TrafficSecret(TlsContext context, boolean forServer)
20122013
private static TlsSecret update13TrafficSecret(SecurityParameters securityParameters, TlsSecret secret) throws IOException
20132014
{
20142015
return TlsCryptoUtils.hkdfExpandLabel(secret, securityParameters.getPRFCryptoHashAlgorithm(), "traffic upd",
2015-
EMPTY_BYTES, securityParameters.getPRFHashLength());
2016+
EMPTY_BYTES, securityParameters.getPRFHashLength(), isDTLS(securityParameters));
2017+
}
2018+
2019+
/**
2020+
* Whether the (D)TLS 1.3 key schedule for these security parameters uses the DTLS 1.3 label prefix (RFC 9147
2021+
* 5.9), i.e. whether the negotiated version is a DTLS version.
2022+
*/
2023+
private static boolean isDTLS(SecurityParameters securityParameters)
2024+
{
2025+
ProtocolVersion negotiatedVersion = securityParameters.getNegotiatedVersion();
2026+
if (null == negotiatedVersion)
2027+
{
2028+
throw new IllegalStateException("(D)TLS 1.3 key derivation before the version is negotiated");
2029+
}
2030+
return negotiatedVersion.isDTLS();
20162031
}
20172032

20182033
public static ASN1ObjectIdentifier getOIDForHashAlgorithm(short hashAlgorithm)
@@ -6117,18 +6132,20 @@ static TlsSecret deriveSecret(SecurityParameters securityParameters, TlsSecret s
61176132
int prfCryptoHashAlgorithm = securityParameters.getPRFCryptoHashAlgorithm();
61186133
int prfHashLength = securityParameters.getPRFHashLength();
61196134

6120-
return deriveSecret(prfCryptoHashAlgorithm, prfHashLength, secret, label, transcriptHash);
6135+
return deriveSecret(prfCryptoHashAlgorithm, prfHashLength, secret, label, transcriptHash,
6136+
isDTLS(securityParameters));
61216137
}
61226138

61236139
static TlsSecret deriveSecret(int prfCryptoHashAlgorithm, int prfHashLength, TlsSecret secret, String label,
6124-
byte[] transcriptHash) throws IOException
6140+
byte[] transcriptHash, boolean isDTLS) throws IOException
61256141
{
61266142
if (transcriptHash.length != prfHashLength)
61276143
{
61286144
throw new TlsFatalAlert(AlertDescription.internal_error);
61296145
}
61306146

6131-
return TlsCryptoUtils.hkdfExpandLabel(secret, prfCryptoHashAlgorithm, label, transcriptHash, prfHashLength);
6147+
return TlsCryptoUtils.hkdfExpandLabel(secret, prfCryptoHashAlgorithm, label, transcriptHash, prfHashLength,
6148+
isDTLS);
61326149
}
61336150

61346151
static TlsSecret getSessionMasterSecret(TlsCrypto crypto, TlsSecret masterSecret)
@@ -6449,7 +6466,7 @@ static OfferedPsks.SelectedConfig selectPreSharedKey(TlsServerContext serverCont
64496466
}
64506467

64516468
byte[] calculatedBinder = calculatePSKBinder(crypto, isExternalPSK, pskCryptoHashAlgorithm,
6452-
earlySecret, transcriptHash);
6469+
earlySecret, transcriptHash, serverContext.getServerVersion().isDTLS());
64536470

64546471
if (!Arrays.constantTimeAreEqual(calculatedBinder, binder))
64556472
{

‎tls/src/main/java/org/bouncycastle/tls/crypto/TlsCryptoUtils.java‎

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ public abstract class TlsCryptoUtils
2121
// "tls13 "
2222
private static final byte[] TLS13_PREFIX = new byte[]{ 0x74, 0x6c, 0x73, 0x31, 0x33, 0x20 };
2323

24+
// "dtls13" (RFC 9147 5.9: no trailing space, so that the expanded label stays within one hash block)
25+
private static final byte[] DTLS13_PREFIX = new byte[]{ 0x64, 0x74, 0x6c, 0x73, 0x31, 0x33 };
26+
2427
public static int getHash(short hashAlgorithm)
2528
{
2629
switch (hashAlgorithm)
@@ -192,17 +195,36 @@ public static int getSignature(short signatureAlgorithm)
192195
}
193196
}
194197

198+
/**
199+
* HKDF-Expand-Label as defined in RFC 8446 7.1, with the "tls13 " label prefix. This is the TLS 1.3 form; for
200+
* DTLS 1.3 use {@link #hkdfExpandLabel(TlsSecret, int, String, byte[], int, boolean)}, since RFC 9147 5.9
201+
* requires the "dtls13" prefix there.
202+
*/
195203
public static TlsSecret hkdfExpandLabel(TlsSecret secret, int cryptoHashAlgorithm, String label, byte[] context,
196204
int length) throws IOException
205+
{
206+
return hkdfExpandLabel(secret, cryptoHashAlgorithm, label, context, length, false);
207+
}
208+
209+
/**
210+
* HKDF-Expand-Label as defined in RFC 8446 7.1, with the label prefix selected by the protocol: "tls13 " for
211+
* TLS 1.3, or "dtls13" for DTLS 1.3 (RFC 9147 5.9, which requires this for key separation between the two).
212+
*
213+
* @param isDTLS true to use the DTLS 1.3 label prefix, false for the TLS 1.3 one.
214+
*/
215+
public static TlsSecret hkdfExpandLabel(TlsSecret secret, int cryptoHashAlgorithm, String label, byte[] context,
216+
int length, boolean isDTLS) throws IOException
197217
{
198218
int labelLength = label.length();
199219
if (labelLength < 1)
200220
{
201221
throw new TlsFatalAlert(AlertDescription.internal_error);
202222
}
203223

224+
byte[] prefix = isDTLS ? DTLS13_PREFIX : TLS13_PREFIX;
225+
204226
int contextLength = context.length;
205-
int expandedLabelLength = TLS13_PREFIX.length + labelLength;
227+
int expandedLabelLength = prefix.length + labelLength;
206228

207229
byte[] hkdfLabel = new byte[2 + (1 + expandedLabelLength) + (1 + contextLength)];
208230

@@ -217,9 +239,9 @@ public static TlsSecret hkdfExpandLabel(TlsSecret secret, int cryptoHashAlgorith
217239
TlsUtils.checkUint8(expandedLabelLength);
218240
TlsUtils.writeUint8(expandedLabelLength, hkdfLabel, 2);
219241

220-
System.arraycopy(TLS13_PREFIX, 0, hkdfLabel, 2 + 1, TLS13_PREFIX.length);
242+
System.arraycopy(prefix, 0, hkdfLabel, 2 + 1, prefix.length);
221243

222-
int labelPos = 2 + (1 + TLS13_PREFIX.length);
244+
int labelPos = 2 + (1 + prefix.length);
223245
for (int i = 0; i < labelLength; ++i)
224246
{
225247
char c = label.charAt(i);

‎tls/src/main/java/org/bouncycastle/tls/crypto/impl/Tls13NullCipher.java‎

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -205,15 +205,18 @@ private void rekeyHmac(SecurityParameters securityParameters, TlsHMAC hmac, byte
205205
throw new TlsFatalAlert(AlertDescription.internal_error);
206206
}
207207

208-
setupHmac(hmac, nonce, secret, securityParameters.getPRFCryptoHashAlgorithm());
208+
// RFC 9147 5.9. DTLS 1.3 derives with the "dtls13" label prefix rather than TLS 1.3's "tls13 ".
209+
boolean isDTLS = securityParameters.getNegotiatedVersion().isDTLS();
210+
211+
setupHmac(hmac, nonce, secret, securityParameters.getPRFCryptoHashAlgorithm(), isDTLS);
209212
}
210213

211-
private void setupHmac(TlsHMAC hmac, byte[] nonce, TlsSecret secret, int cryptoHashAlgorithm)
214+
private void setupHmac(TlsHMAC hmac, byte[] nonce, TlsSecret secret, int cryptoHashAlgorithm, boolean isDTLS)
212215
throws IOException
213216
{
214217
int length = hmac.getMacLength();
215-
byte[] key = hkdfExpandLabel(secret, cryptoHashAlgorithm, "key", length).extract();
216-
byte[] iv = hkdfExpandLabel(secret, cryptoHashAlgorithm, "iv", length).extract();
218+
byte[] key = hkdfExpandLabel(secret, cryptoHashAlgorithm, "key", length, isDTLS).extract();
219+
byte[] iv = hkdfExpandLabel(secret, cryptoHashAlgorithm, "iv", length, isDTLS).extract();
217220

218221
hmac.setKey(key, 0, length);
219222
System.arraycopy(iv, 0, nonce, 0, length);
@@ -241,9 +244,10 @@ private static byte[] getAdditionalData(long seqNo, short recordType, ProtocolVe
241244
return additional_data;
242245
}
243246

244-
private static TlsSecret hkdfExpandLabel(TlsSecret secret, int cryptoHashAlgorithm, String label, int length)
245-
throws IOException
247+
private static TlsSecret hkdfExpandLabel(TlsSecret secret, int cryptoHashAlgorithm, String label, int length,
248+
boolean isDTLS) throws IOException
246249
{
247-
return TlsCryptoUtils.hkdfExpandLabel(secret, cryptoHashAlgorithm, label, TlsUtils.EMPTY_BYTES, length);
250+
return TlsCryptoUtils.hkdfExpandLabel(secret, cryptoHashAlgorithm, label, TlsUtils.EMPTY_BYTES, length,
251+
isDTLS);
248252
}
249253
}

‎tls/src/main/java/org/bouncycastle/tls/crypto/impl/TlsAEADCipher.java‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -671,8 +671,9 @@ private void rekeyCipher(SecurityParameters securityParameters, TlsAEADCipherImp
671671
private void setup13Cipher(TlsAEADCipherImpl cipher, byte[] nonce, TlsRecordNumberMask mask, TlsSecret secret,
672672
int cryptoHashAlgorithm) throws IOException
673673
{
674-
byte[] key = hkdfExpandLabel(secret, cryptoHashAlgorithm, "key", keySize).extract();
675-
byte[] iv = hkdfExpandLabel(secret, cryptoHashAlgorithm, "iv", fixed_iv_length).extract();
674+
// RFC 9147 5.9. DTLS 1.3 derives with the "dtls13" label prefix rather than TLS 1.3's "tls13 ".
675+
byte[] key = hkdfExpandLabel(secret, cryptoHashAlgorithm, "key", keySize, isDTLSv13).extract();
676+
byte[] iv = hkdfExpandLabel(secret, cryptoHashAlgorithm, "iv", fixed_iv_length, isDTLSv13).extract();
676677

677678
cipher.setKey(key, 0, keySize);
678679
System.arraycopy(iv, 0, nonce, 0, fixed_iv_length);
@@ -687,7 +688,7 @@ private void setup13Cipher(TlsAEADCipherImpl cipher, byte[] nonce, TlsRecordNumb
687688
throw new TlsFatalAlert(AlertDescription.internal_error, "No record number mask for DTLS 1.3");
688689
}
689690

690-
byte[] snKey = hkdfExpandLabel(secret, cryptoHashAlgorithm, "sn", keySize).extract();
691+
byte[] snKey = hkdfExpandLabel(secret, cryptoHashAlgorithm, "sn", keySize, true).extract();
691692
mask.setKey(snKey, 0, keySize);
692693
}
693694
}
@@ -708,9 +709,10 @@ private static int getNonceMode(boolean isTLSv13, int aeadType) throws IOExcepti
708709
}
709710
}
710711

711-
private static TlsSecret hkdfExpandLabel(TlsSecret secret, int cryptoHashAlgorithm, String label, int length)
712-
throws IOException
712+
private static TlsSecret hkdfExpandLabel(TlsSecret secret, int cryptoHashAlgorithm, String label, int length,
713+
boolean isDTLS) throws IOException
713714
{
714-
return TlsCryptoUtils.hkdfExpandLabel(secret, cryptoHashAlgorithm, label, TlsUtils.EMPTY_BYTES, length);
715+
return TlsCryptoUtils.hkdfExpandLabel(secret, cryptoHashAlgorithm, label, TlsUtils.EMPTY_BYTES, length,
716+
isDTLS);
715717
}
716718
}

‎tls/src/test/java/org/bouncycastle/tls/AllTests.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public static Test suite()
2323
suite.addTestSuite(AbstractTlsServerResetTest.class);
2424
suite.addTestSuite(Add13CertificateStatusTest.class);
2525
suite.addTestSuite(CheckTlsFeaturesExtensionTest.class);
26+
suite.addTestSuite(DTLS13KeyScheduleLabelTest.class);
2627
suite.addTestSuite(DTLS13UnifiedHeaderTest.class);
2728
suite.addTestSuite(DTLSReassemblerTest.class);
2829
suite.addTestSuite(DTLSRecordLayer13Test.class);

0 commit comments

Comments
 (0)