Cleanup 1

This commit is contained in:
Ugljesa Jovanovic 2020-07-04 19:05:15 +02:00 committed by Ugljesa Jovanovic
parent a1a56487ff
commit 2db5523893
No known key found for this signature in database
GPG Key ID: 178E6DFCECCB0E0F
9 changed files with 19 additions and 172 deletions

View File

@ -72,15 +72,10 @@ interface AuthenticatedEncryption {
data class EncryptedDataPart(val data : UByteArray) data class EncryptedDataPart(val data : UByteArray)
data class DecryptedDataPart(val data : UByteArray) data class DecryptedDataPart(val data : UByteArray)
data class MultipartEncryptedDataDescriptor(val data: UByteArray, val nonce: UByteArray) data class MultipartEncryptionHeader(val nonce: UByteArray)
class InvalidTagException : RuntimeException("Tag mismatch! Encrypted data is corrupted or tampered with.") class InvalidTagException : RuntimeException("Tag mismatch! Encrypted data is corrupted or tampered with.")
interface MultipartAuthenticatedVerification {
fun verifyPartialData(data: EncryptedDataPart)
fun finalizeVerificationAndPrepareDecryptor() : MultipartAuthenticatedDecryption
}
interface MultipartAuthenticatedDecryption { interface MultipartAuthenticatedDecryption {
fun decryptPartialData(data: EncryptedDataPart) : DecryptedDataPart fun decryptPartialData(data: EncryptedDataPart) : DecryptedDataPart
} }

View File

@ -171,32 +171,14 @@ object Crypto {
class MultipartAuthenticatedEncryptor internal constructor(val key : SymmetricKey, additionalData: UByteArray) : MultipartAuthenticatedEncryption { class MultipartAuthenticatedEncryptor internal constructor(val key : SymmetricKey, additionalData: UByteArray) : MultipartAuthenticatedEncryption {
val primitive = XChaCha20Poly1305Delegated(key.value, additionalData) val primitive = XChaCha20Poly1305Delegated(key.value, additionalData)
override fun encryptPartialData(data: UByteArray): EncryptedDataPart { override fun encryptPartialData(data: UByteArray): EncryptedDataPart {
return EncryptedDataPart(primitive.encryptPartialData(data)) return EncryptedDataPart(primitive.encrypt(data))
}
override fun finish(): MultipartEncryptedDataDescriptor {
val finished = primitive.finishEncryption()
return MultipartEncryptedDataDescriptor(finished.first, finished.second)
} }
} }
class MultiplatformAuthenticatedVerificator internal constructor(key: SymmetricKey, multipartEncryptedDataDescriptor: MultipartEncryptedDataDescriptor, additionalData: UByteArray) : MultipartAuthenticatedVerification {
val primitive = XChaCha20Poly1305Delegated(key.value, additionalData)
val tag = multipartEncryptedDataDescriptor.data.sliceArray(
multipartEncryptedDataDescriptor.data.size - 16 until multipartEncryptedDataDescriptor.data.size
)
override fun verifyPartialData(data: EncryptedDataPart) {
primitive.verifyPartialData(data.data)
}
override fun finalizeVerificationAndPrepareDecryptor(): MultipartAuthenticatedDecryption {
primitive.checkTag(tag)
return MultipartAuthenticatedDecryptor(primitive)
}
}
class MultipartAuthenticatedDecryptor internal constructor(val encryptor: XChaCha20Poly1305Delegated) : MultipartAuthenticatedDecryption { class MultipartAuthenticatedDecryptor internal constructor(val encryptor: XChaCha20Poly1305Delegated) : MultipartAuthenticatedDecryption {
override fun decryptPartialData(data: EncryptedDataPart): DecryptedDataPart { override fun decryptPartialData(data: EncryptedDataPart): DecryptedDataPart {

View File

@ -13,11 +13,9 @@ expect class XChaCha20Poly1305Delegated constructor(key: UByteArray, additionalD
fun decrypt(key: UByteArray, nonce: UByteArray, ciphertext: UByteArray, additionalData: UByteArray) : UByteArray fun decrypt(key: UByteArray, nonce: UByteArray, ciphertext: UByteArray, additionalData: UByteArray) : UByteArray
} }
fun encryptPartialData(data: UByteArray) : UByteArray fun encrypt(data: UByteArray) : UByteArray
fun verifyPartialData(data: UByteArray)
fun checkTag(expectedTag: UByteArray)
fun decrypt(data: UByteArray) : UByteArray fun decrypt(data: UByteArray) : UByteArray
fun finishEncryption() : Pair<UByteArray, UByteArray>
} }

View File

@ -4,8 +4,6 @@ import com.ionspin.kotlin.crypto.CryptoInitializerDelegated
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.hexColumsPrint
import com.ionspin.kotlin.crypto.util.testBlocking import com.ionspin.kotlin.crypto.util.testBlocking
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlin.test.Ignore import kotlin.test.Ignore
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertTrue import kotlin.test.assertTrue
@ -145,7 +143,7 @@ class XChaCha20Poly1305Test {
0xcfU, 0x49U 0xcfU, 0x49U
) )
val xChaChaPoly = XChaCha20Poly1305Delegated(key, additionalData) val xChaChaPoly = XChaCha20Poly1305Delegated(key, additionalData)
val firstChunk = xChaChaPoly.encryptPartialData(message) val firstChunk = xChaChaPoly.encrypt(message)
val finalChunk = xChaChaPoly.finishEncryption().first val finalChunk = xChaChaPoly.finishEncryption().first
val result = firstChunk + finalChunk val result = firstChunk + finalChunk
@ -176,7 +174,7 @@ class XChaCha20Poly1305Test {
0x84U, 0x6fU, 0xfcU, 0x75U, 0x31U, 0xbfU, 0x0cU, 0x2dU 0x84U, 0x6fU, 0xfcU, 0x75U, 0x31U, 0xbfU, 0x0cU, 0x2dU
) )
val xChaChaPoly = XChaCha20Poly1305Delegated(key, additionalData) val xChaChaPoly = XChaCha20Poly1305Delegated(key, additionalData)
val firstChunk = xChaChaPoly.encryptPartialData(message) val firstChunk = xChaChaPoly.encrypt(message)
val finalChunk = xChaChaPoly.finishEncryption().first val finalChunk = xChaChaPoly.finishEncryption().first
val result = firstChunk + finalChunk val result = firstChunk + finalChunk
result.contentEquals(expected) result.contentEquals(expected)

View File

@ -55,7 +55,7 @@ actual class XChaCha20Poly1305Delegated actual constructor(key: UByteArray, addi
} }
actual fun encryptPartialData(data: UByteArray): UByteArray { actual fun encrypt(data: UByteArray): UByteArray {
TODO("not implemented yet") TODO("not implemented yet")
} }

