24
24
*/
25
25
/*
26
26
* ===========================================================================
27
- * (c) Copyright IBM Corp. 2018, 2023 All Rights Reserved
27
+ * (c) Copyright IBM Corp. 2018, 2025 All Rights Reserved
28
28
* ===========================================================================
29
29
*/
30
30
33
33
import java .security .InvalidKeyException ;
34
34
import java .security .ProviderException ;
35
35
import java .util .ArrayDeque ;
36
+ import java .util .Arrays ;
37
+
36
38
import com .sun .crypto .provider .AESCrypt ;
37
39
38
40
import jdk .crypto .jniprovider .NativeCrypto ;
47
49
* native implementation of CBC crypto.
48
50
*
49
51
*/
50
-
51
52
class NativeCipherBlockChaining extends FeedbackCipher {
52
53
53
54
protected final static int numContexts = 4096 ;
@@ -58,6 +59,12 @@ class NativeCipherBlockChaining extends FeedbackCipher {
58
59
private static final Cleaner contextCleaner ;
59
60
private int previousKeyLength = -1 ;
60
61
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
+
61
68
/*
62
69
* Initialize the CBC context.
63
70
*/
@@ -104,6 +111,29 @@ public void run() {
104
111
}
105
112
}
106
113
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
+
107
137
/*
108
138
* Get CBC context.
109
139
*/
@@ -266,7 +296,7 @@ int encrypt(byte[] plain, int plainOffset, int plainLen,
266
296
throw new ProviderException ("Internal error in input buffering" );
267
297
}
268
298
269
- /**
299
+ /*
270
300
* OpenSSL doesn't support overlapping buffers, make a copy of plain.
271
301
*/
272
302
if (plain == cipher ) {
@@ -276,15 +306,34 @@ int encrypt(byte[] plain, int plainOffset, int plainLen,
276
306
plainOffset = 0 ;
277
307
}
278
308
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
+
279
315
int ret ;
280
316
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 );
283
323
}
284
324
if (ret == -1 ) {
285
325
throw new ProviderException ("Error in Native CipherBlockChaining" );
286
326
}
287
327
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
+
288
337
// saving current running state
289
338
System .arraycopy (cipher , cipherOffset +plainLen -blockSize , r , 0 , blockSize );
290
339
return ret ;
@@ -339,16 +388,39 @@ int encryptFinal(byte[] plain, int plainOffset, int plainLen,
339
388
340
389
int ret ;
341
390
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
+
342
397
synchronized (this ) {
343
398
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 );
346
405
} 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 );
349
412
}
350
413
}
351
414
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
+
352
424
if (ret == -1 ) {
353
425
throw new ProviderException ("Error in Native CipherBlockChaining" );
354
426
}
0 commit comments