Further reduction in allocations
This commit is contained in:
parent
3902b90b57
commit
4142549d2e
@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
@ -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] }
|
||||
}
|
@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user