Allocation removal progress
This commit is contained in:
parent
76a2a3edf7
commit
3902b90b57
@ -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))
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
@ -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,
|
||||
|
@ -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) }
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user