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));
 


Reply via email to