Skip to content

Commit e99a4ea

Browse files
committed
TLS: Avoid nonce reuse error in JCE AEAD workaround for pre-Java7
- see #2100
1 parent 9b49fab commit e99a4ea

File tree

2 files changed

+33
-7
lines changed

2 files changed

+33
-7
lines changed

docs/releasenotes.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ <h2>2.0 Release History</h2>
2424
<h3>2.1.2 Defects Fixed</h3>
2525
<ul>
2626
<li>SNOVA and MAYO are now correctly added to the JCA provider module-info file.</li>
27+
<li>TLS: Avoid nonce reuse error in JCE AEAD workaround for pre-Java7.</li>
2728
</ul>
2829
<h3>2.1.3 Additional Features and Functionality</h3>
2930
<ul>

tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceAEADCipherImpl.java

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.security.GeneralSecurityException;
77
import java.security.PrivilegedAction;
88
import java.security.SecureRandom;
9+
import java.security.spec.AlgorithmParameterSpec;
910

1011
import javax.crypto.Cipher;
1112
import javax.crypto.SecretKey;
@@ -43,6 +44,7 @@ public Object run()
4344
});
4445
}
4546

47+
// TODO[tls] Once Java 7 or higher is the baseline, this will always be true
4648
private static final boolean canDoAEAD = checkForAEAD();
4749

4850
private static String getAlgParamsName(JcaJceHelper helper, String cipherName)
@@ -68,8 +70,10 @@ private static String getAlgParamsName(JcaJceHelper helper, String cipherName)
6870
private final String algorithmParamsName;
6971

7072
private SecretKey key;
71-
private byte[] nonce;
72-
private int macSize;
73+
74+
// TODO[tls] These two are only needed while the baseline is pre-Java7
75+
private byte[] noncePre7;
76+
private int macSizePre7;
7377

7478
public JceAEADCipherImpl(JcaTlsCrypto crypto, JcaJceHelper helper, String cipherName, String algorithm, int keySize,
7579
boolean isEncrypting)
@@ -120,10 +124,27 @@ public void init(byte[] nonce, int macSize)
120124
}
121125
else
122126
{
123-
// Otherwise fall back to the BC-specific AEADParameterSpec
124-
cipher.init(cipherMode, key, new AEADParameterSpec(nonce, macSize * 8, null), random);
125-
this.nonce = Arrays.clone(nonce);
126-
this.macSize = macSize;
127+
/*
128+
* Otherwise fall back to the BC-specific AEADParameterSpec. Since updateAAD is not available, we
129+
* need to use init to pass the associated data (in doFinal), but in order to call getOutputSize we
130+
* technically need to init the cipher first. So we init with a dummy nonce to avoid duplicate nonce
131+
* error from the init in doFinal.
132+
*/
133+
134+
if (this.noncePre7 == null || this.noncePre7.length != nonce.length)
135+
{
136+
this.noncePre7 = new byte[nonce.length];
137+
}
138+
139+
System.arraycopy(nonce, 0, this.noncePre7, 0, nonce.length);
140+
this.macSizePre7 = macSize;
141+
142+
this.noncePre7[0] ^= 0x80;
143+
144+
AlgorithmParameterSpec params = new AEADParameterSpec(noncePre7, macSizePre7 * 8, null);
145+
cipher.init(cipherMode, key, params, random);
146+
147+
this.noncePre7[0] ^= 0x80;
127148
}
128149
}
129150
catch (Exception e)
@@ -150,7 +171,11 @@ public int doFinal(byte[] additionalData, byte[] input, int inputOffset, int inp
150171
{
151172
try
152173
{
153-
cipher.init(cipherMode, key, new AEADParameterSpec(nonce, macSize * 8, additionalData));
174+
// NOTE: Shouldn't need a SecureRandom, but this is cheaper if the provider would auto-create one
175+
SecureRandom random = crypto.getSecureRandom();
176+
177+
AlgorithmParameterSpec params = new AEADParameterSpec(noncePre7, macSizePre7 * 8, additionalData);
178+
cipher.init(cipherMode, key, params, random);
154179
}
155180
catch (Exception e)
156181
{

0 commit comments

Comments
 (0)