From 13ebfa8be977ec3dca4c133ed3c2bbbd3245baf0 Mon Sep 17 00:00:00 2001 From: Ugljesa Jovanovic Date: Sat, 16 May 2020 19:02:08 +0200 Subject: [PATCH] Add key derivation interface --- .../keyderivation/KeyDerivationFunction.kt | 26 +++++++++ .../crypto/keyderivation/argon2/Argon2.kt | 56 ++++++++----------- .../keyderivation/argon2/Argon2Utils.kt | 10 ++-- 3 files changed, 53 insertions(+), 39 deletions(-) create mode 100644 multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/KeyDerivationFunction.kt diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/KeyDerivationFunction.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/KeyDerivationFunction.kt new file mode 100644 index 0000000..bf8b6ed --- /dev/null +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/KeyDerivationFunction.kt @@ -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 +} \ No newline at end of file diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/argon2/Argon2.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/argon2/Argon2.kt index 1d732bc..15a3c9c 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/argon2/Argon2.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/argon2/Argon2.kt @@ -20,6 +20,7 @@ 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.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.validateArgonParameters @@ -44,16 +45,16 @@ data class SegmentPosition( ) class Argon2( - val password: Array, - val salt: Array = emptyArray(), - val parallelism: Int = 1, - val tagLength: UInt = 64U, + private val password: Array, + private val salt: Array = emptyArray(), + private val parallelism: Int = 1, + private val tagLength: UInt = 64U, requestedMemorySize: UInt = 0U, - val numberOfIterations: UInt = 1U, - val key: Array = emptyArray(), - val associatedData: Array = emptyArray(), - val argonType: ArgonType = ArgonType.Argon2id -) { + private val numberOfIterations: UInt = 1U, + private val key: Array = emptyArray(), + private val associatedData: Array = emptyArray(), + private val argonType: ArgonType = ArgonType.Argon2id +) : KeyDerivationFunction { init { validateArgonParameters( password, @@ -68,23 +69,23 @@ class Argon2( ) } //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 - val memorySize = if (requestedMemorySize == 0U) { + private val memorySize = if (requestedMemorySize == 0U) { ((8 * parallelism) * 2).toUInt() } else { requestedMemorySize } - val blockCount = (memorySize / (4U * parallelism.toUInt())) * (4U * parallelism.toUInt()) - val columnCount = (blockCount / parallelism.toUInt()).toInt() - val segmentLength = columnCount / 4 + private val blockCount = (memorySize / (4U * parallelism.toUInt())) * (4U * parallelism.toUInt()) + private val columnCount = (blockCount / parallelism.toUInt()).toInt() + private val segmentLength = columnCount / 4 - val useIndependentAddressing = argonType == ArgonType.Argon2id || argonType == ArgonType.Argon2i + private val useIndependentAddressing = argonType == ArgonType.Argon2id || argonType == ArgonType.Argon2i // State - val matrix = Array(parallelism) { + private val matrix = Array(parallelism) { Array(columnCount) { Array(1024) { 0U } } @@ -225,7 +226,7 @@ class Argon2( return Pair(l, absolutePosition) } - fun derive(): Array { + override fun derive(): Array { val h0 = Blake2b.digest( parallelism.toUInt() .toLittleEndianUByteArray() + tagLength.toLittleEndianUByteArray() + memorySize.toLittleEndianUByteArray() + @@ -266,33 +267,25 @@ class Argon2( } } //Hash the xored last blocks - println("Tag:") val hash = argonBlake2bArbitraryLenghtHash(result, tagLength) + clearMatrix() return hash } - fun executeArgonWithSingleThread() { + private 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") + for (lane in 0 until parallelism) { 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) { + private fun processSegment(segmentPosition: SegmentPosition) { val iteration = segmentPosition.iteration val slice = segmentPosition.slice val lane = segmentPosition.lane @@ -308,7 +301,6 @@ class Argon2( addressBlock = populateAddressBlock(iteration, slice, lane, addressBlock, addressCounter) addressCounter++ } - val startColumn = if (iteration == 0 && slice == 0) { 2 } else { @@ -327,9 +319,6 @@ class Argon2( } else { column - 1 } - if (iteration == 1) { - println("Breakpoint") - } val (l, z) = computeReferenceBlockIndexes( iteration, slice, @@ -337,7 +326,6 @@ class Argon2( 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], diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/argon2/Argon2Utils.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/argon2/Argon2Utils.kt index 171168c..b27b0d0 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/argon2/Argon2Utils.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/keyderivation/argon2/Argon2Utils.kt @@ -34,7 +34,7 @@ object Argon2Utils { const val R4 = 63 //based on Blake2b mixRound - internal inline fun mixRound(input: Array): Array { + private fun mixRound(input: Array): Array { var v = input.chunked(8).map { it.fromLittleEndianArrayToULong() }.toTypedArray() v = mix(v, 0, 4, 8, 12) v = mix(v, 1, 5, 9, 13) @@ -48,7 +48,7 @@ object Argon2Utils { } //Based on Blake2b mix - private inline fun mix(v: Array, a: Int, b: Int, c: Int, d: Int): Array { + private fun mix(v: Array, a: Int, b: Int, c: Int, d: Int): Array { 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)) @@ -76,7 +76,7 @@ object Argon2Utils { } } - fun compressionFunctionG( + internal fun compressionFunctionG( previousBlock: Array, referenceBlock: Array, currentBlock: Array, @@ -115,7 +115,7 @@ object Argon2Utils { return final } - fun argonBlake2bArbitraryLenghtHash(input: Array, length: UInt): Array { + internal fun argonBlake2bArbitraryLenghtHash(input: Array, length: UInt): Array { if (length <= 64U) { 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 * 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, salt: Array, parallelism: Int ,