package org.apache.cassandra.security;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Cipher;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
import org.apache.cassandra.config.TransparentDataEncryptionOptions;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.io.compress.ICompressor;
import org.apache.cassandra.io.compress.LZ4Compressor;
import org.apache.cassandra.utils.Hex;
public class EncryptionContext
{
public static final String ENCRYPTION_CIPHER = "encCipher";
public static final String ENCRYPTION_KEY_ALIAS = "encKeyAlias";
public static final String ENCRYPTION_IV = "encIV";
private final TransparentDataEncryptionOptions tdeOptions;
private final ICompressor compressor;
private final CipherFactory cipherFactory;
private final byte[] iv;
private final int chunkLength;
public EncryptionContext()
{
this(new TransparentDataEncryptionOptions());
}
public EncryptionContext(TransparentDataEncryptionOptions tdeOptions)
{
this(tdeOptions, null, true);
}
@VisibleForTesting
public EncryptionContext(TransparentDataEncryptionOptions tdeOptions, byte[] iv, boolean init)
{
this.tdeOptions = tdeOptions;
compressor = LZ4Compressor.create(Collections.<String, String>emptyMap());
chunkLength = tdeOptions.chunk_length_kb * 1024;
this.iv = iv;
CipherFactory factory = null;
if (tdeOptions.enabled && init)
{
try
{
factory = new CipherFactory(tdeOptions);
}
catch (Exception e)
{
throw new ConfigurationException("failed to load key provider for transparent data encryption", e);
}
}
cipherFactory = factory;
}
public ICompressor getCompressor()
{
return compressor;
}
public Cipher getEncryptor() throws IOException
{
return cipherFactory.getEncryptor(tdeOptions.cipher, tdeOptions.key_alias);
}
public Cipher getDecryptor() throws IOException
{
if (iv == null || iv.length == 0)
throw new IllegalStateException("no initialization vector (IV) found in this context");
return cipherFactory.getDecryptor(tdeOptions.cipher, tdeOptions.key_alias, iv);
}
public boolean isEnabled()
{
return tdeOptions.enabled;
}
public int getChunkLength()
{
return chunkLength;
}
public byte[] getIV()
{
return iv;
}
public TransparentDataEncryptionOptions getTransparentDataEncryptionOptions()
{
return tdeOptions;
}
public boolean equals(Object o)
{
return o instanceof EncryptionContext && equals((EncryptionContext) o);
}
public boolean equals(EncryptionContext other)
{
return Objects.equal(tdeOptions, other.tdeOptions)
&& Objects.equal(compressor, other.compressor)
&& Arrays.equals(iv, other.iv);
}
public Map<String, String> toHeaderParameters()
{
Map<String, String> map = new HashMap<>(3);
if (tdeOptions.enabled)
{
map.put(ENCRYPTION_CIPHER, tdeOptions.cipher);
map.put(ENCRYPTION_KEY_ALIAS, tdeOptions.key_alias);
if (iv != null && iv.length > 0)
map.put(ENCRYPTION_IV, Hex.bytesToHex(iv));
}
return map;
}
public static EncryptionContext createFromMap(Map<?, ?> parameters, EncryptionContext encryptionContext)
{
if (parameters == null || parameters.isEmpty())
return new EncryptionContext(new TransparentDataEncryptionOptions(false));
String keyAlias = (String)parameters.get(ENCRYPTION_KEY_ALIAS);
String cipher = (String)parameters.get(ENCRYPTION_CIPHER);
String ivString = (String)parameters.get(ENCRYPTION_IV);
if (keyAlias == null || cipher == null)
return new EncryptionContext(new TransparentDataEncryptionOptions(false));
TransparentDataEncryptionOptions tdeOptions = new TransparentDataEncryptionOptions(cipher, keyAlias, encryptionContext.getTransparentDataEncryptionOptions().key_provider);
byte[] iv = ivString != null ? Hex.hexToBytes(ivString) : null;
return new EncryptionContext(tdeOptions, iv, true);
}
}