Multipart API continuation
This commit is contained in:
parent
f107db3312
commit
233ee1bf55
@ -0,0 +1,36 @@
|
|||||||
|
package com.ionspin.kotlin.crypto.authenticated
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Ugljesa Jovanovic
|
||||||
|
* ugljesa.jovanovic@ionspin.com
|
||||||
|
* on 22-Jun-2020
|
||||||
|
*/
|
||||||
|
interface AuthenticatedEncryption {
|
||||||
|
fun encrypt(key: UByteArray, nonce: UByteArray, message: UByteArray, additionalData: UByteArray) : UByteArray
|
||||||
|
fun decrypt(key: UByteArray, nonce: UByteArray, cipherText: UByteArray, additionalData: UByteArray) : UByteArray
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
data class EncryptedDataPart(val data : UByteArray)
|
||||||
|
data class DecryptedDataPart(val data : UByteArray)
|
||||||
|
|
||||||
|
data class MultipartEncryptedDataDescriptor(val data: UByteArray, val nonce: UByteArray)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
interface MultipartAuthenticatedVerification {
|
||||||
|
fun verifyPartialData(data: EncryptedDataPart)
|
||||||
|
fun finalizeVerificationAndPrepareDecryptor() : MultipartAuthenticatedDecryption
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MultipartAuthenticatedDecryption {
|
||||||
|
fun decryptPartialData(data: EncryptedDataPart) : DecryptedDataPart
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MultipartAuthenticatedEncryption {
|
||||||
|
fun encryptPartialData(data: UByteArray) : EncryptedDataPart
|
||||||
|
fun finish() : MultipartEncryptedDataDescriptor
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -22,12 +22,12 @@ package com.ionspin.kotlin.crypto.hash
|
|||||||
* ugljesa.jovanovic@ionspin.com
|
* ugljesa.jovanovic@ionspin.com
|
||||||
* on 20-Jul-2019
|
* on 20-Jul-2019
|
||||||
*/
|
*/
|
||||||
interface Hash {
|
interface HashFunction {
|
||||||
val MAX_HASH_BYTES : Int
|
val MAX_HASH_BYTES : Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
interface UpdatableHash : Hash {
|
interface MultiPartHash : HashFunction {
|
||||||
fun update(data : UByteArray)
|
fun update(data : UByteArray)
|
||||||
|
|
||||||
fun digest() : UByteArray
|
fun digest() : UByteArray
|
||||||
@ -35,7 +35,7 @@ interface UpdatableHash : Hash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
interface StatelessHash : Hash {
|
interface Hash : HashFunction {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
package com.ionspin.kotlin.crypto.hash.blake2b
|
package com.ionspin.kotlin.crypto.hash.blake2b
|
||||||
|
|
||||||
import com.ionspin.kotlin.crypto.hash.StatelessHash
|
import com.ionspin.kotlin.crypto.hash.Hash
|
||||||
import com.ionspin.kotlin.crypto.hash.UpdatableHash
|
import com.ionspin.kotlin.crypto.hash.MultiPartHash
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Ugljesa Jovanovic
|
* Created by Ugljesa Jovanovic
|
||||||
@ -13,12 +13,12 @@ object Blake2bProperties {
|
|||||||
const val MAX_HASH_BYTES = 64
|
const val MAX_HASH_BYTES = 64
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Blake2b : UpdatableHash {
|
interface Blake2b : MultiPartHash {
|
||||||
override val MAX_HASH_BYTES: Int
|
override val MAX_HASH_BYTES: Int
|
||||||
get() = Blake2bProperties.MAX_HASH_BYTES
|
get() = Blake2bProperties.MAX_HASH_BYTES
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Blake2bStateless : StatelessHash {
|
interface Blake2bStateless : Hash {
|
||||||
override val MAX_HASH_BYTES: Int
|
override val MAX_HASH_BYTES: Int
|
||||||
get() = Blake2bProperties.MAX_HASH_BYTES
|
get() = Blake2bProperties.MAX_HASH_BYTES
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package com.ionspin.kotlin.crypto.hash.sha
|
package com.ionspin.kotlin.crypto.hash.sha
|
||||||
|
|
||||||
import com.ionspin.kotlin.crypto.hash.StatelessHash
|
import com.ionspin.kotlin.crypto.hash.Hash
|
||||||
import com.ionspin.kotlin.crypto.hash.UpdatableHash
|
import com.ionspin.kotlin.crypto.hash.MultiPartHash
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Ugljesa Jovanovic
|
* Created by Ugljesa Jovanovic
|
||||||
@ -12,11 +12,11 @@ object Sha256Properties {
|
|||||||
const val MAX_HASH_BYTES = 32
|
const val MAX_HASH_BYTES = 32
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Sha256 : UpdatableHash {
|
interface Sha256 : MultiPartHash {
|
||||||
override val MAX_HASH_BYTES: Int
|
override val MAX_HASH_BYTES: Int
|
||||||
get() = Sha256Properties.MAX_HASH_BYTES
|
get() = Sha256Properties.MAX_HASH_BYTES
|
||||||
}
|
}
|
||||||
interface StatelessSha256 : StatelessHash {
|
interface StatelessSha256 : Hash {
|
||||||
override val MAX_HASH_BYTES: Int
|
override val MAX_HASH_BYTES: Int
|
||||||
get() = Sha256Properties.MAX_HASH_BYTES
|
get() = Sha256Properties.MAX_HASH_BYTES
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package com.ionspin.kotlin.crypto.hash.sha
|
package com.ionspin.kotlin.crypto.hash.sha
|
||||||
|
|
||||||
import com.ionspin.kotlin.crypto.hash.StatelessHash
|
import com.ionspin.kotlin.crypto.hash.Hash
|
||||||
import com.ionspin.kotlin.crypto.hash.UpdatableHash
|
import com.ionspin.kotlin.crypto.hash.MultiPartHash
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Ugljesa Jovanovic
|
* Created by Ugljesa Jovanovic
|
||||||
@ -11,11 +11,11 @@ import com.ionspin.kotlin.crypto.hash.UpdatableHash
|
|||||||
object Sha512Properties {
|
object Sha512Properties {
|
||||||
const val MAX_HASH_BYTES = 64
|
const val MAX_HASH_BYTES = 64
|
||||||
}
|
}
|
||||||
interface Sha512 : UpdatableHash {
|
interface Sha512 : MultiPartHash {
|
||||||
override val MAX_HASH_BYTES: Int
|
override val MAX_HASH_BYTES: Int
|
||||||
get() = Sha256Properties.MAX_HASH_BYTES
|
get() = Sha256Properties.MAX_HASH_BYTES
|
||||||
}
|
}
|
||||||
interface StatelessSha512 : StatelessHash {
|
interface StatelessSha512 : Hash {
|
||||||
override val MAX_HASH_BYTES: Int
|
override val MAX_HASH_BYTES: Int
|
||||||
get() = Sha512Properties.MAX_HASH_BYTES
|
get() = Sha512Properties.MAX_HASH_BYTES
|
||||||
|
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.ionspin.kotlin.crypto.util
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Ugljesa Jovanovic
|
||||||
|
* ugljesa.jovanovic@ionspin.com
|
||||||
|
* on 22-Jun-2020
|
||||||
|
*/
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.ionspin.kotlin.crypto.util
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Ugljesa Jovanovic
|
||||||
|
* ugljesa.jovanovic@ionspin.com
|
||||||
|
* on 22-Jun-2020
|
||||||
|
*/
|
||||||
|
fun UByteArray.overwriteWithZeroes() {
|
||||||
|
for (i in 0 until size) {
|
||||||
|
this[i] = 0U
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun UIntArray.overwriteWithZeroes() {
|
||||||
|
for (i in 0 until size) {
|
||||||
|
this[i] = 0U
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,56 @@
|
|||||||
|
package com.ionspin.kotlin.crypto.util
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Ugljesa Jovanovic
|
||||||
|
* ugljesa.jovanovic@ionspin.com
|
||||||
|
* on 22-Jun-2020
|
||||||
|
*/
|
||||||
|
fun Array<Byte>.hexColumsPrint() {
|
||||||
|
val printout = this.map { it.toString(16) }.chunked(16)
|
||||||
|
printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Array<UByte>.hexColumsPrint(chunk : Int = 16) {
|
||||||
|
val printout = this.map { it.toString(16).padStart(2, '0') }.chunked(chunk)
|
||||||
|
printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun UByteArray.hexColumsPrint(chunk : Int = 16) {
|
||||||
|
val printout = this.map { it.toString(16).padStart(2, '0') }.chunked(chunk)
|
||||||
|
printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Array<ULong>.hexColumsPrint(chunk: Int = 3) {
|
||||||
|
val printout = this.map { it.toString(16) }.chunked(chunk)
|
||||||
|
printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.hexStringToTypedUByteArray() : Array<UByte> {
|
||||||
|
return this.chunked(2).map { it.toUByte(16) }.toTypedArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun String.hexStringToUByteArray() : UByteArray {
|
||||||
|
return this.chunked(2).map { it.toUByte(16) }.toUByteArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Array<UByte>.toHexString() : String {
|
||||||
|
return this.joinToString(separator = "") {
|
||||||
|
if (it <= 0x0FU) {
|
||||||
|
"0${it.toString(16)}"
|
||||||
|
} else {
|
||||||
|
it.toString(16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun UByteArray.toHexString() : String {
|
||||||
|
return this.joinToString(separator = "") {
|
||||||
|
if (it <= 0x0FU) {
|
||||||
|
"0${it.toString(16)}"
|
||||||
|
} else {
|
||||||
|
it.toString(16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -23,25 +23,7 @@ package com.ionspin.kotlin.crypto.util
|
|||||||
* ugljesa.jovanovic@ionspin.com
|
* ugljesa.jovanovic@ionspin.com
|
||||||
* on 15-Jul-2019
|
* on 15-Jul-2019
|
||||||
*/
|
*/
|
||||||
fun Array<Byte>.hexColumsPrint() {
|
|
||||||
val printout = this.map { it.toString(16) }.chunked(16)
|
|
||||||
printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Array<UByte>.hexColumsPrint(chunk : Int = 16) {
|
|
||||||
val printout = this.map { it.toString(16).padStart(2, '0') }.chunked(chunk)
|
|
||||||
printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun UByteArray.hexColumsPrint(chunk : Int = 16) {
|
|
||||||
val printout = this.map { it.toString(16).padStart(2, '0') }.chunked(chunk)
|
|
||||||
printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Array<ULong>.hexColumsPrint(chunk: Int = 3) {
|
|
||||||
val printout = this.map { it.toString(16) }.chunked(chunk)
|
|
||||||
printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) }
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun <reified T> Array<T>.chunked(sliceSize: Int): Array<Array<T>> {
|
inline fun <reified T> Array<T>.chunked(sliceSize: Int): Array<Array<T>> {
|
||||||
val last = this.size % sliceSize
|
val last = this.size % sliceSize
|
||||||
@ -89,36 +71,8 @@ infix fun UByteArray.xor(other : UByteArray) : UByteArray {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun String.hexStringToTypedUByteArray() : Array<UByte> {
|
|
||||||
return this.chunked(2).map { it.toUByte(16) }.toTypedArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun String.hexStringToUByteArray() : UByteArray {
|
|
||||||
return this.chunked(2).map { it.toUByte(16) }.toUByteArray()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun Array<UByte>.toHexString() : String {
|
|
||||||
return this.joinToString(separator = "") {
|
|
||||||
if (it <= 0x0FU) {
|
|
||||||
"0${it.toString(16)}"
|
|
||||||
} else {
|
|
||||||
it.toString(16)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fun UByteArray.toHexString() : String {
|
|
||||||
return this.joinToString(separator = "") {
|
|
||||||
if (it <= 0x0FU) {
|
|
||||||
"0${it.toString(16)}"
|
|
||||||
} else {
|
|
||||||
it.toString(16)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// UInt / Array utils
|
// UInt / Array utils
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.ionspin.kotlin.crypto
|
package com.ionspin.kotlin.crypto
|
||||||
|
|
||||||
import com.ionspin.kotlin.crypto.authenticated.XChaCha20Poly1305Pure
|
import com.ionspin.kotlin.crypto.authenticated.*
|
||||||
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
|
||||||
import com.ionspin.kotlin.crypto.hash.encodeToUByteArray
|
import com.ionspin.kotlin.crypto.hash.encodeToUByteArray
|
||||||
@ -115,17 +114,17 @@ data class EncryptedData internal constructor(val ciphertext: UByteArray, val no
|
|||||||
|
|
||||||
object PublicApi {
|
object PublicApi {
|
||||||
|
|
||||||
object Hash {
|
object Hashing {
|
||||||
fun hash(data: UByteArray, key : UByteArray = ubyteArrayOf()) : HashedData {
|
fun hash(data: UByteArray, key : UByteArray = ubyteArrayOf()) : HashedData {
|
||||||
return HashedData(Blake2bPureStateless.digest(data, key))
|
return HashedData(Blake2bPureStateless.digest(data, key))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateableHash(key: UByteArray? = null) : UpdatableHash {
|
fun multipartHash(key: UByteArray? = null) : com.ionspin.kotlin.crypto.hash.MultiPartHash {
|
||||||
return Blake2bPure(key)
|
return Blake2bPure(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
object Symmetric {
|
object Encryption {
|
||||||
fun encrypt(key: SymmetricKey, data : Encryptable<*>, additionalData : UByteArray = ubyteArrayOf()) : EncryptedData {
|
fun authenticatedEncryption(key: SymmetricKey, data : Encryptable<*>, additionalData : UByteArray = ubyteArrayOf()) : EncryptedData {
|
||||||
if (key.value.size != 32) {
|
if (key.value.size != 32) {
|
||||||
throw RuntimeException("Invalid key size! Required 32, supplied ${key.value.size}")
|
throw RuntimeException("Invalid key size! Required 32, supplied ${key.value.size}")
|
||||||
}
|
}
|
||||||
@ -138,5 +137,49 @@ object PublicApi {
|
|||||||
return byteArrayDeserializer(XChaCha20Poly1305Pure.decrypt(key.value, encryptedData.nonce, encryptedData.ciphertext, additionalData))
|
return byteArrayDeserializer(XChaCha20Poly1305Pure.decrypt(key.value, encryptedData.nonce, encryptedData.ciphertext, additionalData))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun multipartAuthenticatedEncrypt(key: SymmetricKey, additionalData: UByteArray) : MultipartAuthenticatedEncryption {
|
||||||
|
return MultipartAuthenticatedEncryptor(key, additionalData)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getMultipartVerificator(key: SymmetricKey, dataDescriptor: MultipartEncryptedDataDescriptor, additionalData: UByteArray) : MultipartAuthenticatedVerification {
|
||||||
|
return MultiplatformAuthenticatedVerificator(key, dataDescriptor, additionalData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MultipartAuthenticatedEncryptor internal constructor(val key : SymmetricKey, additionalData: UByteArray) : MultipartAuthenticatedEncryption {
|
||||||
|
val primitive = XChaCha20Poly1305Pure(key.value, additionalData)
|
||||||
|
override fun encryptPartialData(data: UByteArray): EncryptedDataPart {
|
||||||
|
return EncryptedDataPart(primitive.encryptPartialData(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun finish(): MultipartEncryptedDataDescriptor {
|
||||||
|
val finished = primitive.finish()
|
||||||
|
return MultipartEncryptedDataDescriptor(finished.first, finished.second)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class MultiplatformAuthenticatedVerificator internal constructor(key: SymmetricKey, multipartEncryptedDataDescriptor: MultipartEncryptedDataDescriptor, additionalData: UByteArray) : MultipartAuthenticatedVerification {
|
||||||
|
val primitive = XChaCha20Poly1305Pure(key.value, additionalData)
|
||||||
|
val tag = multipartEncryptedDataDescriptor.data.sliceArray(
|
||||||
|
multipartEncryptedDataDescriptor.data.size - 16 until multipartEncryptedDataDescriptor.data.size
|
||||||
|
)
|
||||||
|
override fun verifyPartialData(data: EncryptedDataPart) {
|
||||||
|
primitive.encryptPartialData(data.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun finalizeVerificationAndPrepareDecryptor(): MultipartAuthenticatedDecryption {
|
||||||
|
primitive.finalizeVerificationAndPrepareDecryptor(tag)
|
||||||
|
return MultipartAuthenticatedDecryptor(primitive)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class MultipartAuthenticatedDecryptor internal constructor(val encryptor: XChaCha20Poly1305Pure) : MultipartAuthenticatedDecryption {
|
||||||
|
override fun decryptPartialData(data: EncryptedDataPart): DecryptedDataPart {
|
||||||
|
encryptor.decrypt(data.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.ionspin.kotlin.crypto.authenticated
|
package com.ionspin.kotlin.crypto.authenticated
|
||||||
|
|
||||||
|
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
|
||||||
@ -10,10 +11,10 @@ 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 nonce: UByteArray, val additionalData: UByteArray) {
|
class XChaCha20Poly1305Pure(val key: UByteArray, val additionalData: UByteArray) : {
|
||||||
companion object {
|
companion object : AuthenticatedEncryption {
|
||||||
|
|
||||||
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.encrypt(
|
||||||
@ -36,7 +37,7 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray, val addi
|
|||||||
return cipherText + tag
|
return cipherText + tag
|
||||||
}
|
}
|
||||||
|
|
||||||
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.encrypt(
|
||||||
@ -62,8 +63,11 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray, val addi
|
|||||||
//4. Decrypt data
|
//4. Decrypt data
|
||||||
return XChaCha20Pure.xorWithKeystream(key, nonce, cipherTextWithoutTag, 1U)
|
return XChaCha20Pure.xorWithKeystream(key, nonce, cipherTextWithoutTag, 1U)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
@ -93,11 +97,34 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray, val addi
|
|||||||
|
|
||||||
fun encryptPartialData(data: UByteArray) : UByteArray {
|
fun encryptPartialData(data: UByteArray) : UByteArray {
|
||||||
processedBytes += data.size
|
processedBytes += data.size
|
||||||
val encrypted = updateableEncryptionPrimitive.encryptPartialData(data)
|
val encrypted = updateableEncryptionPrimitive.xorWithKeystream(data)
|
||||||
processPolyBytes(encrypted)
|
processPolyBytes(encrypted)
|
||||||
return encrypted
|
return encrypted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun verifyPartialData(data: UByteArray) {
|
||||||
|
processPolyBytes(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun finalizeVerificationAndPrepareDecryptor(expectedTag: UByteArray): MultipartAuthenticatedDecryption {
|
||||||
|
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 RuntimeException("Invalid tag") //TODO Replace with proper exception
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decrypt(data: UByteArray) : UByteArray {
|
||||||
|
processedBytes += data.size
|
||||||
|
val decrypted = updateableEncryptionPrimitive.xorWithKeystream(data)
|
||||||
|
processPolyBytes(decrypted)
|
||||||
|
return decrypted
|
||||||
|
}
|
||||||
|
|
||||||
private fun processPolyBytes(data: UByteArray) {
|
private fun processPolyBytes(data: UByteArray) {
|
||||||
if (polyBufferByteCounter == 0) {
|
if (polyBufferByteCounter == 0) {
|
||||||
val polyBlocks = data.size / 16
|
val polyBlocks = data.size / 16
|
||||||
@ -139,12 +166,7 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray, val addi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun finish() : Pair<UByteArray, UByteArray> {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fun finish() : UByteArray {
|
|
||||||
|
|
||||||
val cipherTextPad = UByteArray(16 - processedBytes % 16) { 0U }
|
val cipherTextPad = UByteArray(16 - processedBytes % 16) { 0U }
|
||||||
val macData = cipherTextPad +
|
val macData = cipherTextPad +
|
||||||
@ -152,9 +174,8 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray, val addi
|
|||||||
processedBytes.toULong().toLittleEndianUByteArray()
|
processedBytes.toULong().toLittleEndianUByteArray()
|
||||||
processPolyBytes(macData)
|
processPolyBytes(macData)
|
||||||
val tag = updateableMacPrimitive.finalizeMac()
|
val tag = updateableMacPrimitive.finalizeMac()
|
||||||
return tag
|
return Pair(tag, nonce)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@ -132,7 +132,7 @@ class XChaCha20Pure(key: UByteArray, nonce: UByteArray, initialCounter: UInt = 0
|
|||||||
hChaChaKey.overwriteWithZeroes()
|
hChaChaKey.overwriteWithZeroes()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun encryptPartialData(data: UByteArray) : UByteArray {
|
fun xorWithKeystream(data: UByteArray) : UByteArray {
|
||||||
val ciphertext = UByteArray(data.size) { 0U }
|
val ciphertext = UByteArray(data.size) { 0U }
|
||||||
//First use remaining keystream
|
//First use remaining keystream
|
||||||
var processedBytes = 0
|
var processedBytes = 0
|
||||||
@ -177,4 +177,4 @@ class XChaCha20Pure(key: UByteArray, nonce: UByteArray, initialCounter: UInt = 0
|
|||||||
return ciphertext
|
return ciphertext
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -344,17 +344,6 @@ fun Array<UByte>.fromBigEndianArrayToUInt() : UInt {
|
|||||||
operator fun UInt.plus(other : UByteArray) : UByteArray {
|
operator fun UInt.plus(other : UByteArray) : UByteArray {
|
||||||
return this.toLittleEndianUByteArray() + other
|
return this.toLittleEndianUByteArray() + other
|
||||||
}
|
}
|
||||||
fun UByteArray.overwriteWithZeroes() {
|
|
||||||
for (i in 0 until size) {
|
|
||||||
this[i] = 0U
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun UIntArray.overwriteWithZeroes() {
|
|
||||||
for (i in 0 until size) {
|
|
||||||
this[i] = 0U
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//AES Flatten
|
//AES Flatten
|
||||||
fun Collection<UByteArray>.flattenToUByteArray(): UByteArray {
|
fun Collection<UByteArray>.flattenToUByteArray(): UByteArray {
|
||||||
|
@ -236,9 +236,9 @@ class XChaCha20Test {
|
|||||||
)
|
)
|
||||||
|
|
||||||
val xChaCha = XChaCha20Pure(key, nonce, 1U)
|
val xChaCha = XChaCha20Pure(key, nonce, 1U)
|
||||||
val firstChunk = xChaCha.encryptPartialData(message.sliceArray(0 until 5))
|
val firstChunk = xChaCha.xorWithKeystream(message.sliceArray(0 until 5))
|
||||||
val secondChunk = xChaCha.encryptPartialData(message.sliceArray(5 until 90))
|
val secondChunk = xChaCha.xorWithKeystream(message.sliceArray(5 until 90))
|
||||||
val thirdChunk = xChaCha.encryptPartialData(message.sliceArray(90 until message.size))
|
val thirdChunk = xChaCha.xorWithKeystream(message.sliceArray(90 until message.size))
|
||||||
|
|
||||||
assertTrue {
|
assertTrue {
|
||||||
(firstChunk + secondChunk + thirdChunk).contentEquals(expected)
|
(firstChunk + secondChunk + thirdChunk).contentEquals(expected)
|
||||||
@ -325,9 +325,9 @@ class XChaCha20Test {
|
|||||||
)
|
)
|
||||||
|
|
||||||
val xChaCha = XChaCha20Pure(key, nonce, 1U)
|
val xChaCha = XChaCha20Pure(key, nonce, 1U)
|
||||||
val firstChunk = xChaCha.encryptPartialData(message.sliceArray(0 until 50))
|
val firstChunk = xChaCha.xorWithKeystream(message.sliceArray(0 until 50))
|
||||||
val secondChunk = xChaCha.encryptPartialData(message.sliceArray(50 until 200))
|
val secondChunk = xChaCha.xorWithKeystream(message.sliceArray(50 until 200))
|
||||||
val thirdChunk = xChaCha.encryptPartialData(message.sliceArray(200 until message.size))
|
val thirdChunk = xChaCha.xorWithKeystream(message.sliceArray(200 until message.size))
|
||||||
val result = (firstChunk + secondChunk + thirdChunk)
|
val result = (firstChunk + secondChunk + thirdChunk)
|
||||||
assertTrue {
|
assertTrue {
|
||||||
result.contentEquals(expected)
|
result.contentEquals(expected)
|
||||||
@ -337,4 +337,4 @@ class XChaCha20Test {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user