WIP desktop client for Chatto reimplemented in ScalaFX and Sapphire Framework
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

135 lines
3.6 KiB

package wow.doge.chatto.service
import wow.doge.chatto.model.MessageCipher;
import java.util.Base64;
import javax.crypto.Cipher;;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom
import scala.util.Try
class EncryptionServiceImpl extends EncryptionService {
override def encrypt(
password: String,
plainText: String
): String Either MessageCipher = {
val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
val secureRandom = new SecureRandom()
val saltLength = 12
val keyLength = 128
val iterationCount = 10000
val tagSize = 128
val encode = (bytes: Array[Byte]) =>
Base64.getEncoder().encodeToString(bytes)
val salt = Array(saltLength.toByte)
secureRandom.nextBytes(salt)
val nonce = Array(12.toByte)
secureRandom.nextBytes(nonce)
// val spec =
// new PBEKeySpec(password.toCharArray(), salt, iterationCount, keyLength)
// val tmp = factory.generateSecret(spec)
// val secretKey = new SecretKeySpec(tmp.getEncoded(), "AES")
// val cipher = Cipher.getInstance("AES/GCM/NoPadding")
// cipher.init(
// Cipher.ENCRYPT_MODE,
// secretKey,
// new GCMParameterSpec(128, nonce)
// )
// val cipherTextByte = cipher.doFinal(plainText.getBytes)
val messageCipher = for {
factory <- {
Try(SecretKeyFactory.getInstance("PBKDFWithHmacSHA56")).toOption
.toRight("Failed to get skf instance")
}
spec <- {
Try(
new PBEKeySpec(
password.toCharArray(),
salt,
iterationCount,
keyLength
)
).toOption.toRight("Failed to get pbekeyspec")
}
secret <- Try(factory.generateSecret(spec)).toOption
.toRight("Failed to get secret")
secretKey <- Try(new SecretKeySpec(secret.getEncoded(), "AES")).toOption
.toRight("Failed to get secret key")
cipher <- Try(Cipher.getInstance("AES/GCM/NoPadding")).toOption
.toRight("Failed to get cipher instance")
_ <- Right(
cipher.init(
Cipher.ENCRYPT_MODE,
secretKey,
new GCMParameterSpec(128, nonce)
)
)
cipherTextByte <- Try(cipher.doFinal(plainText.getBytes())).toOption
.toRight("Failed to generate cipher")
messageCipher = MessageCipher(
v = 1,
salt = encode(salt),
mode = "gcm",
iterations = iterationCount,
cipher = "aes",
adata = "",
cipherText = encode(cipherTextByte),
iv = encode(nonce),
keySize = keyLength,
tagSize = tagSize
)
} yield (messageCipher)
messageCipher
}
override def decrypt(
password: String,
messageCipher: MessageCipher
): Try[String] = {
val decode = (text: String) => {
Base64.getDecoder().decode(text)
}
Try {
val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
val spec = new PBEKeySpec(
password.toCharArray(),
decode(messageCipher.salt),
messageCipher.iterations,
messageCipher.keySize
);
val tmp = factory.generateSecret(spec);
val secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(
Cipher.DECRYPT_MODE,
secretKey,
new GCMParameterSpec(128, decode(messageCipher.iv))
);
new String(cipher.doFinal(decode(messageCipher.cipherText)));
}
}
}
object EncryptionServiceImpl {
def apply() = new EncryptionServiceImpl()
}