Skip to content

Commit

Permalink
Simplify API usage: Remove the ClearText newtype (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
guizmaii authored Oct 16, 2023
1 parent d30e3f8 commit 89a4184
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 19 deletions.
24 changes: 10 additions & 14 deletions zio-aes/src/main/scala/zio/aes/AES.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,28 @@ object AES {

final val UTF_8: Charset = StandardCharsets.UTF_8

object Base64String extends Subtype[String]
type Base64String = Base64String.Type
object Base64String extends Subtype[String]

object CipherText extends Subtype[String] // should be Subtype[Base64String] but https://github.com/zio/zio-prelude/issues/1183
type CipherText = CipherText.Type
object CipherText extends Subtype[String] // should be Subtype[Base64String] but https://github.com/zio/zio-prelude/issues/1183

object IV extends Subtype[String] // should be Subtype[Base64String] but https://github.com/zio/zio-prelude/issues/1183
type IV = IV.Type
object IV extends Subtype[String] // should be Subtype[Base64String] but https://github.com/zio/zio-prelude/issues/1183

type Salt = Salt.Type
object Salt extends Subtype[String] { // should be Subtype[Base64String] but https://github.com/zio/zio-prelude/issues/1183
implicit final class RichSalt(private val salt: Salt) extends AnyVal {
def toRawSalt: RawSalt = RawSalt(base64Decode(salt))
}
}
type Salt = Salt.Type

type RawSalt = RawSalt.Type
object RawSalt extends Subtype[Array[Byte]] {
implicit final class RichRawSalt(private val salt: RawSalt) extends AnyVal {
def toSalt: Salt = Salt(base64Encode(salt))
}
}
type RawSalt = RawSalt.Type

object ClearText extends Subtype[String]
type ClearText = ClearText.Type

def base64Encode(in: Array[Byte]): Base64String = Base64String(new String(java.util.Base64.getEncoder.encode(in), UTF_8))
def base64Decode(in: Base64String): Array[Byte] = java.util.Base64.getDecoder.decode(in.getBytes(UTF_8))
Expand All @@ -65,9 +62,8 @@ import zio.aes.AES.*
* - https://stackoverflow.com/a/13915596
*/
trait AES {
def encrypt(in: ClearText): (CipherText, Salt, IV)

def decrypt(data: CipherText, salt: Salt, iv: IV): ClearText
def encrypt(in: String): (CipherText, Salt, IV)
def decrypt(data: CipherText, salt: Salt, iv: IV): String
}

final class AESLive(password: Array[Char]) extends AES {
Expand All @@ -80,7 +76,7 @@ final class AESLive(password: Array[Char]) extends AES {

private val random: SecureRandom = new SecureRandom()

override def encrypt(in: ClearText): (CipherText, Salt, IV) = {
override def encrypt(in: String): (CipherText, Salt, IV) = {
val rawSalt: RawSalt = generateRawSalt
val key: Key = getAESKeyFromPassword(rawSalt)
val iv: GCMParameterSpec = generateIv
Expand All @@ -89,12 +85,12 @@ final class AESLive(password: Array[Char]) extends AES {
(CipherText(base64Encode(cipherText)), rawSalt.toSalt, IV(base64Encode(iv.getIV)))
}

override def decrypt(data: CipherText, salt: Salt, iv: IV): ClearText = {
override def decrypt(data: CipherText, salt: Salt, iv: IV): String = {
val key: Key = getAESKeyFromPassword(salt.toRawSalt)
val gcmParams: GCMParameterSpec = new GCMParameterSpec(GcmAuthenticationTagLength, base64Decode(iv))
val clearText: Array[Byte] = doDecrypt(base64Decode(data), key, gcmParams)

ClearText(new String(clearText, UTF_8))
new String(clearText, UTF_8)
}

private def doEncrypt(in: Array[Byte], key: Key, gcmParams: GCMParameterSpec): Array[Byte] = {
Expand Down
10 changes: 5 additions & 5 deletions zio-aes/src/test/scala/zio/aes/AESSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package zio.aes

import zio.Config.Secret
import zio.Scope
import zio.aes.AES.{ClearText, UTF_8}
import zio.aes.AES.UTF_8
import zio.test.*
import zio.test.Assertion.*
import zio.test.TestAspect.*
Expand All @@ -24,23 +24,23 @@ object AESSpec extends ZIOSpecDefault {
test("encrypted text is not just base64'ed text") {
check(anyPassword, Gen.string) { (password, secret) =>
val service: AES = new AESLive(password)
val (encrypted, _, _) = service.encrypt(ClearText(secret))
val (encrypted, _, _) = service.encrypt(secret)

assert(new String(AES.base64Decode(encrypted), UTF_8))(not(equalTo(secret)))
}
},
test("salt should be 16 bytes long") {
check(anyPassword, Gen.string) { (password, secret) =>
val service: AES = new AESLive(password)
val (_, salt, _) = service.encrypt(ClearText(secret))
val (_, salt, _) = service.encrypt(secret)

assert(AES.base64Decode(salt).length)(equalTo(16))
}
},
test("iv should be 12 bytes long") {
check(anyPassword, Gen.string) { (password, secret) =>
val service: AES = new AESLive(password)
val (_, _, iv) = service.encrypt(ClearText(secret))
val (_, _, iv) = service.encrypt(secret)

assert(AES.base64Decode(iv).length)(equalTo(12))
}
Expand All @@ -52,7 +52,7 @@ object AESSpec extends ZIOSpecDefault {
test("decrypted encrypted text should be equal to initial text") {
check(anyPassword, Gen.string) { (password, secret) =>
val service: AES = new AESLive(password)
val (encrypted, salt, iv) = service.encrypt(ClearText(secret))
val (encrypted, salt, iv) = service.encrypt(secret)
val decrypted = service.decrypt(encrypted, salt, iv)

assert(decrypted)(equalTo(secret))
Expand Down

0 comments on commit 89a4184

Please sign in to comment.