Permalink
Browse files

CAY-1925 cayenne-crypto: add optional compression to the encryption p…

…ipeline

(in progress) don't compress smaller values

git-svn-id: https://svn.apache.org/repos/asf/cayenne/main/trunk@1588253 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information...
1 parent 05b53c8 commit 5fe40a64039ae1358d948b1098c355a7255326f6 @andrus andrus committed Apr 17, 2014
@@ -32,7 +32,10 @@
* how much empty space to leave in the beginning of the returned
* output array. This would allow the caller to prepend extra
* data to the encrypted array.
+ * @param flags
+ * a byte[1] that allows nested encryptors to manipulate header
+ * flags.
*/
- byte[] encrypt(byte[] input, int outputOffset);
+ byte[] encrypt(byte[] input, int outputOffset, byte[] flags);
}
@@ -65,7 +65,7 @@ public CbcEncryptor(Cipher cipher, Key key, byte[] seedIv) {
}
@Override
- public byte[] encrypt(byte[] input, int outputOffset) {
+ public byte[] encrypt(byte[] input, int outputOffset, byte[] flags) {
try {
return doEncrypt(input, outputOffset);
@@ -29,7 +29,7 @@
*/
class GzipEncryptor implements BytesEncryptor {
- private static final int GZIP_THRESHOLD = 150;
+ static final int GZIP_THRESHOLD = 150;
private BytesEncryptor delegate;
@@ -38,11 +38,11 @@ public GzipEncryptor(BytesEncryptor delegate) {
}
@Override
- public byte[] encrypt(byte[] input, int outputOffset) {
+ public byte[] encrypt(byte[] input, int outputOffset, byte[] flags) {
- boolean compress = input.length >= GZIP_THRESHOLD;
+ boolean compressed = input.length >= GZIP_THRESHOLD;
- if (compress) {
+ if (compressed) {
try {
input = gzip(input);
} catch (IOException e) {
@@ -51,7 +51,8 @@ public GzipEncryptor(BytesEncryptor delegate) {
}
}
- return delegate.encrypt(input, outputOffset);
+ flags[0] = Header.setCompressed(flags[0], compressed);
+ return delegate.encrypt(input, outputOffset, flags);
}
static byte[] gzip(byte[] input) throws IOException {
@@ -113,10 +113,18 @@ public static Header create(byte[] data, int offset) {
return new Header(data, offset);
}
+ public static byte setCompressed(byte bits, boolean compressed) {
+ return compressed ? bitOn(bits, COMPRESS_BIT) : bitOff(bits, COMPRESS_BIT);
+ }
+
private static byte bitOn(byte bits, int position) {
return (byte) (bits | (1 << position));
}
+ private static byte bitOff(byte bits, int position) {
+ return (byte) (bits & ~(1 << position));
+ }
+
private static boolean isBitOn(byte bits, int position) {
return ((bits >> position) & 1) == 1;
}
@@ -132,14 +140,19 @@ public int size() {
}
public boolean isCompressed() {
- return isBitOn(data[offset + FLAGS_POSITION], COMPRESS_BIT);
+ return isBitOn(getFlags(), COMPRESS_BIT);
+ }
+
+ public byte getFlags() {
+ return data[offset + FLAGS_POSITION];
}
/**
* Saves the header bytes in the provided buffer at specified offset.
*/
- public void store(byte[] output, int outputOffset) {
+ public void store(byte[] output, int outputOffset, byte flags) {
System.arraycopy(data, offset, output, outputOffset, size());
+ output[outputOffset + FLAGS_POSITION] = flags;
}
public String getKeyName() {
@@ -29,9 +29,12 @@
}
@Override
- public byte[] encrypt(byte[] input, int outputOffset) {
- byte[] output = delegate.encrypt(input, outputOffset + header.size());
- header.store(output, outputOffset);
+ public byte[] encrypt(byte[] input, int outputOffset, byte[] flags) {
+
+ flags[0] = header.getFlags();
+
+ byte[] output = delegate.encrypt(input, outputOffset + header.size(), flags);
+ header.store(output, outputOffset, flags[0]);
return output;
}
@@ -50,7 +50,7 @@ public Object encrypt(BytesEncryptor encryptor, Object value) {
}
byte[] bytes = preConverter.toBytes(value);
- byte[] transformed = encryptor.encrypt(bytes, 0);
+ byte[] transformed = encryptor.encrypt(bytes, 0, new byte[1]);
return postConverter.fromBytes(transformed);
}
@@ -28,7 +28,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Random;
import org.apache.cayenne.ObjectContext;
import org.apache.cayenne.crypto.db.Table2;
@@ -47,20 +46,13 @@ public void setUp() throws Exception {
super.setUp(true);
}
- byte[] bytesOfSize(int len) {
- Random r = new Random();
- byte[] b = new byte[len];
- r.nextBytes(b);
- return b;
- }
-
@Test
public void testInsert() throws SQLException {
ObjectContext context = runtime.newContext();
// make sure compression is on...
- byte[] cryptoBytes = bytesOfSize(GZIP_THRESHOLD + 100);
+ byte[] cryptoBytes = CryptoUnitUtils.bytesOfSize(GZIP_THRESHOLD + 100);
Table2 t1 = context.newObject(Table2.class);
t1.setPlainBytes("plain_1".getBytes());
@@ -83,7 +75,7 @@ public void testInsert_Small() throws SQLException {
ObjectContext context = runtime.newContext();
// make sure compression is on...
- byte[] cryptoBytes = bytesOfSize(GZIP_THRESHOLD - 20);
+ byte[] cryptoBytes = CryptoUnitUtils.bytesOfSize(GZIP_THRESHOLD - 20);
Table2 t1 = context.newObject(Table2.class);
t1.setPlainBytes("plain_1".getBytes());
@@ -105,8 +97,8 @@ public void testInsert_MultipleObjects() throws SQLException {
ObjectContext context = runtime.newContext();
// make sure compression is on...
- byte[] cryptoBytes1 = bytesOfSize(GZIP_THRESHOLD + 101);
- byte[] cryptoBytes2 = bytesOfSize(GZIP_THRESHOLD + 102);
+ byte[] cryptoBytes1 = CryptoUnitUtils.bytesOfSize(GZIP_THRESHOLD + 101);
+ byte[] cryptoBytes2 = CryptoUnitUtils.bytesOfSize(GZIP_THRESHOLD + 102);
Table2 t1 = context.newObject(Table2.class);
t1.setPlainBytes("a".getBytes());
@@ -141,8 +133,8 @@ public void testInsert_MultipleObjects() throws SQLException {
public void test_SelectQuery() throws SQLException {
// make sure compression is on...
- byte[] cryptoBytes1 = bytesOfSize(GZIP_THRESHOLD + 101);
- byte[] cryptoBytes2 = bytesOfSize(GZIP_THRESHOLD + 102);
+ byte[] cryptoBytes1 = CryptoUnitUtils.bytesOfSize(GZIP_THRESHOLD + 101);
+ byte[] cryptoBytes2 = CryptoUnitUtils.bytesOfSize(GZIP_THRESHOLD + 102);
ObjectContext context = runtime.newContext();
@@ -71,7 +71,7 @@ public void testEncrypt_AES() throws NoSuchAlgorithmException, NoSuchPaddingExce
// copy ivBytes, as they are reset
CbcEncryptor encryptor = new CbcEncryptor(cipher, key, ivBytes);
- byte[] encrypted = encryptor.encrypt(plain, 0);
+ byte[] encrypted = encryptor.encrypt(plain, 0, new byte[1]);
assertEquals(16 * 3, encrypted.length);
assertArrayEquals(ivBytes, Arrays.copyOfRange(encrypted, 0, 16));
@@ -48,29 +48,39 @@ public void testGzip() throws IOException {
public void testEncrypt() throws UnsupportedEncodingException {
BytesEncryptor delegate = mock(BytesEncryptor.class);
- when(delegate.encrypt(any(byte[].class), anyInt())).thenAnswer(new Answer<byte[]>() {
+ when(delegate.encrypt(any(byte[].class), anyInt(), any(byte[].class))).thenAnswer(new Answer<byte[]>() {
@Override
public byte[] answer(InvocationOnMock invocation) throws Throwable {
Object[] args = invocation.getArguments();
byte[] answer = (byte[]) args[0];
int offset = (Integer) args[1];
-
+
assertEquals(1, offset);
-
+
return answer;
}
});
-
GzipEncryptor e = new GzipEncryptor(delegate);
byte[] input1 = "Hello Hello Hello".getBytes("UTF8");
- byte[] output1 = e.encrypt(input1, 1);
- byte[] expectedOutput1 = CryptoUnitUtils.hexToBytes("1f8b0800000000000000f348cdc9c957f0409000a91a078c11000000");
+ byte[] output1 = e.encrypt(input1, 1, new byte[1]);
+ byte[] expectedOutput1 = input1;
assertArrayEquals(expectedOutput1, output1);
+ byte[] input2 = ("Hello AAAAA Hello AAAAA Hello AAAAA Hello AAAAA Hello AAAAA Hello AAAAA Hello "
+ + "Hello AAAAA Hello AAAAA Hello AAAAA Hello AAAAA Hello AAAAA Hello AAAAA Hello").getBytes("UTF8");
+ byte[] output2 = e.encrypt(input2, 1, new byte[1]);
+
+ // somehow 'gzip -c' fills bytes 3..9 with values... the rest of the
+ // gzip string is identical...
+ byte[] expectedOutput2 = CryptoUnitUtils
+ .hexToBytes("1f8b0800000000000000f348cdc9c957700401050f8ad9949b80c40600bbec62509b000000");
+
+ assertArrayEquals(expectedOutput2, output2);
+
}
}
@@ -39,10 +39,10 @@ public void testTransform() throws UnsupportedEncodingException {
// intentionally non-standard block size..
HeaderEncryptor encryptor = new HeaderEncryptor(delegate, encryptionHeader);
- byte[] output1 = encryptor.encrypt(input, 0);
+ byte[] output1 = encryptor.encrypt(input, 0, new byte[1]);
assertArrayEquals(new byte[] { 'C', 'C', '1', 10, 0, 'm', 'y', 'k', 'e', 'y', 8, 7, 6, 5, 4, 3, 2, 1 }, output1);
- byte[] output2 = encryptor.encrypt(input, 1);
+ byte[] output2 = encryptor.encrypt(input, 1, new byte[1]);
assertArrayEquals(new byte[] { 0, 'C', 'C', '1', 10, 0, 'm', 'y', 'k', 'e', 'y', 8, 7, 6, 5, 4, 3, 2, 1 }, output2);
}
@@ -28,6 +28,15 @@
public class HeaderTest {
@Test
+ public void testSetCompressed() {
+ assertEquals(1, Header.setCompressed((byte) 0, true));
+ assertEquals(0, Header.setCompressed((byte) 0, false));
+
+ assertEquals(1, Header.setCompressed((byte) 1, true));
+ assertEquals(0, Header.setCompressed((byte) 1, false));
+ }
+
+ @Test
public void testCreate_WithKeyName() {
Header h1 = Header.create("bcd", false);
@@ -24,6 +24,7 @@
import java.math.BigInteger;
import java.security.Key;
import java.util.Arrays;
+import java.util.Random;
import java.util.zip.GZIPInputStream;
import javax.crypto.Cipher;
@@ -35,6 +36,13 @@
public class CryptoUnitUtils {
+ public static byte[] bytesOfSize(int len) {
+ Random r = new Random();
+ byte[] b = new byte[len];
+ r.nextBytes(b);
+ return b;
+ }
+
public static byte[] hexToBytes(String hexString) {
byte[] bytes = new BigInteger(hexString, 16).toByteArray();
@@ -52,7 +52,7 @@ private SwapBytesTransformer() {
}
@Override
- public byte[] encrypt(byte[] input, int outputOffset) {
+ public byte[] encrypt(byte[] input, int outputOffset, byte[] flags) {
byte[] output = new byte[input.length + outputOffset];
@@ -31,7 +31,7 @@ public void testEncrypt_Odd() {
BytesEncryptor instance = SwapBytesTransformer.encryptor();
byte[] input = { 1, 3, 5 };
- byte[] output = instance.encrypt(input, 3);
+ byte[] output = instance.encrypt(input, 3, new byte[1]);
assertArrayEquals(new byte[] { 0, 0, 0, 5, 3, 1 }, output);
}
@@ -41,7 +41,7 @@ public void testEncrypt_Even() {
BytesEncryptor instance = SwapBytesTransformer.encryptor();
byte[] input = { 1, 3, 5, 8 };
- byte[] output = instance.encrypt(input, 3);
+ byte[] output = instance.encrypt(input, 3, new byte[1]);
assertArrayEquals(new byte[] { 0, 0, 0, 8, 5, 3, 1 }, output);
}

0 comments on commit 5fe40a6

Please sign in to comment.