/*
 * 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.
/** * 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 parameters, those headers are merged with the application-wide encryptionContext.
/** * 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); } }