Cleanup, implementing 2i
This commit is contained in:
parent
6f51a0ec66
commit
6af623eef6
@ -35,7 +35,7 @@ import com.ionspin.kotlin.crypto.util.*
|
|||||||
*/
|
*/
|
||||||
@ExperimentalStdlibApi
|
@ExperimentalStdlibApi
|
||||||
@ExperimentalUnsignedTypes
|
@ExperimentalUnsignedTypes
|
||||||
class Argon2 internal constructor(
|
class Argon2Template internal constructor(
|
||||||
val password: Array<UByte>,
|
val password: Array<UByte>,
|
||||||
val salt: Array<UByte>,
|
val salt: Array<UByte>,
|
||||||
val parallelism: UInt,
|
val parallelism: UInt,
|
@ -0,0 +1,326 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import com.ionspin.kotlin.bignum.integer.toBigInteger
|
||||||
|
import com.ionspin.kotlin.crypto.hash.blake2b.Blake2b
|
||||||
|
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.argonBlake2bArbitraryLenghtHash
|
||||||
|
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.compressionFunctionG
|
||||||
|
import com.ionspin.kotlin.crypto.util.fromLittleEndianArrayToUInt
|
||||||
|
import com.ionspin.kotlin.crypto.util.hexColumsPrint
|
||||||
|
import com.ionspin.kotlin.crypto.util.toLittleEndianUByteArray
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Ugljesa Jovanovic
|
||||||
|
* ugljesa.jovanovic@ionspin.com
|
||||||
|
* on 16-May-2020
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum class ArgonType(val typeId: Int) {
|
||||||
|
Argon2d(0), Argon2i(1), Argon2id(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
data class SegmentPosition(
|
||||||
|
val iteration: Int,
|
||||||
|
val lane: Int,
|
||||||
|
val slice: Int
|
||||||
|
)
|
||||||
|
|
||||||
|
class Argon2(
|
||||||
|
val password: Array<UByte>,
|
||||||
|
val salt: Array<UByte> = emptyArray(),
|
||||||
|
val parallelism: Int = 1,
|
||||||
|
val tagLength: UInt = 64U,
|
||||||
|
requestedMemorySize: UInt = 0U,
|
||||||
|
val numberOfIterations: UInt = 1U,
|
||||||
|
val key: Array<UByte> = emptyArray(),
|
||||||
|
val associatedData: Array<UByte> = emptyArray(),
|
||||||
|
val argonType: ArgonType = ArgonType.Argon2id
|
||||||
|
) {
|
||||||
|
//We support only the latest version
|
||||||
|
val versionNumber: UInt = 0x13U
|
||||||
|
|
||||||
|
//Use either requested memory size, or default, or throw exception if the requested amount is less than 8*parallelism
|
||||||
|
val memorySize = if (requestedMemorySize == 0U) {
|
||||||
|
((8 * parallelism) * 2).toUInt()
|
||||||
|
} else {
|
||||||
|
if (requestedMemorySize < (8 * parallelism).toUInt()) {
|
||||||
|
throw RuntimeException("Requested memory size must be larger than 8 * parallelism. Requested size: $requestedMemorySize")
|
||||||
|
}
|
||||||
|
requestedMemorySize
|
||||||
|
}
|
||||||
|
val blockCount = (memorySize / (4U * parallelism.toUInt())) * (4U * parallelism.toUInt())
|
||||||
|
val columnCount = (blockCount / parallelism.toUInt()).toInt()
|
||||||
|
val segmentLength = columnCount / 4
|
||||||
|
|
||||||
|
val useIndependentAddressing = argonType == ArgonType.Argon2id || argonType == ArgonType.Argon2i
|
||||||
|
|
||||||
|
|
||||||
|
// State
|
||||||
|
val matrix = Array(parallelism) {
|
||||||
|
Array(columnCount) {
|
||||||
|
Array<UByte>(1024) { 0U }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun clearMatrix() {
|
||||||
|
matrix.forEachIndexed { laneIndex, lane ->
|
||||||
|
lane.forEachIndexed { columnIndex, block ->
|
||||||
|
block.forEachIndexed { byteIndex, byte ->
|
||||||
|
matrix[laneIndex][columnIndex][byteIndex] = 0U
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun populateAddressBlock(
|
||||||
|
iteration: Int,
|
||||||
|
slice: Int,
|
||||||
|
lane: Int,
|
||||||
|
addressBlock: Array<UByte>,
|
||||||
|
addressCounter: ULong
|
||||||
|
): Array<UByte> {
|
||||||
|
//Calculate first pass
|
||||||
|
val firstPass = compressionFunctionG(
|
||||||
|
Array<UByte>(1024) { 0U },
|
||||||
|
iteration.toULong().toLittleEndianUByteArray() +
|
||||||
|
lane.toULong().toLittleEndianUByteArray() +
|
||||||
|
slice.toULong().toLittleEndianUByteArray() +
|
||||||
|
blockCount.toULong().toLittleEndianUByteArray() +
|
||||||
|
numberOfIterations.toULong().toLittleEndianUByteArray() +
|
||||||
|
argonType.typeId.toULong().toLittleEndianUByteArray() +
|
||||||
|
addressCounter.toLittleEndianUByteArray() +
|
||||||
|
Array<UByte>(968) { 0U },
|
||||||
|
addressBlock,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
//Calculate second pass
|
||||||
|
val secondPass = compressionFunctionG(
|
||||||
|
firstPass,
|
||||||
|
addressBlock,
|
||||||
|
addressBlock,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
// Put into address block
|
||||||
|
return secondPass
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun computeReferenceBlockIndexes(iteration: Int, slice: Int, lane: Int, column: Int, addressBlock: Array<UByte>?): Pair<Int, Int> {
|
||||||
|
val (j1, j2) = when (argonType) {
|
||||||
|
ArgonType.Argon2d -> {
|
||||||
|
val previousBlock = if (column == 0) {
|
||||||
|
matrix[lane][columnCount - 1] //Get last block in the SAME lane
|
||||||
|
} 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 -> {
|
||||||
|
val selectedAddressBlock = addressBlock!!.sliceArray((column * 8) until (column * 8) + 8)
|
||||||
|
val first32Bit = selectedAddressBlock.sliceArray(0 until 4).fromLittleEndianArrayToUInt()
|
||||||
|
val second32Bit = selectedAddressBlock.sliceArray(4 until 8).fromLittleEndianArrayToUInt()
|
||||||
|
Pair(first32Bit, second32Bit)
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
(j2.toBigInteger() % parallelism).intValue()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
val segmentIndex = (column % (columnCount / 4))
|
||||||
|
val referenceAreaSize = if (iteration == 0) {
|
||||||
|
if (slice == 0) {
|
||||||
|
//All indices except the previous
|
||||||
|
segmentIndex - 1
|
||||||
|
} else {
|
||||||
|
if (lane == l) {
|
||||||
|
//Same lane
|
||||||
|
column - 1
|
||||||
|
} else {
|
||||||
|
slice * (columnCount / 4) + if (segmentIndex == 0) { // Check if column is first block of the SEGMENT
|
||||||
|
-1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (lane == l) {
|
||||||
|
columnCount - (columnCount / 4) + (segmentIndex - 1)
|
||||||
|
} else {
|
||||||
|
columnCount - (columnCount / 4) + if (segmentIndex == 0) {
|
||||||
|
-1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val x = (j1.toULong() * j1) shr 32
|
||||||
|
val y = (referenceAreaSize.toULong() * x) shr 32
|
||||||
|
val z = referenceAreaSize.toULong() - 1U - y
|
||||||
|
|
||||||
|
val startPosition = if (iteration == 0) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
if (slice == 3) {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
(slice + 1) * segmentLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val absolutePosition = (startPosition + z.toInt()) % columnCount
|
||||||
|
|
||||||
|
return Pair(l, absolutePosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun derive(): Array<UByte> {
|
||||||
|
val h0 = Blake2b.digest(
|
||||||
|
parallelism.toUInt()
|
||||||
|
.toLittleEndianUByteArray() + tagLength.toLittleEndianUByteArray() + memorySize.toLittleEndianUByteArray() +
|
||||||
|
numberOfIterations.toLittleEndianUByteArray() + versionNumber.toLittleEndianUByteArray() + argonType.typeId.toUInt()
|
||||||
|
.toLittleEndianUByteArray() +
|
||||||
|
password.size.toUInt().toLittleEndianUByteArray() + password +
|
||||||
|
salt.size.toUInt().toLittleEndianUByteArray() + salt +
|
||||||
|
key.size.toUInt().toLittleEndianUByteArray() + key +
|
||||||
|
associatedData.size.toUInt().toLittleEndianUByteArray() + associatedData
|
||||||
|
)
|
||||||
|
|
||||||
|
//Compute B[i][0]
|
||||||
|
for (i in 0 until parallelism.toInt()) {
|
||||||
|
matrix[i][0] =
|
||||||
|
argonBlake2bArbitraryLenghtHash(
|
||||||
|
h0 + 0.toUInt().toLittleEndianUByteArray() + i.toUInt().toLittleEndianUByteArray(),
|
||||||
|
1024U
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
//Compute B[i][1]
|
||||||
|
for (i in 0 until parallelism.toInt()) {
|
||||||
|
matrix[i][1] =
|
||||||
|
argonBlake2bArbitraryLenghtHash(
|
||||||
|
h0 + 1.toUInt().toLittleEndianUByteArray() + i.toUInt().toLittleEndianUByteArray(),
|
||||||
|
1024U
|
||||||
|
)
|
||||||
|
}
|
||||||
|
executeArgonWithSingleThread()
|
||||||
|
|
||||||
|
val result = matrix.foldIndexed(emptyArray<UByte>()) { lane, acc, laneArray ->
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Hash the xored last blocks
|
||||||
|
println("Tag:")
|
||||||
|
val hash = argonBlake2bArbitraryLenghtHash(result, tagLength)
|
||||||
|
return hash
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun executeArgonWithSingleThread() {
|
||||||
|
for (iteration in 0 until numberOfIterations.toInt()) {
|
||||||
|
for (slice in 0 until 4) {
|
||||||
|
for (lane in 0 until parallelism.toInt()) {
|
||||||
|
println("Processing segment I: $iteration, S: $slice, L: $lane")
|
||||||
|
val segmentPosition = SegmentPosition(iteration, lane, slice)
|
||||||
|
processSegment(segmentPosition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Debug prints
|
||||||
|
println("Done with $iteration")
|
||||||
|
matrix[0][0].slice(0..7).toTypedArray().hexColumsPrint(8)
|
||||||
|
matrix[parallelism.toInt() - 1][columnCount - 1].slice(
|
||||||
|
1016..1023
|
||||||
|
).toTypedArray().hexColumsPrint(8)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun processSegment(segmentPosition: SegmentPosition) {
|
||||||
|
val iteration = segmentPosition.iteration
|
||||||
|
val slice = segmentPosition.slice
|
||||||
|
val lane = segmentPosition.lane
|
||||||
|
|
||||||
|
var addressBlock : Array<UByte>? = null
|
||||||
|
var addressCounter = 1UL //Starts from 1 in each segment as defined by the spec
|
||||||
|
|
||||||
|
//Generate initial segment address block
|
||||||
|
if (useIndependentAddressing) {
|
||||||
|
addressBlock = Array<UByte>(1024) {
|
||||||
|
0U
|
||||||
|
}
|
||||||
|
addressBlock = populateAddressBlock(iteration, slice, lane, addressBlock, addressCounter)
|
||||||
|
addressCounter++
|
||||||
|
|
||||||
|
addressBlock.hexColumsPrint(16)
|
||||||
|
}
|
||||||
|
|
||||||
|
val startColumn = if (iteration == 0 && slice == 0) {
|
||||||
|
2
|
||||||
|
} else {
|
||||||
|
slice * segmentLength
|
||||||
|
}
|
||||||
|
|
||||||
|
for (column in startColumn until (slice + 1) * segmentLength) {
|
||||||
|
//Each address block contains 128 addresses, and we use one per iteration,
|
||||||
|
//so once we do 128 iterations we need to calculate a new address block
|
||||||
|
if (useIndependentAddressing && column % 128 == 0) {
|
||||||
|
addressBlock = populateAddressBlock(iteration, slice, lane, addressBlock!!, addressCounter)
|
||||||
|
addressCounter++
|
||||||
|
}
|
||||||
|
val previousColumn = if (column == 0) {
|
||||||
|
columnCount - 1
|
||||||
|
} else {
|
||||||
|
column - 1
|
||||||
|
}
|
||||||
|
val (l, z) = computeReferenceBlockIndexes(
|
||||||
|
iteration,
|
||||||
|
slice,
|
||||||
|
lane,
|
||||||
|
column,
|
||||||
|
addressBlock
|
||||||
|
)
|
||||||
|
println("Calling compress for I: $iteration S: $slice Lane: $lane Column: $column with l: $l z: $z")
|
||||||
|
matrix[lane][column] =
|
||||||
|
compressionFunctionG(
|
||||||
|
matrix[lane][previousColumn],
|
||||||
|
matrix[l][z],
|
||||||
|
matrix[lane][column],
|
||||||
|
true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
import com.ionspin.kotlin.crypto.hash.blake2b.Blake2b
|
||||||
|
import com.ionspin.kotlin.crypto.keyderivation.Argon2Template
|
||||||
|
import com.ionspin.kotlin.crypto.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Ugljesa Jovanovic
|
||||||
|
* ugljesa.jovanovic@ionspin.com
|
||||||
|
* on 16-May-2020
|
||||||
|
*/
|
||||||
|
object Argon2Utils {
|
||||||
|
|
||||||
|
const val R1 = 32
|
||||||
|
const val R2 = 24
|
||||||
|
const val R3 = 16
|
||||||
|
const val R4 = 63
|
||||||
|
|
||||||
|
//based on Blake2b mixRound
|
||||||
|
internal inline fun mixRound(input: Array<UByte>): Array<ULong> {
|
||||||
|
var v = input.chunked(8).map { it.fromLittleEndianArrayToULong() }.toTypedArray()
|
||||||
|
v = mix(v, 0, 4, 8, 12)
|
||||||
|
v = mix(v, 1, 5, 9, 13)
|
||||||
|
v = mix(v, 2, 6, 10, 14)
|
||||||
|
v = mix(v, 3, 7, 11, 15)
|
||||||
|
v = mix(v, 0, 5, 10, 15)
|
||||||
|
v = mix(v, 1, 6, 11, 12)
|
||||||
|
v = mix(v, 2, 7, 8, 13)
|
||||||
|
v = mix(v, 3, 4, 9, 14)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
//Based on Blake2b mix
|
||||||
|
private inline fun mix(v: Array<ULong>, a: Int, b: Int, c: Int, d: Int): Array<ULong> {
|
||||||
|
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
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun extractColumnFromGBlock(gBlock: Array<UByte>, columnPosition: Int): Array<UByte> {
|
||||||
|
val result = Array<UByte>(128) { 0U }
|
||||||
|
for (i in 0..7) {
|
||||||
|
gBlock.copyOfRange(i * 128 + (columnPosition * 16), i * 128 + (columnPosition * 16) + 16)
|
||||||
|
.copyInto(result, i * 16)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun copyIntoGBlockColumn(gBlock: Array<UByte>, columnPosition: Int, columnData: Array<UByte>) {
|
||||||
|
for (i in 0..7) {
|
||||||
|
val column = columnData.copyOfRange(i * 16, i * 16 + 16)
|
||||||
|
column.copyInto(gBlock, i * 128 + columnPosition * 16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun compressionFunctionG(
|
||||||
|
previousBlock: Array<UByte>,
|
||||||
|
referenceBlock: Array<UByte>,
|
||||||
|
currentBlock: Array<UByte>,
|
||||||
|
xorWithCurrentBlock: Boolean
|
||||||
|
): Array<UByte> {
|
||||||
|
val r = referenceBlock xor previousBlock
|
||||||
|
val q = Array<UByte>(1024) { 0U }
|
||||||
|
val z = Array<UByte>(1024) { 0U }
|
||||||
|
// Do the argon/blake2b mixing on rows
|
||||||
|
for (i in 0..7) {
|
||||||
|
val startOfRow = (i * 8 * 16)
|
||||||
|
val endOfRow = startOfRow + (8 * 16)
|
||||||
|
val rowToMix = r.copyOfRange(startOfRow, endOfRow)
|
||||||
|
mixRound(rowToMix)
|
||||||
|
.map { it.toLittleEndianUByteArray() }
|
||||||
|
.flatMap { it.asIterable() }
|
||||||
|
.toTypedArray()
|
||||||
|
.copyInto(q, startOfRow)
|
||||||
|
}
|
||||||
|
// 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() }
|
||||||
|
.toTypedArray()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val final = if (xorWithCurrentBlock) {
|
||||||
|
(z xor r) xor currentBlock
|
||||||
|
} else {
|
||||||
|
z xor r
|
||||||
|
}
|
||||||
|
return final
|
||||||
|
}
|
||||||
|
|
||||||
|
fun argonBlake2bArbitraryLenghtHash(input: Array<UByte>, length: UInt): Array<UByte> {
|
||||||
|
if (length <= 64U) {
|
||||||
|
return Blake2b.digest(inputMessage = length + input, hashLength = length.toInt())
|
||||||
|
}
|
||||||
|
//We can cast to int because UInt even if MAX_VALUE divided by 32 is guaranteed not to overflow
|
||||||
|
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 numberOf64ByteBlocks) {
|
||||||
|
v[i] = Blake2b.digest(v[i - 1])
|
||||||
|
}
|
||||||
|
val remainingPartOfInput = length.toInt() - numberOf64ByteBlocks * 32
|
||||||
|
val vLast = Blake2b.digest(v[numberOf64ByteBlocks - 1], hashLength = remainingPartOfInput)
|
||||||
|
val concat =
|
||||||
|
(v.map { it.copyOfRange(0, 32) })
|
||||||
|
.plus(listOf(vLast))
|
||||||
|
.foldRight(emptyArray<UByte>()) { arrayOfUBytes, acc -> arrayOfUBytes + acc }
|
||||||
|
|
||||||
|
return concat
|
||||||
|
}
|
||||||
|
}
|
@ -18,9 +18,13 @@
|
|||||||
|
|
||||||
package com.ionspin.kotlin.crypto.hash.keyderivation
|
package com.ionspin.kotlin.crypto.hash.keyderivation
|
||||||
|
|
||||||
import com.ionspin.kotlin.crypto.keyderivation.Argon2
|
import com.ionspin.kotlin.crypto.keyderivation.Argon2Template
|
||||||
|
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2
|
||||||
|
import com.ionspin.kotlin.crypto.keyderivation.argon2.ArgonType
|
||||||
import com.ionspin.kotlin.crypto.util.hexColumsPrint
|
import com.ionspin.kotlin.crypto.util.hexColumsPrint
|
||||||
|
import kotlin.math.exp
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Ugljesa Jovanovic
|
* Created by Ugljesa Jovanovic
|
||||||
@ -46,7 +50,7 @@ class Argon2Test {
|
|||||||
val secret: Array<UByte> = arrayOf(0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U)
|
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 associatedData: Array<UByte> = arrayOf(0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U)
|
||||||
|
|
||||||
val digest = Argon2(
|
val digest = Argon2Template(
|
||||||
password,
|
password,
|
||||||
salt,
|
salt,
|
||||||
parallelism,
|
parallelism,
|
||||||
@ -56,10 +60,92 @@ class Argon2Test {
|
|||||||
0x13U,
|
0x13U,
|
||||||
secret,
|
secret,
|
||||||
associatedData,
|
associatedData,
|
||||||
type = Argon2.ArgonType.Argon2d
|
type = Argon2Template.ArgonType.Argon2d
|
||||||
)
|
)
|
||||||
val result = digest.calculate()
|
val result = digest.calculate()
|
||||||
result.hexColumsPrint(8)
|
result.hexColumsPrint(8)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun argon2dKATTest() {
|
||||||
|
val expected : Array<UByte> = arrayOf(
|
||||||
|
0x51U, 0x2BU, 0x39U, 0x1BU, 0x6FU, 0x11U, 0x62U, 0x97U,
|
||||||
|
0x53U, 0x71U, 0xD3U, 0x09U, 0x19U, 0x73U, 0x42U, 0x94U,
|
||||||
|
0xF8U, 0x68U, 0xE3U, 0xBEU, 0x39U, 0x84U, 0xF3U, 0xC1U,
|
||||||
|
0xA1U, 0x3AU, 0x4DU, 0xB9U, 0xFAU, 0xBEU, 0x4AU, 0xCBU
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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.toInt(),
|
||||||
|
tagLength,
|
||||||
|
memory,
|
||||||
|
iterations,
|
||||||
|
secret,
|
||||||
|
associatedData,
|
||||||
|
ArgonType.Argon2d
|
||||||
|
)
|
||||||
|
val result = digest.derive()
|
||||||
|
result.hexColumsPrint(8)
|
||||||
|
assertTrue { expected.contentEquals(result) }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun argon2iKATTest() {
|
||||||
|
val expected : Array<UByte> = arrayOf(
|
||||||
|
0xc8U, 0x14U, 0xd9U, 0xd1U, 0xdcU, 0x7fU, 0x37U, 0xaaU,
|
||||||
|
0x13U, 0xf0U, 0xd7U, 0x7fU, 0x24U, 0x94U, 0xbdU, 0xa1U,
|
||||||
|
0xc8U, 0xdeU, 0x6bU, 0x01U, 0x6dU, 0xd3U, 0x88U, 0xd2U,
|
||||||
|
0x99U, 0x52U, 0xa4U, 0xc4U, 0x67U, 0x2bU, 0x6cU, 0xe8U
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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.toInt(),
|
||||||
|
tagLength,
|
||||||
|
memory,
|
||||||
|
iterations,
|
||||||
|
secret,
|
||||||
|
associatedData,
|
||||||
|
ArgonType.Argon2i
|
||||||
|
)
|
||||||
|
val result = digest.derive()
|
||||||
|
result.hexColumsPrint(8)
|
||||||
|
assertTrue { expected.contentEquals(result) }
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user