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

scodec.codecs.CipherCodec.scala Maven / Gradle / Ivy

There is a newer version: 2.3.2
Show newest version
/*
 * Copyright (c) 2013, Scodec
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package scodec
package codecs

import java.security.Key
import java.security.spec.AlgorithmParameterSpec
import javax.crypto.{BadPaddingException, Cipher, IllegalBlockSizeException}

import scodec.bits.BitVector

/** Represents the ability to create a `Cipher` for encryption or decryption.
  *
  * Used in conjunction with [[encrypted]].
  */
trait CipherFactory:

  /** Creates a cipher initialized for encryption. */
  def newEncryptCipher: Cipher

  /** Creates a cipher initialized for decryption. */
  def newDecryptCipher: Cipher

/** Companion for [[CipherFactory]].
  */
object CipherFactory:

  /** Creates a cipher factory for the specified transformation (via `Cipher.getInstance(transformation)`)
    * and using the specified functions for initializing for encryption and decryption respectively.
    */
  def apply(
      transformation: String,
      initForEncryption: Cipher => Unit,
      initForDecryption: Cipher => Unit
  ): CipherFactory =
    new SimpleCipherFactory(transformation, initForEncryption, initForDecryption)

  /** Creates a cipher factory for the specified transformation (via `Cipher.getInstance(transformation)`).
    *
    * Ciphers are initialized with the specified key only.
    */
  def apply(transformation: String, key: Key): CipherFactory =
    new SimpleCipherFactory(
      transformation,
      _.init(Cipher.ENCRYPT_MODE, key),
      _.init(Cipher.DECRYPT_MODE, key)
    )

  /** Creates a cipher factory for the specified transformation (via `Cipher.getInstance(transformation)`).
    *
    * Ciphers are initialized with the specified key and algorithm parameter specification.
    */
  def apply(transformation: String, key: Key, spec: AlgorithmParameterSpec): CipherFactory =
    new SimpleCipherFactory(
      transformation,
      _.init(Cipher.ENCRYPT_MODE, key, spec),
      _.init(Cipher.DECRYPT_MODE, key, spec)
    )

  private class SimpleCipherFactory(
      transformation: String,
      initForEncryption: Cipher => Unit,
      initForDecryption: Cipher => Unit
  ) extends CipherFactory:

    private def newCipher: Cipher = Cipher.getInstance(transformation).nn

    def newEncryptCipher: Cipher =
      val cipher = newCipher
      initForEncryption(cipher)
      cipher

    def newDecryptCipher: Cipher =
      val cipher = newCipher
      initForDecryption(cipher)
      cipher

/** @see [[encrypted]] */
private[codecs] final class CipherCodec[A](codec: Codec[A], cipherFactory: CipherFactory)
    extends Codec[A]:

  override def sizeBound = SizeBound.unknown

  override def encode(a: A) =
    codec.encode(a).flatMap(b => encrypt(b))

  private def encrypt(bits: BitVector) =
    val blocks = bits.toByteArray
    try
      val encrypted = cipherFactory.newEncryptCipher.doFinal(blocks).nn
      Attempt.successful(BitVector(encrypted))
    catch
      case _: IllegalBlockSizeException =>
        Attempt.failure(Err(s"Failed to encrypt: invalid block size ${blocks.size}"))

  override def decode(buffer: BitVector) =
    decrypt(buffer).flatMap { result =>
      codec.decode(result).map {
        _.mapRemainder(_ => BitVector.empty)
      }
    }

  private def decrypt(buffer: BitVector): Attempt[BitVector] =
    val blocks = buffer.toByteArray
    try
      val decrypted = cipherFactory.newDecryptCipher.doFinal(blocks).nn
      Attempt.successful(BitVector(decrypted))
    catch
      case e @ (_: IllegalBlockSizeException | _: BadPaddingException) =>
        Attempt.failure(Err("Failed to decrypt: " + e.getMessage))

  override def toString = s"cipher($codec)"




© 2015 - 2024 Weber Informatics LLC | Privacy Policy