diff --git a/src/main/java/com/inteligr8/nio/AbstractBuffer.java b/src/main/java/com/inteligr8/nio/AbstractBuffer.java index ff21d56..160124f 100755 --- a/src/main/java/com/inteligr8/nio/AbstractBuffer.java +++ b/src/main/java/com/inteligr8/nio/AbstractBuffer.java @@ -11,11 +11,13 @@ public abstract class AbstractBuffer { protected long targetBytesWritten = 0L; protected boolean started = false; protected boolean finished = false; + protected boolean initialized = false; public Status getStatus() { if (this.finished) return Status.Finished; else if (this.started) return Status.Started; - else return Status.Initialized; + else if (this.initialized) return Status.Initialized; + else return Status.Uninitialized; } public long getTotalBytesRead() { diff --git a/src/main/java/com/inteligr8/nio/CipherBuffer.java b/src/main/java/com/inteligr8/nio/CipherBuffer.java index c280fbe..39bfbe3 100755 --- a/src/main/java/com/inteligr8/nio/CipherBuffer.java +++ b/src/main/java/com/inteligr8/nio/CipherBuffer.java @@ -75,6 +75,8 @@ public class CipherBuffer extends AbstractBuffer { cipher = cparams.getProvider() == null ? Cipher.getInstance(transformation) : Cipher.getInstance(transformation, cparams.getProvider()); } + this.initialized = true; + if (cparams.getInitializationVector() != null) { if (this.logger.isDebugEnabled()) this.logger.debug("initializing cipher with specified IV scrambler"); @@ -85,6 +87,10 @@ public class CipherBuffer extends AbstractBuffer { byte[] iv = new byte[cipher.getBlockSize()]; new SecureRandom().nextBytes(iv); cipher.init(this.cipherMode, this.key, new IvParameterSpec(iv)); + } else if (cparams.isDeferInitializationVector()) { + if (this.logger.isDebugEnabled()) + this.logger.debug("delaying cipher initialization ..."); + this.initialized = false; } else { if (this.logger.isDebugEnabled()) this.logger.debug("initializing cipher without IV scrambler"); @@ -115,6 +121,7 @@ public class CipherBuffer extends AbstractBuffer { if (this.logger.isDebugEnabled()) this.logger.debug("re-initializing cipher with specified IV scrambler"); this.cipher.init(this.cipherMode, this.key, new IvParameterSpec(initializationVector)); + this.initialized = true; } catch (InvalidKeyException ike) { throw new RuntimeException("This will never happen; the key was already declared valid by previous init()"); } diff --git a/src/main/java/com/inteligr8/nio/CipherParameters.java b/src/main/java/com/inteligr8/nio/CipherParameters.java index 25283bc..05d3273 100755 --- a/src/main/java/com/inteligr8/nio/CipherParameters.java +++ b/src/main/java/com/inteligr8/nio/CipherParameters.java @@ -5,53 +5,68 @@ import java.security.Provider; public class CipherParameters { + public enum IVSource { + None, + Generated, + Deferred, + Provided + } + private int cipherMode; private Key key; private String transformation; - private boolean generateInitializationVector = false; - private byte[] initializationVector; + private boolean generateIV = false; + private boolean deferIV = false; + private byte[] iv; private Provider provider; public CipherParameters(int cipherMode, Key key) { - this(cipherMode, key, null, null, null); + this(cipherMode, key, null, IVSource.None, null, null); } public CipherParameters(int cipherMode, Key key, String transformation) { - this(cipherMode, key, transformation, null, null); + this(cipherMode, key, transformation, IVSource.None, null, null); } - public CipherParameters(int cipherMode, Key key, String transformation, boolean generateInitializationVector) { - this(cipherMode, key, transformation, new byte[0], null); + public CipherParameters(int cipherMode, Key key, String transformation, IVSource ivSource) { + this(cipherMode, key, transformation, ivSource, null, null); } public CipherParameters(int cipherMode, Key key, String transformation, byte[] initializationVector) { - this(cipherMode, key, transformation, initializationVector, null); + this(cipherMode, key, transformation, IVSource.Provided, initializationVector, null); } public CipherParameters(int cipherMode, Key key, Provider provider) { - this(cipherMode, key, null, null, provider); + this(cipherMode, key, null, IVSource.None, null, provider); } public CipherParameters(int cipherMode, Key key, String transformation, Provider provider) { - this(cipherMode, key, transformation, null, provider); + this(cipherMode, key, transformation, IVSource.None, null, provider); } - public CipherParameters(int cipherMode, Key key, String transformation, boolean generateInitializationVector, Provider provider) { - this(cipherMode, key, transformation, new byte[0], provider); + public CipherParameters(int cipherMode, Key key, String transformation, IVSource ivSource, Provider provider) { + this(cipherMode, key, transformation, ivSource, null, provider); } public CipherParameters(int cipherMode, Key key, String transformation, byte[] initializationVector, Provider provider) { + this(cipherMode, key, transformation, IVSource.Provided, initializationVector, provider); + } + + private CipherParameters(int cipherMode, Key key, String transformation, IVSource ivSource, byte[] initializationVector, Provider provider) { this.cipherMode = cipherMode; this.key = key; this.transformation = transformation; this.provider = provider; - if (initializationVector != null) { - if (initializationVector.length == 0) { - this.generateInitializationVector = true; - } else { - this.initializationVector = initializationVector; - } + switch (ivSource) { + case Generated: + this.generateIV = true; + case Deferred: + this.deferIV = true; + case Provided: + this.iv = initializationVector; + case None: + default: } } @@ -83,20 +98,29 @@ public class CipherParameters { } public byte[] getInitializationVector() { - return this.initializationVector; + return this.iv; } public CipherParameters setInitializationVector(byte[] initializationVector) { - this.initializationVector = initializationVector; + this.iv = initializationVector; return this; } public boolean isGenerateInitializationVector() { - return this.generateInitializationVector; + return this.generateIV; } public CipherParameters setGenerateInitializationVector(boolean generateInitializationVector) { - this.generateInitializationVector = generateInitializationVector; + this.generateIV = generateInitializationVector; + return this; + } + + public boolean isDeferInitializationVector() { + return this.deferIV; + } + + public CipherParameters setDeferInitializationVector(boolean deferInitializationVector) { + this.deferIV = deferInitializationVector; return this; } diff --git a/src/main/java/com/inteligr8/nio/DecryptingCipherParameters.java b/src/main/java/com/inteligr8/nio/DecryptingCipherParameters.java index 548293d..206b16d 100755 --- a/src/main/java/com/inteligr8/nio/DecryptingCipherParameters.java +++ b/src/main/java/com/inteligr8/nio/DecryptingCipherParameters.java @@ -15,6 +15,10 @@ public class DecryptingCipherParameters extends CipherParameters { super(Cipher.DECRYPT_MODE, key, transformation); } + public DecryptingCipherParameters(Key key, String transformation, boolean deferInitializationVector) { + super(Cipher.DECRYPT_MODE, key, transformation, deferInitializationVector ? IVSource.Deferred : IVSource.None); + } + public DecryptingCipherParameters(Key key, String transformation, byte[] initializationVector) { super(Cipher.DECRYPT_MODE, key, transformation, initializationVector); } @@ -27,6 +31,10 @@ public class DecryptingCipherParameters extends CipherParameters { super(Cipher.DECRYPT_MODE, key, transformation, provider); } + public DecryptingCipherParameters(Key key, String transformation, boolean deferInitializationVector, Provider provider) { + super(Cipher.DECRYPT_MODE, key, transformation, deferInitializationVector ? IVSource.Deferred : IVSource.None, provider); + } + public DecryptingCipherParameters(Key key, String transformation, byte[] initializationVector, Provider provider) { super(Cipher.DECRYPT_MODE, key, transformation, initializationVector, provider); } @@ -36,11 +44,6 @@ public class DecryptingCipherParameters extends CipherParameters { throw new UnsupportedOperationException(); } - @Override - public boolean isGenerateInitializationVector() { - throw new UnsupportedOperationException(); - } - @Override public DecryptingCipherParameters setGenerateInitializationVector(boolean generateInitializationVector) { throw new UnsupportedOperationException(); diff --git a/src/main/java/com/inteligr8/nio/DigestBuffer.java b/src/main/java/com/inteligr8/nio/DigestBuffer.java index 1536a4a..d3482f4 100755 --- a/src/main/java/com/inteligr8/nio/DigestBuffer.java +++ b/src/main/java/com/inteligr8/nio/DigestBuffer.java @@ -27,6 +27,8 @@ public class DigestBuffer extends AbstractBuffer { if (this.logger.isDebugEnabled()) this.logger.debug("digest algorithm: " + this.digest.getAlgorithm()); + this.initialized = true; + this.leftoverBuffer = ByteBuffer.allocate(this.digest.getDigestLength()); this.leftoverBuffer.flip(); } diff --git a/src/main/java/com/inteligr8/nio/EncryptingCipherParameters.java b/src/main/java/com/inteligr8/nio/EncryptingCipherParameters.java index 0850151..c865933 100755 --- a/src/main/java/com/inteligr8/nio/EncryptingCipherParameters.java +++ b/src/main/java/com/inteligr8/nio/EncryptingCipherParameters.java @@ -15,8 +15,8 @@ public class EncryptingCipherParameters extends CipherParameters { super(Cipher.ENCRYPT_MODE, key, transformation); } - public EncryptingCipherParameters(Key key, String transformation, boolean generateInitializationVector) { - super(Cipher.ENCRYPT_MODE, key, transformation, generateInitializationVector); + public EncryptingCipherParameters(Key key, String transformation, IVSource ivSource) { + super(Cipher.ENCRYPT_MODE, key, transformation, ivSource); } public EncryptingCipherParameters(Key key, String transformation, byte[] initializationVector) { @@ -31,8 +31,8 @@ public class EncryptingCipherParameters extends CipherParameters { super(Cipher.ENCRYPT_MODE, key, transformation, provider); } - public EncryptingCipherParameters(Key key, String transformation, boolean generateInitializationVector, Provider provider) { - super(Cipher.ENCRYPT_MODE, key, transformation, generateInitializationVector, provider); + public EncryptingCipherParameters(Key key, String transformation, IVSource ivSource, Provider provider) { + super(Cipher.ENCRYPT_MODE, key, transformation, ivSource, provider); } public EncryptingCipherParameters(Key key, String transformation, byte[] initializationVector, Provider provider) { diff --git a/src/main/java/com/inteligr8/nio/IVDecryptingReadableByteChannel.java b/src/main/java/com/inteligr8/nio/IVDecryptingReadableByteChannel.java index 28f4c49..f04632e 100644 --- a/src/main/java/com/inteligr8/nio/IVDecryptingReadableByteChannel.java +++ b/src/main/java/com/inteligr8/nio/IVDecryptingReadableByteChannel.java @@ -27,13 +27,13 @@ public class IVDecryptingReadableByteChannel implements ReadableByteChannel, Dec public IVDecryptingReadableByteChannel(ReadableByteChannel rbchannel, Key key, String transformation) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException { this.rbchannel = rbchannel; - this.drbchannel = new DecryptingReadableByteChannel(rbchannel, new DecryptingCipherParameters(key, transformation)); + this.drbchannel = new DecryptingReadableByteChannel(rbchannel, new DecryptingCipherParameters(key, transformation, true)); } public IVDecryptingReadableByteChannel(ReadableByteChannel rbchannel, Key key, String transformation, Provider provider) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException { this.rbchannel = rbchannel; - this.drbchannel = new DecryptingReadableByteChannel(rbchannel, new DecryptingCipherParameters(key, transformation, provider)); + this.drbchannel = new DecryptingReadableByteChannel(rbchannel, new DecryptingCipherParameters(key, transformation, true, provider)); } @Override diff --git a/src/main/java/com/inteligr8/nio/IVEncryptingWritableByteChannel.java b/src/main/java/com/inteligr8/nio/IVEncryptingWritableByteChannel.java index 0388f0d..6f9ada9 100644 --- a/src/main/java/com/inteligr8/nio/IVEncryptingWritableByteChannel.java +++ b/src/main/java/com/inteligr8/nio/IVEncryptingWritableByteChannel.java @@ -12,6 +12,8 @@ import java.security.Provider; import javax.crypto.NoSuchPaddingException; +import com.inteligr8.nio.CipherParameters.IVSource; + /** * This class embeds a random initialization vector at the start of the * encrypted content. Otherwise it acts identical to the @@ -28,7 +30,7 @@ public class IVEncryptingWritableByteChannel implements WritableByteChannel, Flu public IVEncryptingWritableByteChannel(WritableByteChannel wbchannel, Key key, String transformation) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException { this.wbchannel = wbchannel; - this.ewbchannel = new EncryptingWritableByteChannel(wbchannel, new EncryptingCipherParameters(key, transformation, true)); + this.ewbchannel = new EncryptingWritableByteChannel(wbchannel, new EncryptingCipherParameters(key, transformation, IVSource.Generated)); } public IVEncryptingWritableByteChannel(WritableByteChannel wbchannel, Key key, String transformation, byte[] iv) @@ -40,7 +42,7 @@ public class IVEncryptingWritableByteChannel implements WritableByteChannel, Flu public IVEncryptingWritableByteChannel(WritableByteChannel wbchannel, Key key, String transformation, Provider provider) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException { this.wbchannel = wbchannel; - this.ewbchannel = new EncryptingWritableByteChannel(wbchannel, new EncryptingCipherParameters(key, transformation, true, provider)); + this.ewbchannel = new EncryptingWritableByteChannel(wbchannel, new EncryptingCipherParameters(key, transformation, IVSource.Generated, provider)); } public IVEncryptingWritableByteChannel(WritableByteChannel wbchannel, Key key, String transformation, byte[] iv, Provider provider) diff --git a/src/main/java/com/inteligr8/nio/Status.java b/src/main/java/com/inteligr8/nio/Status.java index 5575862..ecf8254 100755 --- a/src/main/java/com/inteligr8/nio/Status.java +++ b/src/main/java/com/inteligr8/nio/Status.java @@ -2,6 +2,7 @@ package com.inteligr8.nio; public enum Status { + Uninitialized, Initialized, Started, Finished diff --git a/src/test/java/com/inteligr8/nio/AbstractHashingByteChannelUnitTest.java b/src/test/java/com/inteligr8/nio/AbstractHashingByteChannelUnitTest.java index 5698008..4775b5f 100644 --- a/src/test/java/com/inteligr8/nio/AbstractHashingByteChannelUnitTest.java +++ b/src/test/java/com/inteligr8/nio/AbstractHashingByteChannelUnitTest.java @@ -52,7 +52,7 @@ public abstract class AbstractHashingByteChannelUnitTest { @Test public void javaDigestBuffer() throws Exception { - this.test("sha-256", new File("src/main/java/com/inteligr8/nio/DigestBuffer.java"), "d01c874ffb0c6633a5d6ff4537da36c44d50af856970abddc1215866b0d08f44"); + this.test("sha-256", new File("src/main/java/com/inteligr8/nio/DigestBuffer.java"), "0cdd4be589eada9c9ff37160ed46b4951361dab6b7eb0bfc1234436e37b6edc7"); } public void test(String algorithm, String text, String hex) throws Exception { diff --git a/src/test/java/com/inteligr8/nio/IVCryptoByteChannelUnitTest.java b/src/test/java/com/inteligr8/nio/IVCryptoByteChannelUnitTest.java index 6700314..107f73b 100644 --- a/src/test/java/com/inteligr8/nio/IVCryptoByteChannelUnitTest.java +++ b/src/test/java/com/inteligr8/nio/IVCryptoByteChannelUnitTest.java @@ -33,11 +33,11 @@ public class IVCryptoByteChannelUnitTest extends AbstractCryptoByteChannelUnitTe int keySizeInBytes = this.getKeySizeInBytes(); ByteBufferChannel bbchannel = new ByteBufferChannel(1024); - IVEncryptingWritableByteChannel ebchannel = new IVEncryptingWritableByteChannel(bbchannel, this.getKey(), "AES/CBC/NoPadding"); + IVEncryptingWritableByteChannel ebchannel = new IVEncryptingWritableByteChannel(bbchannel, this.getKey(), "AES/CBC/PKCS5Padding"); try { int bytesWritten = ebchannel.write(this.getCharset().encode(text)); if (text.length() == 0) Assert.assertEquals(keySizeInBytes, bytesWritten); - else Assert.assertEquals(this.getKeySizeInBytes() + Math.max(1, text.length() / keySizeInBytes * keySizeInBytes), bytesWritten); + else Assert.assertEquals(text.length() / keySizeInBytes * keySizeInBytes + keySizeInBytes, bytesWritten); } finally { ebchannel.close(); } @@ -45,10 +45,11 @@ public class IVCryptoByteChannelUnitTest extends AbstractCryptoByteChannelUnitTe Assert.assertTrue(bbchannel.size() >= text.length()); ByteBuffer bbuffer = ByteBuffer.allocate(1024); - IVDecryptingReadableByteChannel dbchannel = new IVDecryptingReadableByteChannel(bbchannel, this.getKey(), "AES/CBC/NoPadding"); + IVDecryptingReadableByteChannel dbchannel = new IVDecryptingReadableByteChannel(bbchannel, this.getKey(), "AES/CBC/PKCS5Padding"); try { - int bytesRead = dbchannel.read(bbuffer); + int bytesRead = dbchannel.read(bbuffer); Assert.assertEquals(text.length() / keySizeInBytes * keySizeInBytes + 2 * keySizeInBytes, bytesRead); + Assert.assertEquals(-1, dbchannel.read(bbuffer)); } finally { dbchannel.close(); } @@ -63,11 +64,10 @@ public class IVCryptoByteChannelUnitTest extends AbstractCryptoByteChannelUnitTe bbuffer.limit(Math.min(chunkSize, realLimit)); ByteBufferChannel bbchannel = new ByteBufferChannel(1024); - IVEncryptingWritableByteChannel ebchannel = new IVEncryptingWritableByteChannel(bbchannel, getKey(), "AES/CBC/NoPadding"); + IVEncryptingWritableByteChannel ebchannel = new IVEncryptingWritableByteChannel(bbchannel, getKey(), "AES/CBC/PKCS5Padding"); try { - int bytesWritten = 1; - while (bytesWritten > 0) { - bytesWritten = ebchannel.write(bbuffer); + while (bbuffer.hasRemaining()) { + ebchannel.write(bbuffer); bbuffer.limit(Math.min(bbuffer.limit() + chunkSize, realLimit)); } } finally { @@ -79,7 +79,7 @@ public class IVCryptoByteChannelUnitTest extends AbstractCryptoByteChannelUnitTe bbuffer = ByteBuffer.allocate(1024); realLimit = bbuffer.limit(); bbuffer.limit(Math.min(chunkSize, realLimit)); - IVDecryptingReadableByteChannel dbchannel = new IVDecryptingReadableByteChannel(bbchannel, getKey(), "AES/CBC/NoPadding"); + IVDecryptingReadableByteChannel dbchannel = new IVDecryptingReadableByteChannel(bbchannel, getKey(), "AES/CBC/PKCS5Padding"); try { int bytesRead = 1; while (bytesRead >= 0) { @@ -98,10 +98,10 @@ public class IVCryptoByteChannelUnitTest extends AbstractCryptoByteChannelUnitTe int keySizeInBytes = this.getKeySizeInBytes(); ByteBufferChannel bbchannel = new ByteBufferChannel(1024); - IVEncryptingWritableByteChannel ebchannel = new IVEncryptingWritableByteChannel(bbchannel, this.getKey(), "AES/CBC/NoPadding"); + IVEncryptingWritableByteChannel ebchannel = new IVEncryptingWritableByteChannel(bbchannel, this.getKey(), "AES/CBC/PKCS5Padding"); try { int bytesWritten = ebchannel.write(this.getCharset().encode(text)); - Assert.assertEquals(this.getKeySizeInBytes() + Math.max(1, text.length() / keySizeInBytes * keySizeInBytes), bytesWritten); + Assert.assertEquals(text.length() / keySizeInBytes * keySizeInBytes + keySizeInBytes, bytesWritten); } finally { ebchannel.close(); } @@ -109,7 +109,7 @@ public class IVCryptoByteChannelUnitTest extends AbstractCryptoByteChannelUnitTe Assert.assertTrue(bbchannel.size() >= text.length()); ByteBuffer bbuffer = ByteBuffer.allocate(startsWith.length()); - IVDecryptingReadableByteChannel dbchannel = new IVDecryptingReadableByteChannel(bbchannel, this.getKey(), "AES/CBC/NoPadding"); + IVDecryptingReadableByteChannel dbchannel = new IVDecryptingReadableByteChannel(bbchannel, this.getKey(), "AES/CBC/PKCS5Padding"); try { int bytesRead = dbchannel.read(bbuffer); Assert.assertEquals(startsWith.length() / keySizeInBytes * keySizeInBytes + 2 * keySizeInBytes, bytesRead); diff --git a/src/test/java/com/inteligr8/nio/IVSymmetricBlockCipherUnitTest.java b/src/test/java/com/inteligr8/nio/IVSymmetricBlockCipherUnitTest.java index 29e522c..d12af4d 100755 --- a/src/test/java/com/inteligr8/nio/IVSymmetricBlockCipherUnitTest.java +++ b/src/test/java/com/inteligr8/nio/IVSymmetricBlockCipherUnitTest.java @@ -2,16 +2,18 @@ package com.inteligr8.nio; import java.security.Key; +import com.inteligr8.nio.CipherParameters.IVSource; + public class IVSymmetricBlockCipherUnitTest extends SymmetricBlockCipherUnitTest { @Override public String getDefaultTransformation() { - return "AES/CBC/NoPadding"; + return "AES/CBC/PKCS5Padding"; } @Override public CipherBuffer createCipher(int cipherMode, Key key, String transformation) throws Exception { - return new CipherBuffer(new CipherParameters(cipherMode, key, transformation, true)); + return new CipherBuffer(new CipherParameters(cipherMode, key, transformation, IVSource.Generated)); } public CipherBuffer createCipher(int cipherMode, Key key, String transformation, CipherBuffer cipher) throws Exception { diff --git a/src/test/java/com/inteligr8/nio/StreamingCryptoByteChannelUnitTest.java b/src/test/java/com/inteligr8/nio/StreamingCryptoByteChannelUnitTest.java index 178b669..7f27fa4 100644 --- a/src/test/java/com/inteligr8/nio/StreamingCryptoByteChannelUnitTest.java +++ b/src/test/java/com/inteligr8/nio/StreamingCryptoByteChannelUnitTest.java @@ -68,7 +68,7 @@ public class StreamingCryptoByteChannelUnitTest { SocketChannel channel = SocketChannel.open(); Assert.assertTrue(channel.connect(new InetSocketAddress("localhost", port))); - IVEncryptingWritableByteChannel ebchannel = new IVEncryptingWritableByteChannel(channel, key, "AES/CBC/NoPadding"); + IVEncryptingWritableByteChannel ebchannel = new IVEncryptingWritableByteChannel(channel, key, "AES/CBC/PKCS5Padding"); ByteBuffer bbuffer = ByteBuffer.allocate(bufferSize); while (fchannel.read(bbuffer) >= 0) { @@ -100,7 +100,7 @@ public class StreamingCryptoByteChannelUnitTest { schannel.socket().bind(new InetSocketAddress(port)); SocketChannel channel = schannel.accept(); - IVDecryptingReadableByteChannel dbchannel = new IVDecryptingReadableByteChannel(channel, key, "AES/CBC/NoPadding"); + IVDecryptingReadableByteChannel dbchannel = new IVDecryptingReadableByteChannel(channel, key, "AES/CBC/PKCS5Padding"); ByteBuffer bbuffer = ByteBuffer.allocate(bufferSize); ByteBuffer fbuffer = ByteBuffer.allocate(bufferSize);