Skip to content

Commit 360bba2

Browse files
authored
Merge pull request #270 from gvolpe/fix/crypto-interpreter
Fix Crypto interpreter
2 parents fe06dd5 + c21527a commit 360bba2

File tree

2 files changed

+49
-17
lines changed

2 files changed

+49
-17
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
package shop.algebras
22

3-
import javax.crypto.spec.{ PBEKeySpec, SecretKeySpec }
3+
import java.security.SecureRandom
4+
import java.util.Base64
5+
import javax.crypto.spec.{IvParameterSpec, PBEKeySpec, SecretKeySpec}
46
import javax.crypto.{ Cipher, SecretKeyFactory }
57

68
import shop.config.data.PasswordSalt
79
import shop.domain.auth._
810

911
import cats.effect.Sync
1012
import cats.syntax.all._
13+
import eu.timepit.refined.auto._
1114

1215
trait Crypto {
1316
def encrypt(value: Password): EncryptedPassword
@@ -18,15 +21,19 @@ object LiveCrypto {
1821
def make[F[_]: Sync](secret: PasswordSalt): F[Crypto] =
1922
Sync[F]
2023
.delay {
21-
val salt = secret.value.value.value.getBytes("UTF-8")
24+
val random = new SecureRandom()
25+
val ivBytes = new Array[Byte](16)
26+
random.nextBytes(ivBytes)
27+
val iv = new IvParameterSpec(ivBytes);
28+
val salt = secret.value.value.getBytes("UTF-8")
2229
val keySpec = new PBEKeySpec("password".toCharArray(), salt, 65536, 256)
2330
val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
2431
val bytes = factory.generateSecret(keySpec).getEncoded
2532
val sKeySpec = new SecretKeySpec(bytes, "AES")
26-
val eCipher = EncryptCipher(Cipher.getInstance("AES"))
27-
eCipher.value.init(Cipher.ENCRYPT_MODE, sKeySpec)
28-
val dCipher = DecryptCipher(Cipher.getInstance("AES"))
29-
dCipher.value.init(Cipher.DECRYPT_MODE, sKeySpec)
33+
val eCipher = EncryptCipher(Cipher.getInstance("AES/CBC/PKCS5Padding"))
34+
eCipher.value.init(Cipher.ENCRYPT_MODE, sKeySpec, iv)
35+
val dCipher = DecryptCipher(Cipher.getInstance("AES/CBC/PKCS5Padding"))
36+
dCipher.value.init(Cipher.DECRYPT_MODE, sKeySpec, iv)
3037
(eCipher, dCipher)
3138
}
3239
.map {
@@ -40,21 +47,18 @@ final class LiveCrypto private (
4047
dCipher: DecryptCipher
4148
) extends Crypto {
4249

43-
// Workaround for PostgreSQL ERROR: invalid byte sequence for encoding "UTF8": 0x00
44-
private val Key = "=DownInAHole="
45-
4650
def encrypt(password: Password): EncryptedPassword = {
47-
val bytes = password.value.getBytes("UTF-8")
48-
val result = new String(eCipher.value.doFinal(bytes), "UTF-8")
49-
val removeNull = result.replaceAll("\\u0000", Key)
50-
EncryptedPassword(removeNull)
51+
val base64 = Base64.getEncoder()
52+
val bytes = password.value.getBytes("UTF-8")
53+
val result = new String(base64.encode(eCipher.value.doFinal(bytes)), "UTF-8")
54+
EncryptedPassword(result)
5155
}
5256

5357
def decrypt(password: EncryptedPassword): Password = {
54-
val bytes = password.value.getBytes("UTF-8")
55-
val result = new String(dCipher.value.doFinal(bytes), "UTF-8")
56-
val insertNull = result.replaceAll(Key, "\\u0000")
57-
Password(insertNull)
58+
val base64 = Base64.getDecoder()
59+
val bytes = base64.decode(password.value.getBytes("UTF-8"))
60+
val result = new String(dCipher.value.doFinal(bytes), "UTF-8")
61+
Password(result)
5862
}
5963

6064
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package shop.interpreters
2+
3+
import shop.algebras.LiveCrypto
4+
import shop.config.data.PasswordSalt
5+
import shop.domain.auth.Password
6+
7+
import cats.effect.IO
8+
import ciris.Secret
9+
import eu.timepit.refined.auto._
10+
import eu.timepit.refined.cats._
11+
import suite._
12+
13+
final class CryptoSuite extends PureTestSuite {
14+
15+
private val salt = PasswordSalt(Secret("53kr3t"))
16+
17+
test("password encoding and decoding roundtrip") {
18+
IOAssertion {
19+
LiveCrypto.make[IO](salt).map { crypto =>
20+
val ini = Password("simple123")
21+
val enc = crypto.encrypt(ini)
22+
val dec = crypto.decrypt(enc)
23+
assertEquals(dec, ini)
24+
}
25+
}
26+
}
27+
28+
}

0 commit comments

Comments
 (0)