Initial switch to byte array

This commit is contained in:
Ugljesa Jovanovic 2020-05-19 23:33:10 +02:00 committed by Ugljesa Jovanovic
parent d2c52e02e8
commit bc1c50f268
No known key found for this signature in database
GPG Key ID: 178E6DFCECCB0E0F
13 changed files with 82 additions and 81 deletions

View File

@ -22,5 +22,5 @@ package com.ionspin.kotlin.crypto
* on 21-Sep-2019
*/
expect object SRNG {
fun getRandomBytes(amount : Int) : Array<UByte>
fun getRandomBytes(amount : Int) : UByteArray
}

View File

@ -22,5 +22,5 @@ package com.ionspin.kotlin.crypto.keyderivation
* on 16-May-2020
*/
interface KeyDerivationFunction {
fun derive() : Array<UByte>
fun derive() : UByteArray
}

View File

@ -27,6 +27,7 @@ import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.compressionFun
import com.ionspin.kotlin.crypto.keyderivation.argon2.Argon2Utils.validateArgonParameters
import com.ionspin.kotlin.crypto.util.fromLittleEndianArrayToUInt
import com.ionspin.kotlin.crypto.util.toLittleEndianTypedUByteArray
import com.ionspin.kotlin.crypto.util.toLittleEndianUByteArray
/**
* Created by Ugljesa Jovanovic
@ -45,8 +46,8 @@ data class SegmentPosition(
)
data class ArgonResult(
val hashBytes: Array<UByte>,
val salt: Array<UByte>
val hashBytes: UByteArray,
val salt: UByteArray
) {
val hashString by lazy { hashBytes.map { it.toString(16).padStart(2, '0') }.joinToString(separator = "") }
val saltString by lazy { salt.map { it.toString(16).padStart(2, '0') }.joinToString(separator = "") }
@ -55,14 +56,14 @@ data class ArgonResult(
@ExperimentalStdlibApi
class Argon2(
private val password: Array<UByte>,
private val salt: Array<UByte> = emptyArray(),
private val password: UByteArray,
private val salt: UByteArray = ubyteArrayOf(),
private val parallelism: Int = 1,
private val tagLength: UInt = 64U,
requestedMemorySize: UInt = 0U,
private val numberOfIterations: Int = 1,
private val key: Array<UByte> = emptyArray(),
private val associatedData: Array<UByte> = emptyArray(),
private val key: UByteArray = ubyteArrayOf(),
private val associatedData: UByteArray = ubyteArrayOf(),
private val argonType: ArgonType = ArgonType.Argon2id
) : KeyDerivationFunction {
@ -79,14 +80,14 @@ class Argon2(
): ArgonResult {
val salt = SRNG.getRandomBytes(64)
val argon = Argon2(
password.encodeToByteArray().map { it.toUByte() }.toList().toTypedArray(),
password.encodeToByteArray().toUByteArray(),
salt,
parallelism,
tagLength.toUInt(),
memory.toUInt(),
numberOfIterations,
key.encodeToByteArray().map { it.toUByte() }.toList().toTypedArray(),
associatedData.encodeToByteArray().map { it.toUByte() }.toList().toTypedArray(),
key.encodeToByteArray().toUByteArray(),
associatedData.encodeToByteArray().toUByteArray(),
ArgonType.Argon2id
)
val resultArray = argon.derive()
@ -105,14 +106,14 @@ class Argon2(
associatedData: String = "",
argonType: ArgonType = ArgonType.Argon2id
) : this(
password.encodeToByteArray().map { it.toUByte() }.toList().toTypedArray(),
salt.encodeToByteArray().map { it.toUByte() }.toList().toTypedArray(),
password.encodeToByteArray().toUByteArray(),
salt.encodeToByteArray().toUByteArray(),
parallelism,
tagLength,
requestedMemorySize,
numberOfIterations,
key.encodeToByteArray().map { it.toUByte() }.toList().toTypedArray(),
associatedData.encodeToByteArray().map { it.toUByte() }.toList().toTypedArray(),
key.encodeToByteArray().toUByteArray(),
associatedData.encodeToByteArray().toUByteArray(),
argonType
)
@ -168,25 +169,25 @@ class Argon2(
iteration: Int,
slice: Int,
lane: Int,
addressBlock: Array<UByte>,
addressBlock: UByteArray,
addressCounter: ULong
): Array<UByte> {
): UByteArray {
//Calculate first pass
val firstPass = compressionFunctionG(
Array<UByte>(1024) { 0U },
iteration.toULong().toLittleEndianTypedUByteArray() +
lane.toULong().toLittleEndianTypedUByteArray() +
slice.toULong().toLittleEndianTypedUByteArray() +
blockCount.toULong().toLittleEndianTypedUByteArray() +
numberOfIterations.toULong().toLittleEndianTypedUByteArray() +
argonType.typeId.toULong().toLittleEndianTypedUByteArray() +
addressCounter.toLittleEndianTypedUByteArray() +
Array<UByte>(968) { 0U },
UByteArray(1024) { 0U },
iteration.toULong().toLittleEndianUByteArray() +
lane.toULong().toLittleEndianUByteArray() +
slice.toULong().toLittleEndianUByteArray() +
blockCount.toULong().toLittleEndianUByteArray() +
numberOfIterations.toULong().toLittleEndianUByteArray() +
argonType.typeId.toULong().toLittleEndianUByteArray() +
addressCounter.toLittleEndianUByteArray() +
UByteArray(968) { 0U },
addressBlock,
false
)
val secondPass = compressionFunctionG(
Array<UByte>(1024) { 0U },
UByteArray(1024) { 0U },
firstPass,
firstPass,
false
@ -200,7 +201,7 @@ class Argon2(
slice: Int,
lane: Int,
column: Int,
addressBlock: Array<UByte>?
addressBlock: UByteArray?
): Pair<Int, Int> {
val segmentIndex = (column % segmentLength)
val independentIndex = segmentIndex % 128 // 128 is the number of addresses in address block
@ -298,7 +299,7 @@ class Argon2(
return Pair(l, absolutePosition)
}
override fun derive(): Array<UByte> {
override fun derive(): UByteArray {
val h0 = Blake2b.digest(
parallelism.toUInt()
.toLittleEndianTypedUByteArray() + tagLength.toLittleEndianTypedUByteArray() + memorySize.toLittleEndianTypedUByteArray() +
@ -309,13 +310,13 @@ class Argon2(
salt.size.toUInt().toLittleEndianTypedUByteArray() + salt +
key.size.toUInt().toLittleEndianTypedUByteArray() + key +
associatedData.size.toUInt().toLittleEndianTypedUByteArray() + associatedData
)
).toUByteArray()
//Compute B[i][0]
for (i in 0 until parallelism) {
matrix[i][0] =
argonBlake2bArbitraryLenghtHash(
h0 + 0.toUInt().toLittleEndianTypedUByteArray() + i.toUInt().toLittleEndianTypedUByteArray(),
(h0 + 0.toUInt().toLittleEndianUByteArray() + i.toUInt().toLittleEndianUByteArray()).toUByteArray(),
1024U
).toUByteArray()
}
@ -324,19 +325,19 @@ class Argon2(
for (i in 0 until parallelism) {
matrix[i][1] =
argonBlake2bArbitraryLenghtHash(
h0 + 1.toUInt().toLittleEndianTypedUByteArray() + i.toUInt().toLittleEndianTypedUByteArray(),
(h0 + 1.toUInt().toLittleEndianUByteArray() + i.toUInt().toLittleEndianUByteArray()).toUByteArray(),
1024U
).toUByteArray()
}
executeArgonWithSingleThread()
val result = matrix.foldIndexed(emptyArray<UByte>()) { lane, acc, laneArray ->
val result = matrix.foldIndexed(ubyteArrayOf()) { lane, acc, laneArray ->
if (acc.size == 0) {
acc + laneArray[columnCount - 1] // add last element in first lane to the accumulator
} else {
// For each element in our accumulator, xor it with an appropriate element from the last column in current lane (from 1 to `parallelism`)
acc.mapIndexed { index, it -> it xor laneArray[columnCount - 1][index] }
.toTypedArray()
acc.mapIndexed { index, it -> it xor laneArray[columnCount - 1][index] }.toUByteArray()
}
}
//Hash the xored last blocks
@ -363,12 +364,12 @@ class Argon2(
val slice = segmentPosition.slice
val lane = segmentPosition.lane
var addressBlock: Array<UByte>? = null
var addressBlock: UByteArray? = null
var addressCounter = 1UL //Starts from 1 in each segment as defined by the spec
//Generate initial segment address block
if (useIndependentAddressing) {
addressBlock = Array<UByte>(1024) {
addressBlock = UByteArray(1024) {
0U
}
addressBlock = populateAddressBlock(iteration, slice, lane, addressBlock, addressCounter)

View File

@ -33,19 +33,7 @@ object Argon2Utils {
const val R3 = 16
const val R4 = 63
private fun mixInPlace(input: UByteArray, startPosition: Int) {
var v = input.arrayChunked(8).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)
}
//based on Blake2b mixRound //TODO rework so it's in place mix
//based on Blake2b mixRound
private fun mixRound(input: UByteArray): Array<ULong> {
var v = input.arrayChunked(8).map { it.fromLittleEndianArrayToULong() }.toTypedArray()
v = mix(v, 0, 4, 8, 12)
@ -130,21 +118,21 @@ object Argon2Utils {
internal fun argonBlake2bArbitraryLenghtHash(input: UByteArray, length: UInt): UByteArray {
if (length <= 64U) {
return Blake2b.digest(inputMessage = length + input, hashLength = length.toInt())
return Blake2b.digest(inputMessage = length + input.toTypedArray(), hashLength = length.toInt()).toUByteArray()
}
//We can cast to int because UInt even if MAX_VALUE divided by 32 is guaranteed not to overflow
val numberOf64ByteBlocks = (1U + ((length - 1U) / 32U) - 2U).toInt() // equivalent to ceil(length/32) - 2
val v = Array<UByteArray>(numberOf64ByteBlocks) { emptyArray() }
v[0] = Blake2b.digest(length + input)
val v = Array<UByteArray>(numberOf64ByteBlocks) { ubyteArrayOf() }
v[0] = Blake2b.digest(length + input.toTypedArray()).toUByteArray()
for (i in 1 until numberOf64ByteBlocks) {
v[i] = Blake2b.digest(v[i - 1])
v[i] = Blake2b.digest(v[i - 1].toTypedArray()).toUByteArray()
}
val remainingPartOfInput = length.toInt() - numberOf64ByteBlocks * 32
val vLast = Blake2b.digest(v[numberOf64ByteBlocks - 1], hashLength = remainingPartOfInput)
val vLast = Blake2b.digest(v[numberOf64ByteBlocks - 1].toTypedArray(), hashLength = remainingPartOfInput).toUByteArray()
val concat =
(v.map { it.copyOfRange(0, 32) })
.plus(listOf(vLast))
.foldRight(emptyUByteArray()) { arrayOfUBytes, acc -> arrayOfUBytes + acc }
.foldRight(ubyteArrayOf()) { arrayOfUBytes, acc -> arrayOfUBytes + acc }
return concat
}

View File

@ -98,7 +98,7 @@ class AesCbc internal constructor(val aesKey: AesKey, val mode: Mode, initializa
var currentOutput: Array<UByte> = arrayOf()
var previousEncrypted: Array<UByte> = arrayOf()
val initVector = if (initializationVector.isNullOrEmpty()) {
SRNG.getRandomBytes(16)
SRNG.getRandomBytes(16).toTypedArray()
} else {
initializationVector
}

View File

@ -79,7 +79,7 @@ class AesCtr internal constructor(val aesKey: AesKey, val mode: Mode, initialCou
var currentOutput: Array<UByte> = arrayOf()
var previousEncrypted: Array<UByte> = arrayOf()
val counterStart = if (initialCounter.isNullOrEmpty()) {
SRNG.getRandomBytes(16)
SRNG.getRandomBytes(16).toTypedArray()
} else {
initialCounter
}

View File

@ -33,6 +33,11 @@ fun Array<UByte>.hexColumsPrint(chunk : Int = 16) {
printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) }
}
fun UByteArray.hexColumsPrint(chunk : Int = 16) {
val printout = this.map { it.toString(16).padStart(2, '0') }.chunked(chunk)
printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) }
}
fun Array<ULong>.hexColumsPrint(chunk: Int = 3) {
val printout = this.map { it.toString(16) }.chunked(chunk)
printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) }
@ -113,6 +118,13 @@ fun UInt.toLittleEndianTypedUByteArray() : Array<UByte> {
}
}
@ExperimentalUnsignedTypes
fun UInt.toLittleEndianUByteArray() : UByteArray {
return UByteArray (4) {
((this shr (it * 8)) and 0xFFU).toUByte()
}
}
// UInt / Array utils
@ExperimentalUnsignedTypes
fun ULong.toBigEndianUByteArray() : Array<UByte> {

View File

@ -34,7 +34,7 @@ class Argon2Test {
@Test
fun argon2dKATTest() {
val expected : Array<UByte> = arrayOf(
val expected : UByteArray = ubyteArrayOf(
0x51U, 0x2BU, 0x39U, 0x1BU, 0x6FU, 0x11U, 0x62U, 0x97U,
0x53U, 0x71U, 0xD3U, 0x09U, 0x19U, 0x73U, 0x42U, 0x94U,
0xF8U, 0x68U, 0xE3U, 0xBEU, 0x39U, 0x84U, 0xF3U, 0xC1U,
@ -46,15 +46,15 @@ class Argon2Test {
val iterations = 3
val parallelism = 4U
val tagLength = 32U
val password: Array<UByte> = arrayOf(
val password: UByteArray = ubyteArrayOf(
0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U,
0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U,
0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U,
0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U
)
val salt: Array<UByte> = arrayOf(0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U)
val secret: Array<UByte> = arrayOf(0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U)
val associatedData: Array<UByte> = arrayOf(0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U)
val salt: UByteArray = ubyteArrayOf(0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U)
val secret: UByteArray = ubyteArrayOf(0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U)
val associatedData: UByteArray = ubyteArrayOf(0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U)
val digest = Argon2(
password,
@ -75,7 +75,7 @@ class Argon2Test {
@Test
fun argon2iKATTest() {
val expected : Array<UByte> = arrayOf(
val expected : UByteArray = ubyteArrayOf(
0xc8U, 0x14U, 0xd9U, 0xd1U, 0xdcU, 0x7fU, 0x37U, 0xaaU,
0x13U, 0xf0U, 0xd7U, 0x7fU, 0x24U, 0x94U, 0xbdU, 0xa1U,
0xc8U, 0xdeU, 0x6bU, 0x01U, 0x6dU, 0xd3U, 0x88U, 0xd2U,
@ -87,15 +87,15 @@ class Argon2Test {
val iterations = 3
val parallelism = 4U
val tagLength = 32U
val password: Array<UByte> = arrayOf(
val password: UByteArray = ubyteArrayOf(
0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U,
0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U,
0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U,
0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U
)
val salt: Array<UByte> = arrayOf(0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U)
val secret: Array<UByte> = arrayOf(0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U)
val associatedData: Array<UByte> = arrayOf(0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U)
val salt: UByteArray = ubyteArrayOf(0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U)
val secret: UByteArray = ubyteArrayOf(0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U)
val associatedData: UByteArray = ubyteArrayOf(0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U)
val digest = Argon2(
password,
@ -116,7 +116,7 @@ class Argon2Test {
@Test
fun argon2idKATTest() {
val expected : Array<UByte> = arrayOf(
val expected : UByteArray = ubyteArrayOf(
0x0dU, 0x64U, 0x0dU, 0xf5U, 0x8dU, 0x78U, 0x76U, 0x6cU,
0x08U, 0xc0U, 0x37U, 0xa3U, 0x4aU, 0x8bU, 0x53U, 0xc9U,
0xd0U, 0x1eU, 0xf0U, 0x45U, 0x2dU, 0x75U, 0xb6U, 0x5eU,
@ -128,15 +128,15 @@ class Argon2Test {
val iterations = 3
val parallelism = 4U
val tagLength = 32U
val password: Array<UByte> = arrayOf(
val password: UByteArray = ubyteArrayOf(
0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U,
0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U,
0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U,
0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U, 0x01U
)
val salt: Array<UByte> = arrayOf(0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U)
val secret: Array<UByte> = arrayOf(0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U)
val associatedData: Array<UByte> = arrayOf(0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U)
val salt: UByteArray = ubyteArrayOf(0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U, 0x02U)
val secret: UByteArray = ubyteArrayOf(0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U, 0x03U)
val associatedData: UByteArray = ubyteArrayOf(0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U, 0x04U)
val digest = Argon2(
password,

View File

@ -26,7 +26,7 @@ import kotlin.browser.window
actual object SRNG {
var counter = 0
@ExperimentalUnsignedTypes
actual fun getRandomBytes(amount: Int): Array<UByte> {
actual fun getRandomBytes(amount: Int): UByteArray {
val runningOnNode = jsTypeOf(window) == "undefined"
val randomBytes = if (runningOnNode) {
js("require('crypto')").randomBytes(amount).toJSON().data
@ -42,6 +42,6 @@ actual object SRNG {
randomArrayResult
}
return randomBytes as Array<UByte>
return randomBytes as UByteArray
}
}

View File

@ -26,9 +26,9 @@ import java.security.SecureRandom
@ExperimentalUnsignedTypes
actual object SRNG {
val secureRandom = SecureRandom()
actual fun getRandomBytes(amount: Int): Array<UByte> {
actual fun getRandomBytes(amount: Int): UByteArray {
val byteArray = ByteArray(amount)
secureRandom.nextBytes(byteArray)
return byteArray.toUByteArray().toTypedArray()
return byteArray.toUByteArray()
}
}

View File

@ -32,12 +32,12 @@ actual object SRNG {
}
@Suppress("EXPERIMENTAL_UNSIGNED_LITERALS")
actual fun getRandomBytes(amount: Int): Array<UByte> {
actual fun getRandomBytes(amount: Int): UByteArray {
memScoped {
val randArray = allocArray<ByteVar>(amount)
val pointer = randArray.getPointer(this)
val status = advapiRandom(pointer.reinterpret(), amount.convert())
return Array<UByte>(amount) { pointer[it].toUByte() }
return UByteArray(amount) { pointer[it].toUByte() }
}
}
}

View File

@ -26,14 +26,14 @@ import platform.posix.*
*/
actual object SRNG {
@Suppress("EXPERIMENTAL_UNSIGNED_LITERALS")
actual fun getRandomBytes(amount: Int): Array<UByte> {
actual fun getRandomBytes(amount: Int): UByteArray {
memScoped {
val array = allocArray<UByteVar>(amount)
val urandomFile = fopen("/dev/urandom", "rb")
if (urandomFile != null) {
fread(array, 1, amount.convert(), urandomFile)
}
return Array(amount) {
return UByteArray(amount) {
array[it]
}
}