Continuing with index work, found a bug in initial hash, was missing type
This commit is contained in:
parent
1e0a5b516e
commit
4cc7c7e92a
@ -15,6 +15,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.*
|
||||
/**
|
||||
@ -31,6 +32,7 @@ import com.ionspin.kotlin.crypto.util.*
|
||||
* on 08-Jan-2020
|
||||
*
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
@ExperimentalUnsignedTypes
|
||||
class Argon2 internal constructor(
|
||||
val password: Array<UByte>,
|
||||
@ -78,13 +80,13 @@ class Argon2 internal constructor(
|
||||
return Blake2b.digest(length + input)
|
||||
}
|
||||
//We can cast to int because UInt even if MAX_VALUE divided by 32 is guaranteed not to overflow
|
||||
val numberOfBlocks = (1U + ((length - 1U) / 32U) - 1U).toInt() // equivalent to ceil(length/32) - 1
|
||||
val v = Array<Array<UByte>>(numberOfBlocks) { emptyArray() }
|
||||
val numberOf64ByteBlocks = (1U + ((length - 1U) / 32U) - 2U).toInt() // equivalent to ceil(length/32) - 2
|
||||
val v = Array<Array<UByte>>(numberOf64ByteBlocks) { emptyArray() }
|
||||
v[0] = Blake2b.digest(length + input)
|
||||
for (i in 1 until numberOfBlocks - 1) {
|
||||
for (i in 1 until numberOf64ByteBlocks) {
|
||||
v[i] = Blake2b.digest(v[i - 1])
|
||||
}
|
||||
val remainingPartOfInput = input.copyOfRange(input.size - numberOfBlocks * 32, input.size)
|
||||
val remainingPartOfInput = input.copyOfRange(length.toInt() - numberOf64ByteBlocks * 32, input.size)
|
||||
val vLast = Blake2b.digest(remainingPartOfInput, hashLength = remainingPartOfInput.size)
|
||||
val concat =
|
||||
(v.map { it.copyOfRange(0, 32) })
|
||||
@ -217,6 +219,7 @@ class Argon2 internal constructor(
|
||||
counter.toUInt().toLittleEndianUByteArray() +
|
||||
Array<UByte>(968) { 0U }
|
||||
)
|
||||
secondPass.hexColumsPrint()
|
||||
Pair(1U, 1U)
|
||||
}
|
||||
ArgonType.Argon2d -> {
|
||||
@ -255,6 +258,63 @@ class Argon2 internal constructor(
|
||||
val laneCounter: Int
|
||||
)
|
||||
|
||||
private fun computeIndexNew(matrix : Array<Array<Array<UByte>>>, lane: Int, column: Int, columnCount: Int, parallelism: Int, iteration : Int, slice : Int, argonType: ArgonType) : Pair<Int, Int> {
|
||||
val (j1, j2) = when (argonType) {
|
||||
ArgonType.Argon2d -> {
|
||||
val previousBlock = if (column == 0) {
|
||||
matrix[lane - 1][columnCount - 1]
|
||||
} else {
|
||||
matrix[lane][column - 1]
|
||||
}
|
||||
val first32Bit = previousBlock.sliceArray(0 until 4).fromLittleEndianArrayToUInt()
|
||||
val second32Bit = previousBlock.sliceArray(4 until 8).fromLittleEndianArrayToUInt()
|
||||
Pair(first32Bit, second32Bit)
|
||||
}
|
||||
ArgonType.Argon2i -> TODO()
|
||||
ArgonType.Argon2id -> TODO()
|
||||
}
|
||||
|
||||
|
||||
//If this is first iteration and first slice, block is taken from the current lane
|
||||
val l = if (iteration == 0 && slice == 0) {
|
||||
lane
|
||||
} else {
|
||||
val lol = (j2.toBigInteger() % parallelism).intValue()
|
||||
lol
|
||||
}
|
||||
|
||||
//From Argon 2 2020 draft
|
||||
|
||||
// The set W contains the indices that can be referenced according to
|
||||
// the following rules:
|
||||
// 1. If l is the current lane, then W includes the indices of all
|
||||
// blocks in the last SL - 1 = 3 segments computed and finished, as
|
||||
// well as the blocks computed in the current segment in the current
|
||||
// pass excluding B[i][j-1].
|
||||
//
|
||||
// 2. If l is not the current lane, then W includes the indices of all
|
||||
// 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) {
|
||||
if (slice == 0) {
|
||||
//All indices except the previous
|
||||
val from0Until = column - 1
|
||||
} else {
|
||||
if (lane == l) {
|
||||
//Same lane
|
||||
val from0Until = slice * (columnCount / 4) + column - 1
|
||||
} else {
|
||||
val from0Until = slice * (columnCount / 4) + if(column == 0) { -1 } else { 0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val availableIndicesSet =
|
||||
|
||||
return Pair(l, j2.toInt())
|
||||
}
|
||||
|
||||
internal fun derive(
|
||||
password: Array<UByte>,
|
||||
salt: Array<UByte>,
|
||||
@ -267,61 +327,100 @@ class Argon2 internal constructor(
|
||||
associatedData: Array<UByte>,
|
||||
type: ArgonType
|
||||
): Array<UByte> {
|
||||
|
||||
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)
|
||||
val h0 = Blake2b.digest(
|
||||
parallelism.toLittleEndianUByteArray() + tagLength.toLittleEndianUByteArray() + memorySize.toLittleEndianUByteArray() +
|
||||
numberOfIterations.toLittleEndianUByteArray() + versionNumber.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 blockCount = (memorySize / (4U * parallelism)) * (4U * parallelism) // TODO hmmm
|
||||
val columnCount = blockCount / parallelism
|
||||
h0.hexColumsPrint(8)
|
||||
|
||||
//TODO pass handling
|
||||
val allPasses = (0..numberOfIterations.toLong()).map { pass ->
|
||||
//Allocate memory as Array of parallelism rows and columnCount colums
|
||||
val blockCount = (memorySize / (4U * parallelism)) * (4U * parallelism)
|
||||
val columnCount = (blockCount / parallelism).toInt()
|
||||
val segmentLength = columnCount / 4
|
||||
|
||||
// First iteration
|
||||
|
||||
//Allocate memory as Array of parallelism rows (lanes) and columnCount columns
|
||||
val matrix = Array(parallelism.toInt()) {
|
||||
Array(columnCount.toInt()) {
|
||||
Array(columnCount) {
|
||||
Array<UByte>(1024) { 0U }
|
||||
}
|
||||
}
|
||||
// matrix.hexPrint()
|
||||
|
||||
//Compute B[i][0]
|
||||
for (i in 0..parallelism.toInt()) {
|
||||
for (i in 0 until parallelism.toInt()) {
|
||||
matrix[i][0] =
|
||||
argonBlake2bArbitraryLenghtHash(
|
||||
h0 + 0.toUInt().toLittleEndianUByteArray() + i.toUInt().toLittleEndianUByteArray(),
|
||||
64U
|
||||
1024U
|
||||
)
|
||||
}
|
||||
|
||||
//Compute B[i][1]
|
||||
for (i in 0..parallelism.toInt()) {
|
||||
matrix[i][0] =
|
||||
for (i in 0 until parallelism.toInt()) {
|
||||
matrix[i][1] =
|
||||
argonBlake2bArbitraryLenghtHash(
|
||||
h0 + 1.toUInt().toLittleEndianUByteArray() + i.toUInt().toLittleEndianUByteArray(),
|
||||
64U
|
||||
1024U
|
||||
)
|
||||
}
|
||||
|
||||
for (i in 0..parallelism.toInt()) {
|
||||
for (j in 1..columnCount.toInt()) {
|
||||
|
||||
val counter = 0 //TODO handle counter
|
||||
computeIndexes(matrix, parallelism, pass, i, j, blockCount, numberOfIterations, type)
|
||||
val iPrim = -1
|
||||
val jPrim = -1
|
||||
matrix[i][j] = compressionFunctionG(matrix[i][j - 1], matrix[iPrim][jPrim])
|
||||
//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])
|
||||
}
|
||||
}
|
||||
//Remaining iteration
|
||||
val remainingIterations = (1..numberOfIterations.toInt()).map { iteration ->
|
||||
|
||||
val result = matrix.foldIndexed(emptyArray<UByte>()) { index, acc, arrayOfArrays ->
|
||||
return if (acc.size == 0) {
|
||||
acc + arrayOfArrays[columnCount.toInt() - 1]
|
||||
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
|
||||
// )
|
||||
|
||||
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 {
|
||||
acc.mapIndexed { index, it -> it xor arrayOfArrays[columnCount.toInt() - 1][index] }
|
||||
matrix[i][j] = compressionFunctionG(matrix[i][j - 1], matrix[l][z])
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
val result = matrix.foldIndexed(emptyArray<UByte>()) { 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()
|
||||
}
|
||||
}
|
||||
@ -330,10 +429,27 @@ class Argon2 internal constructor(
|
||||
|
||||
|
||||
|
||||
return allPasses.foldRight(emptyArray()) { arrayOfUBytes, acc -> acc xor arrayOfUBytes } //TODO placeholder
|
||||
return remainingIterations.foldRight(emptyArray()) { arrayOfUBytes, acc -> acc xor arrayOfUBytes } //TODO placeholder
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun calculate(): Array<UByte> {
|
||||
return derive(
|
||||
password, salt, parallelism, tagLength, memorySize, numberOfIterations, versionNumber, key, associatedData, type
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
internal object ArgonDebugUtils {
|
||||
fun Array<Array<Array<UByte>>>.hexPrint() {
|
||||
forEachIndexed { i, lane ->
|
||||
lane.forEachIndexed { j, column ->
|
||||
println("Printing position at [$i], [$j]")
|
||||
column.hexColumsPrint(32)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,8 +26,8 @@ fun Array<Byte>.hexColumsPrint() {
|
||||
printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) }
|
||||
}
|
||||
|
||||
fun Array<UByte>.hexColumsPrint() {
|
||||
val printout = this.map { it.toString(16) }.chunked(16)
|
||||
fun Array<UByte>.hexColumsPrint(chunk : Int = 16) {
|
||||
val printout = this.map { it.toString(16).padStart(2, '0') }.chunked(chunk)
|
||||
printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) }
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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_UNSIGNED_LITERALS", "EXPERIMENTAL_API_USAGE")
|
||||
|
||||
package com.ionspin.kotlin.crypto.hash.keyderivation
|
||||
|
||||
import com.ionspin.kotlin.crypto.keyderivation.Argon2
|
||||
import kotlin.test.Test
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 10-May-2020
|
||||
*/
|
||||
@ExperimentalStdlibApi
|
||||
class Argon2Test {
|
||||
|
||||
@Test
|
||||
fun debugTest() {
|
||||
val memory = 32U //KiB
|
||||
val iterations = 3U
|
||||
val parallelism = 4U
|
||||
val tagLength = 32U
|
||||
val password: Array<UByte> = arrayOf(
|
||||
0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U,
|
||||
0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U,
|
||||
0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U,
|
||||
0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U
|
||||
)
|
||||
val salt: Array<UByte> = arrayOf(0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U)
|
||||
val secret: Array<UByte> = arrayOf(0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U)
|
||||
val associatedData: Array<UByte> = arrayOf(0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U)
|
||||
|
||||
val digest = Argon2(
|
||||
password,
|
||||
salt,
|
||||
parallelism,
|
||||
tagLength,
|
||||
memory,
|
||||
iterations,
|
||||
0x13U,
|
||||
secret,
|
||||
associatedData,
|
||||
type = Argon2.ArgonType.Argon2d
|
||||
)
|
||||
val result = digest.calculate()
|
||||
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user