Further reduction in allocations

This commit is contained in:
Ugljesa Jovanovic 2020-05-23 16:18:12 +02:00 committed by Ugljesa Jovanovic
parent 3902b90b57
commit 4142549d2e
No known key found for this signature in database
GPG Key ID: 178E6DFCECCB0E0F
5 changed files with 381 additions and 317 deletions

View File

@ -24,6 +24,7 @@ import com.ionspin.kotlin.crypto.hash.blake2b.Blake2b
import com.ionspin.kotlin.crypto.keyderivation.KeyDerivationFunction
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.argonBlake2bArbitraryLenghtHash
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.compressionFunctionG
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.inplaceCompressionFunctionG
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.validateArgonParameters
import com.ionspin.kotlin.crypto.util.*
@ -133,10 +134,10 @@ class Argon2(
private val useIndependentAddressing = argonType == ArgonType.Argon2id || argonType == ArgonType.Argon2i
// State
private val matrix: Argon2Matrix
private val matrix: ArgonMatrix
init {
matrix = Argon2Matrix(columnCount, parallelism)
matrix = ArgonMatrix(columnCount, parallelism)
validateArgonParameters(
password,
salt,
@ -156,25 +157,27 @@ class Argon2(
iteration: Int,
slice: Int,
lane: Int,
addressBlock: UByteArray,
addressBlock: ArgonBlockPointer,
addressCounter: ULong
): UByteArray {
): ArgonBlockPointer {
//Calculate first pass
val firstPass = compressionFunctionG(
UByteArray(1024) { 0U },
iteration.toULong().toLittleEndianUByteArray() +
val zeroesBlock = ArgonBlock()
val firstPass = inplaceCompressionFunctionG(
zeroesBlock.getBlockPointer(),
ArgonBlock(iteration.toULong().toLittleEndianUByteArray() +
lane.toULong().toLittleEndianUByteArray() +
slice.toULong().toLittleEndianUByteArray() +
blockCount.toULong().toLittleEndianUByteArray() +
numberOfIterations.toULong().toLittleEndianUByteArray() +
argonType.typeId.toULong().toLittleEndianUByteArray() +
addressCounter.toLittleEndianUByteArray() +
UByteArray(968) { 0U },
UByteArray(968) { 0U }
).getBlockPointer(),
addressBlock,
false
)
val secondPass = compressionFunctionG(
UByteArray(1024) { 0U },
val secondPass = inplaceCompressionFunctionG(
zeroesBlock.getBlockPointer(),
firstPass,
firstPass,
false
@ -188,46 +191,41 @@ class Argon2(
slice: Int,
lane: Int,
column: Int,
addressBlock: UByteArray?
addressBlockPointer: ArgonBlockPointer?
): Pair<Int, Int> {
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 (previousBlockStart, previousBlockEnd) = if (column == 0) {
matrix.getBlockStartAndEndPositions(lane, columnCount - 1) //Get last block in the SAME lane
val previousBlockStart = if (column == 0) {
matrix.getBlockPointer(lane, columnCount - 1) //Get last block in the SAME lane
} else {
matrix.getBlockStartAndEndPositions(lane, column - 1)
matrix.getBlockPointer(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()
val first32Bit = matrix.sliceArray(previousBlockStart.asInt() until previousBlockStart.asInt() + 4).fromLittleEndianArrayToUInt()
val second32Bit = matrix.sliceArray(previousBlockStart.asInt() + 4 until previousBlockStart.asInt() + 8).fromLittleEndianArrayToUInt()
Pair(first32Bit, second32Bit)
}
ArgonType.Argon2i -> {
val selectedAddressBlock =
addressBlock!!.sliceArray((independentIndex * 8) until (independentIndex * 8) + 8)
val first32Bit = selectedAddressBlock.sliceArray(0 until 4).fromLittleEndianArrayToUInt()
val second32Bit = selectedAddressBlock.sliceArray(4 until 8).fromLittleEndianArrayToUInt()
val first32Bit = addressBlockPointer!!.getUIntFromPosition(independentIndex * 8)
val second32Bit = addressBlockPointer!!.getUIntFromPosition(independentIndex * 8 + 4)
Pair(first32Bit, second32Bit)
}
ArgonType.Argon2id -> {
if (iteration == 0 && (slice == 0 || slice == 1)) {
val selectedAddressBlock =
addressBlock!!.sliceArray((independentIndex * 8) until (independentIndex * 8) + 8)
val first32Bit = selectedAddressBlock.sliceArray(0 until 4).fromLittleEndianArrayToUInt()
val second32Bit = selectedAddressBlock.sliceArray(4 until 8).fromLittleEndianArrayToUInt()
val first32Bit = addressBlockPointer!!.getUIntFromPosition(independentIndex * 8)
val second32Bit = addressBlockPointer!!.getUIntFromPosition(independentIndex * 8 + 4)
Pair(first32Bit, second32Bit)
} else {
val (previousBlockStart, previousBlockEnd) = if (column == 0) {
matrix.getBlockStartAndEndPositions(lane, columnCount - 1) //Get last block in the SAME lane
val previousBlockStart = if (column == 0) {
matrix.getBlockPointer(lane, columnCount - 1) //Get last block in the SAME lane
} else {
matrix.getBlockStartAndEndPositions(lane, column - 1)
matrix.getBlockPointer(lane, column - 1)
}
val first32Bit = matrix.sliceArray(previousBlockStart until previousBlockStart + 4).fromLittleEndianArrayToUInt()
val second32Bit = matrix.sliceArray(previousBlockStart + 4 until previousBlockStart + 8).fromLittleEndianArrayToUInt()
val first32Bit = matrix.sliceArray(previousBlockStart.asInt() until previousBlockStart.asInt() + 4).fromLittleEndianArrayToUInt()
val second32Bit = matrix.sliceArray(previousBlockStart.asInt() + 4 until previousBlockStart.asInt() + 8).fromLittleEndianArrayToUInt()
Pair(first32Bit, second32Bit)
}
@ -356,14 +354,12 @@ class Argon2(
val slice = segmentPosition.slice
val lane = segmentPosition.lane
var addressBlock: UByteArray? = null
var addressBlock: ArgonBlockPointer? = null
var addressCounter = 1UL //Starts from 1 in each segment as defined by the spec
//Generate initial segment address block
if (useIndependentAddressing) {
addressBlock = UByteArray(1024) {
0U
}
addressBlock = ArgonBlock().getBlockPointer()
addressBlock = populateAddressBlock(iteration, slice, lane, addressBlock, addressCounter)
addressCounter++
}
@ -396,12 +392,12 @@ class Argon2(
)
matrix.setBlockAt(lane, column,
compressionFunctionG(
matrix.getBlockAt(lane, previousColumn),
matrix.getBlockAt(l,z),
matrix.getBlockAt(lane,column),
inplaceCompressionFunctionG(
matrix.getBlockPointer(lane, previousColumn),
matrix.getBlockPointer(l,z),
matrix.getBlockPointer(lane,column),
true
).toUByteArray()
).getAsUByteArray()
)
}

View File

@ -1,233 +0,0 @@
/*
* Copyright 2019 Ugljesa Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
package com.ionspin.kotlin.crypto.keyderivation.argon2
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 21-May-2020
*/
class Argon2Matrix(val columnCount: Int, val rowCount: Int) {
internal val storage: UByteArray = UByteArray(columnCount * rowCount * 1024)
operator fun get(rowPosition: Int, columnPosition: Int, inBlockPosition: Int) : UByte {
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[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[getBlockStartPositionPointer(rowPosition, columnPosition).intStorage + inBlockPosition] = value
}
fun getBlockStartAndEndPositions(rowPosition: Int, columnPosition: Int) : Pair<BlockPointer, BlockPointer> {
val start = getBlockStartPositionPointer(rowPosition, columnPosition)
return Pair(
start,
start + 1024
)
}
fun sliceArray(indices: IntRange): UByteArray {
return storage.sliceArray(indices)
}
fun getBlockAt(rowPosition: Int, columnPosition: Int) : UByteArray {
println("Expensive get")
return storage.copyOfRange(
getBlockStartPositionPointer(rowPosition, columnPosition).intStorage,
getBlockStartPositionPointer(rowPosition, columnPosition).intStorage + 1024
)
}
fun setBlockAt(rowPosition: Int, columnPosition: Int, blockValue: UByteArray) {
println("Expensive set")
blockValue.copyInto(
storage,
getBlockStartPositionPointer(rowPosition, columnPosition).intStorage
)
}
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 {
// println("Expensive.")
// return storage.copyOfRange(
// rowPosition * (columnCount - 1) * 1024 + columnPosition * 1024,
// rowPosition * (columnCount - 1) * 1024 + columnPosition * 1024 + 1024
// )
// }
//
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

@ -111,38 +111,30 @@ 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 inplaceCompressionFunctionG(
previousBlock: ArgonBlockPointer,
referenceBlock: ArgonBlockPointer,
currentBlock: ArgonBlockPointer,
xorWithCurrentBlock: Boolean
): ArgonBlockPointer {
val r = (referenceBlock xorBlocksAndGetPointerToNewBlock previousBlock).getBlockPointer()
val q = ArgonBlock().getBlockPointer()
val z = ArgonBlock().getBlockPointer()
// 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) {
z.setColumnFromMixedULongs(i, inplaceMixRound(q.getColumnOfULongsForMixing(i)))
}
val final = if (xorWithCurrentBlock) {
(z xorInplace r) xorInplace currentBlock
} else {
z xorInplace r
}
return final
}
internal fun compressionFunctionG(
previousBlock: UByteArray,
@ -249,6 +241,6 @@ object Argon2Utils {
// ------------ Arithmetic and other utils
@ExperimentalUnsignedTypes
fun UByteArray.xorWithBlock(other : Argon2Matrix, rowPosition: Int, columnPosition: Int) : UByteArray {
fun UByteArray.xorWithBlock(other : ArgonMatrix, rowPosition: Int, columnPosition: Int) : UByteArray {
return UByteArray(BLOCK_SIZE) { this[it] xor other[rowPosition, columnPosition, it] }
}

View File

@ -0,0 +1,279 @@
/*
* Copyright 2019 Ugljesa Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("EXPERIMENTAL_API_USAGE", "EXPERIMENTAL_UNSIGNED_LITERALS")
package com.ionspin.kotlin.crypto.keyderivation.argon2
/**
* Represents a pointer to a Argon2 Block, this abstracts what the backing structure is, a Argon 2 Matrix or
* or a UByteArray block
*/
interface ArgonBlockPointer {
val blockStartPosition: Int
fun pointerArithmetic(block : (Int, Int) -> Int) : ArgonBlockPointer
infix fun xorBlocksAndGetPointerToNewBlock(other: ArgonBlockPointer) : ArgonBlock
operator fun get(blockPosition: Int) : UByte
operator fun set(blockPosition: Int, value: UByte)
infix fun xorInplace(other: ArgonBlockPointer) : ArgonBlockPointer {
(0 until 1024).forEach {
this[it] = this[it] xor other[it]
}
return this //For chaining
}
fun asInt() : Int
fun getAsUByteArray() : UByteArray
}
fun ArgonBlockPointer.getRowOfULongsForMixing(rowIndex: Int) : ULongArray {
// 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 (columnIndex in 0 until 16) {
var ulong = 0UL
//Now we create the ulong
for (bytePosition in 0 until 8) {
ulong = ulong or (this[rowIndex * 128 + columnIndex * 8 + bytePosition].toULong() shl (bytePosition * 8))
}
ulongArray[columnIndex] = ulong
}
return ulongArray
}
fun ArgonBlockPointer.setRowFromMixedULongs(rowIndex: Int, ulongs: ULongArray) {
// 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 (columnIndex in 0 until 16) {
val ulongToConvert = ulongs[columnIndex]
for (bytePosition in 0 until 8) {
this[rowIndex * 128 + columnIndex * 8 + bytePosition] = ((ulongToConvert shr (bytePosition * 8)) and 0xFFU).toUByte()
}
}
}
fun ArgonBlockPointer.getColumnOfULongsForMixing(columnIndex: Int) : ULongArray {
//In Argon2 representation there are 8 double word registers (numbers, but we work with 16 single word ulongs
val ulongArray = ULongArray(16)
//There are 8 rows that consist of 2 words (registers, in our case ulongs) each
for (rowIndex in 0 until 8) {
var ulong = 0UL
//Now we create the ulong
for (bytePosition in 0 until 8) {
ulong = ulong or (this[rowIndex * 128 + columnIndex * 16 + bytePosition].toULong() shl (bytePosition * 8))
}
ulongArray[rowIndex * 2] = ulong
ulong = 0UL
// But unlike in columns where we can directly iterate and get all TWO WORD registers, here we also need to grab
// the next word
for (bytePosition in 8 until 16) {
ulong = ulong or (this[rowIndex * 128 + columnIndex * 16 + bytePosition].toULong() shl (bytePosition * 8))
}
ulongArray[rowIndex * 2 + 1] = ulong
}
return ulongArray
}
fun ArgonBlockPointer.setColumnFromMixedULongs(columnIndex: Int, ulongs: ULongArray) {
//In Argon2 representation there are 8 double word registers (numbers, but we work with 16 single word ulongs
//There are 8 rows that consist of 2 words (registers, in our case ulongs) each
var ulongToConvert = 0UL
for (rowIndex in 0 until 8) {
ulongToConvert = ulongs[rowIndex * 2]
//Now we create the ulong
for (bytePosition in 0 until 8) {
this[rowIndex * 128 + columnIndex * 16 + bytePosition] = ((ulongToConvert shr (bytePosition * 8)) and 0xFFU).toUByte()
}
// But unlike in columns where we can directly iterate and get all TWO WORD registers, here we also need to set
// the next word
ulongToConvert = ulongs[rowIndex * 2 + 1]
for (bytePosition in 8 until 16) {
this[rowIndex * 128 + columnIndex * 16 + bytePosition] = ((ulongToConvert shr (bytePosition * 8)) and 0xFFU).toUByte()
}
}
}
fun ArgonBlockPointer.getUIntFromPosition(positionInBlock: Int) : UInt {
var uint = 0U
for (i in 0 until 4) {
uint = uint or (this[positionInBlock + i].toUInt() shl (i * 8))
}
return uint
}
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 21-May-2020
*/
class ArgonMatrix(val columnCount: Int, val rowCount: Int) {
internal val storage: UByteArray = UByteArray(columnCount * rowCount * 1024)
operator fun get(rowPosition: Int, columnPosition: Int, inBlockPosition: Int) : UByte {
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[getBlockStartPositionPointer(rowPosition, columnPosition) + inBlockPosition]
}
val size = storage.size
operator fun get(absolutePosition: Int) : UByte {
return storage[absolutePosition]
}
operator fun set(rowPosition: Int, columnPosition: Int, inBlockPosition: Int, value: UByte) {
storage[getBlockStartPositionPointer(rowPosition, columnPosition) + inBlockPosition] = value
}
operator fun set(absolutePosition: Int, value: UByte) {
storage[absolutePosition] = value
}
fun getBlockPointer(rowPosition: Int, columnPosition: Int) : ArgonBlockPointer {
return ArgonBlockPointerWithMatrix(getBlockStartPositionPointer(rowPosition, columnPosition), this)
}
fun sliceArray(indices: IntRange): UByteArray {
return storage.sliceArray(indices)
}
fun getBlockAt(rowPosition: Int, columnPosition: Int) : UByteArray {
println("Expensive get")
return storage.copyOfRange(
getBlockStartPositionPointer(rowPosition, columnPosition),
getBlockStartPositionPointer(rowPosition, columnPosition) + 1024
)
}
fun setBlockAt(rowPosition: Int, columnPosition: Int, blockValue: UByteArray) {
println("Expensive set")
blockValue.copyInto(
storage,
getBlockStartPositionPointer(rowPosition, columnPosition)
)
}
private inline fun getBlockStartPositionPointer(rowPosition: Int, columnPosition: Int) : Int {
return rowPosition * columnCount * 1024 + columnPosition * 1024
}
internal fun clearMatrix() {
for( index in storage.indices) { storage[index] = 0U }
}
private class ArgonBlockPointerWithMatrix constructor(override val blockStartPosition: Int, val matrix: ArgonMatrix) : ArgonBlockPointer {
override fun pointerArithmetic(block: (Int, Int) -> Int): ArgonBlockPointer {
return ArgonBlockPointerWithMatrix(block(blockStartPosition, matrix.size), matrix)
}
override fun asInt(): Int {
return blockStartPosition
}
override operator fun get(blockPosition: Int) : UByte {
return matrix[blockStartPosition + blockPosition]
}
override fun set(blockPosition: Int, value: UByte) {
matrix[blockStartPosition + blockPosition] = value
}
override infix fun xorBlocksAndGetPointerToNewBlock(other: ArgonBlockPointer) : ArgonBlock {
return ArgonBlock(UByteArray(1024){
matrix[blockStartPosition + it] xor other[it]
})
}
override fun getAsUByteArray(): UByteArray {
return matrix.storage.slice(blockStartPosition until blockStartPosition + 1024).toUByteArray()
}
}
}
@Suppress("NON_PUBLIC_PRIMARY_CONSTRUCTOR_OF_INLINE_CLASS")
inline class ArgonBlock internal constructor(internal val storage: UByteArray) {
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)
}
val size: Int get() = storage.size
internal fun getAsUByteArray() : UByteArray = storage
fun getBlockPointer() : ArgonBlockPointer{
return ArgonBlockPointerWithBlock( this)
}
infix fun xorInplace(other: ArgonBlock) : ArgonBlock {
storage.indices.forEach {
this[it] = this[it] xor other[it]
}
return this //For chaining
}
private class ArgonBlockPointerWithBlock constructor(val storageBlock: ArgonBlock) : ArgonBlockPointer {
override val blockStartPosition: Int = 0
override fun pointerArithmetic(block: (Int, Int) -> Int): ArgonBlockPointer {
throw RuntimeException("Haven't really tought out pointer arithmetic with blocks")
}
override fun asInt(): Int {
return blockStartPosition
}
override operator fun get(blockPosition: Int) : UByte {
return storageBlock[blockPosition]
}
override fun set(blockPosition: Int, value: UByte) {
storageBlock[blockPosition] = value
}
override infix fun xorBlocksAndGetPointerToNewBlock(other: ArgonBlockPointer) : ArgonBlock {
return ArgonBlock(UByteArray(1024){
storageBlock[it] xor other[it]
})
}
override fun getAsUByteArray(): UByteArray {
return storageBlock.storage.slice(blockStartPosition until blockStartPosition + 1024).toUByteArray()
}
}
}

View File

@ -18,9 +18,7 @@
package com.ionspin.kotlin.crypto.hash.argon
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Matrix
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils
import com.ionspin.kotlin.crypto.keyderivation.argon2.Block
import com.ionspin.kotlin.crypto.keyderivation.argon2.*
import com.ionspin.kotlin.crypto.util.arrayChunked
import com.ionspin.kotlin.crypto.util.fromLittleEndianArrayToULong
import kotlin.random.Random
@ -45,7 +43,7 @@ class Argon2MatrixTest {
@Test
fun indexAccessTest() {
val argon2Matrix = Argon2Matrix(2, 2)
val argon2Matrix = ArgonMatrix(2, 2)
(zeroesBlock + onesBlock + twosBlock + threesBlock).copyInto(argon2Matrix.storage)
println(argon2Matrix[0, 0, 0])
println(argon2Matrix[0, 1, 0])
@ -74,7 +72,7 @@ class Argon2MatrixTest {
@Test
fun blockRetrievalTest() {
val argon2Matrix = Argon2Matrix(2, 2)
val argon2Matrix = ArgonMatrix(2, 2)
(zeroesBlock + onesBlock + twosBlock + threesBlock).copyInto(argon2Matrix.storage)
assertTrue {
zeroesBlock.contentEquals(argon2Matrix.getBlockAt(0, 0)) &&
@ -86,14 +84,14 @@ class Argon2MatrixTest {
@Test
fun blockColumnToUlongTest() {
val randomBlock = Block(randomBlockArray)
val randomBlock = ArgonBlock(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)
val result = randomBlock.getBlockPointer().getRowOfULongsForMixing(columnIndex)
assertTrue { expected.contentEquals(result) }
}
@ -101,12 +99,44 @@ class Argon2MatrixTest {
@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)
val randomBlock = ArgonBlock(randomBlockArray)
for (rowIndex in 0 until 8) {
val columnToMix = Argon2Utils.extractColumnFromGBlock(randomBlockArray, rowIndex)
val expected = columnToMix.arrayChunked(8).map { it.fromLittleEndianArrayToULong() }.toULongArray()
val result = randomBlock.getBlockPointer().getColumnOfULongsForMixing(rowIndex)
assertTrue { expected.contentEquals(result) }
assertTrue { expected.contentEquals(result) }
}
}
@Test
fun blockSetMixedRowTest() {
val randomBlock = ArgonBlock(randomBlockArray)
val targetBlockArray = zeroesBlock.copyOf()
val targetBlock = ArgonBlock(targetBlockArray)
for (rowIndex in 0 until 8) {
val extracted = randomBlock.getBlockPointer().getRowOfULongsForMixing(rowIndex)
targetBlock.getBlockPointer().setRowFromMixedULongs(rowIndex, extracted)
}
assertTrue {
randomBlockArray.contentEquals(targetBlock.storage)
}
}
@Test
fun blockSetMixedColumnTest() {
val randomBlock = ArgonBlock(randomBlockArray)
val targetBlockArray = zeroesBlock.copyOf()
val targetBlock = ArgonBlock(targetBlockArray)
for (columnIndex in 0 until 8) {
val extracted = randomBlock.getBlockPointer().getColumnOfULongsForMixing(columnIndex)
targetBlock.getBlockPointer().setColumnFromMixedULongs(columnIndex, extracted)
}
assertTrue {
randomBlockArray.contentEquals(targetBlock.storage)
}
}
}