[PATCH] CipherStream produces new byte array on every update or doFinal operation
Valerie Peng
valerie.peng at oracle.com
Fri Feb 20 19:50:08 UTC 2015
I started with this, but I have a pressing higher priority tasks at hand.
So, it will take me a few weeks to wrap this up.
Regards,
Valerie
On 2/17/2015 4:06 PM, Dai Nakanishi wrote:
> Hi,
>
> CipherInputStream and CipherOutputStream call the Cipher methods update
> and doFinal which produce new byte array. It may consume a large amount
> of memory.
>
> The purpose of my patch is to avoid this. Could you review the patch?
>
> Thanks,
> Dai
>
> --- old/src/share/classes/javax/crypto/CipherInputStream.java Thu Feb 12 11:55:05 2015 +0900
> +++ new/src/share/classes/javax/crypto/CipherInputStream.java Tue Feb 17 17:12:08 2015 +0900
> @@ -108,34 +108,50 @@
> read = true;
> if (readin == -1) {
> done = true;
> + ensureCapacity(0);
> try {
> - obuffer = cipher.doFinal();
> - } catch (IllegalBlockSizeException | BadPaddingException e) {
> + ofinish = cipher.doFinal(obuffer, 0);
> + } catch (IllegalBlockSizeException | BadPaddingException
> + | ShortBufferException e) {
> obuffer = null;
> throw new IOException(e);
> }
> - if (obuffer == null)
> + if (ofinish == 0)
> return -1;
> else {
> ostart = 0;
> - ofinish = obuffer.length;
> return ofinish;
> }
> }
> + ensureCapacity(readin);
> try {
> - obuffer = cipher.update(ibuffer, 0, readin);
> + ofinish = cipher.update(ibuffer, 0, readin, obuffer);
> } catch (IllegalStateException e) {
> obuffer = null;
> throw e;
> + } catch (ShortBufferException e) {
> + obuffer = null;
> + throw new IOException(e);
> }
> ostart = 0;
> - if (obuffer == null)
> - ofinish = 0;
> - else ofinish = obuffer.length;
> return ofinish;
> }
>
> /**
> + * Ensure the capacity of the output buffer for the next
> + * update or doFinal operation, given the input length
> + *<code>inputLen</code> (in bytes)
> + *
> + * @param inputLen the input length (in bytes)
> + */
> + private void ensureCapacity(int inputLen) {
> + int outputLen = cipher.getOutputSize(inputLen);
> + if (obuffer == null || obuffer.length< outputLen) {
> + obuffer = new byte[outputLen];
> + }
> + }
> +
> + /**
> * Constructs a CipherInputStream from an InputStream and a
> * Cipher.
> *<br>Note: if the specified input stream or cipher is
> @@ -311,10 +327,12 @@
> try {
> // throw away the unprocessed data
> if (!done) {
> - cipher.doFinal();
> + ensureCapacity(0);
> + cipher.doFinal(obuffer, 0);
> }
> }
> - catch (BadPaddingException | IllegalBlockSizeException ex) {
> + catch (BadPaddingException | IllegalBlockSizeException
> + | ShortBufferException ex) {
> /* If no data has been read from the stream to be en/decrypted,
> we supress any exceptions, and close quietly. */
> if (read) {
>
>
> --- old/src/share/classes/javax/crypto/CipherOutputStream.java Thu Feb 12 11:55:05 2015 +0900
> +++ new/src/share/classes/javax/crypto/CipherOutputStream.java Tue Feb 17 18:48:01 2015 +0900
> @@ -74,6 +74,9 @@
> // the buffer holding data ready to be written out
> private byte[] obuffer;
>
> + // the number of bytes stored in the output buffer
> + private int ostored = 0;
> +
> // stream status
> private boolean closed = false;
>
> @@ -118,10 +121,15 @@
> */
> public void write(int b) throws IOException {
> ibuffer[0] = (byte) b;
> - obuffer = cipher.update(ibuffer, 0, 1);
> - if (obuffer != null) {
> - output.write(obuffer);
> - obuffer = null;
> + ensureCapacity(1);
> + try {
> + ostored = cipher.update(ibuffer, 0, 1, obuffer);
> + } catch (ShortBufferException e) {
> + throw new IOException(e);
> + }
> + if (ostored> 0) {
> + output.write(obuffer, 0, ostored);
> + ostored = 0;
> }
> };
>
> @@ -155,10 +163,15 @@
> * @since JCE1.2
> */
> public void write(byte b[], int off, int len) throws IOException {
> - obuffer = cipher.update(b, off, len);
> - if (obuffer != null) {
> - output.write(obuffer);
> - obuffer = null;
> + ensureCapacity(len);
> + try {
> + ostored = cipher.update(b, off, len, obuffer);
> + } catch (ShortBufferException e) {
> + throw new IOException(e);
> + }
> + if (ostored> 0) {
> + output.write(obuffer, 0, ostored);
> + ostored = 0;
> }
> }
>
> @@ -177,9 +190,9 @@
> * @since JCE1.2
> */
> public void flush() throws IOException {
> - if (obuffer != null) {
> - output.write(obuffer);
> - obuffer = null;
> + if (obuffer != null&& ostored> 0) {
> + output.write(obuffer, 0, ostored);
> + ostored = 0;
> }
> output.flush();
> }
> @@ -206,14 +219,31 @@
> }
>
> closed = true;
> + ensureCapacity(0);
> try {
> - obuffer = cipher.doFinal();
> - } catch (IllegalBlockSizeException | BadPaddingException e) {
> + ostored = cipher.doFinal(obuffer, 0);
> + } catch (IllegalBlockSizeException | BadPaddingException
> + | ShortBufferException e) {
> obuffer = null;
> + ostored = 0;
> }
> try {
> flush();
> } catch (IOException ignored) {}
> out.close();
> + }
> +
> + /**
> + * Ensure the capacity of the output buffer for the next
> + * update or doFinal operation, given the input length
> + *<code>inputLen</code> (in bytes)
> + *
> + * @param inputLen the input length (in bytes)
> + */
> + private void ensureCapacity(int inputLen) {
> + int outputLen = cipher.getOutputSize(inputLen);
> + if (obuffer == null || obuffer.length< outputLen) {
> + obuffer = new byte[outputLen];
> + }
> }
> }
>
More information about the security-dev
mailing list