blob: 8176d6001d4c435534a34593213454430903b306 [file] [log] [blame]
/*
* 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.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;
/**
* A (largely) immutable wrapper for the application-wide file-level encryption settings.
*/
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;
// always attempt to load the cipher factory, as we could be in the situation where the user has disabled encryption,
// but has existing commitlogs and sstables on disk that are still encrypted (and still need to be read)
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);
// add compression options, someday ...
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;
}
/**
* If encryption headers are found in the {@code parameters},
* those headers are merged with the application-wide {@code encryptionContext}.
*/
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);
}
}