/*
 * 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 java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Key;
import org.apache.yetus.audience.InterfaceAudience;

/**
 * A common interface for a cryptographic algorithm.
 */
@InterfaceAudience.Public
public abstract class Cipher {

  public static final int KEY_LENGTH = 16;
  public static final int KEY_LENGTH_BITS = KEY_LENGTH * 8;
  public static final int BLOCK_SIZE = 16;
  public static final int IV_LENGTH = 16;

  public static final String RNG_ALGORITHM_KEY = "hbase.crypto.algorithm.rng";
  public static final String RNG_PROVIDER_KEY = "hbase.crypto.algorithm.rng.provider";

  private final CipherProvider provider;

  public Cipher(CipherProvider provider) {
    this.provider = provider;
  }

  /**
   * Return the provider for this Cipher
   */
  public CipherProvider getProvider() {
    return provider;
  }

  /**
   * Return this Cipher's name
   */
  public abstract String getName();

  /**
   * Return the key length required by this cipher, in bytes
   */
  public abstract int getKeyLength();

  /**
   * Return the expected initialization vector length, in bytes, or 0 if not applicable
   */
  public abstract int getIvLength();

  /**
   * Create a random symmetric key
   * @return the random symmetric key
   */
  public abstract Key getRandomKey();

  /**
   * Get an encryptor for encrypting data.
   */
  public abstract Encryptor getEncryptor();

  /**
   * Return a decryptor for decrypting data.
   */
  public abstract Decryptor getDecryptor();

  /**
   * Create an encrypting output stream given a context and IV
   * @param out     the output stream to wrap
   * @param context the encryption context
   * @param iv      initialization vector
   * @return the encrypting wrapper
   */
  public abstract OutputStream createEncryptionStream(OutputStream out, Context context, byte[] iv)
    throws IOException;

  /**
   * Create an encrypting output stream given an initialized encryptor
   * @param out       the output stream to wrap
   * @param encryptor the encryptor
   * @return the encrypting wrapper
   */
  public abstract OutputStream createEncryptionStream(OutputStream out, Encryptor encryptor)
    throws IOException;

  /**
   * Create a decrypting input stream given a context and IV
   * @param in      the input stream to wrap
   * @param context the encryption context
   * @param iv      initialization vector
   * @return the decrypting wrapper
   */
  public abstract InputStream createDecryptionStream(InputStream in, Context context, byte[] iv)
    throws IOException;

  /**
   * Create a decrypting output stream given an initialized decryptor
   * @param in        the input stream to wrap
   * @param decryptor the decryptor
   * @return the decrypting wrapper
   */
  public abstract InputStream createDecryptionStream(InputStream in, Decryptor decryptor)
    throws IOException;

}
