2007-01-24 Marco Trudel <mtrudel@gmx.ch>

* java/security/Security.java
	Imported the version from GNU classpath 0.92 (kept GCJ differences).
	* 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.

From-SVN: r121154
This commit is contained in:
Marco Trudel
2007-01-25 01:25:05 +01:00
committed by Tom Tromey
parent 3562fb3ff3
commit f6bb2dcbde
5 changed files with 250 additions and 374 deletions

View File

@@ -373,14 +373,24 @@ class CipherAdapter
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 @@ class CipherAdapter
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 @@ class CipherAdapter
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();

View File

@@ -100,8 +100,8 @@ public final class PKCS7
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)

View File

@@ -50,54 +50,20 @@ import java.io.OutputStream;
*/
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.
* Create a new cipher output stream. The cipher argument must have already
* been initialized.
*
* @param out The sink for transformed data.
* @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 @@ public class CipherOutputStream extends FilterOutputStream
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.
*
* <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.
* @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 (javax.crypto.IllegalBlockSizeException ibse)
catch (Exception cause)
{
throw new IOException(ibse.toString());
IOException ioex = new IOException(String.valueOf(cause));
ioex.initCause(cause);
throw ioex;
}
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
{
@@ -166,38 +116,20 @@ public class CipherOutputStream extends FilterOutputStream
* 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
{
@@ -210,59 +142,11 @@ public class CipherOutputStream extends FilterOutputStream
* @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));
}
}