Skip to content

Commit 1a74aff

Browse files
committed
BCJSSE: Add integrity-only cipher suites from RFC 9150
1 parent 7a0bed7 commit 1a74aff

File tree

8 files changed

+320
-3
lines changed

8 files changed

+320
-3
lines changed

tls/src/main/java/org/bouncycastle/tls/CipherSuite.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,12 @@ public static boolean isSCSV(int cipherSuite)
451451
public static final int TLS_SM4_GCM_SM3 = 0x00C6;
452452
public static final int TLS_SM4_CCM_SM3 = 0x00C7;
453453

454+
/*
455+
* RFC 9150
456+
*/
457+
public static final int TLS_SHA256_SHA256 = 0xC0B4;
458+
public static final int TLS_SHA384_SHA384 = 0xC0B5;
459+
454460
/*
455461
* RFC 9189
456462
*/

tls/src/main/java/org/bouncycastle/tls/EncryptionAlgorithm.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,10 @@ public class EncryptionAlgorithm
8484
public static final int KUZNYECHIK_CTR_OMAC = 29;
8585
public static final int MAGMA_CTR_OMAC = 30;
8686
public static final int _28147_CNT_IMIT = 31;
87+
88+
/*
89+
* RFC 9150
90+
*/
91+
public static final int NULL_HMAC_SHA256 = 32;
92+
public static final int NULL_HMAC_SHA384 = 33;
8793
}

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2004,6 +2004,7 @@ static int getPRFAlgorithm(SecurityParameters securityParameters, int cipherSuit
20042004
case CipherSuite.TLS_AES_128_CCM_8_SHA256:
20052005
case CipherSuite.TLS_AES_128_GCM_SHA256:
20062006
case CipherSuite.TLS_CHACHA20_POLY1305_SHA256:
2007+
case CipherSuite.TLS_SHA256_SHA256:
20072008
{
20082009
if (isTLSv13)
20092010
{
@@ -2013,6 +2014,7 @@ static int getPRFAlgorithm(SecurityParameters securityParameters, int cipherSuit
20132014
}
20142015

20152016
case CipherSuite.TLS_AES_256_GCM_SHA384:
2017+
case CipherSuite.TLS_SHA384_SHA384:
20162018
{
20172019
if (isTLSv13)
20182020
{
@@ -2292,9 +2294,11 @@ static int getPRFAlgorithm13(int cipherSuite)
22922294
case CipherSuite.TLS_AES_128_CCM_8_SHA256:
22932295
case CipherSuite.TLS_AES_128_GCM_SHA256:
22942296
case CipherSuite.TLS_CHACHA20_POLY1305_SHA256:
2297+
case CipherSuite.TLS_SHA256_SHA256:
22952298
return PRFAlgorithm.tls13_hkdf_sha256;
22962299

22972300
case CipherSuite.TLS_AES_256_GCM_SHA384:
2301+
case CipherSuite.TLS_SHA384_SHA384:
22982302
return PRFAlgorithm.tls13_hkdf_sha384;
22992303

23002304
case CipherSuite.TLS_SM4_CCM_SM3:
@@ -3046,6 +3050,12 @@ public static int getEncryptionAlgorithm(int cipherSuite)
30463050
case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
30473051
return EncryptionAlgorithm.NULL;
30483052

3053+
case CipherSuite.TLS_SHA256_SHA256:
3054+
return EncryptionAlgorithm.NULL_HMAC_SHA256;
3055+
3056+
case CipherSuite.TLS_SHA384_SHA384:
3057+
return EncryptionAlgorithm.NULL_HMAC_SHA384;
3058+
30493059
case CipherSuite.TLS_DH_anon_WITH_SEED_CBC_SHA:
30503060
case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
30513061
case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA:
@@ -3080,6 +3090,8 @@ public static int getEncryptionAlgorithmType(int encryptionAlgorithm)
30803090
case EncryptionAlgorithm.CAMELLIA_128_GCM:
30813091
case EncryptionAlgorithm.CAMELLIA_256_GCM:
30823092
case EncryptionAlgorithm.CHACHA20_POLY1305:
3093+
case EncryptionAlgorithm.NULL_HMAC_SHA256:
3094+
case EncryptionAlgorithm.NULL_HMAC_SHA384:
30833095
case EncryptionAlgorithm.SM4_CCM:
30843096
case EncryptionAlgorithm.SM4_GCM:
30853097
return CipherType.aead;
@@ -3359,6 +3371,8 @@ public static int getKeyExchangeAlgorithm(int cipherSuite)
33593371
case CipherSuite.TLS_AES_128_GCM_SHA256:
33603372
case CipherSuite.TLS_AES_256_GCM_SHA384:
33613373
case CipherSuite.TLS_CHACHA20_POLY1305_SHA256:
3374+
case CipherSuite.TLS_SHA256_SHA256:
3375+
case CipherSuite.TLS_SHA384_SHA384:
33623376
case CipherSuite.TLS_SM4_CCM_SM3:
33633377
case CipherSuite.TLS_SM4_GCM_SM3:
33643378
return KeyExchangeAlgorithm.NULL;
@@ -3587,6 +3601,8 @@ public static int getMACAlgorithm(int cipherSuite)
35873601
case CipherSuite.TLS_RSA_WITH_ARIA_256_GCM_SHA384:
35883602
case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
35893603
case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
3604+
case CipherSuite.TLS_SHA256_SHA256:
3605+
case CipherSuite.TLS_SHA384_SHA384:
35903606
case CipherSuite.TLS_SM4_CCM_SM3:
35913607
case CipherSuite.TLS_SM4_GCM_SM3:
35923608
return MACAlgorithm._null;
@@ -3786,6 +3802,8 @@ public static ProtocolVersion getMinimumVersion(int cipherSuite)
37863802
case CipherSuite.TLS_AES_128_GCM_SHA256:
37873803
case CipherSuite.TLS_AES_256_GCM_SHA384:
37883804
case CipherSuite.TLS_CHACHA20_POLY1305_SHA256:
3805+
case CipherSuite.TLS_SHA256_SHA256:
3806+
case CipherSuite.TLS_SHA384_SHA384:
37893807
case CipherSuite.TLS_SM4_CCM_SM3:
37903808
case CipherSuite.TLS_SM4_GCM_SM3:
37913809
return ProtocolVersion.TLSv13;
Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
package org.bouncycastle.tls.crypto.impl;
2+
3+
import java.io.IOException;
4+
5+
import org.bouncycastle.tls.AlertDescription;
6+
import org.bouncycastle.tls.ContentType;
7+
import org.bouncycastle.tls.ProtocolVersion;
8+
import org.bouncycastle.tls.SecurityParameters;
9+
import org.bouncycastle.tls.TlsFatalAlert;
10+
import org.bouncycastle.tls.TlsUtils;
11+
import org.bouncycastle.tls.crypto.TlsCipher;
12+
import org.bouncycastle.tls.crypto.TlsCryptoParameters;
13+
import org.bouncycastle.tls.crypto.TlsCryptoUtils;
14+
import org.bouncycastle.tls.crypto.TlsDecodeResult;
15+
import org.bouncycastle.tls.crypto.TlsEncodeResult;
16+
import org.bouncycastle.tls.crypto.TlsHMAC;
17+
import org.bouncycastle.tls.crypto.TlsSecret;
18+
import org.bouncycastle.util.Arrays;
19+
import org.bouncycastle.util.Bytes;
20+
21+
/**
22+
* A generic TLS 1.3 "integrity-only" cipher.
23+
*/
24+
public final class Tls13NullCipher
25+
implements TlsCipher
26+
{
27+
private final TlsCryptoParameters cryptoParams;
28+
29+
private final TlsHMAC readHMAC, writeHMAC;
30+
private final byte[] readNonce, writeNonce;
31+
32+
public Tls13NullCipher(TlsCryptoParameters cryptoParams, TlsHMAC readHMAC, TlsHMAC writeHMAC)
33+
throws IOException
34+
{
35+
final SecurityParameters securityParameters = cryptoParams.getSecurityParametersHandshake();
36+
37+
if (!TlsImplUtils.isTLSv13(securityParameters.getNegotiatedVersion()))
38+
{
39+
throw new TlsFatalAlert(AlertDescription.internal_error);
40+
}
41+
42+
this.cryptoParams = cryptoParams;
43+
this.readHMAC = readHMAC;
44+
this.writeHMAC = writeHMAC;
45+
46+
this.readNonce = new byte[readHMAC.getMacLength()];
47+
this.writeNonce = new byte[writeHMAC.getMacLength()];
48+
49+
final boolean isServer = cryptoParams.isServer();
50+
rekeyHmac(securityParameters, readHMAC, readNonce, !isServer);
51+
rekeyHmac(securityParameters, writeHMAC, writeNonce, isServer);
52+
}
53+
54+
public int getCiphertextDecodeLimit(int plaintextLimit)
55+
{
56+
return plaintextLimit + 1 + readHMAC.getMacLength();
57+
}
58+
59+
public int getCiphertextEncodeLimit(int plaintextLimit)
60+
{
61+
return plaintextLimit + 1 + writeHMAC.getMacLength();
62+
}
63+
64+
public int getPlaintextDecodeLimit(int ciphertextLimit)
65+
{
66+
return ciphertextLimit - readHMAC.getMacLength() - 1;
67+
}
68+
69+
public int getPlaintextEncodeLimit(int ciphertextLimit)
70+
{
71+
return ciphertextLimit - writeHMAC.getMacLength() - 1;
72+
}
73+
74+
public TlsEncodeResult encodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion,
75+
int headerAllocation, byte[] plaintext, int plaintextOffset, int plaintextLength) throws IOException
76+
{
77+
int macLength = writeHMAC.getMacLength();
78+
79+
// TODO Possibly redundant if we reset after any failures (i.e. DTLS)
80+
writeHMAC.reset();
81+
82+
byte[] nonce = createRecordNonce(writeNonce, seqNo);
83+
writeHMAC.update(nonce, 0, nonce.length);
84+
85+
// TODO[tls13, cid] If we support adding padding to (D)TLSInnerPlaintext, this will need review
86+
int innerPlaintextLength = plaintextLength + 1;
87+
int ciphertextLength = innerPlaintextLength + macLength;
88+
byte[] output = new byte[headerAllocation + ciphertextLength];
89+
int outputPos = headerAllocation;
90+
91+
short recordType = ContentType.application_data;
92+
93+
byte[] additionalData = getAdditionalData(seqNo, recordType, recordVersion, ciphertextLength);
94+
95+
try
96+
{
97+
System.arraycopy(plaintext, plaintextOffset, output, outputPos, plaintextLength);
98+
output[outputPos + plaintextLength] = (byte)contentType;
99+
100+
writeHMAC.update(additionalData, 0, additionalData.length);
101+
writeHMAC.update(output, outputPos, innerPlaintextLength);
102+
writeHMAC.calculateMAC(output, outputPos + innerPlaintextLength);
103+
outputPos += innerPlaintextLength + macLength;
104+
}
105+
catch (RuntimeException e)
106+
{
107+
throw new TlsFatalAlert(AlertDescription.internal_error, e);
108+
}
109+
110+
if (outputPos != output.length)
111+
{
112+
// NOTE: The additional data mechanism for AEAD ciphers requires exact output size prediction.
113+
throw new TlsFatalAlert(AlertDescription.internal_error);
114+
}
115+
116+
return new TlsEncodeResult(output, 0, output.length, recordType);
117+
}
118+
119+
public TlsDecodeResult decodeCiphertext(long seqNo, short recordType, ProtocolVersion recordVersion,
120+
byte[] ciphertext, int ciphertextOffset, int ciphertextLength) throws IOException
121+
{
122+
int macLength = readHMAC.getMacLength();
123+
124+
int innerPlaintextLength = ciphertextLength - macLength;
125+
if (innerPlaintextLength < 1)
126+
{
127+
throw new TlsFatalAlert(AlertDescription.decode_error);
128+
}
129+
130+
// TODO Possibly redundant if we reset after any failures (i.e. DTLS)
131+
readHMAC.reset();
132+
133+
byte[] nonce = createRecordNonce(readNonce, seqNo);
134+
readHMAC.update(nonce, 0, nonce.length);
135+
136+
byte[] additionalData = getAdditionalData(seqNo, recordType, recordVersion, ciphertextLength);
137+
138+
try
139+
{
140+
readHMAC.update(additionalData, 0, additionalData.length);
141+
readHMAC.update(ciphertext, ciphertextOffset, innerPlaintextLength);
142+
byte[] calculated = readHMAC.calculateMAC();
143+
if (!Arrays.constantTimeAreEqual(macLength, calculated, 0, ciphertext, ciphertextOffset + innerPlaintextLength))
144+
{
145+
throw new TlsFatalAlert(AlertDescription.bad_record_mac);
146+
}
147+
}
148+
catch (RuntimeException e)
149+
{
150+
throw new TlsFatalAlert(AlertDescription.bad_record_mac, e);
151+
}
152+
153+
short contentType = recordType;
154+
int plaintextLength = innerPlaintextLength;
155+
156+
// Strip padding and read true content type from TLSInnerPlaintext
157+
for (;;)
158+
{
159+
if (--plaintextLength < 0)
160+
{
161+
throw new TlsFatalAlert(AlertDescription.unexpected_message);
162+
}
163+
164+
byte octet = ciphertext[ciphertextOffset + plaintextLength];
165+
if (0 != octet)
166+
{
167+
contentType = (short)(octet & 0xFF);
168+
break;
169+
}
170+
}
171+
172+
return new TlsDecodeResult(ciphertext, ciphertextOffset, plaintextLength, contentType);
173+
}
174+
175+
public void rekeyDecoder() throws IOException
176+
{
177+
rekeyHmac(cryptoParams.getSecurityParametersConnection(), readHMAC, readNonce, !cryptoParams.isServer());
178+
}
179+
180+
public void rekeyEncoder() throws IOException
181+
{
182+
rekeyHmac(cryptoParams.getSecurityParametersConnection(), writeHMAC, writeNonce, cryptoParams.isServer());
183+
}
184+
185+
public boolean usesOpaqueRecordTypeDecode()
186+
{
187+
return true;
188+
}
189+
190+
public boolean usesOpaqueRecordTypeEncode()
191+
{
192+
return true;
193+
}
194+
195+
private void rekeyHmac(SecurityParameters securityParameters, TlsHMAC hmac, byte[] nonce, boolean serverSecret)
196+
throws IOException
197+
{
198+
TlsSecret secret = serverSecret
199+
? securityParameters.getTrafficSecretServer()
200+
: securityParameters.getTrafficSecretClient();
201+
202+
// TODO[tls13] For early data, have to disable server->client
203+
if (null == secret)
204+
{
205+
throw new TlsFatalAlert(AlertDescription.internal_error);
206+
}
207+
208+
setupHmac(hmac, nonce, secret, securityParameters.getPRFCryptoHashAlgorithm());
209+
}
210+
211+
private void setupHmac(TlsHMAC hmac, byte[] nonce, TlsSecret secret, int cryptoHashAlgorithm)
212+
throws IOException
213+
{
214+
int length = hmac.getMacLength();
215+
byte[] key = hkdfExpandLabel(secret, cryptoHashAlgorithm, "key", length).extract();
216+
byte[] iv = hkdfExpandLabel(secret, cryptoHashAlgorithm, "iv", length).extract();
217+
218+
hmac.setKey(key, 0, length);
219+
System.arraycopy(iv, 0, nonce, 0, length);
220+
}
221+
222+
private static byte[] createRecordNonce(byte[] fixedNonce, long seqNo)
223+
{
224+
int nonceLength = fixedNonce.length;
225+
byte[] nonce = new byte[nonceLength];
226+
TlsUtils.writeUint64(seqNo, nonce, nonceLength - 8);
227+
Bytes.xorTo(nonceLength, fixedNonce, nonce);
228+
return nonce;
229+
}
230+
231+
private static byte[] getAdditionalData(long seqNo, short recordType, ProtocolVersion recordVersion,
232+
int ciphertextLength) throws IOException
233+
{
234+
/*
235+
* TLSCiphertext.opaque_type || TLSCiphertext.legacy_record_version || TLSCiphertext.length
236+
*/
237+
byte[] additional_data = new byte[5];
238+
TlsUtils.writeUint8(recordType, additional_data, 0);
239+
TlsUtils.writeVersion(recordVersion, additional_data, 1);
240+
TlsUtils.writeUint16(ciphertextLength, additional_data, 3);
241+
return additional_data;
242+
}
243+
244+
private static TlsSecret hkdfExpandLabel(TlsSecret secret, int cryptoHashAlgorithm, String label, int length)
245+
throws IOException
246+
{
247+
return TlsCryptoUtils.hkdfExpandLabel(secret, cryptoHashAlgorithm, label, TlsUtils.EMPTY_BYTES, length);
248+
}
249+
}

tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import org.bouncycastle.tls.crypto.TlsSRPConfig;
6565
import org.bouncycastle.tls.crypto.TlsSecret;
6666
import org.bouncycastle.tls.crypto.impl.AbstractTlsCrypto;
67+
import org.bouncycastle.tls.crypto.impl.Tls13NullCipher;
6768
import org.bouncycastle.tls.crypto.impl.TlsAEADCipher;
6869
import org.bouncycastle.tls.crypto.impl.TlsBlockCipher;
6970
import org.bouncycastle.tls.crypto.impl.TlsImplUtils;
@@ -178,6 +179,12 @@ public TlsCipher createCipher(TlsCryptoParameters cryptoParams, int encryptionAl
178179
return createChaCha20Poly1305(cryptoParams);
179180
case EncryptionAlgorithm.NULL:
180181
return createNullCipher(cryptoParams, macAlgorithm);
182+
case EncryptionAlgorithm.NULL_HMAC_SHA256:
183+
// NOTE: Ignores macAlgorithm
184+
return create13NullCipher(cryptoParams, MACAlgorithm.hmac_sha256);
185+
case EncryptionAlgorithm.NULL_HMAC_SHA384:
186+
// NOTE: Ignores macAlgorithm
187+
return create13NullCipher(cryptoParams, MACAlgorithm.hmac_sha384);
181188
case EncryptionAlgorithm.SM4_CCM:
182189
// NOTE: Ignores macAlgorithm
183190
return createCipher_SM4_CCM(cryptoParams);
@@ -345,6 +352,12 @@ public boolean hasEncryptionAlgorithm(int encryptionAlgorithm)
345352
case EncryptionAlgorithm.SM4_GCM:
346353
return true;
347354

355+
case EncryptionAlgorithm.NULL_HMAC_SHA256:
356+
return hasMacAlgorithm(MACAlgorithm.hmac_sha256);
357+
358+
case EncryptionAlgorithm.NULL_HMAC_SHA384:
359+
return hasMacAlgorithm(MACAlgorithm.hmac_sha384);
360+
348361
case EncryptionAlgorithm._28147_CNT_IMIT:
349362
case EncryptionAlgorithm.DES_CBC:
350363
case EncryptionAlgorithm.DES40_CBC:
@@ -675,6 +688,12 @@ protected TlsAEADCipher createCipher_SM4_GCM(TlsCryptoParameters cryptoParams)
675688
return new TlsAEADCipher(cryptoParams, encrypt, decrypt, 16, 16, TlsAEADCipher.AEAD_GCM, null);
676689
}
677690

691+
protected Tls13NullCipher create13NullCipher(TlsCryptoParameters cryptoParams, int macAlgorithm)
692+
throws IOException
693+
{
694+
return new Tls13NullCipher(cryptoParams, createHMAC(macAlgorithm), createHMAC(macAlgorithm));
695+
}
696+
678697
protected TlsNullCipher createNullCipher(TlsCryptoParameters cryptoParams, int macAlgorithm)
679698
throws IOException
680699
{

0 commit comments

Comments
 (0)