/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.apache.hadoop.hbase.io.crypto;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.security.Key;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.testclassification.MiscTests;
import org.apache.hadoop.hbase.testclassification.SmallTests;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.Test;
import org.junit.experimental.categories.Category;
@Category({MiscTests.class, SmallTests.class})
public class TestEncryption {
private static final Log LOG = LogFactory.getLog(TestEncryption.class);
@Test
public void testSmallBlocks() throws Exception {
byte[] key = new byte[16];
Bytes.random(key);
byte[] iv = new byte[16];
Bytes.random(iv);
for (int size: new int[] { 4, 8, 16, 32, 64, 128, 256, 512 } ) {
checkTransformSymmetry(key, iv, getRandomBlock(size));
}
}
@Test
public void testLargeBlocks() throws Exception {
byte[] key = new byte[16];
Bytes.random(key);
byte[] iv = new byte[16];
Bytes.random(iv);
for (int size: new int[] { 256 * 1024, 512 * 1024, 1024 * 1024 } ) {
checkTransformSymmetry(key, iv, getRandomBlock(size));
}
}
@Test
public void testOddSizedBlocks() throws Exception {
byte[] key = new byte[16];
Bytes.random(key);
byte[] iv = new byte[16];
Bytes.random(iv);
for (int size: new int[] { 3, 7, 11, 23, 47, 79, 119, 175 } ) {
checkTransformSymmetry(key, iv, getRandomBlock(size));
}
}
@Test
public void testTypicalHFileBlocks() throws Exception {
byte[] key = new byte[16];
Bytes.random(key);
byte[] iv = new byte[16];
Bytes.random(iv);
for (int size: new int[] { 4 * 1024, 8 * 1024, 64 * 1024, 128 * 1024 } ) {
checkTransformSymmetry(key, iv, getRandomBlock(size));
}
}
private void checkTransformSymmetry(byte[] keyBytes, byte[] iv, byte[] plaintext)
throws Exception {
LOG.info("checkTransformSymmetry: AES, plaintext length = " + plaintext.length);
Configuration conf = HBaseConfiguration.create();
String algorithm =
conf.get(HConstants.CRYPTO_KEY_ALGORITHM_CONF_KEY, HConstants.CIPHER_AES);
Cipher aes = Encryption.getCipher(conf, algorithm);
Key key = new SecretKeySpec(keyBytes, algorithm);
Encryptor e = aes.getEncryptor();
e.setKey(key);
e.setIv(iv);
e.reset();
ByteArrayOutputStream encOut = new ByteArrayOutputStream();
Encryption.encrypt(encOut, plaintext, 0, plaintext.length, e);
byte[] encrypted = encOut.toByteArray();
Decryptor d = aes.getDecryptor();
d.setKey(key);
d.setIv(iv);
d.reset();
ByteArrayInputStream encIn = new ByteArrayInputStream(encrypted);
ByteArrayOutputStream decOut = new ByteArrayOutputStream();
Encryption.decrypt(decOut, encIn, plaintext.length, d);
byte[] result = decOut.toByteArray();
assertEquals("Decrypted result has different length than plaintext",
result.length, plaintext.length);
assertTrue("Transformation was not symmetric",
Bytes.equals(result, plaintext));
}
private byte[] getRandomBlock(int size) {
byte[] b = new byte[size];
Bytes.random(b);
return b;
}
}