This is the mail archive of the
java-patches@gcc.gnu.org
mailing list for the Java project.
importing javax.crypto patch from classpath
- From: Marco Trudel <mtrudel at gmx dot ch>
- To: java-patches at gcc dot gnu dot org
- Date: Fri, 27 Oct 2006 12:16:57 +0200
- Subject: 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));
}
}