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
3333import java .security .InvalidKeyException ;
3434import java .security .ProviderException ;
3535import java .util .ArrayDeque ;
36+ import java .util .Arrays ;
37+
3638import com .sun .crypto .provider .AESCrypt ;
3739
3840import jdk .crypto .jniprovider .NativeCrypto ;
4749 * native implementation of CBC crypto.
4850 *
4951 */
50-
5152class 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 }
0 commit comments