diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/blake2b/Blake2b.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/blake2b/Blake2b.kt index b462faa..07f151d 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/blake2b/Blake2b.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/blake2b/Blake2b.kt @@ -21,6 +21,8 @@ import com.ionspin.kotlin.bignum.integer.toBigInteger import com.ionspin.kotlin.crypto.* import com.ionspin.kotlin.crypto.hash.StatelessHash import com.ionspin.kotlin.crypto.hash.UpdatableHash +import com.ionspin.kotlin.crypto.util.chunked +import com.ionspin.kotlin.crypto.util.rotateRight /** * Created by Ugljesa Jovanovic diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha256.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha256.kt index 34995a7..1a222ff 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha256.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha256.kt @@ -16,10 +16,10 @@ package com.ionspin.kotlin.crypto.hash.sha -import com.ionspin.kotlin.crypto.chunked +import com.ionspin.kotlin.crypto.util.chunked import com.ionspin.kotlin.crypto.hash.StatelessHash import com.ionspin.kotlin.crypto.hash.UpdatableHash -import com.ionspin.kotlin.crypto.rotateRight +import com.ionspin.kotlin.crypto.util.rotateRight /** diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha512.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha512.kt index 217175e..69618cf 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha512.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha512.kt @@ -16,10 +16,10 @@ package com.ionspin.kotlin.crypto.hash.sha -import com.ionspin.kotlin.crypto.chunked +import com.ionspin.kotlin.crypto.util.chunked import com.ionspin.kotlin.crypto.hash.StatelessHash import com.ionspin.kotlin.crypto.hash.UpdatableHash -import com.ionspin.kotlin.crypto.rotateRight +import com.ionspin.kotlin.crypto.util.rotateRight /** * Created by Ugljesa Jovanovic 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 2cb0629..300f47c 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 @@ -17,8 +17,7 @@ package com.ionspin.kotlin.crypto.keyderivation import com.ionspin.kotlin.crypto.hash.blake2b.Blake2b -import com.ionspin.kotlin.crypto.plus -import com.ionspin.kotlin.crypto.toLittleEndianUByteArray +import com.ionspin.kotlin.crypto.util.* /** * https://tools.ietf.org/html/draft-irtf-cfrg-argon2-03 @@ -42,13 +41,14 @@ class Argon2 internal constructor( val numberOfIterations: UInt, val versionNumber: UInt, val key: Array, - associatedData: Array + val associatedData: Array, val type: ArgonType ) { enum class ArgonType { Argon2i, Argon2d, Argon2id } + @ExperimentalStdlibApi companion object { @@ -70,47 +70,88 @@ class Argon2 internal constructor( .plus(listOf(vLast)) .foldRight(emptyArray()) { arrayOfUBytes, acc -> arrayOfUBytes + acc } - - return concat } - fun compressionFunctionG(x : Array, y : Array) : Array>> { - val r = Array(1024) { 0U } + fun compressionFunctionG(x: Array, y: Array): Array { + val r = Array(1024) { 0U } // view as 8x8 matrix of 16 byte registers x.forEachIndexed { index, it -> r[index] = it xor y[index] } - // mix rounds - return emptyArray() // TODO + val q = Array(1024) { 0U } + val z = Array(1024) { 0U } + // Do the argon/blake2b mixing on rows + for (i in 0..7) { + val startOfRow = (i * 8 * 16) + val endOfRow = startOfRow + (8 * 16) + mixRound(r.copyOfRange(startOfRow, endOfRow)) + .map { it.toLittleEndianUByteArray() } + .flatMap { it.asIterable() } + .toTypedArray() + .copyInto(q, startOfRow, endOfRow) + } + // 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() + ) + } + // Z = Z xor R + r.forEachIndexed { index, it -> z[index] = it xor z[index] } + return z } - // --------- 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]]) + private fun extractColumnFromGBlock(gBlock: Array, columnPosition: Int): Array { + val result = Array(128) { 0U } + for (i in 0..7) { + result[i] = gBlock[i * 8 + columnPosition] + } + return result + } + + private fun copyIntoGBlockColumn(gBlock: Array, columnPosition: Int, columnData: Array) { + for (i in 0..7) { + gBlock[i * 8 + columnPosition] = columnData[i] + } + } + + + //based on Blake2b mixRound + internal fun mixRound(input: Array): Array { + var v = input.chunked(4).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 } - 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) + const val R1 = 32 + const val R2 = 24 + const val R3 = 16 + const val R4 = 63 + + //Based on Blake2b mix + private fun mix(v: Array, a: Int, b: Int, c: Int, d: Int): Array { + v[a] = (v[a] + v[b] * 2U * a.toUInt() * b.toUInt()) v[d] = (v[d] xor v[a]) rotateRight R1 - v[c] = (v[c] + v[d]) + v[c] = (v[c] + v[d] * 2U * c.toUInt() * d.toUInt()) v[b] = (v[b] xor v[c]) rotateRight R2 - v[a] = (v[a] + v[b] + y) + v[a] = (v[a] + v[b] * 2U * a.toUInt() * b.toUInt()) v[d] = (v[d] xor v[a]) rotateRight R3 - v[c] = (v[c] + v[d]) + v[c] = (v[c] + v[d] * 2U * c.toUInt() * d.toUInt()) v[b] = (v[b] xor v[c]) rotateRight R4 return v } - */ + internal fun derive( password: Array, @@ -155,9 +196,26 @@ class Argon2 internal constructor( argonHash(h0 + 1.toUInt().toLittleEndianUByteArray() + i.toUInt().toLittleEndianUByteArray(), 64U) } + for (i in 0..parallelism.toInt()) { + for (j in 1..columnCount.toInt()) { + //TODO i,j choosing based on type + val iPrim = -1 + val jPrim = -1 + matrix[i][j] = compressionFunctionG(matrix[i][j - 1], matrix[iPrim][jPrim]) + } + } + + val result = matrix.foldIndexed(emptyArray()) { index, acc, arrayOfArrays -> + return if (acc.size == 0) { + acc + arrayOfArrays[columnCount.toInt() - 1] + } else { + acc.mapIndexed { index, it -> it xor arrayOfArrays[columnCount.toInt() - 1][index] }.toTypedArray() + } + } - return emptyArray() + + return result } } diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCbc.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCbc.kt index 4d7125e..3d6d7e0 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCbc.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCbc.kt @@ -17,8 +17,8 @@ package com.ionspin.kotlin.crypto.symmetric import com.ionspin.kotlin.crypto.SRNG -import com.ionspin.kotlin.crypto.chunked -import com.ionspin.kotlin.crypto.xor +import com.ionspin.kotlin.crypto.util.chunked +import com.ionspin.kotlin.crypto.util.xor /** * Advanced encryption standard with cipher block chaining and PKCS #5 diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCtr.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCtr.kt index 4d5373a..66b7423 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCtr.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCtr.kt @@ -20,9 +20,9 @@ import com.ionspin.kotlin.bignum.Endianness import com.ionspin.kotlin.bignum.integer.BigInteger import com.ionspin.kotlin.bignum.modular.ModularBigInteger import com.ionspin.kotlin.crypto.SRNG -import com.ionspin.kotlin.crypto.chunked +import com.ionspin.kotlin.crypto.util.chunked import com.ionspin.kotlin.crypto.symmetric.AesCtr.Companion.encrypt -import com.ionspin.kotlin.crypto.xor +import com.ionspin.kotlin.crypto.util.xor /** * diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/Util.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/Util.kt similarity index 75% rename from multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/Util.kt rename to multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/Util.kt index 81c47b8..8d8924b 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/Util.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/Util.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.ionspin.kotlin.crypto +package com.ionspin.kotlin.crypto.util /** * Created by Ugljesa Jovanovic @@ -103,6 +103,43 @@ fun UInt.toLittleEndianUByteArray() : Array { } } +// UInt / Array utils +@ExperimentalUnsignedTypes +fun ULong.toBigEndianUByteArray() : Array { + return Array (8) { + ((this shr (56 - (it * 8))) and 0xFFU).toUByte() + } +} +@ExperimentalUnsignedTypes +fun ULong.toLittleEndianUByteArray() : Array { + return Array (8) { + ((this shr (it * 8)) and 0xFFU).toUByte() + } +} +@ExperimentalUnsignedTypes +fun Array.fromLittleEndianArrayToULong() : ULong { + if (this.size > 8) { + throw RuntimeException("ore than 8 bytes in input, potential overflow") + } + var ulong = this.foldIndexed(0UL) { index, acc, uByte -> acc or (uByte.toULong() shl (index * 8))} + return ulong +} + + +@ExperimentalUnsignedTypes +fun Array.fromBigEndianArrayToULong() : ULong { + if (this.size > 8) { + throw RuntimeException("ore than 8 bytes in input, potential overflow") + } + var ulong = this.foldIndexed(0UL) { + index, acc, uByte -> + val res = acc or (uByte.toULong() shl (56 - (index * 8))) + res + + } + return ulong +} + @ExperimentalUnsignedTypes operator fun UInt.plus(other : Array) : Array { return this.toLittleEndianUByteArray() + other diff --git a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/UtilTest.kt b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/UtilTest.kt deleted file mode 100644 index 79a6bbd..0000000 --- a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/UtilTest.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 - -import com.ionspin.kotlin.crypto.chunked -import kotlin.test.Test -import kotlin.test.assertTrue - -/** - * Created by Ugljesa Jovanovic - * ugljesa.jovanovic@ionspin.com - * on 17-Jul-2019 - */ -class UtilTest { - - @Test - fun testSlicer() { - val array = arrayOf(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17) - val chunked = array.chunked(2) - assertTrue { - chunked.size == 9 && chunked[8][0] == 17 - } - } -} \ No newline at end of file diff --git a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCbcTest.kt b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCbcTest.kt index b55a715..f0bada0 100644 --- a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCbcTest.kt +++ b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCbcTest.kt @@ -16,8 +16,8 @@ package com.ionspin.kotlin.crypto.symmetric -import com.ionspin.kotlin.crypto.hexStringToUByteArray -import com.ionspin.kotlin.crypto.toHexString +import com.ionspin.kotlin.crypto.util.hexStringToUByteArray +import com.ionspin.kotlin.crypto.util.toHexString import kotlin.test.Test import kotlin.test.assertTrue diff --git a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCtrTest.kt b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCtrTest.kt index e80a995..4b140b5 100644 --- a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCtrTest.kt +++ b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCtrTest.kt @@ -16,8 +16,8 @@ package com.ionspin.kotlin.crypto.symmetric -import com.ionspin.kotlin.crypto.hexStringToUByteArray -import com.ionspin.kotlin.crypto.toHexString +import com.ionspin.kotlin.crypto.util.hexStringToUByteArray +import com.ionspin.kotlin.crypto.util.toHexString import kotlin.test.Test import kotlin.test.assertTrue diff --git a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/util/UtilTest.kt b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/util/UtilTest.kt new file mode 100644 index 0000000..74d11d6 --- /dev/null +++ b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/util/UtilTest.kt @@ -0,0 +1,100 @@ +/* + * 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.util + +import kotlin.test.Test +import kotlin.test.assertTrue + +/** + * Created by Ugljesa Jovanovic + * ugljesa.jovanovic@ionspin.com + * on 17-Jul-2019 + */ +@ExperimentalUnsignedTypes +class UtilTest { + + @Test + fun testSlicer() { + val array = arrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17) + val chunked = array.chunked(2) + assertTrue { + chunked.size == 9 && chunked[8][0] == 17 + } + } + + @Test + fun testUIntToBigEndianArray() { + assertTrue { + val original = 1U + val converted = original.toBigEndianUByteArray() + converted[0] = 1U + true + } + assertTrue { + val original = 0xAABBCCDDU + val converted = original.toBigEndianUByteArray() + converted[0] == 0xAAU.toUByte() && + converted[1] == 0xBBU.toUByte() && + converted[2] == 0xCCU.toUByte() && + converted[3] == 0xDDU.toUByte() + + } + } + + @Test + fun testUIntToLittleEndianArray() { + assertTrue { + val original = 1U + val converted = original.toBigEndianUByteArray() + converted[4] = 1U + true + } + assertTrue { + val original = 0xAABBCCDDU + val converted = original.toBigEndianUByteArray() + converted[0] == 0xDDU.toUByte() && + converted[1] == 0xCCU.toUByte() && + converted[2] == 0xBBU.toUByte() && + converted[3] == 0xAAU.toUByte() + + } + } + + @Test + fun testFromBigEndianByteArrayToLong() { + + assertTrue { + val ubyteArray = ubyteArrayOf(0xA1U, 0xA2U, 0xB1U, 0xB2U, 0xC1U, 0xC2U, 0xD1U, 0xD2U).toTypedArray() + val expected = 0xA1A2B1B2C1C2D1D2U + val reconstructed = ubyteArray.fromBigEndianArrayToULong(); + reconstructed == expected + } + + } + + @Test + fun testFromLittleEndianByteArrayToLong() { + + assertTrue { + val ubyteArray = ubyteArrayOf(0xA1U, 0xA2U, 0xB1U, 0xB2U, 0xC1U, 0xC2U, 0xD1U, 0xD2U).toTypedArray() + val expected = 0xD2D1C2C1B2B1A2A1UL + val reconstructed = ubyteArray.fromLittleEndianArrayToULong(); + reconstructed == expected + } + + } +} \ No newline at end of file