Skip to content

Commit 20b8ee0

Browse files
jasonkatonicapshipton
authored andcommitted
Prevent AES/CBC native buffer overflow
Native AES acceleration is available when using AES and CBC mode of operation. While calling the underlying OpenSSL library operations often do not supply a large enough buffer which is documented by the OpenSSL EVP_EncryptUpdate as input length + an additional block size. This update ensures buffers sent to this API are large enough. Signed-off-by: Jason Katonica <[email protected]>
1 parent b1f3df6 commit 20b8ee0

File tree

2 files changed

+98
-12
lines changed

2 files changed

+98
-12
lines changed

closed/src/java.base/share/classes/com/sun/crypto/provider/NativeCipherBlockChaining.java

Lines changed: 81 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
*/
2525
/*
2626
* ===========================================================================
27-
* (c) Copyright IBM Corp. 2018, 2023 All Rights Reserved
27+
* (c) Copyright IBM Corp. 2018, 2025 All Rights Reserved
2828
* ===========================================================================
2929
*/
3030

@@ -33,6 +33,8 @@
3333
import java.security.InvalidKeyException;
3434
import java.security.ProviderException;
3535
import java.util.ArrayDeque;
36+
import java.util.Arrays;
37+
3638
import com.sun.crypto.provider.AESCrypt;
3739

