Added decryption
This commit is contained in:
parent
3c49128c90
commit
2f0f174b33
@ -1,5 +1,6 @@
|
|||||||
package com.ionspin.kotlin.crypto
|
package com.ionspin.kotlin.crypto
|
||||||
|
|
||||||
|
import com.ionspin.kotlin.crypto.authenticated.XChaCha20Poly1305Pure
|
||||||
import com.ionspin.kotlin.crypto.hash.UpdatableHash
|
import com.ionspin.kotlin.crypto.hash.UpdatableHash
|
||||||
import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bProperties
|
import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bProperties
|
||||||
import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bPure
|
import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bPure
|
||||||
@ -92,6 +93,14 @@ data class HashedData(val hash: UByteArray) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class SymmetricKey(val value : UByteArray) {
|
||||||
|
companion object {
|
||||||
|
fun randomKey() : SymmetricKey {
|
||||||
|
return SymmetricKey(SRNG.getRandomBytes(32))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class EncryptedData(val encrypted: UByteArray)
|
data class EncryptedData(val encrypted: UByteArray)
|
||||||
|
|
||||||
object PublicApi {
|
object PublicApi {
|
||||||
@ -106,8 +115,12 @@ object PublicApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
object Symmetric {
|
object Symmetric {
|
||||||
fun encrypt(data : Encryptable) : EncryptedData {
|
fun encrypt(key: SymmetricKey, data : Encryptable, additionalData : UByteArray = ubyteArrayOf()) : EncryptedData {
|
||||||
TODO()
|
if (key.value.size != 32) {
|
||||||
|
throw RuntimeException("Invalid key size! Required 32, supplied ${key.value.size}")
|
||||||
|
}
|
||||||
|
val nonce = SRNG.getRandomBytes(24)
|
||||||
|
return EncryptedData(XChaCha20Poly1305Pure.encrypt(key.value, nonce, data.encryptableData(), additionalData) + nonce)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T: Encryptable> decrypt(encryptedData : EncryptedData) : T {
|
fun <T: Encryptable> decrypt(encryptedData : EncryptedData) : T {
|
||||||
|
@ -3,7 +3,6 @@ package com.ionspin.kotlin.crypto.authenticated
|
|||||||
import com.ionspin.kotlin.crypto.mac.Poly1305
|
import com.ionspin.kotlin.crypto.mac.Poly1305
|
||||||
import com.ionspin.kotlin.crypto.symmetric.ChaCha20Pure
|
import com.ionspin.kotlin.crypto.symmetric.ChaCha20Pure
|
||||||
import com.ionspin.kotlin.crypto.symmetric.XChaCha20Pure
|
import com.ionspin.kotlin.crypto.symmetric.XChaCha20Pure
|
||||||
import com.ionspin.kotlin.crypto.util.hexColumsPrint
|
|
||||||
import com.ionspin.kotlin.crypto.util.toLittleEndianUByteArray
|
import com.ionspin.kotlin.crypto.util.toLittleEndianUByteArray
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,7 +25,7 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray, val addi
|
|||||||
// at org.jetbrains.kotlin.ir.backend.js.lower.ConstTransformer$visitConst$1$3.invoke(ConstLowering.kt:28)
|
// at org.jetbrains.kotlin.ir.backend.js.lower.ConstTransformer$visitConst$1$3.invoke(ConstLowering.kt:28)
|
||||||
// at org.jetbrains.kotlin.ir.backend.js.lower.ConstTransformer.lowerConst(ConstLowering.kt:38)
|
// at org.jetbrains.kotlin.ir.backend.js.lower.ConstTransformer.lowerConst(ConstLowering.kt:38)
|
||||||
)
|
)
|
||||||
val cipherText = XChaCha20Pure.encrypt(key, nonce, message, 1U)
|
val cipherText = XChaCha20Pure.xorWithKeystream(key, nonce, message, 1U)
|
||||||
val additionalDataPad = UByteArray(16 - additionalData.size % 16) { 0U }
|
val additionalDataPad = UByteArray(16 - additionalData.size % 16) { 0U }
|
||||||
val cipherTextPad = UByteArray(16 - cipherText.size % 16) { 0U }
|
val cipherTextPad = UByteArray(16 - cipherText.size % 16) { 0U }
|
||||||
val macData = additionalData + additionalDataPad +
|
val macData = additionalData + additionalDataPad +
|
||||||
@ -36,6 +35,33 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray, val addi
|
|||||||
val tag = Poly1305.poly1305Authenticate(authKey, macData)
|
val tag = Poly1305.poly1305Authenticate(authKey, macData)
|
||||||
return cipherText + tag
|
return cipherText + tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun decrypt(key: UByteArray, nonce: UByteArray, cipherText: UByteArray, additionalData: UByteArray) : UByteArray {
|
||||||
|
val subKey = XChaCha20Pure.hChacha(key, nonce)
|
||||||
|
val authKey =
|
||||||
|
ChaCha20Pure.encrypt(
|
||||||
|
subKey.toLittleEndianUByteArray(),
|
||||||
|
ubyteArrayOf(0U, 0U, 0U, 0U) + nonce.sliceArray(16 until 24),
|
||||||
|
UByteArray(64) { 0U },
|
||||||
|
0U
|
||||||
|
)
|
||||||
|
//2. Get the tag
|
||||||
|
val tag = cipherText.sliceArray(cipherText.size - 16 until cipherText.size)
|
||||||
|
//3. Verify tag is valid
|
||||||
|
val cipherTextWithoutTag = cipherText.sliceArray(0 until cipherText.size - 16)
|
||||||
|
val additionalDataPad = UByteArray(16 - additionalData.size % 16) { 0U }
|
||||||
|
val cipherTextPad = UByteArray(16 - cipherTextWithoutTag.size % 16) { 0U }
|
||||||
|
val macData = additionalData + additionalDataPad +
|
||||||
|
cipherTextWithoutTag + cipherTextPad +
|
||||||
|
additionalData.size.toULong().toLittleEndianUByteArray() +
|
||||||
|
cipherTextWithoutTag.size.toULong().toLittleEndianUByteArray()
|
||||||
|
val calculatedTag = Poly1305.poly1305Authenticate(authKey, macData)
|
||||||
|
if (!calculatedTag.contentEquals(tag)) {
|
||||||
|
RuntimeException("Bad tag!") //TODO replace with specific exception
|
||||||
|
}
|
||||||
|
//4. Decrypt data
|
||||||
|
return XChaCha20Pure.xorWithKeystream(key, nonce, cipherTextWithoutTag, 1U)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val updateableEncryptionPrimitive = XChaCha20Pure(key, nonce, initialCounter = 1U)
|
private val updateableEncryptionPrimitive = XChaCha20Pure(key, nonce, initialCounter = 1U)
|
||||||
|
@ -37,6 +37,9 @@ class Poly1305(key: UByteArray) {
|
|||||||
//Doesn't have to be every power, just divisible by 8
|
//Doesn't have to be every power, just divisible by 8
|
||||||
val twoToThe128 = BigInteger.ONE.shl(128)
|
val twoToThe128 = BigInteger.ONE.shl(128)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Limit - stop poly calculating tag at desired index, ignored if 0
|
||||||
|
*/
|
||||||
fun poly1305Authenticate(key: UByteArray, message: UByteArray) : UByteArray {
|
fun poly1305Authenticate(key: UByteArray, message: UByteArray) : UByteArray {
|
||||||
val r = clampR(UByteArray(16) { key[it] })
|
val r = clampR(UByteArray(16) { key[it] })
|
||||||
val s= UByteArray(16) { key[it + 16]}
|
val s= UByteArray(16) { key[it + 16]}
|
||||||
|
@ -55,7 +55,7 @@ class XChaCha20Pure(key: UByteArray, nonce: UByteArray, initialCounter: UInt = 0
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun encrypt(key: UByteArray, nonce: UByteArray, message: UByteArray, initialCounter: UInt = 0U): UByteArray {
|
fun xorWithKeystream(key: UByteArray, nonce: UByteArray, message: UByteArray, initialCounter: UInt = 0U): UByteArray {
|
||||||
|
|
||||||
val ciphertext = UByteArray(message.size)
|
val ciphertext = UByteArray(message.size)
|
||||||
val hChaChaKey = hChacha(key, nonce)
|
val hChaChaKey = hChacha(key, nonce)
|
||||||
|
@ -53,8 +53,10 @@ class XChaCha20Poly1305Test {
|
|||||||
0x98U, 0x79U, 0x47U, 0xdeU, 0xafU, 0xd8U, 0x78U, 0x0aU,
|
0x98U, 0x79U, 0x47U, 0xdeU, 0xafU, 0xd8U, 0x78U, 0x0aU,
|
||||||
0xcfU, 0x49U
|
0xcfU, 0x49U
|
||||||
)
|
)
|
||||||
val result = XChaCha20Poly1305Pure.encrypt(key, nonce, message, additionalData)
|
val encrypted = XChaCha20Poly1305Pure.encrypt(key, nonce, message, additionalData)
|
||||||
result.contentEquals(expected)
|
val decrypted = XChaCha20Poly1305Pure.decrypt(key, nonce, encrypted, additionalData)
|
||||||
|
|
||||||
|
encrypted.contentEquals(expected) && decrypted.contentEquals(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue {
|
assertTrue {
|
||||||
@ -80,8 +82,10 @@ class XChaCha20Poly1305Test {
|
|||||||
0xbdU, 0x3bU, 0x8aU, 0xd7U, 0xa1U, 0x9dU, 0xe8U, 0xc4U, 0x55U,
|
0xbdU, 0x3bU, 0x8aU, 0xd7U, 0xa1U, 0x9dU, 0xe8U, 0xc4U, 0x55U,
|
||||||
0x84U, 0x6fU, 0xfcU, 0x75U, 0x31U, 0xbfU, 0x0cU, 0x2dU
|
0x84U, 0x6fU, 0xfcU, 0x75U, 0x31U, 0xbfU, 0x0cU, 0x2dU
|
||||||
)
|
)
|
||||||
val result = XChaCha20Poly1305Pure.encrypt(key, nonce, message, additionalData)
|
val encrypted = XChaCha20Poly1305Pure.encrypt(key, nonce, message, additionalData)
|
||||||
result.contentEquals(expected)
|
val decrypted = XChaCha20Poly1305Pure.decrypt(key, nonce, encrypted, additionalData)
|
||||||
|
|
||||||
|
encrypted.contentEquals(expected) && decrypted.contentEquals(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.ionspin.kotlin.crypto.symmetric
|
package com.ionspin.kotlin.crypto.symmetric
|
||||||
|
|
||||||
import com.ionspin.kotlin.crypto.hash.encodeToUByteArray
|
import com.ionspin.kotlin.crypto.hash.encodeToUByteArray
|
||||||
import com.ionspin.kotlin.crypto.util.hexColumsPrint
|
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
@ -133,7 +132,7 @@ class XChaCha20Test {
|
|||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val result = XChaCha20Pure.encrypt(key, nonce, message, 1U)
|
val result = XChaCha20Pure.xorWithKeystream(key, nonce, message, 1U)
|
||||||
assertTrue {
|
assertTrue {
|
||||||
result.contentEquals(expected)
|
result.contentEquals(expected)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user