View File

@ -59,7 +59,7 @@ actual class XChaCha20Poly1305Delegated actual constructor(key: UByteArray, addi
} }
actual fun encryptPartialData(data: UByteArray): UByteArray { actual fun encrypt(data: UByteArray): UByteArray {
TODO("not implemented yet") TODO("not implemented yet")
} }

View File

@ -97,7 +97,7 @@ actual class XChaCha20Poly1305Delegated actual constructor(val key: UByteArray,v
} }
actual fun encryptPartialData(data: UByteArray): UByteArray { actual fun encrypt(data: UByteArray): UByteArray {
val ciphertextWithTag = UByteArray(data.size + crypto_secretstream_xchacha20poly1305_ABYTES.toInt()) val ciphertextWithTag = UByteArray(data.size + crypto_secretstream_xchacha20poly1305_ABYTES.toInt())
val ciphertextWithTagPinned = ciphertextWithTag.pin() val ciphertextWithTagPinned = ciphertextWithTag.pin()
crypto_secretstream_xchacha20poly1305_push( crypto_secretstream_xchacha20poly1305_push(

View File

@ -69,49 +69,12 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray) {
} }
private val updateableEncryptionPrimitive = XChaCha20Pure(key, nonce, initialCounter = 1U)
private val updateableMacPrimitive : Poly1305
private val polyBuffer = UByteArray(16) private val polyBuffer = UByteArray(16)
private var polyBufferByteCounter = 0 private var polyBufferByteCounter = 0
private var processedBytes = 0 private var processedBytes = 0
init {
val subKey = XChaCha20Pure.hChacha(key, nonce)
val authKey =
ChaCha20Pure.xorWithKeystream(
subKey.toLittleEndianUByteArray(),
ubyteArrayOf(0U, 0U, 0U, 0U) + nonce.sliceArray(16 until 24),
UByteArray(64) { 0U },
0U // If this is moved as a default parameter in encrypt, and not here (in 1.4-M2)
// js compiler dies with: e: java.lang.NullPointerException
// 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)
)
updateableMacPrimitive = Poly1305(authKey)
// val additionalDataPad = UByteArray(16 - additionalData.size % 16) { 0U }
// 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 calcKey : UByteArray = UByteArray(32)
internal val calcNonce : UByteArray = UByteArray(12) internal val calcNonce : UByteArray = UByteArray(12)
@ -131,117 +94,39 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray) {
println("Calcnonce---------") println("Calcnonce---------")
} }
fun encryptPartial(data: UByteArray, additionalData: UByteArray = ubyteArrayOf(), tag : UByte = 0U) : UByteArray { fun streamEncrypt(data: UByteArray, additionalData: UByteArray = ubyteArrayOf(), tag : UByte = 0U) : UByteArray {
val result = UByteArray(1 + data.size + 16) //Tag marker, ciphertext, mac val result = UByteArray(1 + data.size + 16) //Tag marker, ciphertext, mac
//get encryption state //get encryption state
val block = UByteArray(64) { 0U } val block = UByteArray(64) { 0U }
println("--block")
block.hexColumsPrint()
println("--block")
println("--key")
calcKey.hexColumsPrint()
println("--key")
println("--nonce")
calcNonce.hexColumsPrint()
println("--nonce")
ChaCha20Pure.xorWithKeystream(calcKey, calcNonce, block, 0U).copyInto(block) // This is equivalent to the first 64 bytes of keystream ChaCha20Pure.xorWithKeystream(calcKey, calcNonce, block, 0U).copyInto(block) // This is equivalent to the first 64 bytes of keystream
/*
5C 9A C3 7B CA 8F 2B 12 9A D8 41 3B 0C E9 55 EF
25 27 9A 4B 5B 7F 87 75 0C 47 E9 C9 DE 82 44 BA
6C 51 48 F4 9C 0A 24 6B F2 7C 51 5E 62 1A 16 E1
28 23 C6 B5 12 2E AD 58 AD 51 AA 34 78 33 08 C9
*/
println("--block")
block.hexColumsPrint()
println("--block")
val poly1305 = Poly1305(block) val poly1305 = Poly1305(block)
block.overwriteWithZeroes() block.overwriteWithZeroes()
if (additionalData.isNotEmpty()) { if (additionalData.isNotEmpty()) {
val additionalDataPadded = additionalData + UByteArray(16 - additionalData.size % 16) { 0U } val additionalDataPadded = additionalData + UByteArray(16 - additionalData.size % 16) { 0U }
processPolyBytes(poly1305, additionalDataPadded) processPolyBytes(poly1305, additionalDataPadded)
} }
block[0] = tag block[0] = tag
ChaCha20Pure.xorWithKeystream(calcKey, calcNonce, block, 1U).copyInto(block) // This just xors block[0] with keystream ChaCha20Pure.xorWithKeystream(calcKey, calcNonce, block, 1U).copyInto(block) // This just xors block[0] with keystream
processPolyBytes(poly1305, block) // but updates the mac with the full block! processPolyBytes(poly1305, block) // but updates the mac with the full block!
// println("--poly 1")
// 13 10 8E D1 3C B9 77 C1 9B 95 66 C8 1B 8A 5D D3
// poly1305.finalizeMac().hexColumsPrint()
// println("--poly 1")
// In libsodium c code, it now sets the first byte to be a tag, we'll just save it for now // 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] val encryptedTag = block[0]
//And then encrypt the rest of the message //And then encrypt the rest of the message
val ciphertext = ChaCha20Pure.xorWithKeystream(calcKey, calcNonce, data, 2U) // With appropriate counter val ciphertext = ChaCha20Pure.xorWithKeystream(calcKey, calcNonce, data, 2U) // With appropriate counter
println("ciphertext-----") // Next we update the poly1305 with ciphertext and padding, BUT the padding in libsodium is not correctly calculated, so it doesn't
/* // pad correctly. https://github.com/jedisct1/libsodium/issues/976
paddedCipherText-- // We want to use libsodium in delegated flavour, so we will use the same incorrect padding here.
D3 2D 59 B8 C4 66 2E 47 29 C6 F9 93 4B 09 27 24 // From security standpoint there are no obvious drawbacks, as padding was initially added to decrease implementation complexity.
DD F3 05 48 94 67 10 00 21 85 22 96 3C CE 8E B7
53 9D 46 F5 3C 5E 48 9B 8C 13 B7 28 6B B3 6C 3A
04 B7 25 B9 50 45 08 0B 89 A2 0F 70 CC 60 1B C3
17 35 9F AE 82 51 43 1B 9D 53 9E E2 AF 20 1F FD
03 59 11 51 9E AC 83 CD 78 D1 D0 E5 D7 0E 41 DE
FB 5C 7F 1C 00 00 00 00 00 00 00 00 00 00 00 00
paddedCipherText--
*/
(ciphertext + UByteArray(((16 - 64 + data.size) % 16)) { 0U }).hexColumsPrint()
println("pad: ${16 - ((data.size) % 16)}")
println("pad: ${((16U + data.size.toUInt() - block.size.toUInt()) % 16U).toInt()}")
println("ciphertext-----")
processPolyBytes(poly1305, ciphertext + UByteArray(((16U + data.size.toUInt() - block.size.toUInt()) % 16U).toInt()) { 0U } ) //TODO this is inefficient as it creates a new array and copies data processPolyBytes(poly1305, ciphertext + UByteArray(((16U + data.size.toUInt() - block.size.toUInt()) % 16U).toInt()) { 0U } ) //TODO this is inefficient as it creates a new array and copies data
// println("--poly cipher") // Last 16byte block containing actual additional data nad ciphertext sizes
// 93 D9 13 DC AB 1D 07 D7 51 03 17 85 8A 5C F0 84
// poly1305.finalizeMac().hexColumsPrint()
// println("--poly cipher")
val finalMac = additionalData.size.toULong().toLittleEndianUByteArray() + (ciphertext.size + 64).toULong().toLittleEndianUByteArray() val finalMac = additionalData.size.toULong().toLittleEndianUByteArray() + (ciphertext.size + 64).toULong().toLittleEndianUByteArray()
processPolyBytes(poly1305, finalMac) processPolyBytes(poly1305, finalMac)
val mac = poly1305.finalizeMac(polyBuffer.sliceArray(0 until polyBufferByteCounter)) val mac = poly1305.finalizeMac(polyBuffer.sliceArray(0 until polyBufferByteCounter))
//19 F3 39 CC DE 82 35 08 C1 82 DB 3D F1 EF 89 45
println("poly final --")
mac.hexColumsPrint()
println("poly final --")
return ubyteArrayOf(encryptedTag) + ciphertext + mac return ubyteArrayOf(encryptedTag) + ciphertext + mac
} }
// Sketch end fun streamDecrypt(data: UByteArray, additionalData: UByteArray = ubyteArrayOf(), tag: UBy)
fun encryptPartialData(data: UByteArray) : UByteArray {
processedBytes += data.size
val encrypted = updateableEncryptionPrimitive.xorWithKeystream(data)
// processPolyBytes(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) {
// processPolyBytes(data)
}
fun checkTag(expectedTag: UByteArray) {
val cipherTextPad = UByteArray(16 - processedBytes % 16) { 0U }
val macData = cipherTextPad +
// additionalData.size.toULong().toLittleEndianUByteArray() +
processedBytes.toULong().toLittleEndianUByteArray()
// processPolyBytes(macData)
val tag = updateableMacPrimitive.finalizeMac()
if (!tag.contentEquals(expectedTag)) {
throw InvalidTagException()
}
}
fun decrypt(data: UByteArray) : UByteArray {
processedBytes += data.size
val decrypted = updateableEncryptionPrimitive.xorWithKeystream(data)
// processPolyBytes(decrypted)
return decrypted
}
private fun processPolyBytes(updateableMacPrimitive: Poly1305, data: UByteArray) { private fun processPolyBytes(updateableMacPrimitive: Poly1305, data: UByteArray) {
if (polyBufferByteCounter == 0) { if (polyBufferByteCounter == 0) {
@ -284,16 +169,5 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray) {
} }
} }
fun finishEncryption() : Pair<UByteArray, UByteArray> {
val cipherTextPad = UByteArray(16 - processedBytes % 16) { 0U }
val macData = cipherTextPad +
// additionalData.size.toULong().toLittleEndianUByteArray() +
processedBytes.toULong().toLittleEndianUByteArray()
// processPolyBytes(macData)
val tag = updateableMacPrimitive.finalizeMac()
return Pair(tag, nonce)
}
} }

View File

@ -207,6 +207,6 @@ class XChaCha20Poly1305Test {
xcha.calcNonce.contentEquals(state.sliceArray(32 until 44)) xcha.calcNonce.contentEquals(state.sliceArray(32 until 44))
} }
val data = UByteArray(100) { 0U } val data = UByteArray(100) { 0U }
xcha.encryptPartial(data).hexColumsPrint() xcha.streamEncrypt(data).hexColumsPrint()
} }
} }