hello all,

the attached patch was already committed on behalf of Marco Trudel.  the 
ChangeLog entry looks like so:

2006-10-23  Marco Trudel <[EMAIL PROTECTED]>

        * gnu/javax/crypto/pad/PKCS7.java (unpad): Removed an unnecessary test.
        * javax/crypto/CipherOutputStream.java: Re-implemented.
        * gnu/javax/crypto/jce/cipher/CipherAdapter.java
        (engineUpdate(byte[], int, int)): Always keep data for unpadding in 
padded
        decryption mode and check if it is a complete block.
        (engineUpdate(byte[], int, int, byte[], int)): Likewise.
        (engineDoFinal(byte[], int, int)): In padded decryption mode, take
        partially processed data into account.

a Mauve testlet 
(gnu/testlet/gnu/javax/crypto/jce/TestOfCipherOutputStream.java) recently 
committed highlights the problem and validates the fix.


cheers;
rsn
Index: CipherAdapter.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/javax/crypto/jce/cipher/CipherAdapter.java,v
retrieving revision 1.8
diff -u -r1.8 CipherAdapter.java
--- CipherAdapter.java	14 Jul 2006 14:04:59 -0000	1.8
+++ CipherAdapter.java	22 Oct 2006 06:00:43 -0000
@@ -373,14 +373,24 @@
     engineInit(opmode, key, spec, random);
   }

