@@ -838,6 +838,45 @@ private boolean isBlockCipher() {
838838 return isBlockCipher ;
839839 }
840840
841+ /**
842+ * If a call to update() would be a no-op (or just return byte[0]) for the
843+ * selected cipher type, mode, and buffered data, return true.
844+ *
845+ * This happens in cases like RSA, or AES-GCM/CCM which don't support
846+ * streaming and buffer all data until doFinal() is called.
847+ *
848+ * @param inputSz total size in bytes of data available for processing,
849+ * including input data and buffered data.
850+ *
851+ * @return true if update() would be a no-op, otherwise false
852+ */
853+ private boolean isNoOpUpdate (int inputSz ) {
854+
855+ /* RSA keeps buffered data until final() call */
856+ if (cipherType == CipherType .WC_RSA ) {
857+ return true ;
858+ }
859+
860+ /* AES-GCM and AES-CCM keep all data buffered until final() call,
861+ * wolfJCE does not support streaming GCM/CCM yet. */
862+ if (cipherType == CipherType .WC_AES &&
863+ (cipherMode == CipherMode .WC_GCM ||
864+ cipherMode == CipherMode .WC_CCM )) {
865+ return true ;
866+ }
867+
868+ /* If total data input (plus buffered) is less than block size,
869+ * update() is a no-op, except for CTR and OFB which are stream
870+ * ciphers */
871+ if ((inputSz < blockSize ) &&
872+ (cipherMode != CipherMode .WC_CTR ) &&
873+ (cipherMode != CipherMode .WC_OFB )) {
874+ return true ;
875+ }
876+
877+ return false ;
878+ }
879+
841880 private byte [] wolfCryptUpdate (byte [] input , int inputOffset , int len )
842881 throws IllegalArgumentException {
843882
@@ -847,8 +886,15 @@ private byte[] wolfCryptUpdate(byte[] input, int inputOffset, int len)
847886 byte [] tmpIn = null ;
848887 byte [] tmpBuf = null ;
849888
850- if (input == null || len < 0 )
851- throw new IllegalArgumentException ("Null input buffer or len < 0" );
889+ if (input == null || len < 0 || inputOffset < 0 ) {
890+ throw new IllegalArgumentException (
891+ "Null input buffer or len/offset < 0" );
892+ }
893+
894+ if (input .length < (inputOffset + len )) {
895+ throw new IllegalArgumentException (
896+ "Input buffer length smaller than inputOffset + len" );
897+ }
852898
853899 this .operationStarted = true ;
854900
@@ -865,36 +911,32 @@ private byte[] wolfCryptUpdate(byte[] input, int inputOffset, int len)
865911 buffered = tmpIn ;
866912 }
867913
868- /* keep buffered data if RSA or data is less than block size, or doing
869- * AES-GCM/CCM without stream mode compiled natively, but not for
870- * CTR/OFB which are stream ciphers */
871- if ((cipherType == CipherType .WC_RSA ) ||
872- ((cipherType == CipherType .WC_AES ) &&
873- (cipherMode == CipherMode .WC_GCM ||
874- cipherMode == CipherMode .WC_CCM )) ||
875- ((buffered .length < blockSize ) &&
876- (cipherMode != CipherMode .WC_CTR ) &&
877- (cipherMode != CipherMode .WC_OFB ))) {
914+ /* Some algos/modes keep data buffered until the doFinal() call, like
915+ * RSA or AES-GCM/CCM without stream mode compiled natively. Just
916+ * return an empty byte array in those cases here. */
917+ if (isNoOpUpdate (len + buffered .length )) {
878918 return new byte [0 ];
879919 }
880920
881- /* calculate blocks and partial non-block size remaining */
882- blocks = buffered .length / blockSize ;
921+ /* Calculate blocks and partial non-block size remaining */
922+ blocks = buffered .length / blockSize ;
883923 bytesToProcess = blocks * blockSize ;
884924
885925 /* CTR and OFB are stream ciphers, process all available data */
886926 if (cipherMode == CipherMode .WC_CTR ||
887927 cipherMode == CipherMode .WC_OFB ) {
888928 bytesToProcess = buffered .length ;
889929 }
890- /* if PKCS#5/7 padding, and decrypting, hold on to last block for
930+
931+ /* If PKCS#5/7 padding, and decrypting, hold on to last block for
891932 * padding check in wolfCryptFinal() */
892933 else if (paddingType == PaddingType .WC_PKCS5 &&
893- direction == OpMode .WC_DECRYPT ) {
934+ direction == OpMode .WC_DECRYPT &&
935+ bytesToProcess > 0 ) {
894936 bytesToProcess -= blockSize ;
895937 }
896938
897- /* not enough data to process yet return until more or final */
939+ /* Not enough data to process yet return until more or final */
898940 if (bytesToProcess == 0 ) {
899941 return new byte [0 ];
900942 }
@@ -1207,6 +1249,43 @@ protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen)
12071249 return output ;
12081250 }
12091251
1252+ /**
1253+ * Sanity check output buffer size is large enough for update() call,
1254+ * based on padding and buffered data.
1255+ *
1256+ * @param inputSz size of input data to update()
1257+ * @param outputSz total size of output buffer provided
1258+ *
1259+ * @throws ShortBufferException if output buffer is too small
1260+ */
1261+ private void checkUpdateOutputBufferSize (int inputSz , int outputSz )
1262+ throws ShortBufferException {
1263+
1264+ int outSize ;
1265+
1266+ if (!isNoOpUpdate (inputSz )) {
1267+ outSize = engineGetOutputSize (inputSz );
1268+
1269+ /* update() in DECRYPT mode with PKCS5 padding will hold
1270+ * back one block of data for padding check in final() */
1271+ if (direction == OpMode .WC_DECRYPT &&
1272+ paddingType == PaddingType .WC_PKCS5 ) {
1273+ if (outSize % blockSize == 0 ) {
1274+ outSize -= blockSize ;
1275+ }
1276+ else {
1277+ outSize -= (outSize % blockSize );
1278+ }
1279+ }
1280+
1281+ if (outputSz < outSize ) {
1282+ throw new ShortBufferException (
1283+ "Output buffer too small, need " + outSize +
1284+ " bytes, got " + outputSz );
1285+ }
1286+ }
1287+ }
1288+
12101289 @ Override
12111290 protected int engineUpdate (byte [] input , int inputOffset , int inputLen ,
12121291 byte [] output , int outputOffset )
@@ -1219,8 +1298,21 @@ protected int engineUpdate(byte[] input, int inputOffset, int inputLen,
12191298 "Cipher has not been initialized yet" );
12201299 }
12211300
1222- log ("update (in offset: " + inputOffset + ", len: " +
1223- inputLen + ", out offset: " + outputOffset + ")" );
1301+ log ("update (inputOffset: " + inputOffset + ", inputLen: " +
1302+ inputLen + ", outputOffset: " + outputOffset + ")" );
1303+
1304+ if (output == null || (output .length < outputOffset )) {
1305+ throw new IllegalArgumentException (
1306+ "output is null or offset past output array sz" );
1307+ }
1308+
1309+ if (input == null || (inputLen + inputOffset > input .length )) {
1310+ throw new IllegalArgumentException (
1311+ "input is null or inOffset + inputLen past input array size" );
1312+ }
1313+
1314+ /* Sanitize output buffer size, throws ShortBufferException if needed */
1315+ checkUpdateOutputBufferSize (inputLen , output .length - outputOffset );
12241316
12251317 tmpOut = wolfCryptUpdate (input , inputOffset , inputLen );
12261318 if (tmpOut == null ) {
@@ -1278,9 +1370,25 @@ protected int engineDoFinal(byte[] input, int inputOffset,
12781370 "Cipher has not been initialized yet" );
12791371 }
12801372
1281- log ("final (in offset: " + inputOffset + ", len: " +
1282- inputLen + ", out offset: " + outputOffset +
1283- ", buffered: " + buffered .length + ")" );
1373+ log ("final (inputOffset: " + inputOffset + ", inputLen: " +
1374+ inputLen + ", outputOffset: " + outputOffset + ", buffered: " +
1375+ buffered .length + ")" );
1376+
1377+ if (output == null || (outputOffset > output .length )) {
1378+ throw new IllegalArgumentException (
1379+ "output is null or offset past output array sz" );
1380+ }
1381+
1382+ /* SunJCE can save Cipher state so it can throw a more precise
1383+ * ShortBufferException after checking the actual length after
1384+ * stripping padding. But, native wolfCrypt does not support
1385+ * saving/restoring Aes state, so we err on the side of making callers
1386+ * give us up to the next block size of output space */
1387+ if ((output .length - outputOffset ) < engineGetOutputSize (inputLen )) {
1388+ throw new ShortBufferException ("Output buffer too small, need " +
1389+ engineGetOutputSize (inputLen ) + " bytes, got " +
1390+ (output .length - outputOffset ));
1391+ }
12841392
12851393 tmpOut = wolfCryptFinal (input , inputOffset , inputLen );
12861394
0 commit comments