All Downloads are FREE. Search and download functionalities are using the official Maven repository.

pto.0.2.0.source-code.SymmetricEncryption.kt Maven / Gradle / Ivy

There is a newer version: 0.3.1
Show newest version
package se.wollan.crypto

import java.nio.ByteBuffer
import javax.crypto.AEADBadTagException
import javax.crypto.Cipher
import javax.crypto.spec.GCMParameterSpec
import javax.crypto.spec.SecretKeySpec
import kotlin.random.Random

internal interface SymmetricEncryptor {

    fun encrypt(plaintext: ByteArray, encryptionKey: SecretKey): ByteArray

    /**
     * @throws BadSecretKeyException on invalid secret key
     */
    fun decrypt(ciphermessage: ByteArray, decryptionKey: SecretKey): ByteArray
}

class BadSecretKeyException : Exception()

// https://gist.github.com/patrickfav/7e28d4eb4bf500f7ee8012c4a0cf7bbf
// Same settings as https://mprimi.github.io/portable-secret/creator/ but without padding
internal class AesGcmEncryptor(private val random: Random) : SymmetricEncryptor {

    companion object {
        private const val IV_LEN_BYTES = 16
        private const val AUTH_TAG_LEN_BYTES = 16
        private const val ALG = "AES"
        private const val TRANSFORMATION = "AES/GCM/NoPadding"
    }

    override fun encrypt(plaintext: ByteArray, encryptionKey: SecretKey): ByteArray {
        val secretKeySpec = SecretKeySpec(encryptionKey.value, ALG)

        val iv = random.nextBytes(IV_LEN_BYTES) //NEVER REUSE THIS IV WITH SAME KEY
        val cipher = Cipher.getInstance(TRANSFORMATION)
        val paramSpec = GCMParameterSpec(AUTH_TAG_LEN_BYTES * 8, iv)
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, paramSpec)

        val ciphertext = cipher.doFinal(plaintext)

        // save IV as first bytes
        val ciphermessage = ByteBuffer.allocate(iv.size + ciphertext.size)
        ciphermessage.put(iv)
        ciphermessage.put(ciphertext)
        return ciphermessage.array()
    }

    override fun decrypt(ciphermessage: ByteArray, decryptionKey: SecretKey): ByteArray {
        val secretKeySpec = SecretKeySpec(decryptionKey.value, ALG)
        val cipher = Cipher.getInstance(TRANSFORMATION)

        // first bytes are IV
        val paramSpec = GCMParameterSpec(AUTH_TAG_LEN_BYTES * 8, ciphermessage, 0, IV_LEN_BYTES)
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, paramSpec)

        try {
            // use everything else as ciphertext
            return cipher.doFinal(ciphermessage, IV_LEN_BYTES, ciphermessage.size - IV_LEN_BYTES)
        } catch (_: AEADBadTagException) {
            throw BadSecretKeyException()
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy