Add high level AEAD tests, implement missing initializations, implement pure decryption
This commit is contained in:
parent
579c44fcc7
commit
55b5641f14
@ -62,8 +62,8 @@ interface EncryptionApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface AuthenticatedEncryption {
|
interface AuthenticatedEncryption {
|
||||||
fun encrypt(key: UByteArray, nonce: UByteArray, message: UByteArray, additionalData: UByteArray) : UByteArray
|
fun encrypt(key: UByteArray, nonce: UByteArray, message: UByteArray, additionalData: UByteArray = ubyteArrayOf()) : UByteArray
|
||||||
fun decrypt(key: UByteArray, nonce: UByteArray, cipherText: UByteArray, additionalData: UByteArray) : UByteArray
|
fun decrypt(key: UByteArray, nonce: UByteArray, cipherText: UByteArray, additionalData: UByteArray = ubyteArrayOf()) : UByteArray
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,11 +75,11 @@ 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 MultipartAuthenticatedDecryption {
|
interface MultipartAuthenticatedDecryption {
|
||||||
fun decryptPartialData(data: EncryptedDataPart, additionalData: UByteArray) : DecryptedDataPart
|
fun decryptPartialData(data: EncryptedDataPart, additionalData: UByteArray = ubyteArrayOf()) : DecryptedDataPart
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MultipartAuthenticatedEncryption {
|
interface MultipartAuthenticatedEncryption {
|
||||||
fun encryptPartialData(data: UByteArray, additionalData: UByteArray) : EncryptedDataPart
|
fun encryptPartialData(data: UByteArray, additionalData: UByteArray = ubyteArrayOf()) : EncryptedDataPart
|
||||||
fun startEncryption() : MultipartEncryptionHeader
|
fun startEncryption() : MultipartEncryptionHeader
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -43,3 +43,7 @@ fun String.encodeToUByteArray() : UByteArray{
|
|||||||
return encodeToByteArray().asUByteArray()
|
return encodeToByteArray().asUByteArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun UByteArray.decodeToString() : String {
|
||||||
|
return this.asByteArray().contentToString()
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ package com.ionspin.kotlin.crypto.authenticated
|
|||||||
* on 14-Jun-2020
|
* on 14-Jun-2020
|
||||||
*/
|
*/
|
||||||
expect class XChaCha20Poly1305Delegated internal constructor() {
|
expect class XChaCha20Poly1305Delegated internal constructor() {
|
||||||
internal constructor(key: UByteArray, testState : UByteArray, testHeader: UByteArray)
|
internal constructor(key: UByteArray, testState : UByteArray, testHeader: UByteArray, isDecryptor: Boolean)
|
||||||
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
|
||||||
|
@ -211,8 +211,8 @@ class XChaCha20Poly1305Test {
|
|||||||
0xDEU, 0xFBU, 0x5CU, 0x7FU, 0x1CU, 0x26U, 0x32U, 0x2CU, 0x51U, 0xF6U, 0xEFU, 0xC6U, 0x34U, 0xC4U, 0xACU, 0x6CU,
|
0xDEU, 0xFBU, 0x5CU, 0x7FU, 0x1CU, 0x26U, 0x32U, 0x2CU, 0x51U, 0xF6U, 0xEFU, 0xC6U, 0x34U, 0xC4U, 0xACU, 0x6CU,
|
||||||
0xE8U, 0xF9U, 0x4BU, 0xABU, 0xA3U,
|
0xE8U, 0xF9U, 0x4BU, 0xABU, 0xA3U,
|
||||||
)
|
)
|
||||||
val encryptor = XChaCha20Poly1305Delegated(key, state, header)
|
val encryptor = XChaCha20Poly1305Delegated(key, state, header, false)
|
||||||
val decryptor = XChaCha20Poly1305Delegated(key, state, header)
|
val decryptor = XChaCha20Poly1305Delegated(key, state, header, true)
|
||||||
val data = UByteArray(100) { 0U }
|
val data = UByteArray(100) { 0U }
|
||||||
val result = encryptor.encrypt(data)
|
val result = encryptor.encrypt(data)
|
||||||
val decrypted = decryptor.decrypt(result)
|
val decrypted = decryptor.decrypt(result)
|
||||||
@ -228,7 +228,7 @@ class XChaCha20Poly1305Test {
|
|||||||
val messedUpTag = result.copyOf()
|
val messedUpTag = result.copyOf()
|
||||||
messedUpTag[messedUpTag.size - 2] = 0U
|
messedUpTag[messedUpTag.size - 2] = 0U
|
||||||
assertFails {
|
assertFails {
|
||||||
val decryptorForWrongTag = XChaCha20Poly1305Delegated(key, state, header)
|
val decryptorForWrongTag = XChaCha20Poly1305Delegated(key, state, header, true)
|
||||||
val plaintext = decryptorForWrongTag.decrypt(messedUpTag)
|
val plaintext = decryptorForWrongTag.decrypt(messedUpTag)
|
||||||
println("Decrypted with wrong tag -----------")
|
println("Decrypted with wrong tag -----------")
|
||||||
plaintext.hexColumsPrint()
|
plaintext.hexColumsPrint()
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
package com.ionspin.kotlin.crypto.highlevel
|
||||||
|
|
||||||
|
import com.ionspin.kotlin.crypto.Crypto
|
||||||
|
import com.ionspin.kotlin.crypto.Initializer
|
||||||
|
import com.ionspin.kotlin.crypto.SymmetricKey
|
||||||
|
import com.ionspin.kotlin.crypto.hash.decodeToString
|
||||||
|
import com.ionspin.kotlin.crypto.hash.encodeToUByteArray
|
||||||
|
import com.ionspin.kotlin.crypto.util.hexColumsPrint
|
||||||
|
import com.ionspin.kotlin.crypto.util.testBlocking
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Ugljesa Jovanovic
|
||||||
|
* ugljesa.jovanovic@ionspin.com
|
||||||
|
* on 09-Jul-2020
|
||||||
|
*/
|
||||||
|
class EncryptionTest {
|
||||||
|
@Test
|
||||||
|
fun testMultipartEncryption() = testBlocking {
|
||||||
|
Initializer.initialize()
|
||||||
|
val plaintext = ("Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
|
||||||
|
"Vestibulum maximus tincidunt urna. " +
|
||||||
|
"Nullam sit amet erat id arcu porttitor varius ut at metus. " +
|
||||||
|
"Nunc sit amet felis vel velit ornare gravida. " +
|
||||||
|
"Curabitur tellus lacus, pulvinar a diam at tincidunt.").encodeToUByteArray()
|
||||||
|
val additionalData = "Additional data 1".encodeToUByteArray()
|
||||||
|
val keyValue = UByteArray(32) { it.toUByte() }
|
||||||
|
val key = SymmetricKey(keyValue)
|
||||||
|
val encryptor = Crypto.Encryption.createMultipartEncryptor(key)
|
||||||
|
val header = encryptor.startEncryption()
|
||||||
|
val ciphertext1 = encryptor.encryptPartialData(plaintext.sliceArray(0 until 100), additionalData)
|
||||||
|
val ciphertext2 = encryptor.encryptPartialData(plaintext.sliceArray(100 until 200))
|
||||||
|
val ciphertext3 = encryptor.encryptPartialData(plaintext.sliceArray(200 until 250))
|
||||||
|
//decrypt
|
||||||
|
val decryptor = Crypto.Encryption.createMultipartDecryptor(key, header)
|
||||||
|
println("Initialized")
|
||||||
|
val plaintext1 = decryptor.decryptPartialData(ciphertext1, additionalData)
|
||||||
|
val plaintext2 = decryptor.decryptPartialData(ciphertext2)
|
||||||
|
val plaintext3 = decryptor.decryptPartialData(ciphertext3)
|
||||||
|
|
||||||
|
val combinedPlaintext = plaintext1.data + plaintext2.data + plaintext3.data
|
||||||
|
println("---- Plaintext -----")
|
||||||
|
plaintext.hexColumsPrint()
|
||||||
|
println("---- Plaintext result -----")
|
||||||
|
combinedPlaintext.hexColumsPrint()
|
||||||
|
assertTrue {
|
||||||
|
plaintext.contentEquals(combinedPlaintext)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -54,41 +54,65 @@ actual class XChaCha20Poly1305Delegated internal actual constructor() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var state : dynamic = null
|
var state : dynamic = null
|
||||||
|
var isInitialized = false
|
||||||
|
var isEncryptor = false
|
||||||
|
|
||||||
actual fun initializeForEncryption(key: UByteArray) : UByteArray {
|
actual fun initializeForEncryption(key: UByteArray) : UByteArray {
|
||||||
println("Initializaing for encryption")
|
println("Initializaing for encryption")
|
||||||
val stateAndHeader = getSodium().crypto_secretstream_xchacha20poly1305_init_push(key.toUInt8Array())
|
val stateAndHeader = getSodium().crypto_secretstream_xchacha20poly1305_init_push(key.toUInt8Array())
|
||||||
val state = stateAndHeader.state
|
state = stateAndHeader.state
|
||||||
val header = stateAndHeader.header
|
val header = stateAndHeader.header as Uint8Array
|
||||||
console.log(state)
|
console.log(state)
|
||||||
console.log(header)
|
console.log(header)
|
||||||
println("Done initializaing for encryption")
|
println("Done initializaing for encryption")
|
||||||
return header
|
isInitialized = true
|
||||||
|
isEncryptor = true
|
||||||
|
return header.toUByteArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun initializeForDecryption(key: UByteArray, header: UByteArray) {
|
actual fun initializeForDecryption(key: UByteArray, header: UByteArray) {
|
||||||
|
println("Initializing for decryption")
|
||||||
|
header.hexColumsPrint()
|
||||||
|
state = getSodium().crypto_secretstream_xchacha20poly1305_init_pull(header.toUInt8Array(), key.toUInt8Array())
|
||||||
|
console.log(state)
|
||||||
|
isInitialized = true
|
||||||
|
isEncryptor = false
|
||||||
}
|
}
|
||||||
|
|
||||||
internal actual constructor(
|
internal actual constructor(
|
||||||
key: UByteArray,
|
key: UByteArray,
|
||||||
testState: UByteArray,
|
testState: UByteArray,
|
||||||
testHeader: UByteArray
|
testHeader: UByteArray,
|
||||||
|
isDecryptor: Boolean
|
||||||
) : this() {
|
) : this() {
|
||||||
state = getSodium().crypto_secretstream_xchacha20poly1305_init_pull(testHeader.toUInt8Array(), key.toUInt8Array())
|
state = getSodium().crypto_secretstream_xchacha20poly1305_init_pull(testHeader.toUInt8Array(), key.toUInt8Array())
|
||||||
console.log(state)
|
console.log(state)
|
||||||
println("Done initializaing test state")
|
println("Done initializaing test state")
|
||||||
|
isInitialized = true
|
||||||
|
isEncryptor = !isDecryptor
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun encrypt(data: UByteArray, additionalData: UByteArray): UByteArray {
|
actual fun encrypt(data: UByteArray, additionalData: UByteArray): UByteArray {
|
||||||
|
if (!isInitialized) {
|
||||||
|
throw RuntimeException("Not initalized!")
|
||||||
|
}
|
||||||
|
if (!isEncryptor) {
|
||||||
|
throw RuntimeException("Initialized as decryptor, attempted to use as encryptor")
|
||||||
|
}
|
||||||
val encrypted = getSodium().crypto_secretstream_xchacha20poly1305_push(state, data.toUInt8Array(), additionalData.toUInt8Array(), 0U)
|
val encrypted = getSodium().crypto_secretstream_xchacha20poly1305_push(state, data.toUInt8Array(), additionalData.toUInt8Array(), 0U)
|
||||||
return encrypted.toUByteArray()
|
return encrypted.toUByteArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun decrypt(data: UByteArray, additionalData: UByteArray): UByteArray {
|
actual fun decrypt(data: UByteArray, additionalData: UByteArray): UByteArray {
|
||||||
|
if (!isInitialized) {
|
||||||
|
throw RuntimeException("Not initalized!")
|
||||||
|
}
|
||||||
|
if (isEncryptor) {
|
||||||
|
throw RuntimeException("Initialized as encryptor, attempted to use as decryptor")
|
||||||
|
}
|
||||||
val decryptedWithTag = getSodium().crypto_secretstream_xchacha20poly1305_pull(state, data.toUInt8Array(), additionalData.toUInt8Array())
|
val decryptedWithTag = getSodium().crypto_secretstream_xchacha20poly1305_pull(state, data.toUInt8Array(), additionalData.toUInt8Array())
|
||||||
val decrypted = decryptedWithTag.message as Uint8Array
|
val decrypted = decryptedWithTag.message as Uint8Array
|
||||||
val validTag = decryptedWithTag.tag as UInt
|
val validTag = decryptedWithTag.tag
|
||||||
|
|
||||||
if (validTag != 0U) {
|
if (validTag != 0U) {
|
||||||
println("Tag validation failed")
|
println("Tag validation failed")
|
||||||
|
@ -60,23 +60,42 @@ actual class XChaCha20Poly1305Delegated internal actual constructor() {
|
|||||||
val state : SecretStream.State = SecretStream.State()
|
val state : SecretStream.State = SecretStream.State()
|
||||||
val sodium = SodiumJava()
|
val sodium = SodiumJava()
|
||||||
|
|
||||||
|
var isInitialized = false
|
||||||
|
var isEncryptor = false
|
||||||
|
|
||||||
internal actual constructor(
|
internal actual constructor(
|
||||||
key: UByteArray,
|
key: UByteArray,
|
||||||
testState: UByteArray,
|
testState: UByteArray,
|
||||||
testHeader: UByteArray
|
testHeader: UByteArray,
|
||||||
|
isDecryptor: Boolean
|
||||||
) : this() {
|
) : this() {
|
||||||
state.k = testState.sliceArray(0 until 32).toByteArray()
|
state.k = testState.sliceArray(0 until 32).toByteArray()
|
||||||
state.nonce = testState.sliceArray(32 until 44).toByteArray()
|
state.nonce = testState.sliceArray(32 until 44).toByteArray()
|
||||||
|
isInitialized = true
|
||||||
|
isEncryptor = !isDecryptor
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun initializeForEncryption(key: UByteArray) : UByteArray {
|
actual fun initializeForEncryption(key: UByteArray) : UByteArray {
|
||||||
TODO()
|
val header = UByteArray(24)
|
||||||
|
sodium.crypto_secretstream_xchacha20poly1305_init_push(state, header.asByteArray(), key.asByteArray())
|
||||||
|
isInitialized = true
|
||||||
|
isEncryptor = true
|
||||||
|
return header
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun initializeForDecryption(key: UByteArray, header: UByteArray) {
|
actual fun initializeForDecryption(key: UByteArray, header: UByteArray) {
|
||||||
|
sodium.crypto_secretstream_xchacha20poly1305_init_pull(state, header.asByteArray(), key.asByteArray())
|
||||||
|
isInitialized = true
|
||||||
|
isEncryptor = false
|
||||||
}
|
}
|
||||||
|
|
||||||
actual fun encrypt(data: UByteArray, additionalData: UByteArray): UByteArray {
|
actual fun encrypt(data: UByteArray, additionalData: UByteArray): UByteArray {
|
||||||
|
if (!isInitialized) {
|
||||||
|
throw RuntimeException("Not initalized!")
|
||||||
|
}
|
||||||
|
if (!isEncryptor) {
|
||||||
|
throw RuntimeException("Initialized as decryptor, attempted to use as encryptor")
|
||||||
|
}
|
||||||
val ciphertext = ByteArray(1 + data.size + 16)
|
val ciphertext = ByteArray(1 + data.size + 16)
|
||||||
sodium.crypto_secretstream_xchacha20poly1305_push(
|
sodium.crypto_secretstream_xchacha20poly1305_push(
|
||||||
state, ciphertext, null,
|
state, ciphertext, null,
|
||||||
@ -88,6 +107,12 @@ actual class XChaCha20Poly1305Delegated internal actual constructor() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
actual fun decrypt(data: UByteArray, additionalData: UByteArray): UByteArray {
|
actual fun decrypt(data: UByteArray, additionalData: UByteArray): UByteArray {
|
||||||
|
if (!isInitialized) {
|
||||||
|
throw RuntimeException("Not initalized!")
|
||||||
|
}
|
||||||
|
if (isEncryptor) {
|
||||||
|
throw RuntimeException("Initialized as encryptor, attempted to use as decryptor")
|
||||||
|
}
|
||||||
val plaintext = ByteArray(data.size - 17)
|
val plaintext = ByteArray(data.size - 17)
|
||||||
|
|
||||||
val validTag = sodium.crypto_secretstream_xchacha20poly1305_pull(
|
val validTag = sodium.crypto_secretstream_xchacha20poly1305_pull(
|
||||||
|
@ -69,10 +69,14 @@ actual class XChaCha20Poly1305Delegated internal actual constructor() {
|
|||||||
|
|
||||||
val header = UByteArray(crypto_secretstream_xchacha20poly1305_HEADERBYTES.toInt()) { 0U }
|
val header = UByteArray(crypto_secretstream_xchacha20poly1305_HEADERBYTES.toInt()) { 0U }
|
||||||
|
|
||||||
|
var isInitialized = false
|
||||||
|
var isEncryptor = false
|
||||||
|
|
||||||
actual internal constructor(
|
actual internal constructor(
|
||||||
key: UByteArray,
|
key: UByteArray,
|
||||||
testState: UByteArray,
|
testState: UByteArray,
|
||||||
testHeader: UByteArray
|
testHeader: UByteArray,
|
||||||
|
isDecryptor: Boolean
|
||||||
) : this() {
|
) : this() {
|
||||||
val pointer = state.ptr.reinterpret<UByteVar>()
|
val pointer = state.ptr.reinterpret<UByteVar>()
|
||||||
for (i in 0 until crypto_secretstream_xchacha20poly1305_state.size.toInt()) {
|
for (i in 0 until crypto_secretstream_xchacha20poly1305_state.size.toInt()) {
|
||||||
@ -85,6 +89,8 @@ actual class XChaCha20Poly1305Delegated internal actual constructor() {
|
|||||||
testHeader.copyInto(header)
|
testHeader.copyInto(header)
|
||||||
header.hexColumsPrint()
|
header.hexColumsPrint()
|
||||||
println("header after setting-----------")
|
println("header after setting-----------")
|
||||||
|
isInitialized = true
|
||||||
|
isEncryptor = !isDecryptor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -103,7 +109,7 @@ actual class XChaCha20Poly1305Delegated internal actual constructor() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
actual fun initializeForDecryption(key: UByteArray, header: UByteArray) {
|
actual fun initializeForDecryption(key: UByteArray, header: UByteArray) {
|
||||||
|
crypto_secretstream_xchacha20poly1305_init_pull(state.ptr, header.toCValues(), key.toCValues())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -116,8 +122,8 @@ actual class XChaCha20Poly1305Delegated internal actual constructor() {
|
|||||||
null,
|
null,
|
||||||
data.toCValues(),
|
data.toCValues(),
|
||||||
data.size.convert(),
|
data.size.convert(),
|
||||||
null,
|
additionalData.toCValues(),
|
||||||
0U,
|
additionalData.size.convert(),
|
||||||
0U,
|
0U,
|
||||||
)
|
)
|
||||||
println("Encrypt partial")
|
println("Encrypt partial")
|
||||||
@ -141,6 +147,7 @@ actual class XChaCha20Poly1305Delegated internal actual constructor() {
|
|||||||
additionalData.size.convert()
|
additionalData.size.convert()
|
||||||
)
|
)
|
||||||
plaintextPinned.unpin()
|
plaintextPinned.unpin()
|
||||||
|
println("tag: $validTag")
|
||||||
if (validTag != 0) {
|
if (validTag != 0) {
|
||||||
println("Tag validation failed")
|
println("Tag validation failed")
|
||||||
throw InvalidTagException()
|
throw InvalidTagException()
|
||||||
|
@ -95,7 +95,6 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun streamEncrypt(data: UByteArray, additionalData: UByteArray, tag : UByte) : UByteArray {
|
fun streamEncrypt(data: UByteArray, additionalData: UByteArray, tag : UByte) : UByteArray {
|
||||||
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 }
|
||||||
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
|
||||||
@ -107,6 +106,9 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray) {
|
|||||||
}
|
}
|
||||||
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
|
||||||
|
println("encrypt block going into poly ----")
|
||||||
|
block.hexColumsPrint()
|
||||||
|
println("encrypt block going into poly end ----")
|
||||||
processPolyBytes(poly1305, block) // but updates the mac with the full block!
|
processPolyBytes(poly1305, 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
|
// 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]
|
||||||
@ -121,11 +123,57 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray) {
|
|||||||
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))
|
||||||
|
//TODO process state
|
||||||
|
println("Ciphertext ---------")
|
||||||
|
(ubyteArrayOf(encryptedTag) + ciphertext + mac).hexColumsPrint()
|
||||||
|
println("Ciphertext end ---------")
|
||||||
return ubyteArrayOf(encryptedTag) + ciphertext + mac
|
return ubyteArrayOf(encryptedTag) + ciphertext + mac
|
||||||
}
|
}
|
||||||
|
|
||||||
fun streamDecrypt(data: UByteArray, additionalData: UByteArray, tag: UByte) : UByteArray {
|
fun streamDecrypt(data: UByteArray, additionalData: UByteArray, tag: UByte) : UByteArray {
|
||||||
TODO()
|
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()
|
||||||
|
if (additionalData.isNotEmpty()) {
|
||||||
|
val additionalDataPadded = additionalData + UByteArray(16 - additionalData.size % 16) { 0U }
|
||||||
|
processPolyBytes(poly1305, additionalDataPadded)
|
||||||
|
}
|
||||||
|
block[0] = data[0]
|
||||||
|
ChaCha20Pure.xorWithKeystream(calcKey, calcNonce, block, 1U).copyInto(block)// get the keystream xored with zeroes, but also decrypteg tag marker
|
||||||
|
val tag = block[0] //get the decrypted tag
|
||||||
|
block[0] = data[0] // this brings it back to state that is delivered to poly in encryption function
|
||||||
|
println("Decrypted tag $tag")
|
||||||
|
println("decrypt block going into poly ----")
|
||||||
|
block.hexColumsPrint()
|
||||||
|
println("decrypt block going into poly end ----")
|
||||||
|
processPolyBytes(poly1305, block)
|
||||||
|
// 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
|
||||||
|
// We want to use libsodium in delegated flavour, so we will use the same incorrect padding here.
|
||||||
|
// From security standpoint there are no obvious drawbacks, as padding was initially added to decrease implementation complexity.
|
||||||
|
val ciphertext = data.sliceArray(1 until data.size - 16)
|
||||||
|
processPolyBytes(poly1305, ciphertext + UByteArray(((16U + ciphertext.size.toUInt() - block.size.toUInt()) % 16U).toInt()) { 0U } )
|
||||||
|
val plaintext = ChaCha20Pure.xorWithKeystream(calcKey, calcNonce, ciphertext, 2U)
|
||||||
|
val finalMac = additionalData.size.toULong().toLittleEndianUByteArray() + (ciphertext.size + 64).toULong().toLittleEndianUByteArray()
|
||||||
|
processPolyBytes(poly1305, finalMac)
|
||||||
|
val mac = poly1305.finalizeMac(polyBuffer.sliceArray(0 until polyBufferByteCounter))
|
||||||
|
println("--- mac")
|
||||||
|
mac.hexColumsPrint()
|
||||||
|
println("--- mac end")
|
||||||
|
val expectedMac = data.sliceArray(data.size - 16 until data.size)
|
||||||
|
println("--- expectedMac")
|
||||||
|
expectedMac.hexColumsPrint()
|
||||||
|
println("--- expectedMac end")
|
||||||
|
|
||||||
|
//TODO process state
|
||||||
|
println("Plaintext ---------")
|
||||||
|
plaintext.hexColumsPrint()
|
||||||
|
println("Plaintext end ---------")
|
||||||
|
if (expectedMac.contentEquals(mac).not()){
|
||||||
|
throw InvalidTagException()
|
||||||
|
}
|
||||||
|
return plaintext
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
package com.ionspin.kotlin.crypto.highlevel
|
||||||
|
|
||||||
|
import com.ionspin.kotlin.crypto.Crypto
|
||||||
|
import com.ionspin.kotlin.crypto.SymmetricKey
|
||||||
|
import com.ionspin.kotlin.crypto.hash.decodeToString
|
||||||
|
import com.ionspin.kotlin.crypto.hash.encodeToUByteArray
|
||||||
|
import com.ionspin.kotlin.crypto.util.hexColumsPrint
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Ugljesa Jovanovic
|
||||||
|
* ugljesa.jovanovic@ionspin.com
|
||||||
|
* on 09-Jul-2020
|
||||||
|
*/
|
||||||
|
class EncryptionTest {
|
||||||
|
@Test
|
||||||
|
fun testMultipartEncryption() {
|
||||||
|
val plaintext = ("Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
|
||||||
|
"Vestibulum maximus tincidunt urna. " +
|
||||||
|
"Nullam sit amet erat id arcu porttitor varius ut at metus. " +
|
||||||
|
"Nunc sit amet felis vel velit ornare gravida. " +
|
||||||
|
"Curabitur tellus lacus, pulvinar a diam at tincidunt.").encodeToUByteArray()
|
||||||
|
plaintext.hexColumsPrint()
|
||||||
|
val additionalData = "Additional data 1".encodeToUByteArray()
|
||||||
|
// val additionalData = ubyteArrayOf()
|
||||||
|
val keyValue = UByteArray(32) { it.toUByte() }
|
||||||
|
val key = SymmetricKey(keyValue)
|
||||||
|
val encryptor = Crypto.Encryption.createMultipartEncryptor(key)
|
||||||
|
val header = encryptor.startEncryption()
|
||||||
|
val ciphertext1 = encryptor.encryptPartialData(plaintext.sliceArray(0 until 100), additionalData)
|
||||||
|
val ciphertext2 = encryptor.encryptPartialData(plaintext.sliceArray(100 until 200))
|
||||||
|
val ciphertext3 = encryptor.encryptPartialData(plaintext.sliceArray(200 until 250))
|
||||||
|
//decrypt
|
||||||
|
val decryptor = Crypto.Encryption.createMultipartDecryptor(key, header)
|
||||||
|
println("Initialized")
|
||||||
|
val plaintext1 = decryptor.decryptPartialData(ciphertext1, additionalData)
|
||||||
|
val plaintext2 = decryptor.decryptPartialData(ciphertext2)
|
||||||
|
val plaintext3 = decryptor.decryptPartialData(ciphertext3)
|
||||||
|
|
||||||
|
val combinedPlaintext = plaintext1.data + plaintext2.data + plaintext3.data
|
||||||
|
println("---- Plaintext -----")
|
||||||
|
plaintext.hexColumsPrint()
|
||||||
|
println("---- Plaintext result -----")
|
||||||
|
combinedPlaintext.hexColumsPrint()
|
||||||
|
assertTrue {
|
||||||
|
plaintext.contentEquals(combinedPlaintext)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user