001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.io.crypto; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertTrue; 022 023import java.io.ByteArrayInputStream; 024import java.io.ByteArrayOutputStream; 025import java.security.Key; 026import javax.crypto.spec.SecretKeySpec; 027import org.apache.hadoop.conf.Configuration; 028import org.apache.hadoop.hbase.HBaseClassTestRule; 029import org.apache.hadoop.hbase.HBaseConfiguration; 030import org.apache.hadoop.hbase.HConstants; 031import org.apache.hadoop.hbase.testclassification.MiscTests; 032import org.apache.hadoop.hbase.testclassification.SmallTests; 033import org.apache.hadoop.hbase.util.Bytes; 034import org.junit.ClassRule; 035import org.junit.Test; 036import org.junit.experimental.categories.Category; 037import org.slf4j.Logger; 038import org.slf4j.LoggerFactory; 039 040@Category({ MiscTests.class, SmallTests.class }) 041public class TestEncryption { 042 @ClassRule 043 public static final HBaseClassTestRule CLASS_RULE = 044 HBaseClassTestRule.forClass(TestEncryption.class); 045 046 private static final Logger LOG = LoggerFactory.getLogger(TestEncryption.class); 047 048 @Test 049 public void testSmallBlocks() throws Exception { 050 byte[] key = new byte[16]; 051 Bytes.secureRandom(key); 052 byte[] iv = new byte[16]; 053 Bytes.secureRandom(iv); 054 for (int size : new int[] { 4, 8, 16, 32, 64, 128, 256, 512 }) { 055 checkTransformSymmetry(key, iv, getRandomBlock(size)); 056 } 057 } 058 059 @Test 060 public void testLargeBlocks() throws Exception { 061 byte[] key = new byte[16]; 062 Bytes.secureRandom(key); 063 byte[] iv = new byte[16]; 064 Bytes.secureRandom(iv); 065 for (int size : new int[] { 256 * 1024, 512 * 1024, 1024 * 1024 }) { 066 checkTransformSymmetry(key, iv, getRandomBlock(size)); 067 } 068 } 069 070 @Test 071 public void testOddSizedBlocks() throws Exception { 072 byte[] key = new byte[16]; 073 Bytes.secureRandom(key); 074 byte[] iv = new byte[16]; 075 Bytes.secureRandom(iv); 076 for (int size : new int[] { 3, 7, 11, 23, 47, 79, 119, 175 }) { 077 checkTransformSymmetry(key, iv, getRandomBlock(size)); 078 } 079 } 080 081 @Test 082 public void testTypicalHFileBlocks() throws Exception { 083 byte[] key = new byte[16]; 084 Bytes.secureRandom(key); 085 byte[] iv = new byte[16]; 086 Bytes.secureRandom(iv); 087 for (int size : new int[] { 4 * 1024, 8 * 1024, 64 * 1024, 128 * 1024 }) { 088 checkTransformSymmetry(key, iv, getRandomBlock(size)); 089 } 090 } 091 092 @Test 093 public void testIncrementIV() { 094 byte[] iv = new byte[] { 1, 2, 3 }; 095 byte[] iv_neg = new byte[] { -3, -13, 25 }; 096 Encryption.incrementIv(iv); 097 assertTrue(Bytes.equals(iv, new byte[] { 2, 2, 3 })); 098 099 Encryption.incrementIv(iv, 255); 100 assertTrue(Bytes.equals(iv, new byte[] { 1, 3, 3 })); 101 102 Encryption.incrementIv(iv, 1024); 103 assertTrue(Bytes.equals(iv, new byte[] { 1, 7, 3 })); 104 105 Encryption.incrementIv(iv_neg); 106 assertTrue(Bytes.equals(iv_neg, new byte[] { -2, -13, 25 })); 107 108 Encryption.incrementIv(iv_neg, 5); 109 assertTrue(Bytes.equals(iv_neg, new byte[] { 3, -12, 25 })); 110 } 111 112 private void checkTransformSymmetry(byte[] keyBytes, byte[] iv, byte[] plaintext) 113 throws Exception { 114 LOG.info("checkTransformSymmetry: AES, plaintext length = " + plaintext.length); 115 116 Configuration conf = HBaseConfiguration.create(); 117 String algorithm = conf.get(HConstants.CRYPTO_KEY_ALGORITHM_CONF_KEY, HConstants.CIPHER_AES); 118 Cipher aes = Encryption.getCipher(conf, algorithm); 119 Key key = new SecretKeySpec(keyBytes, algorithm); 120 121 Encryptor e = aes.getEncryptor(); 122 e.setKey(key); 123 e.setIv(iv); 124 e.reset(); 125 ByteArrayOutputStream encOut = new ByteArrayOutputStream(); 126 Encryption.encrypt(encOut, plaintext, 0, plaintext.length, e); 127 byte[] encrypted = encOut.toByteArray(); 128 129 Decryptor d = aes.getDecryptor(); 130 d.setKey(key); 131 d.setIv(iv); 132 d.reset(); 133 ByteArrayInputStream encIn = new ByteArrayInputStream(encrypted); 134 ByteArrayOutputStream decOut = new ByteArrayOutputStream(); 135 Encryption.decrypt(decOut, encIn, plaintext.length, d); 136 137 byte[] result = decOut.toByteArray(); 138 assertEquals("Decrypted result has different length than plaintext", result.length, 139 plaintext.length); 140 assertTrue("Transformation was not symmetric", Bytes.equals(result, plaintext)); 141 } 142 143 private byte[] getRandomBlock(int size) { 144 byte[] b = new byte[size]; 145 Bytes.random(b); 146 return b; 147 } 148}