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

tsec.cipher.symmetric.jca.package.scala Maven / Gradle / Ivy

The newest version!
package tsec.cipher.symmetric

import javax.crypto.spec.{GCMParameterSpec, IvParameterSpec}
import javax.crypto.{Cipher => JCipher, SecretKey => JSecretKey}

import cats.effect.Sync
import tsec.cipher.CipherErrors
import tsec.cipher.common.padding._
import tsec.common.ManagedRandom

package object jca extends CipherErrors {

  type SecretKey[A] = SecretKey.Type[A]

  object SecretKey {
    type Base[A]
    trait M1 extends Any

    type Type[A] <: Base[A] with M1

    def apply[A](key: JSecretKey): SecretKey[A]     = key.asInstanceOf[SecretKey[A]]
    def toJavaKey[A](key: SecretKey[A]): JSecretKey = key.asInstanceOf[JSecretKey]
    def subst[A]: SecretKPartiallyApplied[A]        = new SecretKPartiallyApplied[A]

    private[tsec] class SecretKPartiallyApplied[A](val dummy: Boolean = true) extends AnyVal {
      def apply[F[_]](value: F[JSecretKey]): F[SecretKey[A]] = value.asInstanceOf[F[SecretKey[A]]]
    }

    def unsubst[A]: SecretKUnwrap[A] = new SecretKUnwrap[A]

    private[tsec] class SecretKUnwrap[A](val dummy: Boolean = true) extends AnyVal {
      def apply[F[_]](value: F[SecretKey[A]]): F[JSecretKey] = value.asInstanceOf[F[JSecretKey]]
    }
  }

  final class SecretKeySyntax[A](val key: SecretKey[A]) extends AnyVal {
    def toJavaKey: JSecretKey = SecretKey.toJavaKey(key)
    def getEncoded: Array[Byte]       = toJavaKey.getEncoded
  }

  implicit final def _secretKeySyntax[A](key: SecretKey[A]): SecretKeySyntax[A] = new SecretKeySyntax[A](key)

  private[tsec] class WithCipherMode[M](val mode: String) extends CipherMode[M] {
    implicit val m: CipherMode[M] = this
  }

  private[tsec] def standardProcess[A, M, P: SymmetricPadding](
      implicit cipher: BlockCipher[A]
  ): IvProcess[A, M, P] =
    new IvProcess[A, M, P] {

      val ivLengthBytes: Int = cipher.blockSizeBytes

      private[tsec] def encryptInit(cipher: JCipher, iv: Array[Byte], key: JSecretKey): Unit =
        cipher.init(
          JCipher.ENCRYPT_MODE,
          key,
          new IvParameterSpec(iv)
        )

      private[tsec] def decryptInit(cipher: JCipher, iv: Array[Byte], key: JSecretKey): Unit =
        cipher.init(
          JCipher.DECRYPT_MODE,
          key,
          new IvParameterSpec(iv)
        )
    }

  /*
  Modes of operation
   */
  sealed trait CBC

  object CBC extends WithCipherMode[CBC]("CBC") {
    implicit def cbcProcess[A, P: SymmetricPadding](implicit cipher: BlockCipher[A]): IvProcess[A, CBC, P] =
      standardProcess[A, CBC, P]
  }

  sealed trait CFB

  object CFB extends WithCipherMode[CFB]("CFB") {
    implicit def cfbProcess[A](implicit cipher: BlockCipher[A]): IvProcess[A, CFB, NoPadding] =
      standardProcess[A, CFB, NoPadding]
  }

  sealed trait CFBx

  object CFBx extends WithCipherMode[CFBx]("CFBx") {
    implicit def cfbxProcess[A](implicit cipher: BlockCipher[A]): IvProcess[A, CFBx, NoPadding] =
      standardProcess[A, CFBx, NoPadding]
  }

  sealed trait CTR

  object CTR extends WithCipherMode[CTR]("CTR") {
    implicit def ctrProcess[A, P: SymmetricPadding](
        implicit cipher: BlockCipher[A]
    ): IvProcess[A, CTR, P] =
      standardProcess[A, CTR, P]
  }

  sealed trait ECB

  object ECB extends WithCipherMode[ECB]("ECB") {
    implicit def ecbProcess[A: BlockCipher]: IvProcess[A, ECB, NoPadding] =
      new IvProcess[A, ECB, NoPadding] {

        val ivLengthBytes: Int = 0

        private[tsec] def encryptInit(cipher: JCipher, iv: Array[Byte], key: JSecretKey): Unit =
          cipher.init(JCipher.ENCRYPT_MODE, key)

        private[tsec] def decryptInit(cipher: JCipher, iv: Array[Byte], key: JSecretKey): Unit =
          cipher.init(JCipher.DECRYPT_MODE, key)

      }
  }

  sealed trait GCM

  object GCM extends WithCipherMode[GCM]("GCM") {

    /** In our implementation, we will use the most secure tag size as defined
      * by: http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
      * Iv length of 96 bits is recommended as per the spec on page 8
      */
    val NISTTagLengthBits = 128
    val NISTIvLengthBytes = 12

    implicit def gcmProcess[A](implicit aes: AES[A]): IvProcess[A, GCM, NoPadding] =
      new IvProcess[A, GCM, NoPadding] {

        val ivLengthBytes: Int = NISTIvLengthBytes

        private[tsec] def encryptInit(cipher: JCipher, iv: Array[Byte], key: JSecretKey): Unit =
          cipher.init(
            JCipher.ENCRYPT_MODE,
            key,
            new GCMParameterSpec(GCM.NISTTagLengthBits, iv)
          )

        private[tsec] def decryptInit(cipher: JCipher, iv: Array[Byte], key: JSecretKey): Unit =
          cipher.init(
            JCipher.DECRYPT_MODE,
            key,
            new GCMParameterSpec(GCM.NISTTagLengthBits, iv)
          )

      }

    def randomIVStrategy[F[_], Out: AES](implicit F: Sync[F]): IvGen[F, Out] =
      new IvGen[F, Out] with ManagedRandom {

        def genIv: F[Iv[Out]] =
          F.delay(genIvUnsafe)

        def genIvUnsafe: Iv[Out] = {
          val nonce = new Array[Byte](GCM.NISTIvLengthBytes)
          nextBytes(nonce)
          Iv[Out](nonce)
        }
      }

  }

  sealed trait NoMode

  object NoMode extends WithCipherMode[NoMode]("NoMode") {
    implicit def noModeProcess[A: BlockCipher]: IvProcess[A, NoMode, NoPadding] =
      new IvProcess[A, NoMode, NoPadding] {

        val ivLengthBytes: Int = 0

        private[tsec] def encryptInit(cipher: JCipher, iv: Array[Byte], key: JSecretKey): Unit =
          cipher.init(JCipher.ENCRYPT_MODE, key)

        private[tsec] def decryptInit(cipher: JCipher, iv: Array[Byte], key: JSecretKey): Unit =
          cipher.init(JCipher.DECRYPT_MODE, key)
      }
  }

  sealed trait OFB

  object OFB extends WithCipherMode[OFB]("OFB") {
    implicit def ofbProcess[A](implicit cipher: BlockCipher[A]): IvProcess[A, OFB, NoPadding] =
      standardProcess[A, OFB, NoPadding]
  }

  sealed trait OFBx

  object OFBx extends WithCipherMode[OFBx]("OFBx") {
    implicit def ofbxProcess[A](implicit cipher: BlockCipher[A]): IvProcess[A, OFBx, NoPadding] =
      standardProcess[A, OFBx, NoPadding]
  }

  sealed trait PCBC

  object PCBC extends WithCipherMode[PCBC]("PCBC") {
    implicit def pcbcProcess[A, P: SymmetricPadding](
        implicit cipher: BlockCipher[A]
    ): IvProcess[A, PCBC, P] =
      standardProcess[A, PCBC, P]
  }

  object CTOPS {
    private[tsec] def ciphertextFromArray[A, M, P](
        bytes: Array[Byte]
    )(implicit spec: IvProcess[A, M, P], blockCipher: BlockCipher[A]): Either[CipherTextError, CipherText[A]] =
      if (bytes.length < spec.ivLengthBytes + 1)
        Left(CipherTextError("Array must be nonEmpty"))
      else {
        val ivIx         = bytes.length - spec.ivLengthBytes
        val ivArray      = new Array[Byte](spec.ivLengthBytes)
        val contentArray = new Array[Byte](ivIx)
        System.arraycopy(bytes, 0, contentArray, 0, ivIx)
        System.arraycopy(bytes, ivIx, ivArray, 0, spec.ivLengthBytes)

        Right(CipherText[A](RawCipherText(contentArray), Iv(ivArray)))
      }
  }

  type JEncryptor[F[_], A]     = Encryptor[F, A, SecretKey]
  type JAuthEncryptor[F[_], A] = AADEncryptor[F, A, SecretKey]

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy