From 297a2d496917e33ddf68bbb401922f717e9d8e6d Mon Sep 17 00:00:00 2001 From: Ugljesa Jovanovic Date: Wed, 13 May 2020 22:00:41 +0200 Subject: [PATCH] Further step by step build --- .../kotlin/crypto/keyderivation/Argon2.kt | 287 ++++++++++++++---- .../com/ionspin/kotlin/crypto/util/Util.kt | 2 +- 2 files changed, 222 insertions(+), 67 deletions(-) diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/Argon2.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/Argon2.kt index 143fac1..f656ea0 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/Argon2.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/Argon2.kt @@ -18,6 +18,7 @@ package com.ionspin.kotlin.crypto.keyderivation import com.ionspin.kotlin.bignum.integer.toBigInteger import com.ionspin.kotlin.crypto.hash.blake2b.Blake2b import com.ionspin.kotlin.crypto.util.* + /** * * Further resources and examples of implementation: @@ -63,15 +64,14 @@ class Argon2 internal constructor( } - @ExperimentalStdlibApi companion object { - fun Array.xor(target : Array, other : Array) { + fun Array.xor(target: Array, other: Array) { if (this.size != other.size || this.size != target.size) { throw RuntimeException("Invalid array sizes, this ${this.size}, other ${other.size}") } - target.mapIndexed { index, _ -> this[index] xor other[index]} + target.mapIndexed { index, _ -> this[index] xor other[index] } } @@ -99,6 +99,7 @@ class Argon2 internal constructor( fun compressionFunctionG(x: Array, y: Array): Array { val r = x xor y + // Xor works in first pass! // val r = Array(1024) { 0U } // view as 8x8 matrix of 16 byte registers // x.forEachIndexed { index, it -> r[index] = it xor y[index] } // R = X xor Y val q = Array(1024) { 0U } @@ -111,7 +112,7 @@ class Argon2 internal constructor( .map { it.toLittleEndianUByteArray() } .flatMap { it.asIterable() } .toTypedArray() - .copyInto(q, startOfRow, endOfRow) + .copyInto(q, startOfRow) } // Do the argon/blake2b mixing on columns for (i in 0..7) { @@ -179,7 +180,7 @@ class Argon2 internal constructor( private fun computeIndexes( indexContext: IndexContext, - matrix : Array>> + matrix: Array>> ): Pair { val block = indexContext.indexMatrix val parallelism = indexContext.parallelism @@ -258,11 +259,20 @@ class Argon2 internal constructor( val laneCounter: Int ) - private fun computeIndexNew(matrix : Array>>, lane: Int, column: Int, columnCount: Int, parallelism: Int, iteration : Int, slice : Int, argonType: ArgonType) : Pair { + private fun computeIndexNew( + matrix: Array>>, + lane: Int, + column: Int, + columnCount: Int, + parallelism: Int, + iteration: Int, + slice: Int, + argonType: ArgonType + ): Pair { val (j1, j2) = when (argonType) { ArgonType.Argon2d -> { val previousBlock = if (column == 0) { - matrix[lane - 1][columnCount - 1] + matrix[lane][columnCount - 1] //Get last block in the SAME lane } else { matrix[lane][column - 1] } @@ -296,25 +306,79 @@ class Argon2 internal constructor( // blocks in the last SL - 1 = 3 segments computed and finished in // lane l. If B[i][j] is the first block of a segment, then the // very last index from W is excluded. - if (iteration == 0) { + val referenceAreaSize = if (iteration == 0) { if (slice == 0) { //All indices except the previous - val from0Until = column - 1 + column - 1 } else { if (lane == l) { //Same lane - val from0Until = slice * (columnCount / 4) + column - 1 + column - 1 } else { - val from0Until = slice * (columnCount / 4) + if(column == 0) { -1 } else { 0 } + slice * (columnCount / 4) + if (column % (columnCount / 4) == 0) { // Check if column is first block of the SEGMENT + -1 + } else { + 0 + } + } + } + } else { + if (lane == l) { + columnCount - (columnCount / 4) + column - 1 + } else { + columnCount - (columnCount / 4) + if (column == 0) { + -1 + } else { + 0 } } } - val availableIndicesSet = + val x = (j1.toULong() * j1) shr 32 + val y = (referenceAreaSize.toULong() * x) shr 32 + val z = referenceAreaSize.toULong() - 1U - y - return Pair(l, j2.toInt()) + val startPosition = if (iteration == 0) { + 0 + } else { + if (slice == 3) { + 0 + } else { + (slice + 1) * (columnCount / 4) //TODO replace all of these with segment length when consolidating variables + } + } + + val absolutePosition = (startPosition + z.toInt()) % columnCount + + return Pair(l, absolutePosition) } + data class ArgonContext( + val password: Array, + val salt: Array, + val parallelism: UInt, + val tagLength: UInt, + val memorySize: UInt, + val numberOfIterations: UInt, + val versionNumber: UInt, + val key: Array, + val associatedData: Array, + val type: ArgonType + ) + + data class ArgonInternalContext( + val matrix: Array>>, + val blockCount : UInt, + val columnCount : Int, + val segmentLength: Int + ) + + data class SegmentPosition( + val iteration: Int, + val lane: Int, + val slice: Int + ) + internal fun derive( password: Array, salt: Array, @@ -327,19 +391,34 @@ class Argon2 internal constructor( associatedData: Array, type: ArgonType ): Array { + val argonContext = ArgonContext( + password = password, + salt = salt, + parallelism = parallelism, + tagLength = tagLength, + memorySize = memorySize, + numberOfIterations = numberOfIterations, + versionNumber = versionNumber, + key = key, + associatedData = associatedData, + type = type + ) println("H0 Input") - val toDigest = parallelism.toLittleEndianUByteArray() + tagLength.toLittleEndianUByteArray() + memorySize.toLittleEndianUByteArray() + - numberOfIterations.toLittleEndianUByteArray() + versionNumber.toLittleEndianUByteArray() + type.typeId.toUInt().toLittleEndianUByteArray() + - password.size.toUInt().toLittleEndianUByteArray() + password + - salt.size.toUInt().toLittleEndianUByteArray() + salt + - key.size.toUInt().toLittleEndianUByteArray() + key + - associatedData.size.toUInt().toLittleEndianUByteArray() + associatedData + val toDigest = + parallelism.toLittleEndianUByteArray() + tagLength.toLittleEndianUByteArray() + memorySize.toLittleEndianUByteArray() + + numberOfIterations.toLittleEndianUByteArray() + versionNumber.toLittleEndianUByteArray() + type.typeId.toUInt() + .toLittleEndianUByteArray() + + password.size.toUInt().toLittleEndianUByteArray() + password + + salt.size.toUInt().toLittleEndianUByteArray() + salt + + key.size.toUInt().toLittleEndianUByteArray() + key + + associatedData.size.toUInt().toLittleEndianUByteArray() + associatedData toDigest.hexColumsPrint(16) println("Marker H0 Input end") val h0 = Blake2b.digest( parallelism.toLittleEndianUByteArray() + tagLength.toLittleEndianUByteArray() + memorySize.toLittleEndianUByteArray() + - numberOfIterations.toLittleEndianUByteArray() + versionNumber.toLittleEndianUByteArray() + type.typeId.toUInt().toLittleEndianUByteArray()+ + numberOfIterations.toLittleEndianUByteArray() + versionNumber.toLittleEndianUByteArray() + type.typeId.toUInt() + .toLittleEndianUByteArray() + password.size.toUInt().toLittleEndianUByteArray() + password + salt.size.toUInt().toLittleEndianUByteArray() + salt + key.size.toUInt().toLittleEndianUByteArray() + key + @@ -370,6 +449,7 @@ class Argon2 internal constructor( h0 + 0.toUInt().toLittleEndianUByteArray() + i.toUInt().toLittleEndianUByteArray(), 1024U ) + println("Start, matrix [$i][0]") matrix[i][0].hexColumsPrint(16) println("Marker, matrix [$i][0]") } @@ -381,69 +461,144 @@ class Argon2 internal constructor( h0 + 1.toUInt().toLittleEndianUByteArray() + i.toUInt().toLittleEndianUByteArray(), 1024U ) + println("Start, matrix [$i][1]") matrix[i][1].hexColumsPrint(16) println("Marker, matrix [$i][1]") } - //Compute B[i][j] - //Using B[i][j] = G(B[i][j], B[l][z]) where l and z are provided bu computeIndexes - for (i in 0 until parallelism.toInt()) { - for (j in 2..columnCount) { - val (l, z) = computeIndexNew(matrix, i, j, columnCount, parallelism.toInt(), 0, 0, type) - matrix[i][j] = compressionFunctionG(matrix[i][j], matrix[l][z]) + // ---- Good until here at least ---- + val argonInternalContext = ArgonInternalContext( + matrix, blockCount, columnCount, segmentLength + ) + singleThreaded(argonContext, argonInternalContext) + + return emptyArray() + } + + fun singleThreaded(argonContext: ArgonContext, argonInternalContext: ArgonInternalContext ) { + for (iteration in 0 until argonContext.numberOfIterations.toInt()) { + for (slice in 0 until 4) { + for (lane in 0 until argonContext.parallelism.toInt()) { + println("Processing segment I: $iteration, S: $slice, L: $lane") + val segmentPosition = SegmentPosition(iteration, lane, slice) + processSegment(argonContext, argonInternalContext, segmentPosition) + } } } - //Remaining iteration - val remainingIterations = (1..numberOfIterations.toInt()).map { iteration -> + } - for (i in 0 until parallelism.toInt()) { - for (j in 0 until columnCount) { -// val indexContext = IndexContext( -// indexMatrix = emptyArray(), -// parallelism = parallelism, -// pass = pass, -// lane = i, -// column = j, -// blockCount = blockCount, -// iterationCount = numberOfIterations, -// type = type, -// laneCounter = 0 + fun processSegment(argonContext: ArgonContext, argonInternalContext: ArgonInternalContext, segmentPosition: SegmentPosition) { + val password = argonContext.password + val salt = argonContext.salt + val parallelism = argonContext.parallelism + val tagLength = argonContext.tagLength + val memorySize = argonContext.memorySize + val numberOfIterations = argonContext.numberOfIterations + val versionNumber = argonContext.versionNumber + val key = argonContext.key + val associatedData = argonContext.associatedData + val type = argonContext.type + + val matrix = argonInternalContext.matrix + val blockCount = argonInternalContext.blockCount + val columnCount = argonInternalContext.columnCount + val segmentLength = argonInternalContext.segmentLength + + val iteration = segmentPosition.iteration + val lane = segmentPosition.lane + val slice = segmentPosition.slice + + + if (iteration == 0) { + //Compute B[i][j] + //Using B[i][j] = G(B[i][j], B[l][z]) where l and z are provided bu computeIndexes + //Because this is iteration 0 we have B[i][0] and B[i][1] already filled, so whenever we + //are processing first segment we skip these two blocks + if (slice == 0) { + for (column in 2..(slice * segmentLength)) { + val (l, z) = computeIndexNew(matrix, lane, column, columnCount, parallelism.toInt(), 0, 0, type) + println("Calling compress for I: $iteration S: $slice Lane: $lane Column: $column with l: $l z: $z") + matrix[lane][column] = compressionFunctionG(matrix[lane][column - 1], matrix[l][z]) + } + } else { + for (column in (slice * segmentLength)..((slice + 1) * segmentLength)) { + val (l, z) = computeIndexNew(matrix, lane, column, columnCount, parallelism.toInt(), iteration, slice, type) + println("Calling compress for I: $iteration S: $slice Lane: $lane Column: $column with l: $l z: $z") + matrix[lane][column] = compressionFunctionG(matrix[lane][column - 1], matrix[l][z]) + } + } + } else { + val (l, z) = computeIndexNew(matrix, lane, 0, columnCount, parallelism.toInt(), 0, 0, type) + matrix[lane][0] = compressionFunctionG(matrix[lane][columnCount - 1], matrix[l][z]) + for (column in 1..(slice * segmentLength)) { + val (l, z) = computeIndexNew(matrix, lane, column, columnCount, parallelism.toInt(), 0, 0, type) + println("Calling compress for I: $iteration S: $slice Lane: $lane Column: $column with l: $l z: $z") + matrix[lane][column] = compressionFunctionG(matrix[lane][column - 1], matrix[l][z]) + } + + } + + + + +// //Remaining iteration +// val remainingIterations = (1..numberOfIterations.toInt()).map { iteration -> +// +// for (i in 0 until parallelism.toInt()) { +// for (j in 0 until columnCount) { +// val (l, z) = computeIndexNew( +// matrix, +// i, +// j, +// columnCount, +// parallelism.toInt(), +// iteration, +// iteration / segmentLength, +// type // ) - - val (l,z) = computeIndexNew(matrix, i, j, columnCount, parallelism.toInt(), iteration, iteration / segmentLength, type) - if (j == 0) { - matrix[i][j] = compressionFunctionG(matrix[i][columnCount - 1], matrix[l][z]) - } else { - matrix[i][j] = compressionFunctionG(matrix[i][j - 1], matrix[l][z]) - } - - } - } +// if (j == 0) { +// matrix[i][j] = compressionFunctionG(matrix[i][columnCount - 1], matrix[l][z]) +// } else { +// matrix[i][j] = compressionFunctionG(matrix[i][j - 1], matrix[l][z]) +// } +// +// } +// } +// +// +// val result = matrix.foldIndexed(emptyArray()) { lane, acc, laneArray -> +// return if (acc.size == 0) { +// acc + laneArray[columnCount - 1] // add last element in first lane to the accumulator +// } else { +// // For each element in our accumulator, xor it with an appropriate element from the last column in current lane (from 1 to `parallelism`) +// acc.mapIndexed { index, it -> it xor laneArray[columnCount - 1][index] } +// .toTypedArray() +// } +// } +// result +// } - val result = matrix.foldIndexed(emptyArray()) { lane, acc, laneArray -> - return if (acc.size == 0) { - acc + laneArray[columnCount - 1] // add last element in first lane to the accumulator - } else { - // For each element in our accumulator, xor it with an appropriate element from the last column in current lane (from 1 to `parallelism`) - acc.mapIndexed { index, it -> it xor laneArray[columnCount - 1][index] } - .toTypedArray() - } - } - result - } - - - - return remainingIterations.foldRight(emptyArray()) { arrayOfUBytes, acc -> acc xor arrayOfUBytes } //TODO placeholder +// return remainingIterations.foldRight(emptyArray()) { arrayOfUBytes, acc -> acc xor arrayOfUBytes } //TODO placeholder } } + + fun calculate(): Array { return derive( - password, salt, parallelism, tagLength, memorySize, numberOfIterations, versionNumber, key, associatedData, type + password, + salt, + parallelism, + tagLength, + memorySize, + numberOfIterations, + versionNumber, + key, + associatedData, + type ) } diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/Util.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/Util.kt index ebc0085..5c148ef 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/Util.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/Util.kt @@ -70,7 +70,7 @@ infix fun Array.xor(other : Array) : Array { if (this.size != other.size) { throw RuntimeException("Operands of different sizes are not supported yet") } - return Array(this.size) { this[it] xor other [it]} + return Array(this.size) { this[it] xor other[it] } } @ExperimentalUnsignedTypes