Author: aadamchik
Date: Sun Apr 13 13:28:55 2014
New Revision: 1586981
URL: http://svn.apache.org/r1586981
Log:
CAY-1925 cayenne-crypto: add optional compression to the encryption pipeline
a more complex header structure: magic #, size, flags, key_name
Modified:
cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/bytes/Header.java
cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/bytes/HeaderDecryptor.java
cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/bytes/HeaderEncryptor.java
cayenne/main/trunk/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/bytes/HeaderEncryptorTest.java
cayenne/main/trunk/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/bytes/HeaderTest.java
cayenne/main/trunk/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/unit/CryptoUnitUtils.java
Modified:
cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/bytes/Header.java
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/bytes/Header.java?rev=1586981&r1=1586980&r2=1586981&view=diff
==============================================================================
---
cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/bytes/Header.java
(original)
+++
cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/bytes/Header.java
Sun Apr 13 13:28:55 2014
@@ -24,7 +24,16 @@ import org.apache.cayenne.crypto.Cayenne
/**
* Represents a header with metadata about the encrypted data. A header is
- * prependend to each encrypted value, and itself is not encrypted.
+ * prependend to each encrypted value, and itself is not encrypted. Header
+ * format is the following:
+ * <ul>
+ * <li>byte 0..2: "magic" number identifying the format as Cayenne-crypto
+ * encrypted sequence.
+ * <li>byte 3: header length N, i.e. how many bytes the header contains,
+ * including magic number and the length indicator. N can be 0..127.
+ * <li>byte 4: a bit String representing various flags, such as compression.
+ * <li>byte 5..N: UTF8-encoded symbolic name of the encryption key.
+ * </ul>
*
* @since 3.2
*/
@@ -32,25 +41,33 @@ public class Header {
private static final String KEY_NAME_CHARSET = "UTF-8";
+ // "CC1" is a "magic number" identifying Cayenne-crypto version 1 value
+ private static final byte[] MAGIC_NUMBER = { 'C', 'C', '1' };
+
+ /**
+ * Position of the "flags" byte in the header.
+ */
+ private static final int MAGIC_NUMBER_POSITION = 0;
+
/**
- * The size of a header byte[] block.
+ * Position of the header size byte in the header.
*/
- public static final int HEADER_SIZE = 16;
+ private static final int SIZE_POSITION = 3;
/**
- * The size of a key name within the header block.
+ * Position of the "flags" byte in the header.
*/
- public static final int KEY_NAME_SIZE = 8;
+ private static final int FLAGS_POSITION = 4;
/**
* Position of the key name within the header block.
*/
- public static final int KEY_NAME_OFFSET = 8;
+ private static final int KEY_NAME_OFFSET = 5;
/**
- * Position of the "flags" byte in the header.
+ * Max size of a key name within a header.
*/
- public static final int FLAGS_OFFSET = 0;
+ private static final int KEY_NAME_MAX_SIZE = Byte.MAX_VALUE -
KEY_NAME_OFFSET;
private byte[] data;
private int offset;
@@ -63,25 +80,29 @@ public class Header {
throw new CayenneCryptoException("Can't encode in " +
KEY_NAME_CHARSET, e);
}
- byte[] data = new byte[HEADER_SIZE];
-
- if (keyNameBytes.length <= KEY_NAME_SIZE) {
- System.arraycopy(keyNameBytes, 0, data, KEY_NAME_OFFSET,
keyNameBytes.length);
- } else {
+ if (keyNameBytes.length > KEY_NAME_MAX_SIZE) {
throw new CayenneCryptoException("Key name '" + keyName
- + "' is too long. Its UTF8-encoded form should not exceed
" + KEY_NAME_SIZE + " bytes");
+ + "' is too long. Its UTF8-encoded form should not exceed
" + KEY_NAME_MAX_SIZE + " bytes");
}
+ int n = MAGIC_NUMBER.length + 1 + 1 + keyNameBytes.length;
+
+ byte[] data = new byte[n];
+ System.arraycopy(MAGIC_NUMBER, 0, data, MAGIC_NUMBER_POSITION,
MAGIC_NUMBER.length);
+
+ // total header size
+ data[SIZE_POSITION] = (byte) n;
+
+ // flags
+ data[FLAGS_POSITION] = 0;
+
+ // key name
+ System.arraycopy(keyNameBytes, 0, data, KEY_NAME_OFFSET,
keyNameBytes.length);
+
return create(data, 0);
}
public static Header create(byte[] data, int offset) {
-
- if (data.length - offset < HEADER_SIZE) {
- throw new CayenneCryptoException("Unexpected header data size: " +
data.length + ", expected size is "
- + HEADER_SIZE);
- }
-
return new Header(data, offset);
}
@@ -91,14 +112,21 @@ public class Header {
this.offset = offset;
}
+ public int size() {
+ return data[offset + SIZE_POSITION];
+ }
+
+ /**
+ * Saves the header bytes in the provided buffer at specified offset.
+ */
public void store(byte[] output, int outputOffset) {
- System.arraycopy(data, offset, output, outputOffset,
Header.HEADER_SIZE);
+ System.arraycopy(data, offset, output, outputOffset, size());
}
public String getKeyName() {
+
try {
- // 'trim' is to get rid of 0 padding
- return new String(data, offset + KEY_NAME_OFFSET, KEY_NAME_SIZE,
KEY_NAME_CHARSET).trim();
+ return new String(data, offset + KEY_NAME_OFFSET, size() -
KEY_NAME_OFFSET, KEY_NAME_CHARSET);
} catch (UnsupportedEncodingException e) {
throw new CayenneCryptoException("Can't decode with " +
KEY_NAME_CHARSET, e);
}
Modified:
cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/bytes/HeaderDecryptor.java
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/bytes/HeaderDecryptor.java?rev=1586981&r1=1586980&r2=1586981&view=diff
==============================================================================
---
cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/bytes/HeaderDecryptor.java
(original)
+++
cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/bytes/HeaderDecryptor.java
Sun Apr 13 13:28:55 2014
@@ -42,7 +42,7 @@ class HeaderDecryptor implements BytesDe
Header header = header(input, inputOffset);
Key inRecordKey = keySource.getKey(header.getKeyName());
- return delegate.decrypt(input, inputOffset + Header.HEADER_SIZE,
inRecordKey);
+ return delegate.decrypt(input, inputOffset + header.size(),
inRecordKey);
}
Header header(byte[] input, int inputOffset) {
Modified:
cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/bytes/HeaderEncryptor.java
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/bytes/HeaderEncryptor.java?rev=1586981&r1=1586980&r2=1586981&view=diff
==============================================================================
---
cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/bytes/HeaderEncryptor.java
(original)
+++
cayenne/main/trunk/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/bytes/HeaderEncryptor.java
Sun Apr 13 13:28:55 2014
@@ -30,7 +30,7 @@ class HeaderEncryptor implements BytesEn
@Override
public byte[] encrypt(byte[] input, int outputOffset) {
- byte[] output = delegate.encrypt(input, outputOffset +
Header.HEADER_SIZE);
+ byte[] output = delegate.encrypt(input, outputOffset + header.size());
header.store(output, outputOffset);
return output;
}
Modified:
cayenne/main/trunk/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/bytes/HeaderEncryptorTest.java
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/bytes/HeaderEncryptorTest.java?rev=1586981&r1=1586980&r2=1586981&view=diff
==============================================================================
---
cayenne/main/trunk/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/bytes/HeaderEncryptorTest.java
(original)
+++
cayenne/main/trunk/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/bytes/HeaderEncryptorTest.java
Sun Apr 13 13:28:55 2014
@@ -39,9 +39,11 @@ public class HeaderEncryptorTest {
// intentionally non-standard block size..
HeaderEncryptor encryptor = new HeaderEncryptor(delegate,
encryptionHeader);
- byte[] output = encryptor.encrypt(input, 1);
- assertArrayEquals(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 'm', 'y',
'k', 'e', 'y', 0, 0, 0, 8, 7, 6, 5, 4, 3,
- 2, 1 }, output);
+ byte[] output1 = encryptor.encrypt(input, 0);
+ 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);
+ assertArrayEquals(new byte[] { 0, 'C', 'C', '1', 10, 0, 'm', 'y', 'k',
'e', 'y', 8, 7, 6, 5, 4, 3, 2, 1 }, output2);
}
}
Modified:
cayenne/main/trunk/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/bytes/HeaderTest.java
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/bytes/HeaderTest.java?rev=1586981&r1=1586980&r2=1586981&view=diff
==============================================================================
---
cayenne/main/trunk/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/bytes/HeaderTest.java
(original)
+++
cayenne/main/trunk/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/transformer/bytes/HeaderTest.java
Sun Apr 13 13:28:55 2014
@@ -33,15 +33,23 @@ public class HeaderTest {
assertEquals("b", Header.create("b").getKeyName());
}
- @Test
- public void testCreate_WithData() {
- byte[] input1 = { 0, 0, 0, 0, 0, 0, 0, 0, 'a', 'b', 'c', 'd', 'e', 0,
0, 0, 0 };
- assertEquals("bcde", Header.create(input1, 1).getKeyName());
- }
-
@Test(expected = CayenneCryptoException.class)
- public void testCreate_WithData_WrongLength() {
- Header.create(new byte[] { 'a', 'b', 0, 0, 'e' }, 0);
+ public void testCreate_WithKeyName_TooLong() {
+
+ StringBuilder buf = new StringBuilder();
+ for (int i = 0; i < Byte.MAX_VALUE + 1; i++) {
+ buf.append("a");
+ }
+
+ Header.create(buf.toString());
}
+ @Test
+ public void testCreate_WithData() {
+ byte[] input1 = { 'C', 'C', '1', 9, 0, 'a', 'b', 'c', 'd', 'e' };
+ assertEquals("abcd", Header.create(input1, 0).getKeyName());
+
+ byte[] input2 = { 0, 'C', 'C', '1', 9, 0, 'a', 'b', 'c', 'd', 'e' };
+ assertEquals("abcd", Header.create(input2, 1).getKeyName());
+ }
}
Modified:
cayenne/main/trunk/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/unit/CryptoUnitUtils.java
URL:
http://svn.apache.org/viewvc/cayenne/main/trunk/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/unit/CryptoUnitUtils.java?rev=1586981&r1=1586980&r2=1586981&view=diff
==============================================================================
---
cayenne/main/trunk/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/unit/CryptoUnitUtils.java
(original)
+++
cayenne/main/trunk/cayenne-crypto/src/test/java/org/apache/cayenne/crypto/unit/CryptoUnitUtils.java
Sun Apr 13 13:28:55 2014
@@ -47,15 +47,14 @@ public class CryptoUnitUtils {
try {
Cipher decCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+
+ Header header = Header.create(source, 0);
int blockSize = decCipher.getBlockSize();
- byte[] keyNameBytes = Arrays.copyOfRange(source, 0,
Header.HEADER_SIZE);
- byte[] ivBytes = Arrays.copyOfRange(source, Header.HEADER_SIZE,
Header.HEADER_SIZE + blockSize);
- byte[] cipherText = Arrays.copyOfRange(source, Header.HEADER_SIZE
+ blockSize, source.length);
-
- // 'trim' is to get rid of 0 padding
- String keyName = new String(keyNameBytes, Header.KEY_NAME_OFFSET,
Header.KEY_NAME_SIZE, "UTF-8").trim();
- Key key =
runtime.getInjector().getInstance(KeySource.class).getKey(keyName);
+ byte[] ivBytes = Arrays.copyOfRange(source, header.size(),
header.size() + blockSize);
+ byte[] cipherText = Arrays.copyOfRange(source, header.size() +
blockSize, source.length);
+
+ Key key =
runtime.getInjector().getInstance(KeySource.class).getKey(header.getKeyName());
decCipher.init(Cipher.DECRYPT_MODE, key, new
IvParameterSpec(ivBytes));