View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.io.crypto;
19  
20  import static org.junit.Assert.assertEquals;
21  import static org.junit.Assert.assertTrue;
22  
23  import java.io.ByteArrayInputStream;
24  import java.io.ByteArrayOutputStream;
25  import java.security.Key;
26  
27  import javax.crypto.spec.SecretKeySpec;
28  
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  import org.apache.hadoop.conf.Configuration;
32  import org.apache.hadoop.hbase.HBaseConfiguration;
33  import org.apache.hadoop.hbase.HConstants;
34  import org.apache.hadoop.hbase.testclassification.SmallTests;
35  import org.apache.hadoop.hbase.util.Bytes;
36  import org.junit.Test;
37  import org.junit.experimental.categories.Category;
38  
39  @Category(SmallTests.class)
40  public class TestEncryption {
41    private static final Log LOG = LogFactory.getLog(TestEncryption.class);
42  
43    @Test
44    public void testSmallBlocks() throws Exception {
45      byte[] key = new byte[16];
46      Bytes.random(key);
47      byte[] iv = new byte[16];
48      Bytes.random(iv);
49      for (int size: new int[] { 4, 8, 16, 32, 64, 128, 256, 512 }) {
50        checkTransformSymmetry(key, iv, getRandomBlock(size));
51      }
52    }
53  
54    @Test
55    public void testLargeBlocks() throws Exception {
56      byte[] key = new byte[16];
57      Bytes.random(key);
58      byte[] iv = new byte[16];
59      Bytes.random(iv);
60      for (int size: new int[] { 256 * 1024, 512 * 1024, 1024 * 1024 }) {
61        checkTransformSymmetry(key, iv, getRandomBlock(size));
62      }
63    }
64  
65    @Test
66    public void testOddSizedBlocks() throws Exception {
67      byte[] key = new byte[16];
68      Bytes.random(key);
69      byte[] iv = new byte[16];
70      Bytes.random(iv);
71      for (int size: new int[] { 3, 7, 11, 23, 47, 79, 119, 175 }) {
72        checkTransformSymmetry(key, iv, getRandomBlock(size));
73      }
74    }
75  
76    @Test
77    public void testTypicalHFileBlocks() throws Exception {
78      byte[] key = new byte[16];
79      Bytes.random(key);
80      byte[] iv = new byte[16];
81      Bytes.random(iv);
82      for (int size: new int[] { 4 * 1024, 8 * 1024, 64 * 1024, 128 * 1024 }) {
83        checkTransformSymmetry(key, iv, getRandomBlock(size));
84      }
85    }
86  
87    @Test
88    public void testIncrementIV() {
89      byte[] iv = new byte[] {1, 2, 3};
90      byte[] iv_neg = new byte[] {-3, -13, 25};
91      Encryption.incrementIv(iv);
92      assertTrue(Bytes.equals(iv, new byte[] {2, 2, 3}));
93  
94      Encryption.incrementIv(iv, 255);
95      assertTrue(Bytes.equals(iv, new byte[] {1, 3, 3}));
96  
97      Encryption.incrementIv(iv, 1024);
98      assertTrue(Bytes.equals(iv, new byte[] {1, 7, 3}));
99  
100     Encryption.incrementIv(iv_neg);
101     assertTrue(Bytes.equals(iv_neg, new byte[] {-2, -13, 25}));
102 
103     Encryption.incrementIv(iv_neg, 5);
104     assertTrue(Bytes.equals(iv_neg, new byte[] {3, -12, 25}));
105   }
106 
107   private void checkTransformSymmetry(byte[] keyBytes, byte[] iv, byte[] plaintext)
108       throws Exception {
109     LOG.info("checkTransformSymmetry: AES, plaintext length = " + plaintext.length);
110 
111     Configuration conf = HBaseConfiguration.create();
112     String algorithm =
113         conf.get(HConstants.CRYPTO_KEY_ALGORITHM_CONF_KEY, HConstants.CIPHER_AES);
114     Cipher aes = Encryption.getCipher(conf, algorithm);
115     Key key = new SecretKeySpec(keyBytes, algorithm);
116 
117     Encryptor e = aes.getEncryptor();
118     e.setKey(key);
119     e.setIv(iv);
120     e.reset();
121     ByteArrayOutputStream encOut = new ByteArrayOutputStream();
122     Encryption.encrypt(encOut, plaintext, 0, plaintext.length, e);
123     byte[] encrypted = encOut.toByteArray();
124 
125     Decryptor d = aes.getDecryptor();
126     d.setKey(key);
127     d.setIv(iv);
128     d.reset();
129     ByteArrayInputStream encIn = new ByteArrayInputStream(encrypted);
130     ByteArrayOutputStream decOut = new ByteArrayOutputStream();
131     Encryption.decrypt(decOut, encIn, plaintext.length, d);
132 
133     byte[] result = decOut.toByteArray();
134     assertEquals("Decrypted result has different length than plaintext",
135       result.length, plaintext.length);
136     assertTrue("Transformation was not symmetric",
137       Bytes.equals(result, plaintext));
138   }
139 
140   private byte[] getRandomBlock(int size) {
141     byte[] b = new byte[size];
142     Bytes.random(b);
143     return b;
144   }
145 }