Adding delegated implementation

This commit is contained in:
Ugljesa Jovanovic authored and Ugljesa Jovanovic committed 2020-05-24 11:12:00 +02:00
1 parent 3cfbd0ea63
commit 7238b09112
326 files changed
+109874 -20

No files matched your search

@@ -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
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 20-Jul-2019
*/
object Config {
const val DEBUG = false
}
@@ -0,0 +1,13 @@
package com.ionspin.kotlin.crypto
import com.ionspin.kotlin.crypto.hash.sha.Sha256Pure
import com.ionspin.kotlin.crypto.hash.sha.Sha512Pure
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 24-May-2020
*/
typealias Sha256Stateless = Sha256Pure.Companion
typealias Sha512Stateless = Sha512Pure.Companion
@@ -0,0 +1,12 @@
package _multiplatform_crypto_delegated
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 24-May-2020
*/
//Workaround for https://youtrack.jetbrains.com/issue/KT-36878
val byteArray = byteArrayOf(0)
val byte = 0.toByte()
val longArray = longArrayOf(0)
val long = 0L
@@ -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
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 21-Sep-2019
*/
expect object SRNG {
fun getRandomBytes(amount : Int) : UByteArray
}
@@ -0,0 +1,35 @@
/*
* 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.hash.blake2b
import com.ionspin.kotlin.bignum.integer.BigInteger
import com.ionspin.kotlin.bignum.integer.toBigInteger
import com.ionspin.kotlin.crypto.*
import com.ionspin.kotlin.crypto.util.rotateRight
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 14-Jul-2019
*/
@ExperimentalUnsignedTypes
expect class Blake2bDelegated(key: UByteArray? = null, hashLength: Int = 64) : Blake2b
expect class Blake2bStateless : Blake2bStatelessInterface
@@ -0,0 +1,327 @@
/*
* 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.hash.sha
import com.ionspin.kotlin.crypto.hash.StatelessHash
import com.ionspin.kotlin.crypto.hash.UpdatableHash
import com.ionspin.kotlin.crypto.util.rotateRight
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 17-Jul-2019
*/
@ExperimentalUnsignedTypes
class Sha256Pure : Sha256 {
override val MAX_HASH_BYTES: Int = 32
companion object : StatelessSha256 {
const val BLOCK_SIZE = 512
const val BLOCK_SIZE_IN_BYTES = 64
const val UINT_MASK = 0xFFFFFFFFU
const val BYTE_MASK_FROM_ULONG = 0xFFUL
const val BYTE_MASK_FROM_UINT = 0xFFU
override val MAX_HASH_BYTES: Int = 32
val iv = arrayOf(
0x6a09e667U,
0xbb67ae85U,
0x3c6ef372U,
0xa54ff53aU,
0x510e527fU,
0x9b05688cU,
0x1f83d9abU,
0x5be0cd19U
)
val k = arrayOf(
0x428a2f98U, 0x71374491U, 0xb5c0fbcfU, 0xe9b5dba5U, 0x3956c25bU, 0x59f111f1U, 0x923f82a4U, 0xab1c5ed5U,
0xd807aa98U, 0x12835b01U, 0x243185beU, 0x550c7dc3U, 0x72be5d74U, 0x80deb1feU, 0x9bdc06a7U, 0xc19bf174U,
0xe49b69c1U, 0xefbe4786U, 0x0fc19dc6U, 0x240ca1ccU, 0x2de92c6fU, 0x4a7484aaU, 0x5cb0a9dcU, 0x76f988daU,
0x983e5152U, 0xa831c66dU, 0xb00327c8U, 0xbf597fc7U, 0xc6e00bf3U, 0xd5a79147U, 0x06ca6351U, 0x14292967U,
0x27b70a85U, 0x2e1b2138U, 0x4d2c6dfcU, 0x53380d13U, 0x650a7354U, 0x766a0abbU, 0x81c2c92eU, 0x92722c85U,
0xa2bfe8a1U, 0xa81a664bU, 0xc24b8b70U, 0xc76c51a3U, 0xd192e819U, 0xd6990624U, 0xf40e3585U, 0x106aa070U,
0x19a4c116U, 0x1e376c08U, 0x2748774cU, 0x34b0bcb5U, 0x391c0cb3U, 0x4ed8aa4aU, 0x5b9cca4fU, 0x682e6ff3U,
0x748f82eeU, 0x78a5636fU, 0x84c87814U, 0x8cc70208U, 0x90befffaU, 0xa4506cebU, 0xbef9a3f7U, 0xc67178f2U
)
@ExperimentalStdlibApi
override fun digest(inputString: String, key: String?, hashLength: Int): UByteArray {
return digest(
inputString.encodeToByteArray().toUByteArray(),
key?.run { encodeToByteArray().toUByteArray()} ?: ubyteArrayOf(),
hashLength)
}
override fun digest(inputMessage: UByteArray, key: UByteArray, hashLength: Int): UByteArray {
var h = iv.copyOf()
val expansionArray = createExpansionArray(inputMessage.size)
val chunks = (
inputMessage +
expansionArray +
(inputMessage.size * 8).toULong().toPaddedByteArray()
)
.chunked(BLOCK_SIZE_IN_BYTES)
chunks.forEach { chunk ->
val w = expandChunk(chunk.toUByteArray())
mix(h, w).copyInto(h)
}
val digest = h[0].toPaddedByteArray() +
h[1].toPaddedByteArray() +
h[2].toPaddedByteArray() +
h[3].toPaddedByteArray() +
h[4].toPaddedByteArray() +
h[5].toPaddedByteArray() +
h[6].toPaddedByteArray() +
h[7].toPaddedByteArray()
return digest
}
private fun scheduleSigma0(value: UInt): UInt {
return value.rotateRight(7) xor value.rotateRight(18) xor (value shr 3)
}
private fun scheduleSigma1(value: UInt): UInt {
return value.rotateRight(17) xor value.rotateRight(19) xor (value shr 10)
}
private fun compressionSigma0(a: UInt): UInt {
return (a rotateRight 2) xor (a rotateRight 13) xor (a rotateRight 22)
}
private fun compressionSigma1(e: UInt): UInt {
return (e rotateRight 6) xor (e rotateRight 11) xor (e rotateRight 25)
}
private fun ch(x: UInt, y: UInt, z: UInt): UInt {
return ((x and y) xor ((x xor UINT_MASK) and z))
}
private fun maj(x: UInt, y: UInt, z: UInt): UInt {
return (((x and y) xor (x and z) xor (y and z)))
}
private fun expandChunk(chunk: UByteArray): Array<UInt> {
val w = Array<UInt>(BLOCK_SIZE_IN_BYTES) {
when (it) {
in 0 until 16 -> {
var collected = (chunk[(it * 4)].toUInt() shl 24) +
(chunk[(it * 4) + 1].toUInt() shl 16) +
(chunk[(it * 4) + 2].toUInt() shl 8) +
(chunk[(it * 4) + 3].toUInt())
collected
}
else -> 0U
}
}
for (i in 16 until BLOCK_SIZE_IN_BYTES) {
val s0 = scheduleSigma0(w[i - 15])
val s1 = scheduleSigma1(w[i - 2])
w[i] = w[i - 16] + s0 + w[i - 7] + s1
}
return w
}
private fun mix(h: Array<UInt>, w: Array<UInt>): Array<UInt> {
var paramA = h[0]
var paramB = h[1]
var paramC = h[2]
var paramD = h[3]
var paramE = h[4]
var paramF = h[5]
var paramG = h[6]
var paramH = h[7]
for (i in 0 until BLOCK_SIZE_IN_BYTES) {
val s1 = compressionSigma1(paramE)
val ch = ch(paramE, paramF, paramG)
val temp1 = paramH + s1 + ch + k[i] + w[i]
val s0 = compressionSigma0(paramA)
val maj = maj(paramA, paramB, paramC)
val temp2 = s0 + maj
paramH = paramG
paramG = paramF
paramF = paramE
paramE = paramD + temp1
paramD = paramC
paramC = paramB
paramB = paramA
paramA = temp1 + temp2
}
h[0] += paramA
h[1] += paramB
h[2] += paramC
h[3] += paramD
h[4] += paramE
h[5] += paramF
h[6] += paramG
h[7] += paramH
return h
}
fun createExpansionArray(originalSizeInBytes: Int): UByteArray {
val originalMessageSizeInBits = originalSizeInBytes * 8
//K such that L + 1 + K + 64 is a multiple of 512
val expandedRemainderOf512 = (originalMessageSizeInBits + BLOCK_SIZE_IN_BYTES + 1) % BLOCK_SIZE
val zeroAddAmount = when (expandedRemainderOf512) {
0 -> 0
else -> (BLOCK_SIZE - expandedRemainderOf512) / 8
}
val expansionArray = UByteArray(zeroAddAmount + 1) {
when (it) {
0 -> 0b10000000U
else -> 0U
}
}
return expansionArray
}
private fun ULong.toPaddedByteArray(): UByteArray {
val byteMask = BYTE_MASK_FROM_ULONG
return UByteArray(8) {
when (it) {
7 -> (this and byteMask).toUByte()
6 -> ((this shr 8) and byteMask).toUByte()
5 -> ((this shr 16) and byteMask).toUByte()
4 -> ((this shr 24) and byteMask).toUByte()
3 -> ((this shr 32) and byteMask).toUByte()
2 -> ((this shr 40) and byteMask).toUByte()
1 -> ((this shr 48) and byteMask).toUByte()
0 -> ((this shr 54) and byteMask).toUByte()
else -> throw RuntimeException("Invalid conversion")
}
}
}
private fun UInt.toPaddedByteArray(): UByteArray {
val byteMask = BYTE_MASK_FROM_UINT
return UByteArray(4) {
when (it) {
3 -> (this and byteMask).toUByte()
2 -> ((this shr 8) and byteMask).toUByte()
1 -> ((this shr 16) and byteMask).toUByte()
0 -> ((this shr 24) and byteMask).toUByte()
else -> throw RuntimeException("Invalid conversion")
}
}
}
}
var h = iv.copyOf()
var counter = 0
var bufferCounter = 0
var buffer = UByteArray(BLOCK_SIZE_IN_BYTES) { 0U }
@ExperimentalStdlibApi
override fun update(data: String) {
return update(data.encodeToByteArray().toUByteArray())
}
override fun update(data: UByteArray) {
if (data.isEmpty()) {
throw RuntimeException("Updating with empty array is not allowed. If you need empty hash, just call digest without updating")
}
when {
bufferCounter + data.size < BLOCK_SIZE_IN_BYTES -> appendToBuffer(data, bufferCounter)
bufferCounter + data.size >= BLOCK_SIZE_IN_BYTES -> {
val chunked = data.chunked(BLOCK_SIZE_IN_BYTES)
chunked.forEach { chunk ->
if (bufferCounter + chunk.size < BLOCK_SIZE_IN_BYTES) {
appendToBuffer(chunk.toUByteArray(), bufferCounter)
} else {
chunk.toUByteArray().copyInto(
destination = buffer,
destinationOffset = bufferCounter,
startIndex = 0,
endIndex = BLOCK_SIZE_IN_BYTES - bufferCounter
)
counter += BLOCK_SIZE_IN_BYTES
consumeBlock(buffer)
buffer = UByteArray(BLOCK_SIZE_IN_BYTES) {
when (it) {
in (0 until (chunk.size - (BLOCK_SIZE_IN_BYTES - bufferCounter))) -> {
chunk[it + (BLOCK_SIZE_IN_BYTES - bufferCounter)]
}
else -> {
0U
}
}
}
bufferCounter = chunk.size - (BLOCK_SIZE_IN_BYTES - bufferCounter)
}
}
}
}
}
private fun consumeBlock(block: UByteArray) {
val w = expandChunk(block)
mix(h, w).copyInto(h)
}
override fun digest(): UByteArray {
val length = counter + bufferCounter
val expansionArray = createExpansionArray(length)
val finalBlock =
buffer.copyOfRange(0, bufferCounter) + expansionArray + (length * 8).toULong().toPaddedByteArray()
finalBlock.chunked(BLOCK_SIZE_IN_BYTES).forEach {
consumeBlock(it.toUByteArray())
}
val digest = h[0].toPaddedByteArray() +
h[1].toPaddedByteArray() +
h[2].toPaddedByteArray() +
h[3].toPaddedByteArray() +
h[4].toPaddedByteArray() +
h[5].toPaddedByteArray() +
h[6].toPaddedByteArray() +
h[7].toPaddedByteArray()
return digest
}
override fun digestString(): String {
return digest().map { it.toString(16) }.joinToString(separator = "")
}
private fun appendToBuffer(array: UByteArray, start: Int) {
array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
bufferCounter += array.size
}
}
@@ -0,0 +1,402 @@
/*
* 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.hash.sha
import com.ionspin.kotlin.crypto.hash.StatelessHash
import com.ionspin.kotlin.crypto.hash.UpdatableHash
import com.ionspin.kotlin.crypto.util.rotateRight
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 18-Jul-2019
*/
@ExperimentalUnsignedTypes
class Sha512Pure : Sha512 {
override val MAX_HASH_BYTES: Int = 32
companion object : StatelessSha512 {
const val BLOCK_SIZE = 1024
const val BLOCK_SIZE_IN_BYTES = 128
const val CHUNK_SIZE = 80
const val ULONG_MASK = 0xFFFFFFFFFFFFFFFFUL
override val MAX_HASH_BYTES: Int = 32
val k = arrayOf(
0x428a2f98d728ae22UL,
0x7137449123ef65cdUL,
0xb5c0fbcfec4d3b2fUL,
0xe9b5dba58189dbbcUL,
0x3956c25bf348b538UL,
0x59f111f1b605d019UL,
0x923f82a4af194f9bUL,
0xab1c5ed5da6d8118UL,
0xd807aa98a3030242UL,
0x12835b0145706fbeUL,
0x243185be4ee4b28cUL,
0x550c7dc3d5ffb4e2UL,
0x72be5d74f27b896fUL,
0x80deb1fe3b1696b1UL,
0x9bdc06a725c71235UL,
0xc19bf174cf692694UL,
0xe49b69c19ef14ad2UL,
0xefbe4786384f25e3UL,
0x0fc19dc68b8cd5b5UL,
0x240ca1cc77ac9c65UL,
0x2de92c6f592b0275UL,
0x4a7484aa6ea6e483UL,
0x5cb0a9dcbd41fbd4UL,
0x76f988da831153b5UL,
0x983e5152ee66dfabUL,
0xa831c66d2db43210UL,
0xb00327c898fb213fUL,
0xbf597fc7beef0ee4UL,
0xc6e00bf33da88fc2UL,
0xd5a79147930aa725UL,
0x06ca6351e003826fUL,
0x142929670a0e6e70UL,
0x27b70a8546d22ffcUL,
0x2e1b21385c26c926UL,
0x4d2c6dfc5ac42aedUL,
0x53380d139d95b3dfUL,
0x650a73548baf63deUL,
0x766a0abb3c77b2a8UL,
0x81c2c92e47edaee6UL,
0x92722c851482353bUL,
0xa2bfe8a14cf10364UL,
0xa81a664bbc423001UL,
0xc24b8b70d0f89791UL,
0xc76c51a30654be30UL,
0xd192e819d6ef5218UL,
0xd69906245565a910UL,
0xf40e35855771202aUL,
0x106aa07032bbd1b8UL,
0x19a4c116b8d2d0c8UL,
0x1e376c085141ab53UL,
0x2748774cdf8eeb99UL,
0x34b0bcb5e19b48a8UL,
0x391c0cb3c5c95a63UL,
0x4ed8aa4ae3418acbUL,
0x5b9cca4f7763e373UL,
0x682e6ff3d6b2b8a3UL,
0x748f82ee5defb2fcUL,
0x78a5636f43172f60UL,
0x84c87814a1f0ab72UL,
0x8cc702081a6439ecUL,
0x90befffa23631e28UL,
0xa4506cebde82bde9UL,
0xbef9a3f7b2c67915UL,
0xc67178f2e372532bUL,
0xca273eceea26619cUL,
0xd186b8c721c0c207UL,
0xeada7dd6cde0eb1eUL,
0xf57d4f7fee6ed178UL,
0x06f067aa72176fbaUL,
0x0a637dc5a2c898a6UL,
0x113f9804bef90daeUL,
0x1b710b35131c471bUL,
0x28db77f523047d84UL,
0x32caab7b40c72493UL,
0x3c9ebe0a15c9bebcUL,
0x431d67c49c100d4cUL,
0x4cc5d4becb3e42b6UL,
0x597f299cfc657e2aUL,
0x5fcb6fab3ad6faecUL,
0x6c44198c4a475817UL
)
val iv = arrayOf(
0x6a09e667f3bcc908UL,
0xbb67ae8584caa73bUL,
0x3c6ef372fe94f82bUL,
0xa54ff53a5f1d36f1UL,
0x510e527fade682d1UL,
0x9b05688c2b3e6c1fUL,
0x1f83d9abfb41bd6bUL,
0x5be0cd19137e2179UL
)
@ExperimentalStdlibApi
override fun digest(inputString: String, key: String?, hashLength: Int): UByteArray {
return digest(
inputString.encodeToByteArray().toUByteArray(),
key?.run { encodeToByteArray().toUByteArray() } ?: ubyteArrayOf(),
hashLength = hashLength
)
}
override fun digest(inputMessage: UByteArray, key: UByteArray, hashLength: Int): UByteArray {
var h = iv.copyOf()
val expansionArray = createExpansionArray(inputMessage.size)
val chunks =
(inputMessage + expansionArray + (inputMessage.size * 8).toULong().toPadded128BitByteArray()).chunked(
BLOCK_SIZE_IN_BYTES
)
chunks.forEach { chunk ->
val w = expandChunk(chunk.toUByteArray())
mix(h, w)
}
val digest =
h[0].toPaddedByteArray() +
h[1].toPaddedByteArray() +
h[2].toPaddedByteArray() +
h[3].toPaddedByteArray() +
h[4].toPaddedByteArray() +
h[5].toPaddedByteArray() +
h[6].toPaddedByteArray() +
h[7].toPaddedByteArray()
return digest
}
private fun scheduleSigma0(value: ULong): ULong {
return value.rotateRight(1) xor value.rotateRight(8) xor (value shr 7)
}
private fun scheduleSigma1(value: ULong): ULong {
return value.rotateRight(19) xor value.rotateRight(61) xor (value shr 6)
}
private fun compressionSigma0(e: ULong): ULong {
return (e rotateRight 28) xor (e rotateRight 34) xor (e rotateRight 39)
}
private fun compressionSigma1(a: ULong): ULong {
return (a rotateRight 14) xor (a rotateRight 18) xor (a rotateRight 41)
}
private fun ch(x: ULong, y: ULong, z: ULong): ULong {
return ((x and y) xor ((x xor ULONG_MASK) and z))
}
private fun maj(x: ULong, y: ULong, z: ULong): ULong {
return ((x and y) xor (x and z) xor (y and z))
}
private fun expandChunk(chunk: UByteArray): Array<ULong> {
val w = Array<ULong>(CHUNK_SIZE) {
when (it) {
in 0 until 16 -> {
var collected = (chunk[(it * 8)].toULong() shl 56) +
(chunk[(it * 8) + 1].toULong() shl 48) +
(chunk[(it * 8) + 2].toULong() shl 40) +
(chunk[(it * 8) + 3].toULong() shl 32) +
(chunk[(it * 8) + 4].toULong() shl 24) +
(chunk[(it * 8) + 5].toULong() shl 16) +
(chunk[(it * 8) + 6].toULong() shl 8) +
(chunk[(it * 8) + 7].toULong())
collected
}
else -> 0UL
}
}
for (i in 16 until CHUNK_SIZE) {
val s0 = scheduleSigma0(w[i - 15])
val s1 = scheduleSigma1(w[i - 2])
w[i] = w[i - 16] + s0 + w[i - 7] + s1
}
return w
}
private fun mix(h: Array<ULong>, w: Array<ULong>): Array<ULong> {
var paramA = h[0]
var paramB = h[1]
var paramC = h[2]
var paramD = h[3]
var paramE = h[4]
var paramF = h[5]
var paramG = h[6]
var paramH = h[7]
for (i in 0 until CHUNK_SIZE) {
val s1 = compressionSigma1(paramE)
val ch = ch(paramE, paramF, paramG)
val temp1 = paramH + s1 + ch + k[i] + w[i]
val s0 = compressionSigma0(paramA)
val maj = maj(paramA, paramB, paramC)
val temp2 = s0 + maj
paramH = paramG
paramG = paramF
paramF = paramE
paramE = paramD + temp1
paramD = paramC
paramC = paramB
paramB = paramA
paramA = temp1 + temp2
}
h[0] += paramA
h[1] += paramB
h[2] += paramC
h[3] += paramD
h[4] += paramE
h[5] += paramF
h[6] += paramG
h[7] += paramH
return h
}
fun createExpansionArray(originalSizeInBytes: Int): UByteArray {
val originalMessageSizeInBits = originalSizeInBytes * 8
val expandedRemainderOf1024 = (originalMessageSizeInBits + 129) % BLOCK_SIZE
val zeroAddAmount = when (expandedRemainderOf1024) {
0 -> 0
else -> (BLOCK_SIZE - expandedRemainderOf1024) / 8
}
val expansionArray = UByteArray(zeroAddAmount + 1) {
when (it) {
0 -> 0b10000000U
else -> 0U
}
}
return expansionArray
}
private fun ULong.toPaddedByteArray(): UByteArray {
val byteMask = 0xFFUL
//Ignore messages longer than 64 bits for now
return UByteArray(8) {
when (it) {
7 -> (this and byteMask).toUByte()
6 -> ((this shr 8) and byteMask).toUByte()
5 -> ((this shr 16) and byteMask).toUByte()
4 -> ((this shr 24) and byteMask).toUByte()
3 -> ((this shr 32) and byteMask).toUByte()
2 -> ((this shr 40) and byteMask).toUByte()
1 -> ((this shr 48) and byteMask).toUByte()
0 -> ((this shr 56) and byteMask).toUByte()
else -> 0U
}
}
}
private fun ULong.toPadded128BitByteArray(): UByteArray {
val byteMask = 0xFFUL
//Ignore messages longer than 64 bits for now
return UByteArray(16) {
when (it) {
15 -> (this and byteMask).toUByte()
14 -> ((this shr 8) and byteMask).toUByte()
13 -> ((this shr 16) and byteMask).toUByte()
12 -> ((this shr 24) and byteMask).toUByte()
11 -> ((this shr 32) and byteMask).toUByte()
10 -> ((this shr 40) and byteMask).toUByte()
9 -> ((this shr 48) and byteMask).toUByte()
8 -> ((this shr 54) and byteMask).toUByte()
else -> 0U
}
}
}
}
var h = iv.copyOf()
var counter = 0
var bufferCounter = 0
var buffer = UByteArray(BLOCK_SIZE_IN_BYTES) { 0U }
@ExperimentalStdlibApi
override fun update(data: String) {
return update(data.encodeToByteArray().toUByteArray())
}
override fun update(data: UByteArray) {
if (data.isEmpty()) {
throw RuntimeException("Updating with empty array is not allowed. If you need empty hash, just call digest without updating")
}
when {
bufferCounter + data.size < BLOCK_SIZE_IN_BYTES -> appendToBuffer(data, bufferCounter)
bufferCounter + data.size >= BLOCK_SIZE_IN_BYTES -> {
val chunked = data.chunked(BLOCK_SIZE_IN_BYTES)
chunked.forEach { chunk ->
if (bufferCounter + chunk.size < BLOCK_SIZE_IN_BYTES) {
appendToBuffer(chunk.toUByteArray(), bufferCounter)
} else {
chunk.toUByteArray().copyInto(
destination = buffer,
destinationOffset = bufferCounter,
startIndex = 0,
endIndex = BLOCK_SIZE_IN_BYTES - bufferCounter
)
counter += BLOCK_SIZE_IN_BYTES
consumeBlock(buffer)
buffer = UByteArray(BLOCK_SIZE_IN_BYTES) {
when (it) {
in (0 until (chunk.size - (BLOCK_SIZE_IN_BYTES - bufferCounter))) -> {
chunk[it + (BLOCK_SIZE_IN_BYTES - bufferCounter)]
}
else -> {
0U
}
}
}
bufferCounter = chunk.size - (BLOCK_SIZE_IN_BYTES - bufferCounter)
}
}
}
}
}
private fun consumeBlock(block: UByteArray) {
val w = expandChunk(block)
mix(h, w).copyInto(h)
}
override fun digest(): UByteArray {
val length = counter + bufferCounter
val expansionArray = createExpansionArray(length)
val finalBlock =
buffer.copyOfRange(0, bufferCounter) + expansionArray + (length * 8).toULong().toPadded128BitByteArray()
finalBlock.chunked(BLOCK_SIZE_IN_BYTES).forEach {
consumeBlock(it.toUByteArray())
}
val digest = h[0].toPaddedByteArray() +
h[1].toPaddedByteArray() +
h[2].toPaddedByteArray() +
h[3].toPaddedByteArray() +
h[4].toPaddedByteArray() +
h[5].toPaddedByteArray() +
h[6].toPaddedByteArray() +
h[7].toPaddedByteArray()
return digest
}
override fun digestString(): String {
return digest().map { it.toString(16) }.joinToString(separator = "")
}
private fun appendToBuffer(array: UByteArray, start: Int) {
array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
bufferCounter += array.size
}
}
@@ -0,0 +1,10 @@
package com.ionspin.kotlin.crypto.keyderivation.argon2
import com.ionspin.kotlin.crypto.keyderivation.KeyDerivationFunction
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 24-May-2020
*/
interface Argon2 : KeyDerivationFunction
@@ -0,0 +1,34 @@
/*
* 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.parallelization
import kotlin.time.ExperimentalTime
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 17-May-2020
*/
@ExperimentalTime
object Coroutines14 {
fun argonParallel() : Array<UByte> {
// val argon = Argon2()
// argon
println("Placeholder")
return emptyArray()
}
}
@@ -0,0 +1,241 @@
/*
* 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.symmetric
import com.ionspin.kotlin.crypto.SRNG
import com.ionspin.kotlin.crypto.util.xor
/**
* Advanced encryption standard with cipher block chaining and PKCS #5
*
* For bulk encryption/decryption use [AesCbcPure.encrypt] and [AesCbcPure.decrypt]
*
* To get an instance of AesCbc and then feed it data sequentially with [addData] use [createEncryptor] and [createDecryptor]
*
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 21-Sep-2019
*/
@ExperimentalUnsignedTypes
class AesCbcPure internal constructor(val aesKey: AesKey, val mode: Mode, initializationVector: UByteArray? = null) {
companion object {
const val BLOCK_BYTES = 16
/**
* Creates and returns AesCbc instance that can be fed data using [addData]. Once you have submitted all
* data call [encrypt]
*/
fun createEncryptor(aesKey: AesKey) : AesCbcPure {
return AesCbcPure(aesKey, Mode.ENCRYPT)
}
/**
* Creates and returns AesCbc instance that can be fed data using [addData]. Once you have submitted all
* data call [decrypt]
*/
fun createDecryptor(aesKey : AesKey) : AesCbcPure {
return AesCbcPure(aesKey, Mode.DECRYPT)
}
/**
* Bulk encryption, returns encrypted data and a random initialization vector
*/
fun encrypt(aesKey: AesKey, data: UByteArray): EncryptedDataAndInitializationVector {
val aesCbc = AesCbcPure(aesKey, Mode.ENCRYPT)
aesCbc.addData(data)
return aesCbc.encrypt()
}
/**
* Bulk decryption, returns decrypted data
*/
fun decrypt(aesKey: AesKey, data: UByteArray, initialCounter: UByteArray? = null): UByteArray {
val aesCbc = AesCbcPure(aesKey, Mode.DECRYPT, initialCounter)
aesCbc.addData(data)
return aesCbc.decrypt()
}
private fun padToBlock(unpadded: UByteArray): UByteArray {
val paddingSize = 16 - unpadded.size
if (unpadded.size == BLOCK_BYTES) {
return unpadded
}
if (unpadded.size == BLOCK_BYTES) {
return UByteArray(BLOCK_BYTES) {
BLOCK_BYTES.toUByte()
}
}
if (unpadded.size > BLOCK_BYTES) {
throw IllegalStateException("Block larger than 128 bytes")
}
return UByteArray(BLOCK_BYTES) {
when (it) {
in unpadded.indices -> unpadded[it]
else -> paddingSize.toUByte()
}
}
}
}
var currentOutput: UByteArray = ubyteArrayOf()
var previousEncrypted: UByteArray = ubyteArrayOf()
val initVector = if (initializationVector.isNullOrEmpty()) {
SRNG.getRandomBytes(16)
} else {
initializationVector
}
val output = MutableList<UByteArray>(0) { ubyteArrayOf() }
var buffer: UByteArray = UByteArray(16) { 0U }
var bufferCounter = 0
fun addData(data: UByteArray) {
//Padding
when {
bufferCounter + data.size < BLOCK_BYTES -> appendToBuffer(data, bufferCounter)
bufferCounter + data.size >= BLOCK_BYTES -> {
val chunked = data.chunked(BLOCK_BYTES)
chunked.forEach { chunk ->
if (bufferCounter + chunk.size < BLOCK_BYTES) {
appendToBuffer(chunk.toUByteArray(), bufferCounter)
} else {
chunk.toUByteArray().copyInto(
destination = buffer,
destinationOffset = bufferCounter,
startIndex = 0,
endIndex = BLOCK_BYTES - bufferCounter
)
output += consumeBlock(buffer)
buffer = UByteArray(BLOCK_BYTES) {
when (it) {
in (0 until (chunk.size - (BLOCK_BYTES - bufferCounter))) -> {
chunk[it + (BLOCK_BYTES - bufferCounter)]
}
else -> {
0U
}
}
}
bufferCounter = chunk.size - (BLOCK_BYTES - bufferCounter)
}
}
}
}
}
/**
* Encrypt fed data and return it alongside the randomly chosen initialization vector
* @return Encrypted data and initialization vector
*/
fun encrypt(): EncryptedDataAndInitializationVector {
if (bufferCounter > 0) {
val lastBlockPadded = padToBlock(buffer)
if (lastBlockPadded.size > BLOCK_BYTES) {
val chunks = lastBlockPadded.chunked(BLOCK_BYTES).map { it.toUByteArray() }
output += consumeBlock(chunks[0])
output += consumeBlock(chunks[1])
} else {
output += consumeBlock(lastBlockPadded)
}
}
return EncryptedDataAndInitializationVector(
output.reversed().foldRight(UByteArray(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes },
initVector
)
}
/**
* Decrypt data
* @return Decrypted data
*/
fun decrypt(): UByteArray {
val removePaddingCount = output.last().last()
val removedPadding = if (removePaddingCount > 0U && removePaddingCount < 16U) {
output.last().dropLast(removePaddingCount.toInt() and 0x7F)
} else {
output.last().toList()
}.toUByteArray()
val preparedOutput = (output.dropLast(1) + listOf(removedPadding))
//JS compiler freaks out here if we don't supply exact type
val reversed : List<UByteArray> = preparedOutput.reversed() as List<UByteArray>
val folded : UByteArray = reversed.foldRight(UByteArray(0) { 0U }) { uByteArray, acc ->
acc + uByteArray
}
return folded
}
private fun appendToBuffer(array: UByteArray, start: Int) {
array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
bufferCounter += array.size
}
private fun consumeBlock(data: UByteArray): UByteArray {
return when (mode) {
Mode.ENCRYPT -> {
currentOutput = if (currentOutput.isEmpty()) {
println("IV: $initVector")
AesPure.encrypt(aesKey, data xor initVector)
} else {
AesPure.encrypt(aesKey, data xor currentOutput)
}
currentOutput
}
Mode.DECRYPT -> {
if (currentOutput.isEmpty()) {
currentOutput = AesPure.decrypt(aesKey, data) xor initVector
} else {
currentOutput = AesPure.decrypt(aesKey, data) xor previousEncrypted
}
previousEncrypted = data
currentOutput
}
}
}
}
@ExperimentalUnsignedTypes
data class EncryptedDataAndInitializationVector(val encryptedData : UByteArray, val initilizationVector : UByteArray) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false
other as EncryptedDataAndInitializationVector
if (!encryptedData.contentEquals(other.encryptedData)) return false
if (!initilizationVector.contentEquals(other.initilizationVector)) return false
return true
}
override fun hashCode(): Int {
var result = encryptedData.contentHashCode()
result = 31 * result + initilizationVector.contentHashCode()
return result
}
}
@@ -0,0 +1,208 @@
/*
* 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.symmetric
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.symmetric.AesCtrPure.Companion.encrypt
import com.ionspin.kotlin.crypto.util.xor
/**
*
* Advanced encryption standard with counter mode
*
* For bulk encryption/decryption use [AesCtrPure.encrypt] and [AesCtrPure.decrypt]
*
* To get an instance of AesCtr and then feed it data sequentially with [addData] use [createEncryptor] and [createDecryptor]
*
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 22-Sep-2019
*/
@ExperimentalUnsignedTypes
class AesCtrPure internal constructor(val aesKey: AesKey, val mode: Mode, initialCounter: UByteArray? = null) {
companion object {
const val BLOCK_BYTES = 16
val modularCreator = ModularBigInteger.creatorForModulo(BigInteger.ONE.shl(128) - 1)
/**
* Creates and returns AesCtr instance that can be fed data using [addData]. Once you have submitted all
* data call [encrypt]
*/
fun createEncryptor(aesKey: AesKey) : AesCtrPure {
return AesCtrPure(aesKey, Mode.ENCRYPT)
}
/**
* Creates and returns AesCtr instance that can be fed data using [addData]. Once you have submitted all
* data call [decrypt]
*/
fun createDecryptor(aesKey : AesKey) : AesCtrPure {
return AesCtrPure(aesKey, Mode.DECRYPT)
}
/**
* Bulk encryption, returns encrypted data and a random initial counter
*/
fun encrypt(aesKey: AesKey, data: UByteArray): EncryptedDataAndInitialCounter {
val aesCtr = AesCtrPure(aesKey, Mode.ENCRYPT)
aesCtr.addData(data)
return aesCtr.encrypt()
}
/**
* Bulk decryption, returns decrypted data
*/
fun decrypt(aesKey: AesKey, data: UByteArray, initialCounter: UByteArray? = null): UByteArray {
val aesCtr = AesCtrPure(aesKey, Mode.DECRYPT, initialCounter)
aesCtr.addData(data)
return aesCtr.decrypt()
}
}
var currentOutput: UByteArray = ubyteArrayOf()
var previousEncrypted: UByteArray = ubyteArrayOf()
val counterStart = if (initialCounter.isNullOrEmpty()) {
SRNG.getRandomBytes(16)
} else {
initialCounter
}
var blockCounter = modularCreator.fromBigInteger(BigInteger.fromUByteArray(counterStart.toTypedArray(), Endianness.BIG))
val output = MutableList<UByteArray>(0) { ubyteArrayOf() }
var buffer: UByteArray = UByteArray(16) { 0U }
var bufferCounter = 0
fun addData(data: UByteArray) {
//Padding
when {
bufferCounter + data.size < BLOCK_BYTES -> appendToBuffer(data, bufferCounter)
bufferCounter + data.size >= BLOCK_BYTES -> {
val chunked = data.chunked(BLOCK_BYTES)
chunked.forEach { chunk ->
if (bufferCounter + chunk.size < BLOCK_BYTES) {
appendToBuffer(chunk.toUByteArray(), bufferCounter)
} else {
chunk.toUByteArray().copyInto(
destination = buffer,
destinationOffset = bufferCounter,
startIndex = 0,
endIndex = BLOCK_BYTES - bufferCounter
)
output += consumeBlock(buffer, blockCounter)
blockCounter += 1
buffer = UByteArray(BLOCK_BYTES) {
when (it) {
in (0 until (chunk.size - (BLOCK_BYTES - bufferCounter))) -> {
chunk[it + (BLOCK_BYTES - bufferCounter)]
}
else -> {
0U
}
}
}
bufferCounter = chunk.size - (BLOCK_BYTES - bufferCounter)
}
}
}
}
}
/**
* Encrypt fed data and return it alongside the randomly chosen initial counter state
* @return Encrypted data and initial counter state
*/
fun encrypt(): EncryptedDataAndInitialCounter {
if (bufferCounter > 0) {
output += consumeBlock(buffer, blockCounter)
}
return EncryptedDataAndInitialCounter(
output.reversed().foldRight(UByteArray(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes },
counterStart
)
}
/**
* Decrypt data
* @return Decrypted data
*/
fun decrypt(): UByteArray {
if (bufferCounter > 0) {
output += consumeBlock(buffer, blockCounter)
}
//JS compiler freaks out here if we don't supply exact type
val reversed: List<UByteArray> = output.reversed() as List<UByteArray>
val folded: UByteArray = reversed.foldRight(UByteArray(0) { 0U }) { arrayOfUBytes, acc ->
acc + arrayOfUBytes
}
return folded
}
private fun appendToBuffer(array: UByteArray, start: Int) {
array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
bufferCounter += array.size
}
private fun consumeBlock(data: UByteArray, blockCount: ModularBigInteger): UByteArray {
val blockCountAsByteArray = blockCount.toUByteArray(Endianness.BIG).toUByteArray().expandCounterTo16Bytes()
return when (mode) {
Mode.ENCRYPT -> {
AesPure.encrypt(aesKey, blockCountAsByteArray) xor data
}
Mode.DECRYPT -> {
AesPure.encrypt(aesKey, blockCountAsByteArray) xor data
}
}
}
private fun UByteArray.expandCounterTo16Bytes() : UByteArray {
return if (this.size < 16) {
println("Expanding")
val diff = 16 - this.size
val pad = UByteArray(diff) { 0U }
pad + this
} else {
this
}
}
}
@ExperimentalUnsignedTypes
data class EncryptedDataAndInitialCounter(val encryptedData : UByteArray, val initialCounter : UByteArray) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false
other as EncryptedDataAndInitializationVector
if (!encryptedData.contentEquals(other.encryptedData)) return false
if (!initialCounter.contentEquals(other.initilizationVector)) return false
return true
}
override fun hashCode(): Int {
var result = encryptedData.contentHashCode()
result = 31 * result + initialCounter.contentHashCode()
return result
}
}
@@ -0,0 +1,378 @@
package com.ionspin.kotlin.crypto.symmetric
import com.ionspin.kotlin.crypto.util.flattenToUByteArray
/**
* Created by Ugljesa Jovanovic (jovanovic.ugljesa@gmail.com) on 07/Sep/2019
*/
@ExperimentalUnsignedTypes
internal class AesPure internal constructor(val aesKey: AesKey, val input: UByteArray) {
companion object {
private val debug = false
private val sBox: UByteArray =
ubyteArrayOf(
// @formatter:off
0x63U, 0x7cU, 0x77U, 0x7bU, 0xf2U, 0x6bU, 0x6fU, 0xc5U, 0x30U, 0x01U, 0x67U, 0x2bU, 0xfeU, 0xd7U, 0xabU, 0x76U,
0xcaU, 0x82U, 0xc9U, 0x7dU, 0xfaU, 0x59U, 0x47U, 0xf0U, 0xadU, 0xd4U, 0xa2U, 0xafU, 0x9cU, 0xa4U, 0x72U, 0xc0U,
0xb7U, 0xfdU, 0x93U, 0x26U, 0x36U, 0x3fU, 0xf7U, 0xccU, 0x34U, 0xa5U, 0xe5U, 0xf1U, 0x71U, 0xd8U, 0x31U, 0x15U,
0x04U, 0xc7U, 0x23U, 0xc3U, 0x18U, 0x96U, 0x05U, 0x9aU, 0x07U, 0x12U, 0x80U, 0xe2U, 0xebU, 0x27U, 0xb2U, 0x75U,
0x09U, 0x83U, 0x2cU, 0x1aU, 0x1bU, 0x6eU, 0x5aU, 0xa0U, 0x52U, 0x3bU, 0xd6U, 0xb3U, 0x29U, 0xe3U, 0x2fU, 0x84U,
0x53U, 0xd1U, 0x00U, 0xedU, 0x20U, 0xfcU, 0xb1U, 0x5bU, 0x6aU, 0xcbU, 0xbeU, 0x39U, 0x4aU, 0x4cU, 0x58U, 0xcfU,
0xd0U, 0xefU, 0xaaU, 0xfbU, 0x43U, 0x4dU, 0x33U, 0x85U, 0x45U, 0xf9U, 0x02U, 0x7fU, 0x50U, 0x3cU, 0x9fU, 0xa8U,
0x51U, 0xa3U, 0x40U, 0x8fU, 0x92U, 0x9dU, 0x38U, 0xf5U, 0xbcU, 0xb6U, 0xdaU, 0x21U, 0x10U, 0xffU, 0xf3U, 0xd2U,
0xcdU, 0x0cU, 0x13U, 0xecU, 0x5fU, 0x97U, 0x44U, 0x17U, 0xc4U, 0xa7U, 0x7eU, 0x3dU, 0x64U, 0x5dU, 0x19U, 0x73U,
0x60U, 0x81U, 0x4fU, 0xdcU, 0x22U, 0x2aU, 0x90U, 0x88U, 0x46U, 0xeeU, 0xb8U, 0x14U, 0xdeU, 0x5eU, 0x0bU, 0xdbU,
0xe0U, 0x32U, 0x3aU, 0x0aU, 0x49U, 0x06U, 0x24U, 0x5cU, 0xc2U, 0xd3U, 0xacU, 0x62U, 0x91U, 0x95U, 0xe4U, 0x79U,
0xe7U, 0xc8U, 0x37U, 0x6dU, 0x8dU, 0xd5U, 0x4eU, 0xa9U, 0x6cU, 0x56U, 0xf4U, 0xeaU, 0x65U, 0x7aU, 0xaeU, 0x08U,
0xbaU, 0x78U, 0x25U, 0x2eU, 0x1cU, 0xa6U, 0xb4U, 0xc6U, 0xe8U, 0xddU, 0x74U, 0x1fU, 0x4bU, 0xbdU, 0x8bU, 0x8aU,
0x70U, 0x3eU, 0xb5U, 0x66U, 0x48U, 0x03U, 0xf6U, 0x0eU, 0x61U, 0x35U, 0x57U, 0xb9U, 0x86U, 0xc1U, 0x1dU, 0x9eU,
0xe1U, 0xf8U, 0x98U, 0x11U, 0x69U, 0xd9U, 0x8eU, 0x94U, 0x9bU, 0x1eU, 0x87U, 0xe9U, 0xceU, 0x55U, 0x28U, 0xdfU,
0x8cU, 0xa1U, 0x89U, 0x0dU, 0xbfU, 0xe6U, 0x42U, 0x68U, 0x41U, 0x99U, 0x2dU, 0x0fU, 0xb0U, 0x54U, 0xbbU, 0x16U
// @formatter:on
)
private val inverseSBox: UByteArray =
ubyteArrayOf(
// @formatter:off
0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U, 0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU,
0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U, 0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU,
0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU, 0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU,
0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U, 0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U,
0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U, 0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U,
0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU, 0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U,
0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU, 0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U,
0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U, 0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU,
0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU, 0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U,
0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U, 0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU,
0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U, 0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU,
0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U, 0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U,
0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U, 0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU,
0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU, 0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU,
0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U, 0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U,
0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U, 0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU
// @formatter:on
)
val rcon: UByteArray = ubyteArrayOf(0x8DU, 0x01U, 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U, 0x1BU, 0x36U)
fun encrypt(aesKey: AesKey, input: UByteArray): UByteArray {
return AesPure(aesKey, input).encrypt()
}
fun decrypt(aesKey: AesKey, input: UByteArray): UByteArray {
return AesPure(aesKey, input).decrypt()
}
}
val state: Array<UByteArray> = (0 until 4).map { outerCounter ->
UByteArray(4) { innerCounter -> input[innerCounter * 4 + outerCounter] }
}.toTypedArray()
val numberOfRounds = when (aesKey) {
is AesKey.Aes128Key -> 10
is AesKey.Aes192Key -> 12
is AesKey.Aes256Key -> 14
}
val expandedKey: Array<UByteArray> = expandKey()
var round = 0
var completed : Boolean = false
private set
fun subBytes() {
state.forEachIndexed { indexRow, row ->
row.forEachIndexed { indexColumn, element ->
state[indexRow][indexColumn] = getSBoxValue(element)
}
}
}
fun getSBoxValue(element: UByte): UByte {
val firstDigit = (element / 16U).toInt()
val secondDigit = (element % 16U).toInt()
return sBox[firstDigit * 16 + secondDigit]
}
fun inverseSubBytes() {
state.forEachIndexed { indexRow, row ->
row.forEachIndexed { indexColumn, element ->
state[indexRow][indexColumn] = getInverseSBoxValue(element)
}
}
}
fun getInverseSBoxValue(element: UByte): UByte {
val firstDigit = (element / 16U).toInt()
val secondDigit = (element % 16U).toInt()
return inverseSBox[firstDigit * 16 + secondDigit]
}
fun shiftRows() {
state[0] = ubyteArrayOf(state[0][0], state[0][1], state[0][2], state[0][3])
state[1] = ubyteArrayOf(state[1][1], state[1][2], state[1][3], state[1][0])
state[2] = ubyteArrayOf(state[2][2], state[2][3], state[2][0], state[2][1])
state[3] = ubyteArrayOf(state[3][3], state[3][0], state[3][1], state[3][2])
}
fun inversShiftRows() {
state[0] = ubyteArrayOf(state[0][0], state[0][1], state[0][2], state[0][3])
state[1] = ubyteArrayOf(state[1][3], state[1][0], state[1][1], state[1][2])
state[2] = ubyteArrayOf(state[2][2], state[2][3], state[2][0], state[2][1])
state[3] = ubyteArrayOf(state[3][1], state[3][2], state[3][3], state[3][0])
}
fun mixColumns() {
val stateMixed: Array<UByteArray> = (0 until 4).map {
UByteArray(4) { 0U }
}.toTypedArray()
for (c in 0..3) {
stateMixed[0][c] = (2U gfm state[0][c]) xor (3U gfm state[1][c]) xor state[2][c] xor state[3][c]
stateMixed[1][c] = state[0][c] xor (2U gfm state[1][c]) xor (3U gfm state[2][c]) xor state[3][c]
stateMixed[2][c] = state[0][c] xor state[1][c] xor (2U gfm state[2][c]) xor (3U gfm state[3][c])
stateMixed[3][c] = 3U gfm state[0][c] xor state[1][c] xor state[2][c] xor (2U gfm state[3][c])
}
stateMixed.copyInto(state)
}
fun inverseMixColumns() {
val stateMixed: Array<UByteArray> = (0 until 4).map {
UByteArray(4) { 0U }
}.toTypedArray()
for (c in 0..3) {
stateMixed[0][c] =
(0x0eU gfm state[0][c]) xor (0x0bU gfm state[1][c]) xor (0x0dU gfm state[2][c]) xor (0x09U gfm state[3][c])
stateMixed[1][c] =
(0x09U gfm state[0][c]) xor (0x0eU gfm state[1][c]) xor (0x0bU gfm state[2][c]) xor (0x0dU gfm state[3][c])
stateMixed[2][c] =
(0x0dU gfm state[0][c]) xor (0x09U gfm state[1][c]) xor (0x0eU gfm state[2][c]) xor (0x0bU gfm state[3][c])
stateMixed[3][c] =
(0x0bU gfm state[0][c]) xor (0x0dU gfm state[1][c]) xor (0x09U gfm state[2][c]) xor (0x0eU gfm state[3][c])
}
stateMixed.copyInto(state)
}
fun galoisFieldAdd(first: UByte, second: UByte): UByte {
return first xor second
}
fun galoisFieldMultiply(first: UByte, second: UByte): UByte {
var result: UInt = 0U
var firstInt = first.toUInt()
var secondInt = second.toUInt()
var carry: UInt = 0U
for (i in 0..7) {
if (secondInt and 0x01U == 1U) {
result = result xor firstInt
}
carry = firstInt and 0x80U
firstInt = firstInt shl 1
if (carry == 0x80U) {
firstInt = firstInt xor 0x001BU
}
secondInt = secondInt shr 1
firstInt = firstInt and 0xFFU
}
return result.toUByte()
}
fun addRoundKey() {
for (i in 0 until 4) {
state[0][i] = state[0][i] xor expandedKey[round * 4 + i][0]
state[1][i] = state[1][i] xor expandedKey[round * 4 + i][1]
state[2][i] = state[2][i] xor expandedKey[round * 4 + i][2]
state[3][i] = state[3][i] xor expandedKey[round * 4 + i][3]
}
round++
}
fun inverseAddRoundKey() {
for (i in 0 until 4) {
state[0][i] = state[0][i] xor expandedKey[round * 4 + i][0]
state[1][i] = state[1][i] xor expandedKey[round * 4 + i][1]
state[2][i] = state[2][i] xor expandedKey[round * 4 + i][2]
state[3][i] = state[3][i] xor expandedKey[round * 4 + i][3]
}
round--
}
infix fun UInt.gfm(second: UByte): UByte {
return galoisFieldMultiply(this.toUByte(), second)
}
fun expandKey(): Array<UByteArray> {
val expandedKey = (0 until 4 * (numberOfRounds + 1)).map {
UByteArray(4) { 0U }
}.toTypedArray()
// First round
for (i in 0 until aesKey.numberOf32BitWords) {
expandedKey[i][0] = aesKey.keyArray[i * 4 + 0]
expandedKey[i][1] = aesKey.keyArray[i * 4 + 1]
expandedKey[i][2] = aesKey.keyArray[i * 4 + 2]
expandedKey[i][3] = aesKey.keyArray[i * 4 + 3]
}
for (i in aesKey.numberOf32BitWords until 4 * (numberOfRounds + 1)) {
val temp = expandedKey[i - 1].copyOf()
if (i % aesKey.numberOf32BitWords == 0) {
//RotWord
val tempTemp = temp[0]
temp[0] = temp[1]
temp[1] = temp[2]
temp[2] = temp[3]
temp[3] = tempTemp
//SubWord
temp[0] = getSBoxValue(temp[0])
temp[1] = getSBoxValue(temp[1])
temp[2] = getSBoxValue(temp[2])
temp[3] = getSBoxValue(temp[3])
temp[0] = temp[0] xor rcon[i / aesKey.numberOf32BitWords]
} else if (aesKey is AesKey.Aes256Key && i % aesKey.numberOf32BitWords == 4) {
temp[0] = getSBoxValue(temp[0])
temp[1] = getSBoxValue(temp[1])
temp[2] = getSBoxValue(temp[2])
temp[3] = getSBoxValue(temp[3])
}
expandedKey[i] = expandedKey[i - aesKey.numberOf32BitWords].mapIndexed { index, it ->
it xor temp[index]
}.toUByteArray()
clearArray(temp)
}
return expandedKey
}
fun encrypt(): UByteArray {
if (completed) {
throw RuntimeException("Encrypt can only be called once per Aes instance, since the state is cleared at the " +
"end of the operation")
}
printState()
addRoundKey()
printState()
for (i in 0 until numberOfRounds - 1) {
subBytes()
printState()
shiftRows()
printState()
mixColumns()
printState()
addRoundKey()
printState()
}
subBytes()
printState()
shiftRows()
printState()
addRoundKey()
printState()
val transposedMatrix = (0 until 4).map { outerCounter ->
UByteArray(4) { 0U }
}
for (i in 0 until 4) {
for (j in 0 until 4) {
transposedMatrix[i][j] = state[j][i]
}
}
state.forEach { clearArray(it) }
completed = true
return transposedMatrix.flattenToUByteArray()
}
fun decrypt(): UByteArray {
if (completed) {
throw RuntimeException("Decrypt can only be called once per Aes instance, since the state is cleared at the " +
"end of the operation")
}
round = numberOfRounds
printState()
inverseAddRoundKey()
printState()
for (i in 0 until numberOfRounds - 1) {
inversShiftRows()
printState()
inverseSubBytes()
printState()
inverseAddRoundKey()
printState()
inverseMixColumns()
printState()
}
inversShiftRows()
printState()
inverseSubBytes()
printState()
inverseAddRoundKey()
printState()
val transposedMatrix = (0 until 4).map { outerCounter ->
UByteArray(4) { 0U }
}
for (i in 0 until 4) {
for (j in 0 until 4) {
transposedMatrix[i][j] = state[j][i]
}
}
state.forEach { clearArray(it) }
completed = true
return transposedMatrix.flattenToUByteArray()
}
private fun clearArray(array : UByteArray) {
array.indices.forEach { array[it] = 0U }
}
private fun printState() {
if (!debug) {
return
}
println()
state.forEach {
println(it.joinToString(separator = " ") { it.toString(16) })
}
}
private fun printState(specific : List<UByteArray>) {
if (!debug) {
return
}
println()
specific.forEach {
println(it.joinToString(separator = " ") { it.toString(16) })
}
}
}
sealed class AesKey(val key: String, val keyLength: Int) {
val keyArray: UByteArray = key.chunked(2).map { it.toUByte(16) }.toUByteArray()
val numberOf32BitWords = keyLength / 32
class Aes128Key(key: String) : AesKey(key, 128)
class Aes192Key(key: String) : AesKey(key, 192)
class Aes256Key(key: String) : AesKey(key, 256)
init {
checkKeyLength(key, keyLength)
}
fun checkKeyLength(key: String, expectedLength: Int) {
if ((key.length / 2) != expectedLength / 8) {
throw RuntimeException("Invalid key length")
}
}
}
@@ -0,0 +1,27 @@
/*
* 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.symmetric
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 18-Sep-2019
*/
enum class Mode {
ENCRYPT, DECRYPT
}
@@ -0,0 +1,262 @@
/*
* 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.
*/
@file:Suppress("EXPERIMENTAL_API_USAGE")
package com.ionspin.kotlin.crypto.util
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 15-Jul-2019
*/
fun Array<Byte>.hexColumsPrint() {
val printout = this.map { it.toString(16) }.chunked(16)
printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) }
}
fun Array<UByte>.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 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() }) }
}
inline fun <reified T> Array<T>.chunked(sliceSize: Int): Array<Array<T>> {
val last = this.size % sliceSize
val hasLast = last != 0
val numberOfSlices = this.size / sliceSize
val result : MutableList<List<T>> = MutableList<List<T>>(0) { emptyList() }
for (i in 0 until numberOfSlices) {
result.add(this.slice(i * sliceSize until (i + 1) * sliceSize))
}
if (hasLast) {
result.add(this.slice(numberOfSlices * sliceSize until this.size))
}
return result.map { it.toTypedArray() }.toTypedArray()
}
@ExperimentalUnsignedTypes
infix fun UInt.rotateRight(places: Int): UInt {
return (this shr places) xor (this shl (32 - places))
}
@ExperimentalUnsignedTypes
infix fun ULong.rotateRight(places: Int): ULong {
return (this shr places) xor (this shl (64 - places))
}
@ExperimentalUnsignedTypes
infix fun Array<UByte>.xor(other : Array<UByte>) : Array<UByte> {
if (this.size != other.size) {
throw RuntimeException("Operands of different sizes are not supported yet")
}
return Array(this.size) { this[it] xor other[it] }
}
@ExperimentalUnsignedTypes
infix fun UByteArray.xor(other : UByteArray) : UByteArray {
if (this.size != other.size) {
throw RuntimeException("Operands of different sizes are not supported yet")
}
return UByteArray(this.size) { this[it] xor other[it] }
}
@ExperimentalUnsignedTypes
fun String.hexStringToTypedUByteArray() : Array<UByte> {
return this.chunked(2).map { it.toUByte(16) }.toTypedArray()
}
@ExperimentalUnsignedTypes
fun String.hexStringToUByteArray() : UByteArray {
return this.chunked(2).map { it.toUByte(16) }.toUByteArray()
}
@ExperimentalUnsignedTypes
fun Array<UByte>.toHexString() : String {
return this.joinToString(separator = "") {
if (it <= 0x0FU) {
"0${it.toString(16)}"
} else {
it.toString(16)
}
}
}
@ExperimentalUnsignedTypes
fun UByteArray.toHexString() : String {
return this.joinToString(separator = "") {
if (it <= 0x0FU) {
"0${it.toString(16)}"
} else {
it.toString(16)
}
}
}
// UInt / Array utils
@ExperimentalUnsignedTypes
fun UInt.toBigEndianUByteArray() : Array<UByte> {
return Array<UByte> (4) {
((this shr (24 - (it * 8))) and 0xFFU).toUByte()
}
}
@ExperimentalUnsignedTypes
fun UInt.toLittleEndianTypedUByteArray() : Array<UByte> {
return Array<UByte> (4) {
((this shr (it * 8)) and 0xFFU).toUByte()
}
}
@ExperimentalUnsignedTypes
fun UInt.toLittleEndianUByteArray() : UByteArray {
return UByteArray (4) {
((this shr (it * 8)) and 0xFFU).toUByte()
}
}
// UInt / Array utils
@ExperimentalUnsignedTypes
fun ULong.toBigEndianUByteArray() : Array<UByte> {
return Array<UByte> (8) {
((this shr (56 - (it * 8))) and 0xFFU).toUByte()
}
}
@ExperimentalUnsignedTypes
fun ULong.toLittleEndianTypedUByteArray() : Array<UByte> {
return Array<UByte> (8) {
((this shr (it * 8)) and 0xFFU).toUByte()
}
}
@ExperimentalUnsignedTypes
fun ULong.toLittleEndianUByteArray() :UByteArray {
return UByteArray (8) {
((this shr (it * 8)) and 0xFFU).toUByte()
}
}
@ExperimentalUnsignedTypes
fun Array<UByte>.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 UByteArray.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
}
fun UByteArray.arrayChunked(sliceSize: Int): List<UByteArray> {
val last = this.size % sliceSize
val hasLast = last != 0
val numberOfSlices = this.size / sliceSize
val result : MutableList<UByteArray> = MutableList<UByteArray>(0) { ubyteArrayOf() }
for (i in 0 until numberOfSlices) {
result.add(this.sliceArray(i * sliceSize until (i + 1) * sliceSize))
}
if (hasLast) {
result.add(this.sliceArray(numberOfSlices * sliceSize until this.size))
}
return result
}
@ExperimentalUnsignedTypes
fun Array<UByte>.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
fun Array<UByte>.fromLittleEndianArrayToUInt() : UInt {
if (this.size > 4) {
throw RuntimeException("ore than 8 bytes in input, potential overflow")
}
var uint = this.foldIndexed(0U) { index, acc, uByte -> acc or (uByte.toUInt() shl (index * 8))}
return uint
}
@ExperimentalUnsignedTypes
fun UByteArray.fromLittleEndianArrayToUInt() : UInt {
if (this.size > 4) {
throw RuntimeException("ore than 8 bytes in input, potential overflow")
}
var uint = this.foldIndexed(0U) { index, acc, uByte -> acc or (uByte.toUInt() shl (index * 8))}
return uint
}
@ExperimentalUnsignedTypes
fun Array<UByte>.fromBigEndianArrayToUInt() : UInt {
if (this.size > 4) {
throw RuntimeException("ore than 8 bytes in input, potential overflow")
}
var uint = this.foldIndexed(0U) { index, acc, uByte -> acc or (uByte.toUInt() shl (24 - (index * 8))) }
return uint
}
@ExperimentalUnsignedTypes
operator fun UInt.plus(other : UByteArray) : UByteArray {
return this.toLittleEndianUByteArray() + other
}
//AES Flatten
fun Collection<UByteArray>.flattenToUByteArray(): UByteArray {
val result = UByteArray(sumBy { it.size })
var position = 0
for (element in this) {
element.forEach { uByte ->
result[position] = uByte
position++
}
}
return result
}
@@ -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.util
import kotlinx.coroutines.CoroutineScope
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 20-Jul-2019
*/
expect fun testBlocking(block : suspend () -> Unit)
@@ -0,0 +1,60 @@
/*
* 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 kotlin.browser.window
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 21-Sep-2019
*/
actual object SRNG {
var counter = 0
@ExperimentalUnsignedTypes
actual fun getRandomBytes(amount: Int): UByteArray {
val runningOnNode = jsTypeOf(window) == "undefined"
val randomBytes = if (runningOnNode) {
println("Running on node")
js("require('crypto')").randomBytes(amount).toJSON().data
} else {
println("Running in browser")
js(
"""
var randomArray = new Uint8Array(amount);
var crypto = (self.crypto || self.msCrypto);
crypto.getRandomValues(randomArray);
"""
)
var randomArrayResult = js("Array.prototype.slice.call(randomArray);")
randomArrayResult
}
println("Random bytes: $randomBytes")
print("Byte at ${randomBytes[0]}")
val randomBytesUByteArray = UByteArray(amount) {
0U
}
for (i in 0 until amount) {
js("""
randomBytesUByteArray[i] = randomBytes[i]
""")
}
return randomBytesUByteArray
}
}
@@ -0,0 +1,44 @@
package com.ionspin.kotlin.crypto.hash.blake2b
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 14-Jul-2019
*/
@ExperimentalUnsignedTypes
actual class Blake2bDelegated actual constructor(key: UByteArray?, hashLength: Int) : Blake2b {
override val MAX_HASH_BYTES: Int = 128
override fun update(data: UByteArray) {
TODO("not implemented yet")
}
override fun update(data: String) {
TODO("not implemented yet")
}
override fun digest(): UByteArray {
TODO("not implemented yet")
}
override fun digestString(): String {
TODO("not implemented yet")
}
}
@ExperimentalUnsignedTypes
actual class Blake2bStateless : Blake2bStatelessInterface {
override fun digest(inputString: String, key: String?, hashLength: Int): UByteArray {
TODO("not implemented yet")
}
override fun digest(inputMessage: UByteArray, key: UByteArray, hashLength: Int): UByteArray {
TODO("not implemented yet")
}
override val MAX_HASH_BYTES: Int
get() = TODO("not implemented yet")
}
@@ -0,0 +1,49 @@
/*
* 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 kotlin.test.Test
import kotlin.test.assertTrue
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 05-Jan-2020
*/
@ExperimentalUnsignedTypes
class SRNGJsTest {
@Test
fun testJsSrng() {
val bytes1 = SRNG.getRandomBytes(10)
val bytes2 = SRNG.getRandomBytes(10)
println("BYTES1")
bytes1.forEach {
print(it.toString(16).padStart(2, '0'))
}
println("BYTES2")
bytes2.forEach {
print(it.toString(16).padStart(2, '0'))
}
assertTrue {
!bytes1.contentEquals(bytes2) &&
bytes1.size == 10 &&
bytes2.size == 10
}
}
}
@@ -0,0 +1,28 @@
/*
* 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 kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.promise
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 20-Jul-2019
*/
actual fun testBlocking(block: suspend ()-> Unit) : dynamic = GlobalScope.promise { block() }
@@ -0,0 +1,34 @@
/*
* 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 java.security.SecureRandom
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 21-Sep-2019
*/
@ExperimentalUnsignedTypes
actual object SRNG {
val secureRandom = SecureRandom()
actual fun getRandomBytes(amount: Int): UByteArray {
val byteArray = ByteArray(amount)
secureRandom.nextBytes(byteArray)
return byteArray.toUByteArray()
}
}
@@ -0,0 +1,43 @@
package com.ionspin.kotlin.crypto.hash.blake2b
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 14-Jul-2019
*/
@ExperimentalUnsignedTypes
actual class Blake2bDelegated actual constructor(key: UByteArray?, hashLength: Int) : Blake2b {
override val MAX_HASH_BYTES: Int
get() = TODO("not implemented yet")
override fun update(data: UByteArray) {
TODO("not implemented yet")
}
override fun update(data: String) {
TODO("not implemented yet")
}
override fun digest(): UByteArray {
TODO("not implemented yet")
}
override fun digestString(): String {
TODO("not implemented yet")
}
}
@ExperimentalUnsignedTypes
actual class Blake2bStateless : Blake2bStatelessInterface {
override fun digest(inputString: String, key: String?, hashLength: Int): UByteArray {
TODO("not implemented yet")
}
override fun digest(inputMessage: UByteArray, key: UByteArray, hashLength: Int): UByteArray {
TODO("not implemented yet")
}
override val MAX_HASH_BYTES: Int
get() = TODO("not implemented yet")
}
@@ -0,0 +1,27 @@
/*
* 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 kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.runBlocking
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 20-Jul-2019
*/
actual fun testBlocking(block: suspend () -> Unit) = runBlocking { block() }
@@ -0,0 +1,43 @@
package com.ionspin.kotlin.crypto.hash.blake2b
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 14-Jul-2019
*/
@ExperimentalUnsignedTypes
actual class Blake2bDelegated actual constructor(key: UByteArray?, hashLength: Int) : Blake2b {
override val MAX_HASH_BYTES: Int
get() = TODO("not implemented yet")
override fun update(data: UByteArray) {
TODO("not implemented yet")
}
override fun update(data: String) {
TODO("not implemented yet")
}
override fun digest(): UByteArray {
TODO("not implemented yet")
}
override fun digestString(): String {
TODO("not implemented yet")
}
}
actual class Blake2bStateless : Blake2bStatelessInterface {
override fun digest(inputString: String, key: String?, hashLength: Int): UByteArray {
TODO("not implemented yet")
}
override fun digest(inputMessage: UByteArray, key: UByteArray, hashLength: Int): UByteArray {
TODO("not implemented yet")
}
override val MAX_HASH_BYTES: Int
get() = TODO("not implemented yet")
}
@@ -0,0 +1,43 @@
/*
* 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 kotlinx.cinterop.*
import platform.windows.*
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 21-Sep-2019
*/
actual object SRNG {
private val advapi by lazy { LoadLibraryA("ADVAPI32.DLL")}
private val advapiRandom by lazy {
GetProcAddress(advapi, "SystemFunction036")?.reinterpret<CFunction<Function2<CPointer<ByteVar>, ULong, Int>>>() ?: error("Failed getting advapi random")
}
@Suppress("EXPERIMENTAL_UNSIGNED_LITERALS")
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 UByteArray(amount) { pointer[it].toUByte() }
}
}
}
@@ -0,0 +1,27 @@
/*
* 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 kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.runBlocking
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 20-Jul-2019
*/
actual fun testBlocking(block: suspend (scope: CoroutineScope) -> Unit) = runBlocking { block(this) }
@@ -0,0 +1,6 @@
headers = sodium.h
headerFilter = sodium.h sodium/**
staticLibraries = libsodium.a
libraryPaths = lib
compilerOpts = -Iinclude
linkerOpts = {{LDFLAGS}}
@@ -0,0 +1,41 @@
/*
* 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 kotlinx.cinterop.*
import platform.posix.*
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 21-Sep-2019
*/
actual object SRNG {
@Suppress("EXPERIMENTAL_UNSIGNED_LITERALS")
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 UByteArray(amount) {
array[it]
}
}
}
}
@@ -0,0 +1,27 @@
/*
* 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 kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.runBlocking
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 20-Jul-2019
*/
actual fun testBlocking(block: suspend () -> Unit) = runBlocking { block() }