3840
import jdk.crypto.jniprovider.NativeCrypto;
@@ -47,7 +49,6 @@
4749
* native implementation of CBC crypto.
4850
*
4951
*/
50-
5152
class NativeCipherBlockChaining extends FeedbackCipher {
5253

5354
protected final static int numContexts = 4096;
@@ -58,6 +59,12 @@ class NativeCipherBlockChaining extends FeedbackCipher {
5859
private static final Cleaner contextCleaner;
5960
private int previousKeyLength = -1;
6061

62+
/**
63+
* OpenSSL requires an additional block size for operations. This will be added
64+
* to all calculated output buffer sizes whenever native CBC operations are enabled.
65+
*/
66+
static final int OPENSSL_ENCRYPTION_RESIDUE = 16;
67+
6168
/*
6269
* Initialize the CBC context.
6370
*/
@@ -104,6 +111,29 @@ public void run() {
104111
}
105112
}
106113

114+
/**
115+
* This method checks if there is enough space in the provided output buffer
116+
* to accommodate encryption in OpenSSL. OpenSSL requires an extra full block size
117+
* for its operations according to the documentation associated with EVP_CipherUpdate
118+
* (EVP_EncryptUpdate).
119+
*
120+
* @param output the original output buffer
121+
* @param outputOffset the current offset in the output buffer
122+
* @param inputLen the length of the input data
123+
* @return a new byte array that can hold the combined output and residue (extra block),
124+
* or the original buffer if there is enough space in the output buffer {@code output}
125+
*/
126+
private static byte[] getOptionalLocalOpenSSLOutputBuffer(byte[] output, int outputOffset, int inputLen) {
127+
byte[] tmpOutputBuffer;
128+
int extraLen = Math.addExact(inputLen, OPENSSL_ENCRYPTION_RESIDUE);
129+
if (extraLen > (output.length - outputOffset)) {
130+
tmpOutputBuffer = new byte[extraLen];
131+
} else {
132+
tmpOutputBuffer = output;
133+
}
134+
return tmpOutputBuffer;
135+
}
136+
107137
/*
108138
* Get CBC context.
109139
*/
@@ -266,7 +296,7 @@ int encrypt(byte[] plain, int plainOffset, int plainLen,
266296
throw new ProviderException("Internal error in input buffering");
267297
}
268298

269-
/**
299+
/*
270300
* OpenSSL doesn't support overlapping buffers, make a copy of plain.
271301
*/
272302
if (plain == cipher) {
@@ -276,15 +306,34 @@ int encrypt(byte[] plain, int plainOffset, int plainLen,
276306
plainOffset = 0;
277307
}
278308

309+
/*
310+
* Determine if our output buffer is big enough for OpenSSL operations. Allocate a new
311+
* one if required.
312+
*/
313+
byte[] tmpOutputBuffer = getOptionalLocalOpenSSLOutputBuffer(cipher, cipherOffset, plainLen);
314+
279315
int ret;
280316
synchronized (this) {
281-
ret = nativeCrypto.CBCUpdate(nativeContext, plain, plainOffset,
282-
plainLen, cipher, cipherOffset);
317+
ret = nativeCrypto.CBCUpdate(nativeContext,
318+
plain,
319+
plainOffset,
320+
plainLen,
321+
tmpOutputBuffer,
322+
(cipher == tmpOutputBuffer) ? cipherOffset : 0);
283323
}
284324
if (ret == -1) {
285325
throw new ProviderException("Error in Native CipherBlockChaining");
286326
}
287327

328+
/*
329+
* If a larger output buffer was required for OpenSSL operations then copy back the results
330+
* into the callers output buffer.
331+
*/
332+
if (cipher != tmpOutputBuffer) {
333+
System.arraycopy(tmpOutputBuffer, 0, cipher, cipherOffset, ret);
334+
Arrays.fill(tmpOutputBuffer, (byte)0x00);
335+
}
336+
288337
// saving current running state
289338
System.arraycopy(cipher, cipherOffset+plainLen-blockSize, r, 0, blockSize);
290339
return ret;
@@ -339,16 +388,39 @@ int encryptFinal(byte[] plain, int plainOffset, int plainLen,
339388

340389
int ret;
341390

391+
/*
392+
* Determine if our output buffer is big enough for OpenSSL operations. Allocate a new
393+
* one if required.
394+
*/
395+
byte[] tmpOutputBuffer = getOptionalLocalOpenSSLOutputBuffer(cipher, cipherOffset, plainLen);
396+
342397
synchronized (this) {
343398
if (plain == cipher) {
344-
ret = nativeCrypto.CBCFinalEncrypt(nativeContext, plain.clone(),
345-
plainOffset, plainLen, cipher, cipherOffset);
399+
ret = nativeCrypto.CBCFinalEncrypt(nativeContext,
400+
plain.clone(),
401+
plainOffset,
402+
plainLen,
403+
tmpOutputBuffer,
404+
(cipher == tmpOutputBuffer) ? cipherOffset : 0);
346405
} else {
347-
ret = nativeCrypto.CBCFinalEncrypt(nativeContext, plain, plainOffset,
348-
plainLen, cipher, cipherOffset);
406+
ret = nativeCrypto.CBCFinalEncrypt(nativeContext,
407+
plain,
408+
plainOffset,
409+
plainLen,
410+
tmpOutputBuffer,
411+
(cipher == tmpOutputBuffer) ? cipherOffset : 0);
349412
}
350413
}
351414

415+
/*
416+
* If a larger output buffer was required for OpenSSL operations then copy back the results
417+
* into the callers output buffer.
418+
*/
419+
if (cipher != tmpOutputBuffer) {
420+
System.arraycopy(tmpOutputBuffer, 0, cipher, cipherOffset, ret);
421+
Arrays.fill(tmpOutputBuffer, (byte)0x00);
422+
}
423+
352424
if (ret == -1) {
353425
throw new ProviderException("Error in Native CipherBlockChaining");
354426
}

src/java.base/share/classes/com/sun/crypto/provider/CipherCore.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
*/
2525
/*
2626
* ===========================================================================
27-
* (c) Copyright IBM Corp. 2018, 2023 All Rights Reserved
27+
* (c) Copyright IBM Corp. 2018, 2025 All Rights Reserved
2828
* ===========================================================================
2929
*/
3030

@@ -323,6 +323,20 @@ private int getOutputSizeByOperation(int inputLen, boolean isDoFinal) {
323323
return totalLen;
324324
}
325325

326+
/**
327+
* Get appropriate sized local working buffer. An additional block size is added
328+
* for operations that will use the NativeCipherBlockChaining cipher.
329+
*
330+
* @see {@link NativeCipherBlockChaining#getOptionalLocalOpenSSLOutputBuffer(byte[], int, int)
331+
*/
332+
private int getLocalWorkingBufferSize(int inputLen, boolean isDoFinal) {
333+
int size = getOutputSizeByOperation(inputLen, isDoFinal);
334+
if (cipher instanceof NativeCipherBlockChaining) {
335+
size = Math.addExact(size, NativeCipherBlockChaining.OPENSSL_ENCRYPTION_RESIDUE);
336+
}
337+
return size;
338+
}
339+
326340
/**
327341
* Returns the initialization vector (IV) in a new buffer.
328342
*
@@ -570,7 +584,7 @@ byte[] update(byte[] input, int inputOffset, int inputLen) {
570584

571585
byte[] output = null;
572586
try {
573-
output = new byte[getOutputSizeByOperation(inputLen, false)];
587+
output = new byte[getLocalWorkingBufferSize(inputLen, false)];
574588
int len = update(input, inputOffset, inputLen, output,
575589
0);
576590
if (len == output.length) {
@@ -748,7 +762,7 @@ int update(byte[] input, int inputOffset, int inputLen, byte[] output,
748762
byte[] doFinal(byte[] input, int inputOffset, int inputLen)
749763
throws IllegalBlockSizeException, BadPaddingException {
750764
try {
751-
byte[] output = new byte[getOutputSizeByOperation(inputLen, true)];
765+
byte[] output = new byte[getLocalWorkingBufferSize(inputLen, true)];
752766
int outputOffset = 0;
753767
int outLen = 0;
754768

0 commit comments

Comments
 (0)