package com.airlock.iam.security.util.crypto;

import com.airlock.iam.security.util.crypto.iv.IvGenerator;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public final class AesCbcIvIntegratedCipher extends IvIntegratedCipher {
   private static final int IV_LENGTH = 16;
   public static final String AES_CBC_PKCS5_PADDING = "AES/CBC/PKCS5Padding";

   @SuppressFBWarnings({"STATIC_IV", "CIPHER_INTEGRITY", "PADDING_ORACLE"})
   @Override
   public byte[] encrypt(byte[] dataToEncrypt, byte[] key, IvGenerator ivGenerator) throws EncryptionException {
      try {
         javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding");
         byte[] iv = ivGenerator.generateIv(16);
         cipher.init(1, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
         byte[] encryptedBytes = cipher.doFinal(dataToEncrypt);
         byte[] combinedIvAndCipherText = new byte[iv.length + encryptedBytes.length];
         System.arraycopy(iv, 0, combinedIvAndCipherText, 0, iv.length);
         System.arraycopy(encryptedBytes, 0, combinedIvAndCipherText, iv.length, encryptedBytes.length);
         return combinedIvAndCipherText;
      } catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidAlgorithmParameterException | BadPaddingException | InvalidKeyException | IllegalBlockSizeException e) {
         throw new EncryptionException("failed to encrypt", e);
      }
   }

   @SuppressFBWarnings({"CIPHER_INTEGRITY", "PADDING_ORACLE"})
   @Override
   public byte[] decrypt(byte[] dataToDecrypt, byte[] key) throws DecryptionException {
      byte[] iv = new byte[16];
      System.arraycopy(dataToDecrypt, 0, iv, 0, iv.length);
      byte[] encryptedText = new byte[dataToDecrypt.length - 16];
      System.arraycopy(dataToDecrypt, 16, encryptedText, 0, encryptedText.length);

      try {
         javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding");
         cipher.init(2, new SecretKeySpec(key, "AES"), new IvParameterSpec(iv));
         return cipher.doFinal(encryptedText);
      } catch (NoSuchPaddingException | IllegalBlockSizeException | NoSuchAlgorithmException | InvalidAlgorithmParameterException | BadPaddingException | InvalidKeyException e) {
         throw new DecryptionException("failed to decrypt", e);
      }
   }
}
