Allocation removal progress
This commit is contained in:
parent
76a2a3edf7
commit
3902b90b57
@ -52,6 +52,8 @@ data class ArgonResult(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ExperimentalStdlibApi
|
@ExperimentalStdlibApi
|
||||||
class Argon2(
|
class Argon2(
|
||||||
private val password: UByteArray,
|
private val password: UByteArray,
|
||||||
@ -198,6 +200,7 @@ class Argon2(
|
|||||||
} else {
|
} else {
|
||||||
matrix.getBlockStartAndEndPositions(lane, column - 1)
|
matrix.getBlockStartAndEndPositions(lane, column - 1)
|
||||||
}
|
}
|
||||||
|
val bla = 1 until 3
|
||||||
val first32Bit = matrix.sliceArray(previousBlockStart until previousBlockStart + 4).fromLittleEndianArrayToUInt()
|
val first32Bit = matrix.sliceArray(previousBlockStart until previousBlockStart + 4).fromLittleEndianArrayToUInt()
|
||||||
val second32Bit = matrix.sliceArray(previousBlockStart + 4 until previousBlockStart + 8).fromLittleEndianArrayToUInt()
|
val second32Bit = matrix.sliceArray(previousBlockStart + 4 until previousBlockStart + 8).fromLittleEndianArrayToUInt()
|
||||||
|
|
||||||
@ -325,7 +328,7 @@ class Argon2(
|
|||||||
|
|
||||||
|
|
||||||
//Temporary fold
|
//Temporary fold
|
||||||
val acc = matrix.getBlockAt(0, columnCount - 1).copyOf()
|
val acc = matrix.getBlockAt(0, columnCount - 1)
|
||||||
for (i in 1 until parallelism) {
|
for (i in 1 until parallelism) {
|
||||||
(acc.xorWithBlock(matrix, i, columnCount - 1).copyInto(acc))
|
(acc.xorWithBlock(matrix, i, columnCount - 1).copyInto(acc))
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@file:Suppress("EXPERIMENTAL_API_USAGE")
|
@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
|
||||||
|
|
||||||
package com.ionspin.kotlin.crypto.keyderivation.argon2
|
package com.ionspin.kotlin.crypto.keyderivation.argon2
|
||||||
|
|
||||||
@ -34,15 +34,23 @@ package com.ionspin.kotlin.crypto.keyderivation.argon2
|
|||||||
if (columnPosition > columnCount - 1) {
|
if (columnPosition > columnCount - 1) {
|
||||||
throw RuntimeException("Invalid column requested: $columnPosition, columnCount: $columnCount")
|
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) {
|
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> {
|
fun getBlockStartAndEndPositions(rowPosition: Int, columnPosition: Int) : Pair<BlockPointer, BlockPointer> {
|
||||||
val start = getBlockStartPosition(rowPosition, columnPosition)
|
val start = getBlockStartPositionPointer(rowPosition, columnPosition)
|
||||||
return Pair(
|
return Pair(
|
||||||
start,
|
start,
|
||||||
start + 1024
|
start + 1024
|
||||||
@ -56,8 +64,8 @@ package com.ionspin.kotlin.crypto.keyderivation.argon2
|
|||||||
fun getBlockAt(rowPosition: Int, columnPosition: Int) : UByteArray {
|
fun getBlockAt(rowPosition: Int, columnPosition: Int) : UByteArray {
|
||||||
println("Expensive get")
|
println("Expensive get")
|
||||||
return storage.copyOfRange(
|
return storage.copyOfRange(
|
||||||
getBlockStartPosition(rowPosition, columnPosition),
|
getBlockStartPositionPointer(rowPosition, columnPosition).intStorage,
|
||||||
getBlockStartPosition(rowPosition, columnPosition) + 1024
|
getBlockStartPositionPointer(rowPosition, columnPosition).intStorage + 1024
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,12 +73,12 @@ package com.ionspin.kotlin.crypto.keyderivation.argon2
|
|||||||
println("Expensive set")
|
println("Expensive set")
|
||||||
blockValue.copyInto(
|
blockValue.copyInto(
|
||||||
storage,
|
storage,
|
||||||
getBlockStartPosition(rowPosition, columnPosition)
|
getBlockStartPositionPointer(rowPosition, columnPosition).intStorage
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private inline fun getBlockStartPosition(rowPosition: Int, columnPosition: Int) : Int {
|
private inline fun getBlockStartPositionPointer(rowPosition: Int, columnPosition: Int) : BlockPointer {
|
||||||
return rowPosition * columnCount * 1024 + columnPosition * 1024
|
return BlockPointer(rowPosition * columnCount * 1024 + columnPosition * 1024, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
// operator fun get(rowPosition: Int, columnPosition: Int) : UByteArray {
|
// 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() {
|
internal fun clearMatrix() {
|
||||||
for( index in storage.indices) { storage[index] = 0U }
|
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 R3 = 16
|
||||||
const val R4 = 63
|
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
|
//based on Blake2b mixRound
|
||||||
internal fun mixRound(input: UByteArray): Array<ULong> {
|
internal fun mixRound(input: UByteArray): Array<ULong> {
|
||||||
var v = input.arrayChunked(8).map { it.fromLittleEndianArrayToULong() }.toTypedArray()
|
var v = input.arrayChunked(8).map { it.fromLittleEndianArrayToULong() }.toTypedArray()
|
||||||
@ -71,6 +83,18 @@ object Argon2Utils {
|
|||||||
return v
|
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 {
|
internal fun extractColumnFromGBlock(gBlock: UByteArray, columnPosition: Int): UByteArray {
|
||||||
val result = UByteArray(128) { 0U }
|
val result = UByteArray(128) { 0U }
|
||||||
for (i in 0..7) {
|
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(
|
internal fun compressionFunctionG(
|
||||||
previousBlock: UByteArray,
|
previousBlock: UByteArray,
|
||||||
referenceBlock: UByteArray,
|
referenceBlock: UByteArray,
|
||||||
|
@ -19,7 +19,12 @@
|
|||||||
package com.ionspin.kotlin.crypto.hash.argon
|
package com.ionspin.kotlin.crypto.hash.argon
|
||||||
|
|
||||||
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Matrix
|
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.Test
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
@ -34,6 +39,9 @@ class Argon2MatrixTest {
|
|||||||
val twosBlock = UByteArray(1024) { 2U }
|
val twosBlock = UByteArray(1024) { 2U }
|
||||||
val threesBlock = UByteArray(1024) { 3U }
|
val threesBlock = UByteArray(1024) { 3U }
|
||||||
|
|
||||||
|
val random = Random(1)
|
||||||
|
val randomBlockArray = random.nextUBytes(1024)
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun indexAccessTest() {
|
fun indexAccessTest() {
|
||||||
@ -75,4 +83,30 @@ class Argon2MatrixTest {
|
|||||||
threesBlock.contentEquals(argon2Matrix.getBlockAt(1, 1))
|
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