Adopting libsodium secret stream approach for multipart, currently correct state and keystream, but mac is still incorrect on pure implementation
This commit is contained in:
parent
e6f560ba8e
commit
85e3e2e4ee
@ -55,7 +55,7 @@ object CryptoPrimitives : PrimitivesApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
object Sha512 {
|
object Sha512 {
|
||||||
fun updateable(): com.ionspin.kotlin.crypto.hash.sha.Sha512Multipart {
|
fun updateable(): Sha512Multipart {
|
||||||
checkInitialization()
|
checkInitialization()
|
||||||
return Sha512Delegated()
|
return Sha512Delegated()
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ package com.ionspin.kotlin.crypto.authenticated
|
|||||||
* on 14-Jun-2020
|
* on 14-Jun-2020
|
||||||
*/
|
*/
|
||||||
expect class XChaCha20Poly1305Delegated constructor(key: UByteArray, additionalData: UByteArray) {
|
expect class XChaCha20Poly1305Delegated constructor(key: UByteArray, additionalData: UByteArray) {
|
||||||
|
internal constructor(key: UByteArray, additionalData: UByteArray, testState : UByteArray, testHeader: UByteArray)
|
||||||
companion object {
|
companion object {
|
||||||
fun encrypt(key: UByteArray, nonce: UByteArray, message: UByteArray, additionalData: UByteArray) : UByteArray
|
fun encrypt(key: UByteArray, nonce: UByteArray, message: UByteArray, additionalData: UByteArray) : UByteArray
|
||||||
fun decrypt(key: UByteArray, nonce: UByteArray, ciphertext: UByteArray, additionalData: UByteArray) : UByteArray
|
fun decrypt(key: UByteArray, nonce: UByteArray, ciphertext: UByteArray, additionalData: UByteArray) : UByteArray
|
||||||
|
@ -184,4 +184,32 @@ class XChaCha20Poly1305Test {
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testStreamingImpl() {
|
||||||
|
val key = UByteArray(32) { 0U}
|
||||||
|
val state = ubyteArrayOf(
|
||||||
|
0x2DU, 0xDBU, 0xC7U, 0xB2U, 0x03U, 0xBCU, 0xC3U, 0x22U, 0xBDU, 0x0CU, 0xBAU, 0x82U, 0xADU, 0x77U, 0x79U, 0x44U,
|
||||||
|
0xE6U, 0x8FU, 0xA9U, 0x94U, 0x89U, 0xB1U, 0xDFU, 0xBEU, 0x00U, 0x9FU, 0x69U, 0xECU, 0x21U, 0x88U, 0x47U, 0x55U,
|
||||||
|
0x01U, 0x00U, 0x00U, 0x00U, 0xC5U, 0x55U, 0x06U, 0x38U, 0xEBU, 0xA3U, 0x12U, 0x7BU, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||||
|
0x00U, 0x00U, 0x00U, 0x00U,
|
||||||
|
)
|
||||||
|
val header = ubyteArrayOf(
|
||||||
|
0x49U, 0x62U, 0x22U, 0x03U, 0xB7U, 0x46U, 0x11U, 0x97U, 0x8FU, 0x46U, 0x4AU, 0x3BU, 0x2FU, 0x2AU, 0x81U, 0x03U,
|
||||||
|
0xC5U, 0x55U, 0x06U, 0x38U, 0xEBU, 0xA3U, 0x12U, 0x7BU,
|
||||||
|
)
|
||||||
|
val expected = ubyteArrayOf(
|
||||||
|
0xAFU, 0xD3U, 0x2DU, 0x59U, 0xB8U, 0xC4U, 0x66U, 0x2EU, 0x47U, 0x29U, 0xC6U, 0xF9U, 0x93U, 0x4BU, 0x09U, 0x27U,
|
||||||
|
0x24U, 0xDDU, 0xF3U, 0x05U, 0x48U, 0x94U, 0x67U, 0x10U, 0x00U, 0x21U, 0x85U, 0x22U, 0x96U, 0x3CU, 0xCEU, 0x8EU,
|
||||||
|
0xB7U, 0x53U, 0x9DU, 0x46U, 0xF5U, 0x3CU, 0x5EU, 0x48U, 0x9BU, 0x8CU, 0x13U, 0xB7U, 0x28U, 0x6BU, 0xB3U, 0x6CU,
|
||||||
|
0x3AU, 0x04U, 0xB7U, 0x25U, 0xB9U, 0x50U, 0x45U, 0x08U, 0x0BU, 0x89U, 0xA2U, 0x0FU, 0x70U, 0xCCU, 0x60U, 0x1BU,
|
||||||
|
0xC3U, 0x17U, 0x35U, 0x9FU, 0xAEU, 0x82U, 0x51U, 0x43U, 0x1BU, 0x9DU, 0x53U, 0x9EU, 0xE2U, 0xAFU, 0x20U, 0x1FU,
|
||||||
|
0xFDU, 0x03U, 0x59U, 0x11U, 0x51U, 0x9EU, 0xACU, 0x83U, 0xCDU, 0x78U, 0xD1U, 0xD0U, 0xE5U, 0xD7U, 0x0EU, 0x41U,
|
||||||
|
0xDEU, 0xFBU, 0x5CU, 0x7FU, 0x1CU, 0x26U, 0x32U, 0x2CU, 0x51U, 0xF6U, 0xEFU, 0xC6U, 0x34U, 0xC4U, 0xACU, 0x6CU,
|
||||||
|
0xE8U, 0xF9U, 0x4BU, 0xABU, 0xA3U,
|
||||||
|
)
|
||||||
|
val xcha = XChaCha20Poly1305Delegated(key, ubyteArrayOf(), state, header)
|
||||||
|
val data = UByteArray(100) { 0U }
|
||||||
|
xcha.encryptPartialData(data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,10 @@ actual class XChaCha20Poly1305Delegated actual constructor(key: UByteArray, addi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal actual constructor(key: UByteArray, additionalData: UByteArray, testState : UByteArray, testHeader: UByteArray) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
actual fun encryptPartialData(data: UByteArray): UByteArray {
|
actual fun encryptPartialData(data: UByteArray): UByteArray {
|
||||||
TODO("not implemented yet")
|
TODO("not implemented yet")
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,10 @@ actual class XChaCha20Poly1305Delegated actual constructor(key: UByteArray, addi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal actual constructor(key: UByteArray, additionalData: UByteArray, testState : UByteArray, testHeader: UByteArray) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
actual fun encryptPartialData(data: UByteArray): UByteArray {
|
actual fun encryptPartialData(data: UByteArray): UByteArray {
|
||||||
TODO("not implemented yet")
|
TODO("not implemented yet")
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,16 @@
|
|||||||
package com.ionspin.kotlin.crypto.authenticated
|
package com.ionspin.kotlin.crypto.authenticated
|
||||||
|
|
||||||
import kotlinx.cinterop.addressOf
|
import com.ionspin.kotlin.bignum.integer.util.hexColumsPrint
|
||||||
import kotlinx.cinterop.convert
|
import kotlinx.cinterop.*
|
||||||
import kotlinx.cinterop.pin
|
import libsodium.*
|
||||||
import kotlinx.cinterop.toCValues
|
import platform.posix.malloc
|
||||||
import libsodium.crypto_aead_xchacha20poly1305_IETF_ABYTES
|
|
||||||
import libsodium.crypto_aead_xchacha20poly1305_ietf_decrypt
|
|
||||||
import libsodium.crypto_aead_xchacha20poly1305_ietf_encrypt
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Ugljesa Jovanovic
|
* Created by Ugljesa Jovanovic
|
||||||
* ugljesa.jovanovic@ionspin.com
|
* ugljesa.jovanovic@ionspin.com
|
||||||
* on 14-Jun-2020
|
* on 14-Jun-2020
|
||||||
*/
|
*/
|
||||||
actual class XChaCha20Poly1305Delegated actual constructor(key: UByteArray, additionalData: UByteArray) {
|
actual class XChaCha20Poly1305Delegated actual constructor(val key: UByteArray,val additionalData: UByteArray) {
|
||||||
actual companion object {
|
actual companion object {
|
||||||
actual fun encrypt(
|
actual fun encrypt(
|
||||||
key: UByteArray,
|
key: UByteArray,
|
||||||
@ -64,8 +61,58 @@ actual class XChaCha20Poly1305Delegated actual constructor(key: UByteArray, addi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
actual internal constructor(key: UByteArray, additionalData: UByteArray, testState : UByteArray, testHeader: UByteArray) : this(key, additionalData) {
|
||||||
|
val pointer = state.ptr.reinterpret<UByteVar>()
|
||||||
|
for (i in 0 until crypto_secretstream_xchacha20poly1305_state.size.toInt()) {
|
||||||
|
pointer[i] = testState[i]
|
||||||
|
}
|
||||||
|
println("state after setting-----------")
|
||||||
|
state.ptr.readBytes(crypto_secretstream_xchacha20poly1305_state.size.toInt()).toUByteArray().hexColumsPrint()
|
||||||
|
println("state after setting-----------")
|
||||||
|
println("header after setting-----------")
|
||||||
|
testHeader.copyInto(header)
|
||||||
|
header.hexColumsPrint()
|
||||||
|
println("header after setting-----------")
|
||||||
|
}
|
||||||
|
|
||||||
|
var state =
|
||||||
|
malloc(crypto_secretstream_xchacha20poly1305_state.size.convert())!!
|
||||||
|
.reinterpret<crypto_secretstream_xchacha20poly1305_state>()
|
||||||
|
.pointed
|
||||||
|
|
||||||
|
val header = UByteArray(crypto_secretstream_xchacha20poly1305_HEADERBYTES.toInt()) { 0U }
|
||||||
|
|
||||||
|
init {
|
||||||
|
val pinnedHeader = header.pin()
|
||||||
|
crypto_secretstream_xchacha20poly1305_init_push(state.ptr, pinnedHeader.addressOf(0), key.toCValues())
|
||||||
|
println("state-----------")
|
||||||
|
state.ptr.readBytes(crypto_secretstream_xchacha20poly1305_state.size.toInt()).toUByteArray().hexColumsPrint()
|
||||||
|
println("state-----------")
|
||||||
|
println("--------header-----------")
|
||||||
|
header.hexColumsPrint()
|
||||||
|
println("--------header-----------")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
actual fun encryptPartialData(data: UByteArray): UByteArray {
|
actual fun encryptPartialData(data: UByteArray): UByteArray {
|
||||||
TODO("not implemented yet")
|
val ciphertextWithTag = UByteArray(data.size + crypto_secretstream_xchacha20poly1305_ABYTES.toInt())
|
||||||
|
val ciphertextWithTagPinned = ciphertextWithTag.pin()
|
||||||
|
crypto_secretstream_xchacha20poly1305_push(
|
||||||
|
state.ptr,
|
||||||
|
ciphertextWithTagPinned.addressOf(0),
|
||||||
|
null,
|
||||||
|
data.toCValues(),
|
||||||
|
data.size.convert(),
|
||||||
|
null,
|
||||||
|
0U,
|
||||||
|
0U,
|
||||||
|
)
|
||||||
|
println("Encrypt partial")
|
||||||
|
ciphertextWithTag.hexColumsPrint()
|
||||||
|
println("Encrypt partial end")
|
||||||
|
ciphertextWithTagPinned.unpin()
|
||||||
|
return ciphertextWithTag
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun verifyPartialData(data: UByteArray) {
|
actual fun verifyPartialData(data: UByteArray) {
|
||||||
|
@ -2,9 +2,7 @@ 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.util.fromLittleEndianArrayToUIntWithPosition
|
import com.ionspin.kotlin.crypto.util.fromLittleEndianArrayToUIntWithPosition
|
||||||
import com.ionspin.kotlin.crypto.util.hexColumsPrint
|
|
||||||
import com.ionspin.kotlin.crypto.util.toLittleEndianUByteArray
|
import com.ionspin.kotlin.crypto.util.toLittleEndianUByteArray
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,7 +36,7 @@ internal class ChaCha20Poly1305Pure {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val oneTimeKey = ChaCha20Pure.hash(state).sliceArray(0 until 32)
|
val oneTimeKey = ChaCha20Pure.hash(state).sliceArray(0 until 32)
|
||||||
val cipherText = ChaCha20Pure.encrypt(key, nonce, message, 1U)
|
val cipherText = ChaCha20Pure.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 +
|
||||||
|
@ -2,11 +2,11 @@ package com.ionspin.kotlin.crypto.authenticated
|
|||||||
|
|
||||||
import com.ionspin.kotlin.crypto.AuthenticatedEncryption
|
import com.ionspin.kotlin.crypto.AuthenticatedEncryption
|
||||||
import com.ionspin.kotlin.crypto.InvalidTagException
|
import com.ionspin.kotlin.crypto.InvalidTagException
|
||||||
import com.ionspin.kotlin.crypto.MultipartAuthenticatedDecryption
|
|
||||||
import com.ionspin.kotlin.crypto.SRNG
|
|
||||||
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.overwriteWithZeroes
|
||||||
import com.ionspin.kotlin.crypto.util.toLittleEndianUByteArray
|
import com.ionspin.kotlin.crypto.util.toLittleEndianUByteArray
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -14,13 +14,13 @@ import com.ionspin.kotlin.crypto.util.toLittleEndianUByteArray
|
|||||||
* ugljesa.jovanovic@ionspin.com
|
* ugljesa.jovanovic@ionspin.com
|
||||||
* on 17-Jun-2020
|
* on 17-Jun-2020
|
||||||
*/
|
*/
|
||||||
class XChaCha20Poly1305Pure(val key: UByteArray, val additionalData: UByteArray) {
|
class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray) {
|
||||||
companion object : AuthenticatedEncryption {
|
companion object : AuthenticatedEncryption {
|
||||||
|
|
||||||
override fun encrypt(key: UByteArray, nonce: UByteArray, message: UByteArray, additionalData: UByteArray) : UByteArray {
|
override fun encrypt(key: UByteArray, nonce: UByteArray, message: UByteArray, additionalData: UByteArray) : UByteArray {
|
||||||
val subKey = XChaCha20Pure.hChacha(key, nonce)
|
val subKey = XChaCha20Pure.hChacha(key, nonce)
|
||||||
val authKey =
|
val authKey =
|
||||||
ChaCha20Pure.encrypt(
|
ChaCha20Pure.xorWithKeystream(
|
||||||
subKey.toLittleEndianUByteArray(),
|
subKey.toLittleEndianUByteArray(),
|
||||||
ubyteArrayOf(0U, 0U, 0U, 0U) + nonce.sliceArray(16 until 24),
|
ubyteArrayOf(0U, 0U, 0U, 0U) + nonce.sliceArray(16 until 24),
|
||||||
UByteArray(64) { 0U },
|
UByteArray(64) { 0U },
|
||||||
@ -43,7 +43,7 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val additionalData: UByteArray)
|
|||||||
override fun decrypt(key: UByteArray, nonce: UByteArray, cipherText: UByteArray, additionalData: UByteArray) : UByteArray {
|
override fun decrypt(key: UByteArray, nonce: UByteArray, cipherText: UByteArray, additionalData: UByteArray) : UByteArray {
|
||||||
val subKey = XChaCha20Pure.hChacha(key, nonce)
|
val subKey = XChaCha20Pure.hChacha(key, nonce)
|
||||||
val authKey =
|
val authKey =
|
||||||
ChaCha20Pure.encrypt(
|
ChaCha20Pure.xorWithKeystream(
|
||||||
subKey.toLittleEndianUByteArray(),
|
subKey.toLittleEndianUByteArray(),
|
||||||
ubyteArrayOf(0U, 0U, 0U, 0U) + nonce.sliceArray(16 until 24),
|
ubyteArrayOf(0U, 0U, 0U, 0U) + nonce.sliceArray(16 until 24),
|
||||||
UByteArray(64) { 0U },
|
UByteArray(64) { 0U },
|
||||||
@ -69,8 +69,6 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val additionalData: UByteArray)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val nonce = SRNG.getRandomBytes(24)
|
|
||||||
|
|
||||||
private val updateableEncryptionPrimitive = XChaCha20Pure(key, nonce, initialCounter = 1U)
|
private val updateableEncryptionPrimitive = XChaCha20Pure(key, nonce, initialCounter = 1U)
|
||||||
private val updateableMacPrimitive : Poly1305
|
private val updateableMacPrimitive : Poly1305
|
||||||
|
|
||||||
@ -82,7 +80,7 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val additionalData: UByteArray)
|
|||||||
init {
|
init {
|
||||||
val subKey = XChaCha20Pure.hChacha(key, nonce)
|
val subKey = XChaCha20Pure.hChacha(key, nonce)
|
||||||
val authKey =
|
val authKey =
|
||||||
ChaCha20Pure.encrypt(
|
ChaCha20Pure.xorWithKeystream(
|
||||||
subKey.toLittleEndianUByteArray(),
|
subKey.toLittleEndianUByteArray(),
|
||||||
ubyteArrayOf(0U, 0U, 0U, 0U) + nonce.sliceArray(16 until 24),
|
ubyteArrayOf(0U, 0U, 0U, 0U) + nonce.sliceArray(16 until 24),
|
||||||
UByteArray(64) { 0U },
|
UByteArray(64) { 0U },
|
||||||
@ -92,17 +90,88 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val additionalData: UByteArray)
|
|||||||
// 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)
|
||||||
)
|
)
|
||||||
updateableMacPrimitive = Poly1305(authKey)
|
updateableMacPrimitive = Poly1305(authKey)
|
||||||
val additionalDataPad = UByteArray(16 - additionalData.size % 16) { 0U }
|
// val additionalDataPad = UByteArray(16 - additionalData.size % 16) { 0U }
|
||||||
processPolyBytes(additionalData + additionalDataPad)
|
// processPolyBytes(additionalData + additionalDataPad)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sketch libsodium stream cipher with chachapoly, because my two pass approach to multipart encryption is
|
||||||
|
// inneficient, to put it mildly.
|
||||||
|
|
||||||
|
// libsodium-like state
|
||||||
|
// key, 32 Bytes
|
||||||
|
// nonce, 12 Bytes
|
||||||
|
// pad, 8 bytes
|
||||||
|
|
||||||
|
//libsodium like header
|
||||||
|
//random header bytes 24 and put that into out
|
||||||
|
//then hchacha20 of key and random bytes (out) to generate state key
|
||||||
|
//the reset counter to 1
|
||||||
|
//then copy to state->NONCE, HCHACHAINPUTBYTES (16B) from out, length of INONCE_BYTES which is 8, which uses up all random from previous step
|
||||||
|
//Pad state with 8B of zeroes
|
||||||
|
|
||||||
|
//header is a 24byte nonce
|
||||||
|
|
||||||
|
internal val calcKey : UByteArray = UByteArray(32)
|
||||||
|
internal val calcNonce : UByteArray = UByteArray(12)
|
||||||
|
|
||||||
|
init {
|
||||||
|
val calc = XChaCha20Pure.hChacha(key, nonce).toLittleEndianUByteArray()
|
||||||
|
calc.sliceArray(0 until 32).copyInto(calcKey)
|
||||||
|
nonce.sliceArray(16 until 24).copyInto(calcNonce, 4)
|
||||||
|
calcNonce[0] = 1U
|
||||||
|
calcNonce[1] = 0U
|
||||||
|
calcNonce[2] = 0U
|
||||||
|
calcNonce[3] = 0U
|
||||||
|
println("Calckey-------=")
|
||||||
|
calcKey.hexColumsPrint()
|
||||||
|
println("Calckey-------=")
|
||||||
|
println("Calcnonce---------")
|
||||||
|
calcNonce.hexColumsPrint()
|
||||||
|
println("Calcnonce---------")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun encryptPartial(data: UByteArray, additionalData: UByteArray = ubyteArrayOf(), tag : UByte = 0U) : UByteArray {
|
||||||
|
val result = UByteArray(1 + data.size + 16) //Tag marker, ciphertext, mac
|
||||||
|
//get encryption state
|
||||||
|
val block = UByteArray(64) { 0U }
|
||||||
|
ChaCha20Pure.xorWithKeystream(calcKey, calcNonce, block, 0U).copyInto(block) // This is equivalent to the first 64 bytes of keystream
|
||||||
|
val poly1305 = Poly1305(block)
|
||||||
|
block.overwriteWithZeroes()
|
||||||
|
val additionalDataPadded = additionalData + UByteArray(16 - additionalData.size % 16) { 0U }
|
||||||
|
poly1305.updateMac(additionalDataPadded)
|
||||||
|
block[0] = tag
|
||||||
|
ChaCha20Pure.xorWithKeystream(calcKey, calcNonce, block, 1U).copyInto(block) // This just xors block[0] with keystream
|
||||||
|
poly1305.updateMac(block) // but updates the mac with the full block!
|
||||||
|
// In libsodium c code, it now sets the first byte to be a tag, we'll just save it for now
|
||||||
|
val encryptedTag = block[0]
|
||||||
|
//And then encrypt the rest of the message
|
||||||
|
val ciphertext = ChaCha20Pure.xorWithKeystream(calcKey, calcNonce, data, 2U) // With appropriate counter
|
||||||
|
println("ciphertext-----")
|
||||||
|
ciphertext.hexColumsPrint()
|
||||||
|
println("ciphertext-----")
|
||||||
|
poly1305.updateMac(ciphertext + UByteArray(16 - data.size % 16) { 0U } ) //TODO this is inefficient as it creates a new array and copies data
|
||||||
|
val finalMac = additionalData.size.toULong().toLittleEndianUByteArray() + ciphertext.size.toULong().toLittleEndianUByteArray()
|
||||||
|
val mac = poly1305.finalizeMac(finalMac)
|
||||||
|
|
||||||
|
return ubyteArrayOf(encryptedTag) + ciphertext + mac
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sketch end
|
||||||
|
|
||||||
fun encryptPartialData(data: UByteArray) : UByteArray {
|
fun encryptPartialData(data: UByteArray) : UByteArray {
|
||||||
processedBytes += data.size
|
processedBytes += data.size
|
||||||
val encrypted = updateableEncryptionPrimitive.xorWithKeystream(data)
|
val encrypted = updateableEncryptionPrimitive.xorWithKeystream(data)
|
||||||
processPolyBytes(encrypted)
|
processPolyBytes(encrypted)
|
||||||
return encrypted
|
|
||||||
|
val cipherTextPad = UByteArray(16 - processedBytes % 16) { 0U }
|
||||||
|
val macData = cipherTextPad +
|
||||||
|
// additionalData.size.toULong().toLittleEndianUByteArray() +
|
||||||
|
processedBytes.toULong().toLittleEndianUByteArray()
|
||||||
|
processPolyBytes(macData)
|
||||||
|
val tag = updateableMacPrimitive.finalizeMac()
|
||||||
|
|
||||||
|
return encrypted + tag
|
||||||
}
|
}
|
||||||
|
|
||||||
fun verifyPartialData(data: UByteArray) {
|
fun verifyPartialData(data: UByteArray) {
|
||||||
@ -112,7 +181,7 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val additionalData: UByteArray)
|
|||||||
fun checkTag(expectedTag: UByteArray) {
|
fun checkTag(expectedTag: UByteArray) {
|
||||||
val cipherTextPad = UByteArray(16 - processedBytes % 16) { 0U }
|
val cipherTextPad = UByteArray(16 - processedBytes % 16) { 0U }
|
||||||
val macData = cipherTextPad +
|
val macData = cipherTextPad +
|
||||||
additionalData.size.toULong().toLittleEndianUByteArray() +
|
// additionalData.size.toULong().toLittleEndianUByteArray() +
|
||||||
processedBytes.toULong().toLittleEndianUByteArray()
|
processedBytes.toULong().toLittleEndianUByteArray()
|
||||||
processPolyBytes(macData)
|
processPolyBytes(macData)
|
||||||
val tag = updateableMacPrimitive.finalizeMac()
|
val tag = updateableMacPrimitive.finalizeMac()
|
||||||
@ -173,7 +242,7 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val additionalData: UByteArray)
|
|||||||
|
|
||||||
val cipherTextPad = UByteArray(16 - processedBytes % 16) { 0U }
|
val cipherTextPad = UByteArray(16 - processedBytes % 16) { 0U }
|
||||||
val macData = cipherTextPad +
|
val macData = cipherTextPad +
|
||||||
additionalData.size.toULong().toLittleEndianUByteArray() +
|
// additionalData.size.toULong().toLittleEndianUByteArray() +
|
||||||
processedBytes.toULong().toLittleEndianUByteArray()
|
processedBytes.toULong().toLittleEndianUByteArray()
|
||||||
processPolyBytes(macData)
|
processPolyBytes(macData)
|
||||||
val tag = updateableMacPrimitive.finalizeMac()
|
val tag = updateableMacPrimitive.finalizeMac()
|
||||||
|
@ -80,7 +80,6 @@ class Poly1305(key: UByteArray) {
|
|||||||
var accumulator = BigInteger.ZERO
|
var accumulator = BigInteger.ZERO
|
||||||
|
|
||||||
fun updateMac(data : UByteArray) {
|
fun updateMac(data : UByteArray) {
|
||||||
data.hexColumsPrint()
|
|
||||||
val blockAsInt = BigInteger.fromUByteArray(data, Endianness.LITTLE) + powersOfTwo[128]
|
val blockAsInt = BigInteger.fromUByteArray(data, Endianness.LITTLE) + powersOfTwo[128]
|
||||||
accumulator += blockAsInt
|
accumulator += blockAsInt
|
||||||
accumulator *= rAsBigInt
|
accumulator *= rAsBigInt
|
||||||
@ -89,7 +88,6 @@ class Poly1305(key: UByteArray) {
|
|||||||
|
|
||||||
fun finalizeMac(data: UByteArray = ubyteArrayOf()) : UByteArray{
|
fun finalizeMac(data: UByteArray = ubyteArrayOf()) : UByteArray{
|
||||||
if (data.size != 0) {
|
if (data.size != 0) {
|
||||||
data.hexColumsPrint()
|
|
||||||
val blockAsInt = BigInteger.fromUByteArray(data, Endianness.LITTLE) + powersOfTwo[data.size * 8]
|
val blockAsInt = BigInteger.fromUByteArray(data, Endianness.LITTLE) + powersOfTwo[data.size * 8]
|
||||||
accumulator += blockAsInt
|
accumulator += blockAsInt
|
||||||
accumulator *= rAsBigInt
|
accumulator *= rAsBigInt
|
||||||
|
@ -57,7 +57,7 @@ internal class ChaCha20Pure {
|
|||||||
val sigma2_32 = 2036477234U //ubyteArrayOf(50U, 45U, 98U, 121U)
|
val sigma2_32 = 2036477234U //ubyteArrayOf(50U, 45U, 98U, 121U)
|
||||||
val sigma3_32 = 1797285236U //ubyteArrayOf(116U, 101U, 32U, 107U)
|
val sigma3_32 = 1797285236U //ubyteArrayOf(116U, 101U, 32U, 107U)
|
||||||
|
|
||||||
fun encrypt(key: UByteArray, nonce: UByteArray, message: UByteArray, initialCounter: UInt): UByteArray {
|
fun xorWithKeystream(key: UByteArray, nonce: UByteArray, message: UByteArray, initialCounter: UInt): UByteArray {
|
||||||
val ciphertext = UByteArray(message.size)
|
val ciphertext = UByteArray(message.size)
|
||||||
val state = UIntArray(16) {
|
val state = UIntArray(16) {
|
||||||
when (it) {
|
when (it) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.ionspin.kotlin.crypto.authenticated
|
package com.ionspin.kotlin.crypto.authenticated
|
||||||
|
|
||||||
import com.ionspin.kotlin.crypto.hash.encodeToUByteArray
|
import com.ionspin.kotlin.crypto.hash.encodeToUByteArray
|
||||||
|
import com.ionspin.kotlin.crypto.util.hexColumsPrint
|
||||||
import kotlin.test.Ignore
|
import kotlin.test.Ignore
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
@ -173,4 +174,39 @@ class XChaCha20Poly1305Test {
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun multipartXChaCha20Poly1305() {
|
||||||
|
val key = UByteArray(32) { 0U}
|
||||||
|
|
||||||
|
val state = ubyteArrayOf(
|
||||||
|
0x2DU, 0xDBU, 0xC7U, 0xB2U, 0x03U, 0xBCU, 0xC3U, 0x22U, 0xBDU, 0x0CU, 0xBAU, 0x82U, 0xADU, 0x77U, 0x79U, 0x44U,
|
||||||
|
0xE6U, 0x8FU, 0xA9U, 0x94U, 0x89U, 0xB1U, 0xDFU, 0xBEU, 0x00U, 0x9FU, 0x69U, 0xECU, 0x21U, 0x88U, 0x47U, 0x55U,
|
||||||
|
0x01U, 0x00U, 0x00U, 0x00U, 0xC5U, 0x55U, 0x06U, 0x38U, 0xEBU, 0xA3U, 0x12U, 0x7BU, 0x00U, 0x00U, 0x00U, 0x00U,
|
||||||
|
0x00U, 0x00U, 0x00U, 0x00U,
|
||||||
|
)
|
||||||
|
val header = ubyteArrayOf(
|
||||||
|
0x49U, 0x62U, 0x22U, 0x03U, 0xB7U, 0x46U, 0x11U, 0x97U, 0x8FU, 0x46U, 0x4AU, 0x3BU, 0x2FU, 0x2AU, 0x81U, 0x03U,
|
||||||
|
0xC5U, 0x55U, 0x06U, 0x38U, 0xEBU, 0xA3U, 0x12U, 0x7BU,
|
||||||
|
)
|
||||||
|
val expected = ubyteArrayOf(
|
||||||
|
0xAFU, 0xD3U, 0x2DU, 0x59U, 0xB8U, 0xC4U, 0x66U, 0x2EU, 0x47U, 0x29U, 0xC6U, 0xF9U, 0x93U, 0x4BU, 0x09U, 0x27U,
|
||||||
|
0x24U, 0xDDU, 0xF3U, 0x05U, 0x48U, 0x94U, 0x67U, 0x10U, 0x00U, 0x21U, 0x85U, 0x22U, 0x96U, 0x3CU, 0xCEU, 0x8EU,
|
||||||
|
0xB7U, 0x53U, 0x9DU, 0x46U, 0xF5U, 0x3CU, 0x5EU, 0x48U, 0x9BU, 0x8CU, 0x13U, 0xB7U, 0x28U, 0x6BU, 0xB3U, 0x6CU,
|
||||||
|
0x3AU, 0x04U, 0xB7U, 0x25U, 0xB9U, 0x50U, 0x45U, 0x08U, 0x0BU, 0x89U, 0xA2U, 0x0FU, 0x70U, 0xCCU, 0x60U, 0x1BU,
|
||||||
|
0xC3U, 0x17U, 0x35U, 0x9FU, 0xAEU, 0x82U, 0x51U, 0x43U, 0x1BU, 0x9DU, 0x53U, 0x9EU, 0xE2U, 0xAFU, 0x20U, 0x1FU,
|
||||||
|
0xFDU, 0x03U, 0x59U, 0x11U, 0x51U, 0x9EU, 0xACU, 0x83U, 0xCDU, 0x78U, 0xD1U, 0xD0U, 0xE5U, 0xD7U, 0x0EU, 0x41U,
|
||||||
|
0xDEU, 0xFBU, 0x5CU, 0x7FU, 0x1CU, 0x26U, 0x32U, 0x2CU, 0x51U, 0xF6U, 0xEFU, 0xC6U, 0x34U, 0xC4U, 0xACU, 0x6CU,
|
||||||
|
0xE8U, 0xF9U, 0x4BU, 0xABU, 0xA3U,
|
||||||
|
)
|
||||||
|
|
||||||
|
val xcha = XChaCha20Poly1305Pure(key, header)
|
||||||
|
//Verify the state is correctly created
|
||||||
|
assertTrue {
|
||||||
|
xcha.calcKey.contentEquals(state.sliceArray(0 until 32))
|
||||||
|
xcha.calcNonce.contentEquals(state.sliceArray(32 until 44))
|
||||||
|
}
|
||||||
|
val data = UByteArray(100) { 0U }
|
||||||
|
xcha.encryptPartial(data).hexColumsPrint()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +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 com.ionspin.kotlin.crypto.util.toHexString
|
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
@ -80,7 +78,7 @@ class ChaCha20Test {
|
|||||||
0x5aU, 0xf9U, 0x0bU, 0xbfU, 0x74U, 0xa3U, 0x5bU, 0xe6U, 0xb4U, 0x0bU, 0x8eU, 0xedU, 0xf2U, 0x78U, 0x5eU, 0x42U,
|
0x5aU, 0xf9U, 0x0bU, 0xbfU, 0x74U, 0xa3U, 0x5bU, 0xe6U, 0xb4U, 0x0bU, 0x8eU, 0xedU, 0xf2U, 0x78U, 0x5eU, 0x42U,
|
||||||
0x87U, 0x4dU,
|
0x87U, 0x4dU,
|
||||||
)
|
)
|
||||||
val result = ChaCha20Pure.encrypt(key, nonce, message, 1U)
|
val result = ChaCha20Pure.xorWithKeystream(key, nonce, message, 1U)
|
||||||
assertTrue {
|
assertTrue {
|
||||||
expected.contentEquals(result)
|
expected.contentEquals(result)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user