Add key derivation interface
This commit is contained in:
parent
2bc3051748
commit
13ebfa8be9
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.ionspin.kotlin.crypto.keyderivation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Ugljesa Jovanovic
|
||||||
|
* ugljesa.jovanovic@ionspin.com
|
||||||
|
* on 16-May-2020
|
||||||
|
*/
|
||||||
|
interface KeyDerivationFunction {
|
||||||
|
fun derive() : Array<UByte>
|
||||||
|
}
|
@ -20,6 +20,7 @@ package com.ionspin.kotlin.crypto.keyderivation.argon2
|
|||||||
|
|
||||||
import com.ionspin.kotlin.bignum.integer.toBigInteger
|
import com.ionspin.kotlin.bignum.integer.toBigInteger
|
||||||
import com.ionspin.kotlin.crypto.hash.blake2b.Blake2b
|
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.argonBlake2bArbitraryLenghtHash
|
||||||
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.compressionFunctionG
|
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.compressionFunctionG
|
||||||
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.validateArgonParameters
|
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.validateArgonParameters
|
||||||
@ -44,16 +45,16 @@ data class SegmentPosition(
|
|||||||
)
|
)
|
||||||
|
|
||||||
class Argon2(
|
class Argon2(
|
||||||
val password: Array<UByte>,
|
private val password: Array<UByte>,
|
||||||
val salt: Array<UByte> = emptyArray(),
|
private val salt: Array<UByte> = emptyArray(),
|
||||||
val parallelism: Int = 1,
|
private val parallelism: Int = 1,
|
||||||
val tagLength: UInt = 64U,
|
private val tagLength: UInt = 64U,
|
||||||
requestedMemorySize: UInt = 0U,
|
requestedMemorySize: UInt = 0U,
|
||||||
val numberOfIterations: UInt = 1U,
|
private val numberOfIterations: UInt = 1U,
|
||||||
val key: Array<UByte> = emptyArray(),
|
private val key: Array<UByte> = emptyArray(),
|
||||||
val associatedData: Array<UByte> = emptyArray(),
|
private val associatedData: Array<UByte> = emptyArray(),
|
||||||
val argonType: ArgonType = ArgonType.Argon2id
|
private val argonType: ArgonType = ArgonType.Argon2id
|
||||||
) {
|
) : KeyDerivationFunction {
|
||||||
init {
|
init {
|
||||||
validateArgonParameters(
|
validateArgonParameters(
|
||||||
password,
|
password,
|
||||||
@ -68,23 +69,23 @@ class Argon2(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
//We support only the latest version
|
//We support only the latest version
|
||||||
val versionNumber: UInt = 0x13U
|
private val versionNumber: UInt = 0x13U
|
||||||
|
|
||||||
//Use either requested memory size, or default, or throw exception if the requested amount is less than 8*parallelism
|
//Use either requested memory size, or default, or throw exception if the requested amount is less than 8*parallelism
|
||||||
val memorySize = if (requestedMemorySize == 0U) {
|
private val memorySize = if (requestedMemorySize == 0U) {
|
||||||
((8 * parallelism) * 2).toUInt()
|
((8 * parallelism) * 2).toUInt()
|
||||||
} else {
|
} else {
|
||||||
requestedMemorySize
|
requestedMemorySize
|
||||||
}
|
}
|
||||||
val blockCount = (memorySize / (4U * parallelism.toUInt())) * (4U * parallelism.toUInt())
|
private val blockCount = (memorySize / (4U * parallelism.toUInt())) * (4U * parallelism.toUInt())
|
||||||
val columnCount = (blockCount / parallelism.toUInt()).toInt()
|
private val columnCount = (blockCount / parallelism.toUInt()).toInt()
|
||||||
val segmentLength = columnCount / 4
|
private val segmentLength = columnCount / 4
|
||||||
|
|
||||||
val useIndependentAddressing = argonType == ArgonType.Argon2id || argonType == ArgonType.Argon2i
|
private val useIndependentAddressing = argonType == ArgonType.Argon2id || argonType == ArgonType.Argon2i
|
||||||
|
|
||||||
|
|
||||||
// State
|
// State
|
||||||
val matrix = Array(parallelism) {
|
private val matrix = Array(parallelism) {
|
||||||
Array(columnCount) {
|
Array(columnCount) {
|
||||||
Array<UByte>(1024) { 0U }
|
Array<UByte>(1024) { 0U }
|
||||||
}
|
}
|
||||||
@ -225,7 +226,7 @@ class Argon2(
|
|||||||
return Pair(l, absolutePosition)
|
return Pair(l, absolutePosition)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun derive(): Array<UByte> {
|
override fun derive(): Array<UByte> {
|
||||||
val h0 = Blake2b.digest(
|
val h0 = Blake2b.digest(
|
||||||
parallelism.toUInt()
|
parallelism.toUInt()
|
||||||
.toLittleEndianUByteArray() + tagLength.toLittleEndianUByteArray() + memorySize.toLittleEndianUByteArray() +
|
.toLittleEndianUByteArray() + tagLength.toLittleEndianUByteArray() + memorySize.toLittleEndianUByteArray() +
|
||||||
@ -266,33 +267,25 @@ class Argon2(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Hash the xored last blocks
|
//Hash the xored last blocks
|
||||||
println("Tag:")
|
|
||||||
val hash = argonBlake2bArbitraryLenghtHash(result, tagLength)
|
val hash = argonBlake2bArbitraryLenghtHash(result, tagLength)
|
||||||
|
clearMatrix()
|
||||||
return hash
|
return hash
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun executeArgonWithSingleThread() {
|
private fun executeArgonWithSingleThread() {
|
||||||
for (iteration in 0 until numberOfIterations.toInt()) {
|
for (iteration in 0 until numberOfIterations.toInt()) {
|
||||||
for (slice in 0 until 4) {
|
for (slice in 0 until 4) {
|
||||||
for (lane in 0 until parallelism.toInt()) {
|
for (lane in 0 until parallelism) {
|
||||||
println("Processing segment I: $iteration, S: $slice, L: $lane")
|
|
||||||
val segmentPosition = SegmentPosition(iteration, lane, slice)
|
val segmentPosition = SegmentPosition(iteration, lane, slice)
|
||||||
processSegment(segmentPosition)
|
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) {
|
private fun processSegment(segmentPosition: SegmentPosition) {
|
||||||
val iteration = segmentPosition.iteration
|
val iteration = segmentPosition.iteration
|
||||||
val slice = segmentPosition.slice
|
val slice = segmentPosition.slice
|
||||||
val lane = segmentPosition.lane
|
val lane = segmentPosition.lane
|
||||||
@ -308,7 +301,6 @@ class Argon2(
|
|||||||
addressBlock = populateAddressBlock(iteration, slice, lane, addressBlock, addressCounter)
|
addressBlock = populateAddressBlock(iteration, slice, lane, addressBlock, addressCounter)
|
||||||
addressCounter++
|
addressCounter++
|
||||||
}
|
}
|
||||||
|
|
||||||
val startColumn = if (iteration == 0 && slice == 0) {
|
val startColumn = if (iteration == 0 && slice == 0) {
|
||||||
2
|
2
|
||||||
} else {
|
} else {
|
||||||
@ -327,9 +319,6 @@ class Argon2(
|
|||||||
} else {
|
} else {
|
||||||
column - 1
|
column - 1
|
||||||
}
|
}
|
||||||
if (iteration == 1) {
|
|
||||||
println("Breakpoint")
|
|
||||||
}
|
|
||||||
val (l, z) = computeReferenceBlockIndexes(
|
val (l, z) = computeReferenceBlockIndexes(
|
||||||
iteration,
|
iteration,
|
||||||
slice,
|
slice,
|
||||||
@ -337,7 +326,6 @@ class Argon2(
|
|||||||
column,
|
column,
|
||||||
addressBlock
|
addressBlock
|
||||||
)
|
)
|
||||||
// println("Calling compress for I: $iteration S: $slice Lane: $lane Column: $column with l: $l z: $z")
|
|
||||||
matrix[lane][column] =
|
matrix[lane][column] =
|
||||||
compressionFunctionG(
|
compressionFunctionG(
|
||||||
matrix[lane][previousColumn],
|
matrix[lane][previousColumn],
|
||||||
|
@ -34,7 +34,7 @@ object Argon2Utils {
|
|||||||
const val R4 = 63
|
const val R4 = 63
|
||||||
|
|
||||||
//based on Blake2b mixRound
|
//based on Blake2b mixRound
|
||||||
internal inline fun mixRound(input: Array<UByte>): Array<ULong> {
|
private fun mixRound(input: Array<UByte>): Array<ULong> {
|
||||||
var v = input.chunked(8).map { it.fromLittleEndianArrayToULong() }.toTypedArray()
|
var v = input.chunked(8).map { it.fromLittleEndianArrayToULong() }.toTypedArray()
|
||||||
v = mix(v, 0, 4, 8, 12)
|
v = mix(v, 0, 4, 8, 12)
|
||||||
v = mix(v, 1, 5, 9, 13)
|
v = mix(v, 1, 5, 9, 13)
|
||||||
@ -48,7 +48,7 @@ object Argon2Utils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Based on Blake2b mix
|
//Based on Blake2b mix
|
||||||
private inline fun mix(v: Array<ULong>, a: Int, b: Int, c: Int, d: Int): Array<ULong> {
|
private 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[a] = (v[a] + v[b] + 2U * (v[a] and 0xFFFFFFFFUL) * (v[b] and 0xFFFFFFFFUL))
|
||||||
v[d] = (v[d] xor v[a]) rotateRight R1
|
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[c] = (v[c] + v[d] + 2U * (v[c] and 0xFFFFFFFFUL) * (v[d] and 0xFFFFFFFFUL))
|
||||||
@ -76,7 +76,7 @@ object Argon2Utils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun compressionFunctionG(
|
internal fun compressionFunctionG(
|
||||||
previousBlock: Array<UByte>,
|
previousBlock: Array<UByte>,
|
||||||
referenceBlock: Array<UByte>,
|
referenceBlock: Array<UByte>,
|
||||||
currentBlock: Array<UByte>,
|
currentBlock: Array<UByte>,
|
||||||
@ -115,7 +115,7 @@ object Argon2Utils {
|
|||||||
return final
|
return final
|
||||||
}
|
}
|
||||||
|
|
||||||
fun argonBlake2bArbitraryLenghtHash(input: Array<UByte>, length: UInt): Array<UByte> {
|
internal fun argonBlake2bArbitraryLenghtHash(input: Array<UByte>, length: UInt): Array<UByte> {
|
||||||
if (length <= 64U) {
|
if (length <= 64U) {
|
||||||
return Blake2b.digest(inputMessage = length + input, hashLength = length.toInt())
|
return Blake2b.digest(inputMessage = length + input, hashLength = length.toInt())
|
||||||
}
|
}
|
||||||
@ -142,7 +142,7 @@ object Argon2Utils {
|
|||||||
* sizes for password, salt, key and associated data. Also since UInt is 32bit we cant set more than 2^32-1 of
|
* sizes for password, salt, key and associated data. Also since UInt is 32bit we cant set more than 2^32-1 of
|
||||||
* tagLength, requested memory size and number of iterations, so no need to check for upper bound, just lower.
|
* tagLength, requested memory size and number of iterations, so no need to check for upper bound, just lower.
|
||||||
*/
|
*/
|
||||||
fun validateArgonParameters(
|
internal fun validateArgonParameters(
|
||||||
password: Array<UByte>,
|
password: Array<UByte>,
|
||||||
salt: Array<UByte>,
|
salt: Array<UByte>,
|
||||||
parallelism: Int ,
|
parallelism: Int ,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user