diff --git a/README.md b/README.md index 2f2be39..e2a562a 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ It's not peer reviewed, not guaranteed to be bug free, and not guaranteed to be * SHA512 * SHA256 -## Symmetric cipher (Currently only available only in 0.0.3-SNAPSHOT) +## Symmetric cipher * AES * Modes: CBC, CTR @@ -50,7 +50,7 @@ It's not peer reviewed, not guaranteed to be bug free, and not guaranteed to be ## AEAD -More to come. +TODO() ## Integration @@ -187,6 +187,35 @@ plainText == decrypted.toHexString() ``` +### Key derivation + +#### Argon2 + +NOTE: This implementation is tested against KAT generated by reference Argon2 implementation, which does not follow +specification completely. See this issue https://github.com/P-H-C/phc-winner-argon2/issues/183 + +```kotlin +val argon2Instance = Argon2( + password = "Password", + salt = "RandomSalt", + parallelism = 8, + tagLength = 64U, + requestedMemorySize = 256U, //4GB + numberOfIterations = 4U, + key = "", + associatedData = "", + argonType = ArgonType.Argon2id + ) +val tag = argon2Instance.derive() +val tagString = tag.map { it.toString(16).padStart(2, '0') }.joinToString(separator = "") +val expectedTagString = "c255e3e94305817d5e09a7c771e574e3a81cc78fef5da4a9644b6df0" + + "0ba1c9b424e3dd0ce7e600b1269b14c84430708186a8a60403e1bfbda935991592b9ff37" +println("Tag: ${tagString}") +assertEquals(tagString, expectedTagString) +``` + + + 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 07119b4..b294f51 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 @@ -25,6 +25,7 @@ import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.argonBlake2bAr import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.compressionFunctionG import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.validateArgonParameters import com.ionspin.kotlin.crypto.util.fromLittleEndianArrayToUInt +import com.ionspin.kotlin.crypto.util.hexColumsPrint import com.ionspin.kotlin.crypto.util.toLittleEndianUByteArray /** @@ -164,6 +165,7 @@ class Argon2( addressBlock: Array? ): Pair { val segmentIndex = (column % segmentLength) + val independentIndex = segmentIndex % 128 // 128 is the number of addresses in address block val (j1, j2) = when (argonType) { ArgonType.Argon2d -> { val previousBlock = if (column == 0) { @@ -176,7 +178,7 @@ class Argon2( Pair(first32Bit, second32Bit) } ArgonType.Argon2i -> { - val selectedAddressBlock = addressBlock!!.sliceArray((segmentIndex * 8) until (segmentIndex * 8) + 8) + val selectedAddressBlock = addressBlock!!.sliceArray((independentIndex * 8) until (independentIndex * 8) + 8) val first32Bit = selectedAddressBlock.sliceArray(0 until 4).fromLittleEndianArrayToUInt() val second32Bit = selectedAddressBlock.sliceArray(4 until 8).fromLittleEndianArrayToUInt() Pair(first32Bit, second32Bit) @@ -184,7 +186,7 @@ class Argon2( ArgonType.Argon2id -> { if (iteration == 0 && (slice == 0 || slice == 1)) { val selectedAddressBlock = - addressBlock!!.sliceArray((segmentIndex * 8) until (segmentIndex * 8) + 8) + addressBlock!!.sliceArray((independentIndex * 8) until (independentIndex * 8) + 8) val first32Bit = selectedAddressBlock.sliceArray(0 until 4).fromLittleEndianArrayToUInt() val second32Bit = selectedAddressBlock.sliceArray(4 until 8).fromLittleEndianArrayToUInt() Pair(first32Bit, second32Bit) @@ -338,12 +340,15 @@ class Argon2( slice * segmentLength } + for (column in startColumn until (slice + 1) * segmentLength) { + val segmentIndex = column - (slice * 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 != 0 && column % 128 == 0) { + if (useIndependentAddressing && segmentIndex != 0 && segmentIndex % 128 == 0) { addressBlock = populateAddressBlock(iteration, slice, lane, addressBlock!!, addressCounter) addressCounter++ + addressBlock.hexColumsPrint(16) } val previousColumn = if (column == 0) { columnCount - 1 diff --git a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/ReadmeTest.kt b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/ReadmeTest.kt index ee449cb..f18f78d 100644 --- a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/ReadmeTest.kt +++ b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/ReadmeTest.kt @@ -19,8 +19,10 @@ package com.ionspin.kotlin.crypto import com.ionspin.kotlin.crypto.hash.blake2b.Blake2b import com.ionspin.kotlin.crypto.hash.sha.Sha256 import com.ionspin.kotlin.crypto.hash.sha.Sha512 +import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2 +import com.ionspin.kotlin.crypto.keyderivation.argon2.ArgonType import kotlin.test.Test - +import kotlin.test.assertEquals import kotlin.test.assertTrue /** @@ -73,7 +75,7 @@ class ReadmeTest { @ExperimentalStdlibApi @Test fun sha256Example() { - val input ="abc" + val input = "abc" val result = Sha256.digest(inputString = input) val expectedResult = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" assertTrue { @@ -86,9 +88,9 @@ class ReadmeTest { @ExperimentalStdlibApi @Test fun sha512Example() { - val input ="abc" + val input = "abc" val result = Sha512.digest(inputMessage = input.encodeToByteArray().map { it.toUByte() }.toTypedArray()) - println(result.map {it.toString(16)}) + println(result.map { it.toString(16) }) val expectedResult = "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a" + "2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f" assertTrue { @@ -124,4 +126,26 @@ class ReadmeTest { } + + @Test + fun argon2StringExample() { + val argon2Instance = Argon2( + password = "Password", + salt = "RandomSalt", + parallelism = 8, + tagLength = 64U, + requestedMemorySize = 256U, //4GB + numberOfIterations = 4U, + key = "", + associatedData = "", + argonType = ArgonType.Argon2id + ) + val tag = argon2Instance.derive() + val tagString = tag.map { it.toString(16).padStart(2, '0') }.joinToString(separator = "") + val expectedTagString = "c255e3e94305817d5e09a7c771e574e3a81cc78fef5da4a9644b6df0" + + "0ba1c9b424e3dd0ce7e600b1269b14c84430708186a8a60403e1bfbda935991592b9ff37" + println("Tag: ${tagString}") + assertEquals(tagString, expectedTagString) + + } } \ No newline at end of file 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 index c7f0c8e..407368a 100644 --- 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 @@ -72,6 +72,15 @@ class UtilTest { converted[3] == 0xAAU.toUByte() } + assertTrue { + val original = 123456U + val converted = original.toLittleEndianUByteArray() + converted[0] == 0x40U.toUByte() && + converted[1] == 0xE2U.toUByte() && + converted[2] == 0x01U.toUByte() && + converted[3] == 0x00U.toUByte() + + } } @Test