From 13b60a5eee9b54774d37a8f269598c1a1cf1eef2 Mon Sep 17 00:00:00 2001 From: Ugljesa Jovanovic Date: Thu, 21 May 2020 21:02:19 +0200 Subject: [PATCH] Replacing array of arrays of ubyte arrays with custom argon2 matrix --- .../crypto/keyderivation/argon2/Argon2.kt | 82 +++++++++---------- .../keyderivation/argon2/Argon2Matrix.kt | 66 ++++++++++++--- .../crypto/hash/argon/Argon2MatrixTest.kt | 31 ++++++- 3 files changed, 126 insertions(+), 53 deletions(-) diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/argon2/Argon2.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/argon2/Argon2.kt index 2381308..0b053f0 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/argon2/Argon2.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/argon2/Argon2.kt @@ -27,6 +27,7 @@ import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.compressionFun import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.validateArgonParameters import com.ionspin.kotlin.crypto.util.fromLittleEndianArrayToUInt import com.ionspin.kotlin.crypto.util.toLittleEndianUByteArray +import com.ionspin.kotlin.crypto.util.xor /** * Created by Ugljesa Jovanovic @@ -132,15 +133,10 @@ class Argon2( private val useIndependentAddressing = argonType == ArgonType.Argon2id || argonType == ArgonType.Argon2i // State - private val matrix: Array> + private val matrix: Argon2Matrix init { - matrix = Array(parallelism) { - Array(columnCount) { - UByteArray(1024) - } - } - + matrix = Argon2Matrix(columnCount, parallelism) validateArgonParameters( password, salt, @@ -154,15 +150,7 @@ class Argon2( ) } - private fun clearMatrix() { - matrix.forEachIndexed { laneIndex, lane -> - lane.forEachIndexed { columnIndex, block -> - block.forEachIndexed { byteIndex, byte -> - matrix[laneIndex][columnIndex][byteIndex] = 0U - } - } - } - } + private fun populateAddressBlock( iteration: Int, @@ -202,17 +190,18 @@ class Argon2( column: Int, addressBlock: UByteArray? ): Pair { + val segmentIndex = (column % segmentLength) val independentIndex = segmentIndex % 128 // 128 is the number of addresses in address block val (j1, j2) = when (argonType) { ArgonType.Argon2d -> { - val previousBlock = if (column == 0) { - matrix[lane][columnCount - 1] //Get last block in the SAME lane + val (previousBlockStart, previousBlockEnd) = if (column == 0) { + matrix.getBlockStartAndEndPositions(lane, columnCount - 1) //Get last block in the SAME lane } else { - matrix[lane][column - 1] + matrix.getBlockStartAndEndPositions(lane, column - 1) } - val first32Bit = previousBlock.sliceArray(0 until 4).fromLittleEndianArrayToUInt() - val second32Bit = previousBlock.sliceArray(4 until 8).fromLittleEndianArrayToUInt() + val first32Bit = matrix.sliceArray(previousBlockStart until previousBlockStart + 4).fromLittleEndianArrayToUInt() + val second32Bit = matrix.sliceArray(previousBlockStart + 4 until previousBlockStart + 8).fromLittleEndianArrayToUInt() Pair(first32Bit, second32Bit) } ArgonType.Argon2i -> { @@ -230,13 +219,13 @@ class Argon2( val second32Bit = selectedAddressBlock.sliceArray(4 until 8).fromLittleEndianArrayToUInt() Pair(first32Bit, second32Bit) } else { - val previousBlock = if (column == 0) { - matrix[lane][columnCount - 1] //Get last block in the SAME lane + val (previousBlockStart, previousBlockEnd) = if (column == 0) { + matrix.getBlockStartAndEndPositions(lane, columnCount - 1) //Get last block in the SAME lane } else { - matrix[lane][column - 1] + matrix.getBlockStartAndEndPositions(lane, column - 1) } - val first32Bit = previousBlock.sliceArray(0 until 4).fromLittleEndianArrayToUInt() - val second32Bit = previousBlock.sliceArray(4 until 8).fromLittleEndianArrayToUInt() + val first32Bit = matrix.sliceArray(previousBlockStart until previousBlockStart + 4).fromLittleEndianArrayToUInt() + val second32Bit = matrix.sliceArray(previousBlockStart + 4 until previousBlockStart + 8).fromLittleEndianArrayToUInt() Pair(first32Bit, second32Bit) } @@ -315,35 +304,43 @@ class Argon2( //Compute B[i][0] for (i in 0 until parallelism) { - matrix[i][0] = + matrix.setBlockAt(i, 0, argonBlake2bArbitraryLenghtHash( (h0 + 0.toUInt().toLittleEndianUByteArray() + i.toUInt().toLittleEndianUByteArray()).toUByteArray(), 1024U ) + ) } //Compute B[i][1] for (i in 0 until parallelism) { - matrix[i][1] = + matrix.setBlockAt(i, 1, argonBlake2bArbitraryLenghtHash( (h0 + 1.toUInt().toLittleEndianUByteArray() + i.toUInt().toLittleEndianUByteArray()).toUByteArray(), 1024U ) + ) } + //Run all iterations over all lanes and all segments executeArgonWithSingleThread() - val result = matrix.foldIndexed(ubyteArrayOf()) { lane, acc, laneArray -> - if (acc.size == 0) { - acc + laneArray[columnCount - 1] // add last element in first lane to the accumulator - } else { - // For each element in our accumulator, xor it with an appropriate element from the last column in current lane (from 1 to `parallelism`) - acc.mapIndexed { index, it -> it xor laneArray[columnCount - 1][index] }.toUByteArray() - - } +// val result = matrix.foldIndexed(ubyteArrayOf()) { lane, acc, laneArray -> +// if (acc.size == 0) { +// acc + laneArray[columnCount - 1] // add last element in first lane to the accumulator +// } else { +// // For each element in our accumulator, xor it with an appropriate element from the last column in current lane (from 1 to `parallelism`) +// acc.mapIndexed { index, it -> it xor laneArray[columnCount - 1][index] }.toUByteArray() +// +// } +// } + //Temporary fold + val acc = matrix.getBlockAt(0, columnCount - 1).copyOf() + for (i in 1 until parallelism) { + acc.xor(matrix.getBlockAt(i, columnCount -1)) } //Hash the xored last blocks - val hash = argonBlake2bArbitraryLenghtHash(result, tagLength) - clearMatrix() + val hash = argonBlake2bArbitraryLenghtHash(acc, tagLength) + matrix.clearMatrix() return hash @@ -403,13 +400,14 @@ class Argon2( column, addressBlock ) - matrix[lane][column] = + matrix.setBlockAt(lane, column, compressionFunctionG( - matrix[lane][previousColumn], - matrix[l][z], - matrix[lane][column], + matrix.getBlockAt(lane, previousColumn), + matrix.getBlockAt(l,z), + matrix.getBlockAt(lane,column), true ).toUByteArray() + ) } } diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/argon2/Argon2Matrix.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/argon2/Argon2Matrix.kt index a90a0ac..80b3523 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/argon2/Argon2Matrix.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/argon2/Argon2Matrix.kt @@ -28,21 +28,67 @@ package com.ionspin.kotlin.crypto.keyderivation.argon2 internal val storage: UByteArray = UByteArray(columnCount * rowCount * 1024) operator fun get(rowPosition: Int, columnPosition: Int, inBlockPosition: Int) : UByte { - return storage[rowPosition * (columnCount - 1) * 1024 + columnPosition * 1024 + inBlockPosition] + if (rowPosition > rowCount - 1) { + throw RuntimeException("Invalid row (lane) requested: $rowPosition, rowCount: $rowCount") + } + if (columnPosition > columnCount - 1) { + throw RuntimeException("Invalid column requested: $columnPosition, columnCount: $columnCount") + } + return storage[getBlockStartPosition(rowPosition, columnPosition) + inBlockPosition] } - operator fun get(rowPosition: Int, columnPosition: Int) : UByteArray { - println("Expensive.") - return storage.copyOfRange( - rowPosition * (columnCount - 1) * 1024 + columnPosition * 1024, - rowPosition * (columnCount - 1) * 1024 + columnPosition * 1024 + 1024 + operator fun set(rowPosition: Int, columnPosition: Int, inBlockPosition: Int, value: UByte) { + storage[getBlockStartPosition(rowPosition, columnPosition) + inBlockPosition] = value + } + + fun getBlockStartAndEndPositions(rowPosition: Int, columnPosition: Int) : Pair { + val start = getBlockStartPosition(rowPosition, columnPosition) + return Pair( + start, + start + 1024 ) } - operator fun get(rowPosition: Int) : Array { - return Array(columnCount) { - this.get(rowPosition, it) - } + fun sliceArray(indices: IntRange): UByteArray { + return storage.sliceArray(indices) + } + fun getBlockAt(rowPosition: Int, columnPosition: Int) : UByteArray { + println("Expensive get") + return storage.copyOfRange( + getBlockStartPosition(rowPosition, columnPosition), + getBlockStartPosition(rowPosition, columnPosition) + 1024 + ) + } + + fun setBlockAt(rowPosition: Int, columnPosition: Int, blockValue: UByteArray) { + println("Expensive set") + blockValue.copyInto( + storage, + getBlockStartPosition(rowPosition, columnPosition) + ) + } + + private inline fun getBlockStartPosition(rowPosition: Int, columnPosition: Int) : Int { + return rowPosition * columnCount * 1024 + columnPosition * 1024 + } + +// operator fun get(rowPosition: Int, columnPosition: Int) : UByteArray { +// println("Expensive.") +// return storage.copyOfRange( +// rowPosition * (columnCount - 1) * 1024 + columnPosition * 1024, +// rowPosition * (columnCount - 1) * 1024 + columnPosition * 1024 + 1024 +// ) +// } +// +// operator fun get(rowPosition: Int) : Array { +// return Array(columnCount) { +// this.get(rowPosition, it) +// } +// +// } + + internal fun clearMatrix() { + for( index in storage.indices) { storage[index] = 0U } } } \ No newline at end of file diff --git a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/hash/argon/Argon2MatrixTest.kt b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/hash/argon/Argon2MatrixTest.kt index a6aa3e5..9c156e9 100644 --- a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/hash/argon/Argon2MatrixTest.kt +++ b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/hash/argon/Argon2MatrixTest.kt @@ -36,7 +36,36 @@ class Argon2MatrixTest { @Test - fun debugTest() { + fun indexAccessTest() { + val argon2Matrix = Argon2Matrix(2, 2) + (zeroesBlock + onesBlock + twosBlock + threesBlock).copyInto(argon2Matrix.storage) + println(argon2Matrix[0, 0, 0]) + println(argon2Matrix[0, 1, 0]) + println(argon2Matrix[1, 0, 0]) + println(argon2Matrix[1, 1, 0]) +// argon2Matrix.storage.hexColumsPrint(1024) + var expectedByteValue = 0U.toUByte() + for (lane in 0 until 2) { + for (column in 0 until 2) { + for (blockPosition in 0 until 1024) { + assertTrue { + argon2Matrix[lane, column, blockPosition] == expectedByteValue + } + } + expectedByteValue++ + } + } + assertTrue { + argon2Matrix[0, 0, 0] == 0U.toUByte() && + argon2Matrix[0, 1, 0] == 1U.toUByte() && + argon2Matrix[1, 0, 0] == 2U.toUByte() && + argon2Matrix[1, 1, 0] == 3U.toUByte() + + } + } + + @Test + fun blockRetrievalTest() { val argon2Matrix = Argon2Matrix(2, 2) (zeroesBlock + onesBlock + twosBlock + threesBlock).copyInto(argon2Matrix.storage) println(argon2Matrix[0, 0, 0])