-  protected byte[] engineUpdate(byte[] input, int off, int len)
+  protected byte[] engineUpdate(byte[] input, int inOff, int inLen)
   {
+    if (inLen == 0) // nothing to process
+      return new byte[0];
     final int blockSize = mode.currentBlockSize();
-    final int count = (partLen + len) / blockSize;
-    final byte[] out = new byte[count * blockSize];
+    int blockCount = (partLen + inLen) / blockSize;
+
+    // always keep data for unpadding in padded decryption mode;
+    // might even be a complete block
+    if (pad != null
+        && ((Integer) attributes.get(IMode.STATE)).intValue() == IMode.DECRYPTION
+        && (partLen + inLen) % blockSize == 0)
+      blockCount--;
+
+    final byte[] out = new byte[blockCount * blockSize];
     try
       {
-        engineUpdate(input, off, len, out, 0);
+        engineUpdate(input, inOff, inLen, out, 0);
       }
     catch (ShortBufferException x) // should not happen
       {
@@ -395,7 +405,15 @@
     if (inLen == 0) // nothing to process
       return 0;
     final int blockSize = mode.currentBlockSize();
-    final int blockCount = (partLen + inLen) / blockSize;
+    int blockCount = (partLen + inLen) / blockSize;
+
+    // always keep data for unpadding in padded decryption mode;
+    // might even be a complete block
+    if (pad != null
+        && ((Integer) attributes.get(IMode.STATE)).intValue() == IMode.DECRYPTION
+        && (partLen + inLen) % blockSize == 0)
+      blockCount--;
+
     final int result = blockCount * blockSize;
     if (result > out.length - outOff)
       throw new ShortBufferException();
@@ -447,16 +465,21 @@
             break;
           case IMode.DECRYPTION:
             int padLen;
+            byte[] buf3 = new byte[buf.length + partLen];
             try
               {
-                padLen = pad.unpad(buf, 0, buf.length);
+                if (partLen != mode.currentBlockSize())
+                  throw new WrongPaddingException();
+                System.arraycopy(buf, 0, buf3, 0, buf.length);
+                mode.update(partBlock, 0, buf3, buf.length);
+                padLen = pad.unpad(buf3, 0, buf3.length);
               }
             catch (WrongPaddingException wpe)
               {
                 throw new BadPaddingException(wpe.getMessage());
               }
-            result = new byte[buf.length - padLen];
-            System.arraycopy(buf, 0, result, 0, result.length);
+            result = new byte[buf3.length - padLen];
+            System.arraycopy(buf3, 0, result, 0, result.length);
             break;
           default:
             throw new IllegalStateException();
Index: PKCS7.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/javax/crypto/pad/PKCS7.java,v
retrieving revision 1.5
diff -u -r1.5 PKCS7.java
--- PKCS7.java	2 Jul 2006 01:58:46 -0000	1.5
+++ PKCS7.java	22 Oct 2006 06:02:29 -0000
@@ -100,8 +100,8 @@
       throws WrongPaddingException
   {
     int limit = offset + length;
-    int result = in[limit - 1] & 0xFF;
-    for (int i = 0; i < result; i++)
+    int result = in[--limit] & 0xFF;
+    for (int i = 0; i < result - 1; i++)
       if (result != (in[--limit] & 0xFF))
         throw new WrongPaddingException();
     if (Configuration.DEBUG)
Index: CipherOutputStream.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/crypto/CipherOutputStream.java,v
retrieving revision 1.2
diff -u -r1.2 CipherOutputStream.java
--- CipherOutputStream.java	2 Jul 2005 20:32:45 -0000	1.2
+++ CipherOutputStream.java	22 Oct 2006 06:03:02 -0000
@@ -45,59 +45,25 @@
 /**
  * A filtered output stream that transforms data written to it with a
  * [EMAIL PROTECTED] Cipher} before sending it to the underlying output stream.
- *
+ *
  * @author Casey Marshall ([EMAIL PROTECTED])
  */
 public class CipherOutputStream extends FilterOutputStream
 {
-
-  // Fields.
-  // ------------------------------------------------------------------------
-
   /** The underlying cipher. */
   private Cipher cipher;

-  private byte[][] inBuffer;
-
-  private int inLength;
-
-  private byte[] outBuffer;
-
-  private static final int FIRST_TIME  = 0;
-  private static final int SECOND_TIME = 1;
-  private static final int SEASONED    = 2;
-  private int state;
-
-  /** True if the cipher is a stream cipher (blockSize == 1) */
-  private boolean isStream;
-
-  // Constructors.
-  // ------------------------------------------------------------------------
-
   /**
-   * Create a new cipher output stream. The cipher argument must have
-   * already been initialized.
-   *
-   * @param out    The sink for transformed data.
+   * Create a new cipher output stream. The cipher argument must have already
+   * been initialized.
+   *
+   * @param out The sink for transformed data.
    * @param cipher The cipher to transform data with.
    */
   public CipherOutputStream(OutputStream out, Cipher cipher)
   {
     super(out);
-    if (cipher != null)
-      {
-        this.cipher = cipher;
-        if (!(isStream = cipher.getBlockSize() == 1))
-          {
-            inBuffer = new byte[2][];
-            inBuffer[0] = new byte[cipher.getBlockSize()];
-            inBuffer[1] = new byte[cipher.getBlockSize()];
-            inLength = 0;
-            state = FIRST_TIME;
-          }
-      }
-    else
-      this.cipher = new NullCipher();
+    this.cipher = (cipher != null) ? cipher : new NullCipher();
   }

   /**
@@ -110,52 +76,36 @@
     super(out);
   }

-  // Instance methods.
-  // ------------------------------------------------------------------------
-
   /**
    * Close this output stream, and the sink output stream.
-   *
-   * <p>This method will first invoke the [EMAIL PROTECTED] Cipher#doFinal()}
-   * method of the underlying [EMAIL PROTECTED] Cipher}, and writes the output of
-   * that method to the sink output stream.
-   *
-   * @throws java.io.IOException If an I/O error occurs, or if an error
-   *         is caused by finalizing the transformation.
+   * <p>
+   * This method will first invoke the [EMAIL PROTECTED] Cipher#doFinal()} method of the
+   * underlying [EMAIL PROTECTED] Cipher}, and writes the output of that method to the
+   * sink output stream.
+   *
+   * @throws IOException If an I/O error occurs, or if an error is caused by
+   *           finalizing the transformation.
    */
   public void close() throws IOException
   {
     try
       {
-        int len;
-        if (state != FIRST_TIME)
-          {
-            len = cipher.update(inBuffer[0], 0, inBuffer[0].length, outBuffer);
-            out.write(outBuffer, 0, len);
-          }
-        len = cipher.doFinal(inBuffer[0], 0, inLength, outBuffer);
-        out.write(outBuffer, 0, len);
+        out.write(cipher.doFinal());
+        out.flush();
+        out.close();
+      }
+    catch (Exception cause)
+      {
+        IOException ioex = new IOException(String.valueOf(cause));
+        ioex.initCause(cause);
+        throw ioex;
       }
-    catch (javax.crypto.IllegalBlockSizeException ibse)
-      {
-        throw new IOException(ibse.toString());
-      }
-    catch (javax.crypto.BadPaddingException bpe)
-      {
-        throw new IOException(bpe.toString());
-      }
-    catch (ShortBufferException sbe)
-      {
-        throw new IOException(sbe.toString());
-      }
-    out.flush();
-    out.close();
   }

   /**
    * Flush any pending output.
    *
-   * @throws java.io.IOException If an I/O error occurs.
+   * @throws IOException If an I/O error occurs.
    */
   public void flush() throws IOException
   {
@@ -164,40 +114,22 @@

   /**
    * Write a single byte to the output stream.
-   *
+   *
    * @param b The next byte.
-   * @throws java.io.IOException If an I/O error occurs, or if the
-   *         underlying cipher is not in the correct state to transform
-   *         data.
+   * @throws IOException If an I/O error occurs, or if the underlying cipher is
+   *           not in the correct state to transform data.
    */
   public void write(int b) throws IOException
   {
-    if (isStream)
-      {
-        byte[] buf = new byte[] { (byte) b };
-        try
-          {
-            cipher.update(buf, 0, 1, buf, 0);
-          }
-        catch (ShortBufferException sbe)
-          {
-            throw new IOException(sbe.toString());
-          }
-        out.write(buf);
-        return;
-      }
-    inBuffer[1][inLength++] = (byte) b;
-    if (inLength == inBuffer[1].length)
-      process();
+    write(new byte[] { (byte) b }, 0, 1);
   }

   /**
    * Write a byte array to the output stream.
-   *
+   *
    * @param buf The next bytes.
-   * @throws java.io.IOException If an I/O error occurs, or if the
-   *         underlying cipher is not in the correct state to transform
-   *         data.
+   * @throws IOException If an I/O error occurs, or if the underlying cipher is
+   *           not in the correct state to transform data.
    */
   public void write(byte[] buf) throws IOException
   {
@@ -206,63 +138,15 @@

   /**
    * Write a portion of a byte array to the output stream.
-   *
+   *
    * @param buf The next bytes.
    * @param off The offset in the byte array to start.
    * @param len The number of bytes to write.
-   * @throws java.io.IOException If an I/O error occurs, or if the
-   *         underlying cipher is not in the correct state to transform
-   *         data.
+   * @throws IOException If an I/O error occurs, or if the underlying cipher is
+   *           not in the correct state to transform data.
    */
   public void write(byte[] buf, int off, int len) throws IOException
   {
-    if (isStream)
-      {
-        out.write(cipher.update(buf, off, len));
-        return;
-      }
-    int count = 0;
-    while (count < len)
-      {
-        int l = Math.min(inBuffer[1].length - inLength, len - count);
-        System.arraycopy(buf, off+count, inBuffer[1], inLength, l);
-        count += l;
-        inLength += l;
-        if (inLength == inBuffer[1].length)
-          process();
-      }
-  }
-
-  // Own method.
-  // -------------------------------------------------------------------------
-
-  private void process() throws IOException
-  {
-    if (state == SECOND_TIME)
-      {
-        state = SEASONED;
-      }
-    else
-      {
-        byte[] temp = inBuffer[0];
-        inBuffer[0] = inBuffer[1];
-        inBuffer[1] = temp;
-      }
-    if (state == FIRST_TIME)
-      {
-        inLength = 0;
-        state = SECOND_TIME;
-        return;
-      }
-    try
-      {
-        cipher.update(inBuffer[0], 0, inBuffer[0].length, outBuffer);
-      }
-    catch (ShortBufferException sbe)
-      {
-        throw new IOException(sbe.toString());
-      }
-    out.write(outBuffer);
-    inLength = 0;
+    out.write(cipher.update(buf, off, len));
   }
 }

Attachment: pgpENQAf9kDDj.pgp
Description: PGP signature

Reply via email to