All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tsec.cipher.symmetric.jca.primitive.JCAAEADPrimitive.scala Maven / Gradle / Ivy

The newest version!
package tsec.cipher.symmetric.jca.primitive

import java.util.{Arrays => JaRule}
import javax.crypto.{Cipher => JCipher}

import cats.MonadError
import cats.effect.Sync
import cats.syntax.all._
import tsec.cipher.common.padding.SymmetricPadding
import tsec.cipher.symmetric._
import tsec.cipher.symmetric.jca._

sealed abstract class JCAAEADPrimitive[F[_], A, M, P](
    implicit algoTag: BlockCipher[A],
    aead: AEADCipher[A],
    modeSpec: CipherMode[M],
    paddingTag: SymmetricPadding[P],
    F: MonadError[F, Throwable],
    private[tsec] val ivProcess: IvProcess[A, M, P]
) extends AADEncryptor[F, A, SecretKey] {

  private def getInstance: JCipher =
    JCAPrimitiveCipher.getJCipherUnsafe[A, M, P]

  private[tsec] def catchF[Y](a: => Y): F[Y]

  def encrypt(plainText: PlainText, key: SecretKey[A], iv: Iv[A]): F[CipherText[A]] =
    catchF {
      val instance = getInstance
      ivProcess.encryptInit(instance, iv, key.toJavaKey)
      val encrypted = instance.doFinal(plainText)
      CipherText[A](RawCipherText(encrypted), iv)
    }

  def decrypt(cipherText: CipherText[A], key: SecretKey[A]): F[PlainText] =
    catchF {
      val instance = getInstance
      ivProcess.decryptInit(instance, cipherText.nonce, key.toJavaKey)
      val out = instance.doFinal(cipherText.content)
      PlainText(out)
    }

  def encryptWithAAD(plainText: PlainText, key: SecretKey[A], iv: Iv[A], aad: AAD): F[CipherText[A]] =
    catchF {
      val instance = getInstance
      ivProcess.encryptInit(instance, iv, key.toJavaKey)
      instance.updateAAD(aad)
      val encrypted = RawCipherText[A](instance.doFinal(plainText))
      CipherText[A](encrypted, iv)
    }

  def decryptWithAAD(cipherText: CipherText[A], key: SecretKey[A], aad: AAD): F[PlainText] =
    catchF {
      val instance = getInstance
      ivProcess.decryptInit(instance, cipherText.nonce, key.toJavaKey)
      instance.updateAAD(aad)
      val out = instance.doFinal(cipherText.content)
      PlainText(out)
    }

  def encryptDetached(plainText: PlainText, key: SecretKey[A], iv: Iv[A]): F[(CipherText[A], AuthTag[A])] =
    catchF {
      val instance = getInstance
      ivProcess.encryptInit(instance, iv, key.toJavaKey)
      val encrypted  = instance.doFinal(plainText)
      val cipherText = RawCipherText[A](JaRule.copyOfRange(encrypted, 0, encrypted.length - aead.tagSizeBytes))
      val tag        = JaRule.copyOfRange(encrypted, encrypted.length - aead.tagSizeBytes, encrypted.length)
      (CipherText[A](cipherText, iv), AuthTag[A](tag))
    }

  def decryptDetached(cipherText: CipherText[A], key: SecretKey[A], tag: AuthTag[A]): F[PlainText] =
    if (tag.length != aead.tagSizeBytes)
      F.raiseError(AuthTagError("Authentication tag of incorrect length"))
    else
      catchF {
        val instance = getInstance
        ivProcess.decryptInit(instance, cipherText.nonce, key.toJavaKey)
        //Re-combine the auth tag and the ciphertext, because thx JCA
        val combined = new Array[Byte](aead.tagSizeBytes + cipherText.content.length)
        System.arraycopy(cipherText.content, 0, combined, 0, cipherText.content.length)
        System.arraycopy(tag, 0, combined, cipherText.content.length, tag.length)

        val out = instance.doFinal(combined)
        PlainText(out)
      }

  def encryptWithAADDetached(
      plainText: PlainText,
      key: SecretKey[A],
      iv: Iv[A],
      aad: AAD
  ): F[(CipherText[A], AuthTag[A])] =
    catchF {
      val instance = getInstance
      ivProcess.encryptInit(instance, iv, key.toJavaKey)
      instance.updateAAD(aad)
      val encrypted  = instance.doFinal(plainText)
      val cipherText = RawCipherText[A](JaRule.copyOfRange(encrypted, 0, encrypted.length - aead.tagSizeBytes))
      val tag        = JaRule.copyOfRange(encrypted, encrypted.length - aead.tagSizeBytes, encrypted.length)
      (CipherText[A](cipherText, iv), AuthTag[A](tag))
    }

  def decryptWithAADDetached(cipherText: CipherText[A], key: SecretKey[A], aad: AAD, tag: AuthTag[A]): F[PlainText] =
    if (tag.length != aead.tagSizeBytes)
      F.raiseError(AuthTagError("Authentication tag of incorrect length"))
    else
      catchF {
        val instance = getInstance
        ivProcess.decryptInit(instance, cipherText.nonce, key.toJavaKey)

        //Re-combine the auth tag and the ciphertext, because thx JCA
        val combined = new Array[Byte](aead.tagSizeBytes + cipherText.content.length)
        System.arraycopy(cipherText.content, 0, combined, 0, cipherText.content.length)
        System.arraycopy(tag, 0, combined, cipherText.content.length, tag.length)

        instance.updateAAD(aad)
        val out = instance.doFinal(combined)
        PlainText(out)
      }

}

object JCAAEADPrimitive {

  private[tsec] def sync[F[_], A: BlockCipher: AEADCipher, M: CipherMode, P: SymmetricPadding](
      implicit F: Sync[F],
      ivProcess: IvProcess[A, M, P]
  ): AADEncryptor[F, A, SecretKey] =
    new JCAAEADPrimitive[F, A, M, P] {
      private[tsec] def catchF[C](thunk: => C): F[C] = F.delay(thunk)
    }

  private[tsec] def monadError[F[_], A: BlockCipher: AEADCipher, M: CipherMode, P: SymmetricPadding](
      implicit F: MonadError[F, Throwable],
      ivProcess: IvProcess[A, M, P]
  ): JCAAEADPrimitive[F, A, M, P] =
    new JCAAEADPrimitive[F, A, M, P] {
      private[tsec] def catchF[C](thunk: => C): F[C] =
        F.catchNonFatal(thunk)
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy