Skip to content

ScalaCrypto/cryptic

Repository files navigation

Cryptic

Keeps your secrets. Cryptic is a monad for encrypting and decryptic data. To use cryptic you must select a crypto and a serializer.

Cryptic supports several crypto libraries and serializers, select the modules you nedd or write your own

Module Supports
crypto-javax AES, RSA
crypto-bouncycastle EC (Elliptic curve)
serialization-chill Chill
serialization-fst Fst
serialization-upickle Upickle

Installation

sbt

"scalacrypto" %% "core" % "1.0.0"

To encrypt with AES or RSA

"scalacrypto" %% "crypto-javax" % "1.0.0"

To encrypt with EC

"scalacrypto" %% "crypto-bouncycastle" % "1.0.0"

Select serializer one of

"scalacrypto" %% "serialization-chill" % "1.0.0"

"scalacrypto" %% "serialization-fst" % "1.0.0"

"scalacrypto" %% "serialization-upickle" % "1.0.0"

Usage

Import base package and syntax extension:

import cryptic._
import cryptic.syntax._

Define your data types:

case class EmailAddress(literal: String)

object EmailAddress {
  val rw: ReadWriter[EmailAddress] = macroRW // If you  use the Upickle serializer
}

case class User(id: Long, email: Encrypted[EmailAddress])

object User {
  val rw: ReadWriter[User] = macroRW // If you use the Upickle serializer
}

Encrypt your data using convenient syntax, a crypto and a serializer must be available:

import java.security.{KeyPair, PrivateKey, PublicKey}
import cryptic.serialization.Chill._ // Brings the Chill serializer in scope
import cryptic.crypto.EC // Elliptic Curve encryption

// We need an implicit public key to enable encryption for EC
private val keyPair: KeyPair = EC.keygen(256)
implicit private val publicKey: PublicKey = keyPair.getPublic

val user = User(123, EmailAddress("[email protected]").encrypted)

To use the Upickle serializer:

import cryptic.serialization.Upickle

implicit def serializer[V](implicit rw: ReadWriter[V]): Serializer[V] = Upickle[V]

Access your data in encrypted form (can be done without crypto/serializer):

val bytes: Array[Byte] = user.email.bytes

Transform your data, can be done without crypto/serializer:

val lowered = user.email.
  map(_.copy(literal = _.literal.toLower))

Run your staged transformations, a crypto and a serializer must be in scope:

import cryptic.crypto.RSA._
import cryptic.serialization.Chill._

val user2 = user.copy(email = lowered.run())

Decrypt your transformed data, a crypto and a serializer must be in scope:

import cryptic.crypto.RSA._
import cryptic.serialization.Fst._

val emailInLower = user2.email.decrypted

Provided cryptos

  • AES
  • RSA
  • EC

Provided serializers

  • Chill
  • Fst
  • Upickle

Roll your own

Serializer

Provide an implementation of the Serializer trait:

trait Serializer[V] {
  def serialize(value: V): PlainText

  def deserialize(plainText: PlainText): Either[String, V]
}

Example:

import scala.util.Try

object MySerializer {
  implicit def serializer[V]: Serializer[V] = new Serializer[V] {

    override def serialize(value: V): PlainText = ???

    override def deserialize(plainText: PlainText): Either[String, V] = ???
  }
}

Crypto

Provide implementations of the Encrypt and Decrypt traits and probably a Key:

trait Encrypt {
  def apply(plainText: PlainText): CipherText
}

trait Decrypt {
  def apply(cipherText: CipherText): Either[String, PlainText]
}

Example Caesar crypto:

object Caesar {
  case class Key(offset: Int) {
    require(offset != 0)
  }

  implicit def encrypt(implicit key: Key): Encrypt = (plainText: PlainText) => {
    val bytes = plainText.map(b  (b + key.offset).toByte)
    CipherText(bytes)
  }

  implicit def decrypt(implicit key: Key): Decrypt = (cipherText: CipherText) =>
    Right[String, PlainText](
      PlainText(cipherText.bytes.map(b => (b - key.offset).toByte))
    )

  def keygen(offset: Int): Key = Key(offset)
}

About

Keeps your secrets

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •  

Languages