Allocation removal progress

This commit is contained in:
Ugljesa Jovanovic 2020-05-23 11:20:36 +02:00 committed by Ugljesa Jovanovic
parent 76a2a3edf7
commit 3902b90b57
No known key found for this signature in database
GPG Key ID: 178E6DFCECCB0E0F
4 changed files with 251 additions and 18 deletions

View File

@ -52,6 +52,8 @@ data class ArgonResult(
}
@ExperimentalStdlibApi
class Argon2(
private val password: UByteArray,
@ -198,6 +200,7 @@ class Argon2(
} else {
matrix.getBlockStartAndEndPositions(lane, column - 1)
}
val bla = 1 until 3
val first32Bit = matrix.sliceArray(previousBlockStart until previousBlockStart + 4).fromLittleEndianArrayToUInt()
val second32Bit = matrix.sliceArray(previousBlockStart + 4 until previousBlockStart + 8).fromLittleEndianArrayToUInt()
@ -325,7 +328,7 @@ class Argon2(
//Temporary fold
val acc = matrix.getBlockAt(0, columnCount - 1).copyOf()
val acc = matrix.getBlockAt(0, columnCount - 1)
for (i in 1 until parallelism) {
(acc.xorWithBlock(matrix, i, columnCount - 1).copyInto(acc))
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
@file:Suppress("EXPERIMENTAL_API_USAGE")
@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
package com.ionspin.kotlin.crypto.keyderivation.argon2
@ -34,15 +34,23 @@ package com.ionspin.kotlin.crypto.keyderivation.argon2
if (columnPosition > columnCount - 1) {
throw RuntimeException("Invalid column requested: $columnPosition, columnCount: $columnCount")
}
return storage[getBlockStartPosition(rowPosition, columnPosition) + inBlockPosition]
return storage[getBlockStartPositionPointer(rowPosition, columnPosition).intStorage + inBlockPosition]
}
operator fun get(absolutePosition: Int) : UByte {
return storage[absolutePosition]
}
operator fun get(absolutePosition: BlockPointer) : UByte {
return storage[absolutePosition.intStorage]
}
operator fun set(rowPosition: Int, columnPosition: Int, inBlockPosition: Int, value: UByte) {
storage[getBlockStartPosition(rowPosition, columnPosition) + inBlockPosition] = value
storage[getBlockStartPositionPointer(rowPosition, columnPosition).intStorage + inBlockPosition] = value
}
fun getBlockStartAndEndPositions(rowPosition: Int, columnPosition: Int) : Pair<Int, Int> {
val start = getBlockStartPosition(rowPosition, columnPosition)
fun getBlockStartAndEndPositions(rowPosition: Int, columnPosition: Int) : Pair<BlockPointer, BlockPointer> {
val start = getBlockStartPositionPointer(rowPosition, columnPosition)
return Pair(
start,
start + 1024
@ -56,8 +64,8 @@ package com.ionspin.kotlin.crypto.keyderivation.argon2
fun getBlockAt(rowPosition: Int, columnPosition: Int) : UByteArray {
println("Expensive get")
return storage.copyOfRange(
getBlockStartPosition(rowPosition, columnPosition),
getBlockStartPosition(rowPosition, columnPosition) + 1024
getBlockStartPositionPointer(rowPosition, columnPosition).intStorage,
getBlockStartPositionPointer(rowPosition, columnPosition).intStorage + 1024
)
}
@ -65,12 +73,12 @@ package com.ionspin.kotlin.crypto.keyderivation.argon2
println("Expensive set")
blockValue.copyInto(
storage,
getBlockStartPosition(rowPosition, columnPosition)
getBlockStartPositionPointer(rowPosition, columnPosition).intStorage
)
}
private inline fun getBlockStartPosition(rowPosition: Int, columnPosition: Int) : Int {
return rowPosition * columnCount * 1024 + columnPosition * 1024
private inline fun getBlockStartPositionPointer(rowPosition: Int, columnPosition: Int) : BlockPointer {
return BlockPointer(rowPosition * columnCount * 1024 + columnPosition * 1024, this)
}
// operator fun get(rowPosition: Int, columnPosition: Int) : UByteArray {
@ -81,14 +89,145 @@ package com.ionspin.kotlin.crypto.keyderivation.argon2
// )
// }
//
// operator fun get(rowPosition: Int) : Array<UByteArray> {
// return Array(columnCount) {
// this.get(rowPosition, it)
// }
//
// }
internal fun clearMatrix() {
for( index in storage.indices) { storage[index] = 0U }
}
}
//TODO Decide: inline class without matrix reference?
class BlockPointer(val intStorage: Int, val matrix: Argon2Matrix) {
operator fun rangeTo(other: BlockPointer): IntRange {
return intStorage.rangeTo(other.intStorage)
}
infix fun until(to: BlockPointer): IntRange {
if (to.intStorage <= Int.MIN_VALUE) return IntRange.EMPTY
return this .. BlockPointer(to.intStorage - 1, matrix)
}
inline operator fun plus(other: BlockPointer) : BlockPointer {
return BlockPointer(intStorage.plus(other.intStorage), matrix)
}
inline operator fun plus(other: Int) : BlockPointer {
return BlockPointer(intStorage.plus(other), matrix)
}
inline operator fun minus(other: BlockPointer) : BlockPointer {
return BlockPointer(intStorage.minus(other.intStorage), matrix)
}
inline operator fun times(other: BlockPointer) : BlockPointer {
return BlockPointer(intStorage.times(other.intStorage), matrix)
}
inline operator fun div(other: BlockPointer) : BlockPointer {
return BlockPointer(intStorage.div(other.intStorage), matrix)
}
inline operator fun rem(other: BlockPointer) : BlockPointer {
return BlockPointer(intStorage.rem(other.intStorage), matrix)
}
infix fun xorBlocks(other: BlockPointer) : Block {
return Block(UByteArray(1024){
matrix[this.intStorage + it] xor other.matrix[other.intStorage + it]
})
}
}
@Suppress("NON_PUBLIC_PRIMARY_CONSTRUCTOR_OF_INLINE_CLASS")
inline class Block internal constructor(val storage: UByteArray) : Collection<UByte> {
constructor() : this(UByteArray(1024))
operator fun get(index: Int) : UByte {
return storage.get(index)
}
operator fun set(index: Int, value: UByte) {
storage.set(index, value)
}
override val size: Int get() = storage.size
override operator fun iterator(): UByteIterator = Iterator(storage.toByteArray())
private class Iterator(private val array: ByteArray) : UByteIterator() {
private var index = 0
override fun hasNext() = index < array.size
override fun nextUByte() = if (index < array.size) array[index++].toUByte() else throw NoSuchElementException(index.toString())
}
//Taken from UByteArray implementation
override fun contains(element: UByte): Boolean {
// TODO: Eliminate this check after KT-30016 gets fixed.
// Currently JS BE does not generate special bridge method for this method.
if ((element as Any?) !is UByte) return false
return storage.contains(element.toByte())
}
override fun containsAll(elements: Collection<UByte>): Boolean {
return (elements as Collection<*>).all { it is UByte && storage.contains(it.toByte()) }
}
override fun isEmpty(): Boolean = this.storage.size == 0
fun getRowOfULongsForMixing(rowIndex: Int) : ULongArray {
val startOfRow = (rowIndex * 8 * 16)
// Each row has 16 unsigned longs (16 ulongs * 8 bytes = 128 bytes) -- Argon2 considers this as 2 word unsigned
// numbers, so strictly speaking argon representation is 8 * 8 matrix of 2 word unsigned numbers (registers
val ulongArray = ULongArray(16)
for (i in 0 until 16) {
var ulong = 0UL
//Now we create the ulong
for (j in 0 until 8) {
ulong = ulong or (storage[startOfRow + i * 8 + j].toULong() shl (j * 8))
}
ulongArray[i] = ulong
}
return ulongArray
}
/*
@ExperimentalUnsignedTypes
fun ULong.toLittleEndianUByteArray() :UByteArray {
return UByteArray (8) {
((this shr (it * 8)) and 0xFFU).toUByte()
}
}
//copy into gblock column
for (i in 0..7) {
val column = columnData.copyOfRange(i * 16, i * 16 + 16)
column.copyInto(gBlock, i * 128 + columnPosition * 16)
}
*/
fun setRowFromMixedULongs(rowIndex: Int, ulongs: ULongArray) {
val startOfRow = (rowIndex * 8 * 16)
// Each row has 16 unsigned longs (16 ulongs * 8 bytes = 128 bytes) -- Argon2 considers this as 2 word unsigned
// numbers, so strictly speaking argon representation is 8 * 8 matrix of 2 word unsigned numbers (registers
for (i in 0 until 16) {
val ulongToConvert = ulongs[i]
for (j in 0 until 8) {
storage[startOfRow + i * 8 + j] = ((ulongToConvert shr (j * 8)) and 0xFFU).toUByte()
}
}
}
/*
val result = UByteArray(128) { 0U }
for (i in 0..7) {
gBlock.copyOfRange(i * 128 + (columnPosition * 16), i * 128 + (columnPosition * 16) + 16)
.copyInto(result, i * 16)
}
return result
*/
fun getColumnOfULongsForMixing(columnIndex: Int) : ULongArray {
val ulongArray = ULongArray(16)
for (i in 0 until 16) {
var ulong = 0UL
//Now we create the ulong
for (j in 0 until 8) {
ulong = ulong or (storage[i * 128 + columnIndex * 16 + j].toULong() shl (j * 8))
}
}
return ulongArray
}
}

View File

@ -40,6 +40,18 @@ object Argon2Utils {
const val R3 = 16
const val R4 = 63
internal fun inplaceMixRound(v : ULongArray) : ULongArray{
mix(v, 0, 4, 8, 12)
mix(v, 1, 5, 9, 13)
mix(v, 2, 6, 10, 14)
mix(v, 3, 7, 11, 15)
mix(v, 0, 5, 10, 15)
mix(v, 1, 6, 11, 12)
mix(v, 2, 7, 8, 13)
mix(v, 3, 4, 9, 14)
return v //Just for chaining, array is already mixed
}
//based on Blake2b mixRound
internal fun mixRound(input: UByteArray): Array<ULong> {
var v = input.arrayChunked(8).map { it.fromLittleEndianArrayToULong() }.toTypedArray()
@ -71,6 +83,18 @@ object Argon2Utils {
return v
}
//Based on Blake2b mix
private fun mix(v: ULongArray, a: Int, b: Int, c: Int, d: Int) {
v[a] = (v[a] + v[b] + 2U * (v[a] and 0xFFFFFFFFUL) * (v[b] and 0xFFFFFFFFUL))
v[d] = (v[d] xor v[a]) rotateRight R1
v[c] = (v[c] + v[d] + 2U * (v[c] and 0xFFFFFFFFUL) * (v[d] and 0xFFFFFFFFUL))
v[b] = (v[b] xor v[c]) rotateRight R2
v[a] = (v[a] + v[b] + 2U * (v[a] and 0xFFFFFFFFUL) * (v[b] and 0xFFFFFFFFUL))
v[d] = (v[d] xor v[a]) rotateRight R3
v[c] = (v[c] + v[d] + 2U * (v[c] and 0xFFFFFFFFUL) * (v[d] and 0xFFFFFFFFUL))
v[b] = (v[b] xor v[c]) rotateRight R4
}
internal fun extractColumnFromGBlock(gBlock: UByteArray, columnPosition: Int): UByteArray {
val result = UByteArray(128) { 0U }
for (i in 0..7) {
@ -87,6 +111,39 @@ object Argon2Utils {
}
}
// internal fun allocationlessCompressionFunctionG(
// matrix: Argon2Matrix,
// previousBlock: BlockPointer,
// referenceBlock: BlockPointer,
// currentBlock: BlockPointer,
// xorWithCurrentBlock: Boolean
// ): UByteArray {
// val r = referenceBlock xorBlocks previousBlock
// val q = Block()
// val z = Block()
// // Do the argon/blake2b mixing on rows
// for (i in 0..7) {
// q.setRowFromMixedULongs(i, inplaceMixRound(r.getRowOfULongsForMixing(i)))
// }
// // Do the argon/blake2b mixing on columns
// for (i in 0..7) {
// copyIntoGBlockColumn(
// z,
// i,
// mixRound(extractColumnFromGBlock(q, i))
// .map { it.toLittleEndianUByteArray() }
// .flatMap { it.asIterable() }
// .toUByteArray()
// )
// }
// val final = if (xorWithCurrentBlock) {
// (z xor r) xor currentBlock
// } else {
// z xor r
// }
// return final
// }
internal fun compressionFunctionG(
previousBlock: UByteArray,
referenceBlock: UByteArray,

View File

@ -19,7 +19,12 @@
package com.ionspin.kotlin.crypto.hash.argon
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Matrix
import com.ionspin.kotlin.crypto.util.hexColumsPrint
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils
import com.ionspin.kotlin.crypto.keyderivation.argon2.Block
import com.ionspin.kotlin.crypto.util.arrayChunked
import com.ionspin.kotlin.crypto.util.fromLittleEndianArrayToULong
import kotlin.random.Random
import kotlin.random.nextUBytes
import kotlin.test.Test
import kotlin.test.assertTrue
@ -34,6 +39,9 @@ class Argon2MatrixTest {
val twosBlock = UByteArray(1024) { 2U }
val threesBlock = UByteArray(1024) { 3U }
val random = Random(1)
val randomBlockArray = random.nextUBytes(1024)
@Test
fun indexAccessTest() {
@ -75,4 +83,30 @@ class Argon2MatrixTest {
threesBlock.contentEquals(argon2Matrix.getBlockAt(1, 1))
}
}
@Test
fun blockColumnToUlongTest() {
val randomBlock = Block(randomBlockArray)
for (columnIndex in 0 until 8) {
val startOfRow = (columnIndex * 8 * 16)
val endOfRow = startOfRow + (8 * 16)
val rowToMix = randomBlockArray.copyOfRange(startOfRow, endOfRow)
val expected = rowToMix.arrayChunked(8).map { it.fromLittleEndianArrayToULong() }.toULongArray()
val result = randomBlock.getRowOfULongsForMixing(columnIndex)
assertTrue { expected.contentEquals(result) }
}
}
@Test
fun blockRowToULongTest() {
val randomBlock = Block(randomBlockArray)
val columnToMix = Argon2Utils.extractColumnFromGBlock(randomBlockArray, 0)
val expected = columnToMix.arrayChunked(8).map { it.fromLittleEndianArrayToULong() }.toULongArray()
val result = randomBlock.getColumnOfULongsForMixing(0)
assertTrue { expected.contentEquals(result) }
}
}