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.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.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.HBaseConfiguration; 029import org.apache.hadoop.hbase.HConstants; 030import org.apache.hadoop.hbase.testclassification.MiscTests; 031import org.apache.hadoop.hbase.testclassification.SmallTests; 032import org.apache.hadoop.hbase.util.Bytes; 033import org.junit.jupiter.api.Tag; 034import org.junit.jupiter.api.Test; 035import org.slf4j.Logger; 036import org.slf4j.LoggerFactory; 037 038@Tag(MiscTests.TAG) 039@Tag(SmallTests.TAG) 040public class TestEncryption { 041 042 private static final Logger LOG = LoggerFactory.getLogger(TestEncryption.class); 043 044 @Test 045 public void testSmallBlocks() throws Exception { 046 byte[] key = new byte[16]; 047 Bytes.secureRandom(key); 048 byte[] iv = new byte[16]; 049 Bytes.secureRandom(iv); 050 for (int size : new int[] { 4, 8, 16, 32, 64, 128, 256, 512 }) { 051 checkTransformSymmetry(key, iv, getRandomBlock(size)); 052 } 053 } 054 055 @Test 056 public void testLargeBlocks() throws Exception { 057 byte[] key = new byte[16]; 058 Bytes.secureRandom(key); 059 byte[] iv = new byte[16]; 060 Bytes.secureRandom(iv); 061 for (int size : new int[] { 256 * 1024, 512 * 1024, 1024 * 1024 }) { 062 checkTransformSymmetry(key, iv, getRandomBlock(size)); 063 } 064 } 065 066 @Test 067 public void testOddSizedBlocks() throws Exception { 068 byte[] key = new byte[16]; 069 Bytes.secureRandom(key); 070 byte[] iv = new byte[16]; 071 Bytes.secureRandom(iv); 072 for (int size : new int[] { 3, 7, 11, 23, 47, 79, 119, 175 }) { 073 checkTransformSymmetry(key, iv, getRandomBlock(size)); 074 } 075 } 076 077 @Test 078 public void testTypicalHFileBlocks() throws Exception { 079 byte[] key = new byte[16]; 080 Bytes.secureRandom(key); 081 byte[] iv = new byte[16]; 082 Bytes.secureRandom(iv); 083 for (int size : new int[] { 4 * 1024, 8 * 1024, 64 * 1024, 128 * 1024 }) { 084 checkTransformSymmetry(key, iv, getRandomBlock(size)); 085 } 086 } 087 088 @Test 089 public void testIncrementIV() { 090 byte[] iv = new byte[] { 1, 2, 3 }; 091 byte[] iv_neg = new byte[] { -3, -13, 25 }; 092 Encryption.incrementIv(iv); 093 assertTrue(Bytes.equals(iv, new byte[] { 2, 2, 3 })); 094 095 Encryption.incrementIv(iv, 255); 096 assertTrue(Bytes.equals(iv, new byte[] { 1, 3, 3 })); 097 098 Encryption.incrementIv(iv, 1024); 099 assertTrue(Bytes.equals(iv, new byte[] { 1, 7, 3 })); 100 101 Encryption.incrementIv(iv_neg); 102 assertTrue(Bytes.equals(iv_neg, new byte[] { -2, -13, 25 })); 103 104 Encryption.incrementIv(iv_neg, 5); 105 assertTrue(Bytes.equals(iv_neg, new byte[] { 3, -12, 25 })); 106 } 107 108 private void checkTransformSymmetry(byte[] keyBytes, byte[] iv, byte[] plaintext) 109 throws Exception { 110 LOG.info("checkTransformSymmetry: AES, plaintext length = " + plaintext.length); 111 112 Configuration conf = HBaseConfiguration.create(); 113 String algorithm = 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(result.length, plaintext.length, 135 "Decrypted result has different length than plaintext"); 136 assertTrue(Bytes.equals(result, plaintext), "Transformation was not symmetric"); 137 } 138 139 private byte[] getRandomBlock(int size) { 140 byte[] b = new byte[size]; 141 Bytes.random(b); 142 return b; 143 } 144}