From d22f5b73f7cc2b368f7598fa708f4542f77af7ac Mon Sep 17 00:00:00 2001 From: Ugljesa Jovanovic Date: Fri, 10 Jan 2020 23:58:28 +0100 Subject: [PATCH] Continued Argon2 implementation --- README.md | 6 + .../kotlin/crypto/keyderivation/Argon2.kt | 132 +++++++++++++++--- 2 files changed, 118 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 37e22d1..2f2be39 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,12 @@ It's not peer reviewed, not guaranteed to be bug free, and not guaranteed to be ## Symmetric cipher (Currently only available only in 0.0.3-SNAPSHOT) * AES * Modes: CBC, CTR + +## Key Derivation + +* Argon2 + +## AEAD More to come. 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 9cacd68..2cb0629 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 @@ -16,34 +16,43 @@ package com.ionspin.kotlin.crypto.keyderivation -import com.ionspin.kotlin.crypto.chunked import com.ionspin.kotlin.crypto.hash.blake2b.Blake2b import com.ionspin.kotlin.crypto.plus +import com.ionspin.kotlin.crypto.toLittleEndianUByteArray /** + * https://tools.ietf.org/html/draft-irtf-cfrg-argon2-03 + * https://en.wikipedia.org/wiki/Argon2 + * https://www.cryptolux.org/images/0/0d/Argon2.pdf + * * Created by Ugljesa Jovanovic * ugljesa.jovanovic@ionspin.com * on 08-Jan-2020 + * + * + * */ @ExperimentalUnsignedTypes class Argon2 internal constructor( - 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 type : ArgonType -){ + val password: Array, + val salt: Array, + val parallelism: UInt, + val tagLength: UInt, + val memorySize: UInt, + val numberOfIterations: UInt, + val versionNumber: UInt, + val key: Array, + associatedData: Array + val type: ArgonType +) { enum class ArgonType { Argon2i, Argon2d, Argon2id } + companion object { - fun hash(input : Array, length : UInt) : Array { + fun argonHash(input: Array, length: UInt): Array { if (length <= 64U) { return Blake2b.digest(length + input) } @@ -52,11 +61,99 @@ class Argon2 internal constructor( val v = Array>(numberOfBlocks) { emptyArray() } v[0] = Blake2b.digest(length + input) for (i in 1 until numberOfBlocks - 1) { - v[i] = Blake2b.digest(v[i-1]) + v[i] = Blake2b.digest(v[i - 1]) } val remainingPartOfInput = input.copyOfRange(input.size - numberOfBlocks * 32, input.size) - v[numberOfBlocks] = Blake2b.digest(remainingPartOfInput, hashLength = remainingPartOfInput.size) - v.chunked(8) + val vLast = Blake2b.digest(remainingPartOfInput, hashLength = remainingPartOfInput.size) + val concat = + (v.map { it.copyOfRange(0, 32) }) + .plus(listOf(vLast)) + .foldRight(emptyArray()) { arrayOfUBytes, acc -> arrayOfUBytes + acc } + + + + return concat + } + + fun compressionFunctionG(x : Array, y : Array) : Array>> { + val r = Array(1024) { 0U } + x.forEachIndexed { index, it -> r[index] = it xor y[index] } + // mix rounds + return emptyArray() // TODO + } + + // --------- Unmodified blake2b mixing + /* + internal fun mixRound(input: Array, message: Array, round: Int): Array { + var v = input + val selectedSigma = sigma[round % 10] + v = mix(v, 0, 4, 8, 12, message[selectedSigma[0]], message[selectedSigma[1]]) + v = mix(v, 1, 5, 9, 13, message[selectedSigma[2]], message[selectedSigma[3]]) + v = mix(v, 2, 6, 10, 14, message[selectedSigma[4]], message[selectedSigma[5]]) + v = mix(v, 3, 7, 11, 15, message[selectedSigma[6]], message[selectedSigma[7]]) + v = mix(v, 0, 5, 10, 15, message[selectedSigma[8]], message[selectedSigma[9]]) + v = mix(v, 1, 6, 11, 12, message[selectedSigma[10]], message[selectedSigma[11]]) + v = mix(v, 2, 7, 8, 13, message[selectedSigma[12]], message[selectedSigma[13]]) + v = mix(v, 3, 4, 9, 14, message[selectedSigma[14]], message[selectedSigma[15]]) + return v + + } + + private fun mix(v: Array, a: Int, b: Int, c: Int, d: Int, x: ULong, y: ULong): Array { + v[a] = (v[a] + v[b] + x) + v[d] = (v[d] xor v[a]) rotateRight R1 + v[c] = (v[c] + v[d]) + v[b] = (v[b] xor v[c]) rotateRight R2 + v[a] = (v[a] + v[b] + y) + v[d] = (v[d] xor v[a]) rotateRight R3 + v[c] = (v[c] + v[d]) + v[b] = (v[b] xor v[c]) rotateRight R4 + return v + } + */ + + internal fun derive( + password: Array, + salt: Array, + parallelism: UInt, + tagLength: UInt, + memorySize: UInt, + numberOfIterations: UInt, + versionNumber: UInt, + key: Array, + associatedData: Array, + type: ArgonType + ): Array { + val h0 = Blake2b.digest( + parallelism.toLittleEndianUByteArray() + tagLength.toLittleEndianUByteArray() + memorySize.toLittleEndianUByteArray() + + numberOfIterations.toLittleEndianUByteArray() + versionNumber.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) // + val columnCount = blockCount / parallelism + + //Allocate memory as Array of parallelism rows and columnCount colums + val matrix = Array(parallelism.toInt()) { + Array(columnCount.toInt()) { + Array(1024) { 0U } + } + } + + //Compute B[i][0] + for (i in 0..parallelism.toInt()) { + matrix[i][0] = + argonHash(h0 + 0.toUInt().toLittleEndianUByteArray() + i.toUInt().toLittleEndianUByteArray(), 64U) + } + + //Compute B[i][1] + for (i in 0..parallelism.toInt()) { + matrix[i][0] = + argonHash(h0 + 1.toUInt().toLittleEndianUByteArray() + i.toUInt().toLittleEndianUByteArray(), 64U) + } @@ -66,9 +163,4 @@ class Argon2 internal constructor( } - - - - - } \ No newline at end of file