This is the mail archive of the java-patches@gcc.gnu.org mailing list for the Java project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

importing javax.crypto patch from classpath


Hello all

My javax.crypto fix made it into the classpath trunk. I would like to see it in the GCJ trunk too. Without it, JCE is pretty useless.

A testlet is attached and here the suggested ChangeLog entry:

2006-10-23 Marco Trudel <mtrudel@gmx.ch>

* classpath/gnu/javax/crypto/pad/PKCS7.java (unpad): Removed an unnecessary test.
* classpath/javax/crypto/CipherOutputStream.java: Re-implemented.
* classpath/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.



I hope someone can commit that for me although Tom is currently away...


thanks
Marco
/* TestOfCipherOutputStream.java
   Copyright (C) 2006 Free Software Foundation, Inc.
This file is part of Mauve.

Mauve is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

Mauve is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with Mauve; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.

*/

// Tags: GNU-CRYPTO JDK1.4

package gnu.testlet.gnu.javax.crypto.jce;

import gnu.java.security.util.Util;
import gnu.testlet.TestHarness;
import gnu.testlet.Testlet;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * Test for problems reported (and fixed) by Marco Trudel (see thread
 * http://gcc.gnu.org/ml/java/2006-09/msg00105.html).
 */
public class TestOfCipherOutputStream
    implements Testlet
{
  private static final byte[] PLAINTEXT;
  static
  {
    PLAINTEXT = new byte[511];
    for (int i = 0; i < 511; i++)
      PLAINTEXT[i] = (byte) i;
  }
  private static final byte[] CIPHERTEXT = Util.toBytesFromString(
        "0A940BB5416EF045F1C39458C653EA5A07FEEF74E1D5036E900EEE118E949293"
      + "5BE87E2E5B447C944B21C9AF7756C0D803F2C3BDCA826BF082D7CFB035CDB8C1"
      + "D533E59B45A153ED7E5E9C5DFCFD4AAA3EF0B1A5E3059DAB21FCE23A7B61C4CA"
      + "ADDE68F7AD497268D31A0DDD5C74B08F3D2D90DCEF49D32822298B878F815581"
      + "AC26591C0F8BD80EE7C7E3A2D14E2B2276F0DFA4F107BD6303879DAC0E2FD795"
      + "5E18D1FEF61D087EC0A33ED734A7918FE315209ED0E7C94F74A65C99F6EADC1E"
      + "AD393003D3E6BC5268F0D833E0050B78D2001826302BD313C41809FFDA1713E8"
      + "D02A48244ECCDC2379224DBC5470361266A7C7E8345231489751DE073316ADAD"
      + "0A940BB5416EF045F1C39458C653EA5A07FEEF74E1D5036E900EEE118E949293"
      + "5BE87E2E5B447C944B21C9AF7756C0D803F2C3BDCA826BF082D7CFB035CDB8C1"
      + "D533E59B45A153ED7E5E9C5DFCFD4AAA3EF0B1A5E3059DAB21FCE23A7B61C4CA"
      + "ADDE68F7AD497268D31A0DDD5C74B08F3D2D90DCEF49D32822298B878F815581"
      + "AC26591C0F8BD80EE7C7E3A2D14E2B2276F0DFA4F107BD6303879DAC0E2FD795"
      + "5E18D1FEF61D087EC0A33ED734A7918FE315209ED0E7C94F74A65C99F6EADC1E"
      + "AD393003D3E6BC5268F0D833E0050B78D2001826302BD313C41809FFDA1713E8"
      + "D02A48244ECCDC2379224DBC54703612D59D78BE4A3F7494AC0ECEC7FA122305");

  public void test(TestHarness harness)
  {
    byte[] k = new byte[16];
    for (int i = 0; i < k.length; i++)
      k[i] = (byte) i;
    try
      {
        SecretKey key = new SecretKeySpec(k, "AES");
        encryptionTest(harness, key);
        decryptionTest(harness, key);
      }
    catch (Exception x)
      {
        harness.debug(x);
        harness.fail("test(): " + x);
      }
  }

  private void encryptionTest(TestHarness harness, SecretKey key)
    throws Exception
  {
      ByteArrayInputStream in = new ByteArrayInputStream(PLAINTEXT);
      ByteArrayOutputStream outStream = new ByteArrayOutputStream(1024 + 32);

      Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7");

      cipher.init(Cipher.ENCRYPT_MODE, key);
      CipherOutputStream out = new CipherOutputStream(outStream, cipher);

      byte[] b = new byte[2048];
      int n;
      while ((n = in.read(b)) != -1)
        if (n > 0)
          out.write(b, 0, n);

      out.close();

      byte[] ciphertxt = outStream.toByteArray();
      harness.check(Arrays.equals(ciphertxt, CIPHERTEXT), "Cipher text MUST match");
  }

  private void decryptionTest(TestHarness harness, SecretKey key)
    throws Exception
  {
    ByteArrayInputStream inStream = new ByteArrayInputStream(CIPHERTEXT);
    ByteArrayOutputStream out = new ByteArrayOutputStream(1024 + 32);

    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7");

    cipher.init(Cipher.DECRYPT_MODE, key);
    CipherInputStream in = new CipherInputStream(inStream, cipher);

    byte[] b = new byte[2048];
    int n;
    while ((n = in.read(b)) != -1)
      if (n > 0)
        out.write(b, 0, n);

    in.close();

    byte[] plaintxt = out.toByteArray();
    harness.check(Arrays.equals(plaintxt, PLAINTEXT), "Plain text MUST match");
  }
}
Index: classpath/gnu/javax/crypto/jce/cipher/CipherAdapter.java
===================================================================
--- classpath/gnu/javax/crypto/jce/cipher/CipherAdapter.java	(revision 117742)
+++ classpath/gnu/javax/crypto/jce/cipher/CipherAdapter.java	(working copy)
@@ -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: classpath/gnu/javax/crypto/pad/PKCS7.java
===================================================================
--- classpath/gnu/javax/crypto/pad/PKCS7.java	(revision 117742)
+++ classpath/gnu/javax/crypto/pad/PKCS7.java	(working copy)
@@ -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: classpath/javax/crypto/CipherOutputStream.java
===================================================================
--- classpath/javax/crypto/CipherOutputStream.java	(revision 117742)
+++ classpath/javax/crypto/CipherOutputStream.java	(working copy)
@@ -45,59 +45,25 @@
 /**
  * A filtered output stream that transforms data written to it with a
  * {@link Cipher} before sending it to the underlying output stream.
- *
+ *
  * @author Casey Marshall (csm@gnu.org)
  */
 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 {@link Cipher#doFinal()}
-   * method of the underlying {@link 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 {@link Cipher#doFinal()} method of the
+   * underlying {@link 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));
   }
 }

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]