Skip to content

Commit 48167a8

Browse files
committed
Mechanism to configure AES-GCM in TLS 1.2 with custom nonce generator
1 parent c349210 commit 48167a8

File tree

6 files changed

+164
-14
lines changed

6 files changed

+164
-14
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package org.bouncycastle.jsse.provider;
2+
3+
import org.bouncycastle.tls.crypto.TlsNonceGenerator;
4+
5+
final public class GcmTls12NonceGeneratorUtil
6+
{
7+
private static TlsNonceGeneratorFactory tlsNonceGeneratorFactory = null;
8+
9+
public static void setGcmTlsNonceGeneratorFactory(final TlsNonceGeneratorFactory factory)
10+
{
11+
tlsNonceGeneratorFactory = factory;
12+
}
13+
14+
public static boolean isGcmFipsNonceGeneratorFactorySet()
15+
{
16+
return tlsNonceGeneratorFactory != null;
17+
}
18+
19+
public static TlsNonceGenerator createGcmFipsNonceGenerator(final byte[] baseNonce, final int counterSizeInBits)
20+
{
21+
return tlsNonceGeneratorFactory != null
22+
? tlsNonceGeneratorFactory.create(baseNonce, counterSizeInBits)
23+
: null;
24+
}
25+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package org.bouncycastle.jsse.provider;
2+
3+
import org.bouncycastle.tls.crypto.TlsNonceGenerator;
4+
5+
public interface TlsNonceGeneratorFactory
6+
{
7+
TlsNonceGenerator create(byte[] baseNonce, int counterSizeInBits);
8+
}

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

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@
1313
import org.bouncycastle.tls.crypto.TlsCryptoUtils;
1414
import org.bouncycastle.tls.crypto.TlsDecodeResult;
1515
import org.bouncycastle.tls.crypto.TlsEncodeResult;
16+
import org.bouncycastle.tls.crypto.TlsNonceGenerator;
1617
import org.bouncycastle.tls.crypto.TlsSecret;
1718
import org.bouncycastle.util.Arrays;
1819

20+
import static org.bouncycastle.jsse.provider.GcmTls12NonceGeneratorUtil.createGcmFipsNonceGenerator;
21+
import static org.bouncycastle.jsse.provider.GcmTls12NonceGeneratorUtil.isGcmFipsNonceGeneratorFactorySet;
22+
1923
/**
2024
* A generic TLS 1.2 AEAD cipher.
2125
*/
@@ -30,6 +34,8 @@ public final class TlsAEADCipher
3034
private static final int NONCE_RFC7905 = 2;
3135
private static final long SEQUENCE_NUMBER_PLACEHOLDER = -1L;
3236

37+
private static final byte[] EPOCH_1 = {0x00, 0x01};
38+
3339
private final TlsCryptoParameters cryptoParams;
3440
private final int keySize;
3541
private final int macSize;
@@ -43,6 +49,7 @@ public final class TlsAEADCipher
4349

4450
private final boolean isTLSv13;
4551
private final int nonceMode;
52+
private final TlsNonceGenerator gcmFipsNonceGenerator;
4653

4754
public TlsAEADCipher(TlsCryptoParameters cryptoParams, TlsAEADCipherImpl encryptCipher, TlsAEADCipherImpl decryptCipher,
4855
int keySize, int macSize, int aeadType) throws IOException
@@ -91,6 +98,7 @@ public TlsAEADCipher(TlsCryptoParameters cryptoParams, TlsAEADCipherImpl encrypt
9198
final boolean isServer = cryptoParams.isServer();
9299
if (isTLSv13)
93100
{
101+
gcmFipsNonceGenerator = null;
94102
rekeyCipher(securityParameters, decryptCipher, decryptNonce, !isServer);
95103
rekeyCipher(securityParameters, encryptCipher, encryptNonce, isServer);
96104
return;
@@ -121,6 +129,28 @@ public TlsAEADCipher(TlsCryptoParameters cryptoParams, TlsAEADCipherImpl encrypt
121129
{
122130
throw new TlsFatalAlert(AlertDescription.internal_error);
123131
}
132+
133+
if (AEAD_GCM == aeadType && isGcmFipsNonceGeneratorFactorySet())
134+
{
135+
final int nonceLength = fixed_iv_length + record_iv_length;
136+
final byte[] baseNonce = Arrays.copyOf(encryptNonce, nonceLength);
137+
final int counterSizeInBits;
138+
if (negotiatedVersion.isDTLS())
139+
{
140+
counterSizeInBits = (record_iv_length - 2) * 8; // 48
141+
baseNonce[baseNonce.length - 8] ^= EPOCH_1[0];
142+
baseNonce[baseNonce.length - 7] ^= EPOCH_1[1];
143+
}
144+
else
145+
{
146+
counterSizeInBits = record_iv_length * 8; // 64
147+
}
148+
gcmFipsNonceGenerator = createGcmFipsNonceGenerator(baseNonce, counterSizeInBits);
149+
}
150+
else
151+
{
152+
gcmFipsNonceGenerator = null;
153+
}
124154
}
125155

126156
public int getCiphertextDecodeLimit(int plaintextLimit)
@@ -154,24 +184,33 @@ public int getPlaintextEncodeLimit(int ciphertextLimit)
154184
public TlsEncodeResult encodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion,
155185
int headerAllocation, byte[] plaintext, int plaintextOffset, int plaintextLength) throws IOException
156186
{
157-
byte[] nonce = new byte[encryptNonce.length + record_iv_length];
187+
final int nonceSize = encryptNonce.length + record_iv_length;
188+
final byte[] nonce;
158189

159-
switch (nonceMode)
190+
if (null != gcmFipsNonceGenerator)
160191
{
161-
case NONCE_RFC5288:
162-
System.arraycopy(encryptNonce, 0, nonce, 0, encryptNonce.length);
163-
// RFC 5288/6655: The nonce_explicit MAY be the 64-bit sequence number.
164-
TlsUtils.writeUint64(seqNo, nonce, encryptNonce.length);
165-
break;
166-
case NONCE_RFC7905:
167-
TlsUtils.writeUint64(seqNo, nonce, nonce.length - 8);
168-
for (int i = 0; i < encryptNonce.length; ++i)
192+
nonce = gcmFipsNonceGenerator.generateNonce(nonceSize);
193+
}
194+
else
195+
{
196+
nonce = new byte[nonceSize];
197+
switch (nonceMode)
169198
{
170-
nonce[i] ^= encryptNonce[i];
199+
case NONCE_RFC5288:
200+
System.arraycopy(encryptNonce, 0, nonce, 0, encryptNonce.length);
201+
// RFC 5288/6655: The nonce_explicit MAY be the 64-bit sequence number.
202+
TlsUtils.writeUint64(seqNo, nonce, encryptNonce.length);
203+
break;
204+
case NONCE_RFC7905:
205+
TlsUtils.writeUint64(seqNo, nonce, nonce.length - 8);
206+
for (int i = 0; i < encryptNonce.length; ++i)
207+
{
208+
nonce[i] ^= encryptNonce[i];
209+
}
210+
break;
211+
default:
212+
throw new TlsFatalAlert(AlertDescription.internal_error);
171213
}
172-
break;
173-
default:
174-
throw new TlsFatalAlert(AlertDescription.internal_error);
175214
}
176215

177216
// TODO[tls13, cid] If we support adding padding to (D)TLSInnerPlaintext, this will need review

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.bouncycastle.tls.test;
22

3+
import org.bouncycastle.jsse.provider.GcmTls12NonceGeneratorUtil;
34
import org.bouncycastle.test.PrintTestResult;
45

56
import junit.extensions.TestSetup;
@@ -14,6 +15,13 @@ public static void main(String[] args)
1415
throws Exception
1516
{
1617
PrintTestResult.printResult(junit.textui.TestRunner.run(suite()));
18+
PrintTestResult.printResult(junit.textui.TestRunner.run(suiteWithCustomNonceGeneratorForTls12()));
19+
}
20+
21+
public static Test suiteWithCustomNonceGeneratorForTls12() throws Exception
22+
{
23+
GcmTls12NonceGeneratorUtil.setGcmTlsNonceGeneratorFactory(TestTlsNonceGeneratorFactory.INSTANCE);
24+
return suite();
1725
}
1826

1927
public static Test suite()
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package org.bouncycastle.tls.test;
2+
3+
import org.bouncycastle.tls.crypto.TlsNonceGenerator;
4+
5+
import java.util.Arrays;
6+
7+
class TestNonceGenerator implements TlsNonceGenerator
8+
{
9+
private final byte[] baseNonce;
10+
private final long counterMask;
11+
private final int counterBytes;
12+
13+
private long counterValue;
14+
private boolean counterExhausted;
15+
16+
TestNonceGenerator(final byte[] baseNonce, final int counterBits)
17+
{
18+
this.baseNonce = Arrays.copyOf(baseNonce, baseNonce.length);
19+
this.counterMask = -1L >>> (64 - counterBits);
20+
this.counterBytes = (counterBits + 7) / 8;
21+
22+
this.counterValue = 0L;
23+
this.counterExhausted = false;
24+
}
25+
26+
@Override
27+
public byte[] generateNonce(final int size)
28+
{
29+
if (size != baseNonce.length)
30+
{
31+
throw new IllegalArgumentException("requested length is not equal to the length of the base nonce.");
32+
}
33+
34+
if (counterExhausted)
35+
{
36+
throw new IllegalStateException("TLS nonce generator exhausted");
37+
}
38+
39+
final byte[] nonce = Arrays.copyOf(baseNonce, baseNonce.length);
40+
final int offset = baseNonce.length - counterBytes;
41+
42+
for (int i = 0; i < counterBytes; i++)
43+
{
44+
nonce[offset + i] ^= (byte)(counterValue >>> ((counterBytes - 1 - i) * 8));
45+
}
46+
47+
counterExhausted |= ((++counterValue & counterMask) == 0);
48+
49+
return nonce;
50+
}
51+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package org.bouncycastle.tls.test;
2+
3+
import org.bouncycastle.jsse.provider.TlsNonceGeneratorFactory;
4+
import org.bouncycastle.tls.crypto.TlsNonceGenerator;
5+
6+
class TestTlsNonceGeneratorFactory implements TlsNonceGeneratorFactory {
7+
public static final TlsNonceGeneratorFactory INSTANCE = new TestTlsNonceGeneratorFactory();
8+
9+
private TestTlsNonceGeneratorFactory()
10+
{
11+
// no op
12+
}
13+
14+
@Override
15+
public TlsNonceGenerator create(final byte[] baseNonce, final int counterSizeInBits)
16+
{
17+
return new TestNonceGenerator(baseNonce, counterSizeInBits);
18+
}
19+
}

0 commit comments

Comments
 (0)