Skip to content

Instantly share code, notes, and snippets.

@kolemannix
Last active August 3, 2017 21:33
Show Gist options
  • Save kolemannix/c8bd6f58c270a77a9b7a593a6ee182b2 to your computer and use it in GitHub Desktop.
Save kolemannix/c8bd6f58c270a77a9b7a593a6ee182b2 to your computer and use it in GitHub Desktop.
Phantom Types, higher kinds, and F-Bounded polymorphism PoC (abstraction over en/decryption
package lshack
import scala.concurrent.{ ExecutionContext, Future }
import scala.languageFeature.higherKinds
import scala.util.{ Success, Try }
import lshack.Values._
object Values {
sealed trait State
trait Encrypted extends State
trait Decrypted extends State
trait Encryptable[V[S <: State], Dep, F[A]] {
def encrypt[S <: Decrypted](d: V[S], dep: Dep): F[V[Encrypted]]
def decrypt[S <: Encrypted](d: V[S], dep: Dep): F[V[Decrypted]]
}
implicit class EncryptOp[S <: Decrypted, V[S <: State], F[A], Dep](v: V[S])(implicit val ev: Encryptable[V, Dep, F], dep: Dep) {
def encrypt: F[V[Encrypted]] = ev.encrypt(v, dep)
}
implicit class DecryptOp[S <: Encrypted, V[S <: State], F[A], Dep](v: V[S])(implicit val ev: Encryptable[V, Dep, F], dep: Dep) {
def decrypt: F[V[Decrypted]] = ev.decrypt(v, dep)
}
type BasicEncryptable[V[S <: State]] = Encryptable[V, Unit, Box]
implicit class BasicEncryptOp[S <: Decrypted, V[S <: State]](v: V[S])(implicit val ev: BasicEncryptable[V]) { def encrypt: Box[V[Encrypted]] = ev.encrypt(v, ()) }
implicit class BasicDecryptOp[S <: Encrypted, V[S <: State]](v: V[S])(implicit val ev: BasicEncryptable[V]) { def decrypt: Box[V[Decrypted]] = ev.decrypt(v, ()) }
}
class Box[A](val a: A)
case class IntValue[S <: State](x: Int)
object IntValue {
implicit val encryptable: BasicEncryptable[IntValue] = new BasicEncryptable[IntValue] {
def encrypt[S <: Decrypted](i: IntValue[S], dep: Unit): Box[IntValue[Encrypted]] = new Box(i.copy(x = i.x + 100))
def decrypt[S <: Encrypted](i: IntValue[S], dep: Unit): Box[IntValue[Decrypted]] = new Box(i.copy(x = i.x - 100))
}
}
case class StringValue[S <: State](x: String)
object StringValue {
implicit val encryptable: Encryptable[StringValue, String, Try] = new Encryptable[StringValue, String, Try] {
def encrypt[S <: Decrypted](i: StringValue[S], dep: String): Try[StringValue[Encrypted]] = {
Success(i.copy(x = dep + i.x))
}
def decrypt[S <: Encrypted](i: StringValue[S], dep: String): Try[StringValue[Decrypted]] = {
Success(i.copy(x = dep + i.x))
}
}
}
trait KmsService {
type Key
def getKey(key: String): Key
}
trait AsyncKmsService {
type Key
val executor: ExecutionContext
def getKey(key: String): Future[Key]
}
case class KmsEncryptedString[S <: State](value: String, key: String)
object KmsEncryptedString {
implicit val encryptableAsync: Encryptable[KmsEncryptedString, AsyncKmsService, Future] = new Encryptable[KmsEncryptedString, AsyncKmsService, Future] {
def encrypt[S <: Decrypted](i: KmsEncryptedString[S], dep: AsyncKmsService): Future[KmsEncryptedString[Encrypted]] = {
dep.getKey(i.key).map { key =>
i.copy[Encrypted](value = i.value + key)
}(dep.executor)
}
def decrypt[S <: Encrypted](i: KmsEncryptedString[S], dep: AsyncKmsService): Future[KmsEncryptedString[Decrypted]] = {
dep.getKey(i.key).map { key =>
i.copy[Decrypted]()
}(dep.executor)
}
}
// implicit val encryptable: Encryptable[KmsEncryptedString, KmsService, Try] = new Encryptable[KmsEncryptedString, KmsService, Try] {
// def encrypt[S <: Decrypted](i: KmsEncryptedString[S], dep: AsyncKmsService): Try[KmsEncryptedString[Encrypted]] = {
// val key = dep.getKey(i.key)
// println("Yay I have a KmsService")
// Success(i.copy[Encrypted](value = i.value + key))
// }
// def decrypt[S <: Encrypted](i: KmsEncryptedString[S], dep: AsyncKmsService): Try[KmsEncryptedString[Decrypted]] = {
// Success(i.copy[Decrypted]())
// }
// }
}
@kolemannix
Copy link
Author

Usage:

case class ShapeEntity[S <: State](id: Int, `type`: String, size: String, color: String, encryptionKey: Option[String])

object ShapeEntity {
  implicit val encryptable: Encryptable[ShapeEntity, AsyncKmsService, Future] = new Encryptable[ShapeEntity, AsyncKmsService, Future] {
    def encrypt[S <: Decrypted](shape: ShapeEntity[S], service: AsyncKmsService): Future[ShapeEntity[Encrypted]] = {
      shape.encryptionKey match {
        case Some(encryptionKey) =>
          service.getKey(encryptionKey).map { cipher =>
            shape.copy[Encrypted](
              // Encrypt things using 'cipher'
              size = shape.size + cipher,
              color = shape.color + cipher
            )
          }(service.executor)
        case None => Future successful shape[Encrypted]
      }
    }
    def decrypt[S <: Encrypted](shape: ShapeEntity[S], service: AsyncKmsService): Future[ShapeEntity[Decrypted]] = {
      shape.encryptionKey match {
        case Some(encryptionKey) =>
          service.getKey(encryptionKey).map { key =>
            shape.copy[Decrypted](
              // Decrypt things using 'cipher'
            )
          }(service.executor)
        case None => Future successful shape.copy[Decrypted]()
      }
    }
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment