Extracted interfaces for hashes
This commit is contained in:
		
							parent
							
								
									995cb23176
								
							
						
					
					
						commit
						6d8024cf7f
					
				@ -14,14 +14,37 @@
 | 
			
		||||
 *    limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.ionspin.kotlin.crypto
 | 
			
		||||
package com.ionspin.kotlin.crypto.hash
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Created by Ugljesa Jovanovic
 | 
			
		||||
 * ugljesa.jovanovic@ionspin.com
 | 
			
		||||
 * on 20-Jul-2019
 | 
			
		||||
 */
 | 
			
		||||
interface Hash
 | 
			
		||||
interface Hash {
 | 
			
		||||
    val MAX_HASH_BYTES : Int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface UpdateableHash : Hash
 | 
			
		||||
@ExperimentalUnsignedTypes
 | 
			
		||||
interface UpdateableHash : Hash {
 | 
			
		||||
    fun update(data : Array<UByte>)
 | 
			
		||||
 | 
			
		||||
    fun update(data : String)
 | 
			
		||||
 | 
			
		||||
    fun digest() : Array<UByte>
 | 
			
		||||
 | 
			
		||||
    fun digestString() : String
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ExperimentalUnsignedTypes
 | 
			
		||||
interface StatelessHash : Hash {
 | 
			
		||||
    fun digest(inputString: String, key: String? = null, hashLength: Int = MAX_HASH_BYTES): Array<UByte>
 | 
			
		||||
 | 
			
		||||
    fun digest(
 | 
			
		||||
        inputMessage: Array<UByte> = emptyArray(),
 | 
			
		||||
        key: Array<UByte> = emptyArray(),
 | 
			
		||||
        hashLength: Int = MAX_HASH_BYTES
 | 
			
		||||
    ): Array<UByte>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -14,14 +14,13 @@
 | 
			
		||||
 *    limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.ionspin.kotlin.crypto.blake2b
 | 
			
		||||
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 kotlinx.coroutines.CoroutineScope
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.Job
 | 
			
		||||
import com.ionspin.kotlin.crypto.hash.StatelessHash
 | 
			
		||||
import com.ionspin.kotlin.crypto.hash.UpdateableHash
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Created by Ugljesa Jovanovic
 | 
			
		||||
@ -31,12 +30,13 @@ import kotlinx.coroutines.Job
 | 
			
		||||
 | 
			
		||||
@ExperimentalUnsignedTypes
 | 
			
		||||
class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : UpdateableHash {
 | 
			
		||||
    companion object : Hash {
 | 
			
		||||
 | 
			
		||||
    companion object : StatelessHash {
 | 
			
		||||
 | 
			
		||||
        const val BITS_IN_WORD = 64
 | 
			
		||||
        const val ROUNDS_IN_COMPRESS = 12
 | 
			
		||||
        const val BLOCK_BYTES = 128
 | 
			
		||||
        const val MAX_HASH_BYTES = 64
 | 
			
		||||
        override val MAX_HASH_BYTES = 64
 | 
			
		||||
        const val MIN_HASH_BYTES = 1
 | 
			
		||||
        const val MAX_KEY_BYTES = 64
 | 
			
		||||
        const val MIN_KEY_BYTES = 0
 | 
			
		||||
@ -143,38 +143,39 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatea
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @ExperimentalStdlibApi
 | 
			
		||||
        fun digest(inputString: String, key: String? = null): Array<UByte> {
 | 
			
		||||
            val chunked = inputString.encodeToByteArray().map { it.toUByte() }.toList().chunked(BLOCK_BYTES)
 | 
			
		||||
                .map { it.toTypedArray() }.toTypedArray()
 | 
			
		||||
        override fun digest(inputString: String, key: String?, hashLength: Int): Array<UByte> {
 | 
			
		||||
            val array = inputString.encodeToByteArray().map { it.toUByte() }.toList().toTypedArray()
 | 
			
		||||
            val keyBytes = key?.run {
 | 
			
		||||
                encodeToByteArray().map { it.toUByte() }.toTypedArray()
 | 
			
		||||
            } ?: emptyArray()
 | 
			
		||||
            return digest(inputMessage = chunked, secretKey = keyBytes)
 | 
			
		||||
            return digest(inputMessage = array, key = keyBytes)
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun digest(
 | 
			
		||||
            inputMessage: Array<Array<UByte>> = emptyArray(),
 | 
			
		||||
            secretKey: Array<UByte> = emptyArray(),
 | 
			
		||||
            hashLength: Int = MAX_HASH_BYTES
 | 
			
		||||
        override fun digest(
 | 
			
		||||
            inputMessage: Array<UByte>,
 | 
			
		||||
            key: Array<UByte>,
 | 
			
		||||
            hashLength: Int
 | 
			
		||||
        ): Array<UByte> {
 | 
			
		||||
            val chunkedMessage = inputMessage.chunked(BLOCK_BYTES)
 | 
			
		||||
 | 
			
		||||
            val h = iv.copyOf()
 | 
			
		||||
 | 
			
		||||
            h[0] = h[0] xor 0x01010000UL xor (secretKey.size.toULong() shl 8) xor hashLength.toULong()
 | 
			
		||||
            h[0] = h[0] xor 0x01010000UL xor (key.size.toULong() shl 8) xor hashLength.toULong()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            val message = if (secretKey.isEmpty()) {
 | 
			
		||||
                if (inputMessage.isEmpty()) {
 | 
			
		||||
            val message = if (key.isEmpty()) {
 | 
			
		||||
                if (chunkedMessage.isEmpty()) {
 | 
			
		||||
                    Array(1) {
 | 
			
		||||
                        Array<UByte>(128) {
 | 
			
		||||
                            0U
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    inputMessage
 | 
			
		||||
                    chunkedMessage
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                arrayOf(padToBlock(secretKey), *inputMessage)
 | 
			
		||||
                arrayOf(padToBlock(key), *chunkedMessage)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (message.size > 1) {
 | 
			
		||||
@ -246,6 +247,8 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatea
 | 
			
		||||
        requestedHashLenght
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    override val MAX_HASH_BYTES: Int = Blake2b.MAX_HASH_BYTES
 | 
			
		||||
 | 
			
		||||
    var h = iv.copyOf()
 | 
			
		||||
    var counter = BigInteger.ZERO
 | 
			
		||||
    var bufferCounter = 0
 | 
			
		||||
@ -260,15 +263,15 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatea
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun update(array: Array<UByte>) {
 | 
			
		||||
        if (array.isEmpty()) {
 | 
			
		||||
    override fun update(data: Array<UByte>) {
 | 
			
		||||
        if (data.isEmpty()) {
 | 
			
		||||
            throw RuntimeException("Updating with empty array is not allowed. If you need empty hash, just call digest without updating")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        when {
 | 
			
		||||
            bufferCounter + array.size < BLOCK_BYTES -> appendToBuffer(array, bufferCounter)
 | 
			
		||||
            bufferCounter + array.size >= BLOCK_BYTES -> {
 | 
			
		||||
                val chunked = array.chunked(BLOCK_BYTES)
 | 
			
		||||
            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, bufferCounter)
 | 
			
		||||
@ -301,8 +304,8 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatea
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    @ExperimentalStdlibApi
 | 
			
		||||
    fun update(input: String) {
 | 
			
		||||
        update(input.encodeToByteArray().map { it.toUByte() }.toTypedArray())
 | 
			
		||||
    override fun update(data: String) {
 | 
			
		||||
        update(data.encodeToByteArray().map { it.toUByte() }.toTypedArray())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun appendToBuffer(array: Array<UByte>, start: Int) {
 | 
			
		||||
@ -314,7 +317,7 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatea
 | 
			
		||||
        h = compress(h, block, counter, false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun digest(): Array<UByte> {
 | 
			
		||||
    override fun digest(): Array<UByte> {
 | 
			
		||||
        val lastBlockPadded = padToBlock(buffer)
 | 
			
		||||
        counter += bufferCounter
 | 
			
		||||
        compress(h, lastBlockPadded, counter, true)
 | 
			
		||||
@ -325,7 +328,7 @@ class Blake2b(val key: Array<UByte>? = null, val hashLength: Int = 64) : Updatea
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun digestString(): String {
 | 
			
		||||
    override fun digestString(): String {
 | 
			
		||||
        return digest().map { it.toString(16) }.joinToString(separator = "")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -14,10 +14,11 @@
 | 
			
		||||
 *    limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.ionspin.kotlin.crypto.sha
 | 
			
		||||
package com.ionspin.kotlin.crypto.hash.sha
 | 
			
		||||
 | 
			
		||||
import com.ionspin.kotlin.crypto.Hash
 | 
			
		||||
import com.ionspin.kotlin.crypto.chunked
 | 
			
		||||
import com.ionspin.kotlin.crypto.hash.StatelessHash
 | 
			
		||||
import com.ionspin.kotlin.crypto.hash.UpdateableHash
 | 
			
		||||
import com.ionspin.kotlin.crypto.rotateRight
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -29,15 +30,20 @@ import com.ionspin.kotlin.crypto.rotateRight
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ExperimentalUnsignedTypes
 | 
			
		||||
class Sha256 : Hash {
 | 
			
		||||
class Sha256 : UpdateableHash {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
    override val MAX_HASH_BYTES: Int = 32
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    companion object : StatelessHash {
 | 
			
		||||
        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,
 | 
			
		||||
@ -61,22 +67,25 @@ class Sha256 : Hash {
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        @ExperimentalStdlibApi
 | 
			
		||||
        fun digest(message: String): Array<UByte> {
 | 
			
		||||
            return digest(message.encodeToByteArray().map { it.toUByte() }.toTypedArray())
 | 
			
		||||
        override fun digest(inputString: String, key: String?, hashLength: Int): Array<UByte> {
 | 
			
		||||
            return digest(
 | 
			
		||||
                inputString.encodeToByteArray().map { it.toUByte() }.toTypedArray(),
 | 
			
		||||
                key?.run { encodeToByteArray().map { it.toUByte() }.toTypedArray() } ?: emptyArray<UByte>(),
 | 
			
		||||
                hashLength)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun digest(message: Array<UByte>): Array<UByte> {
 | 
			
		||||
        override fun digest(inputMessage: Array<UByte>, key: Array<UByte>, hashLength: Int): Array<UByte> {
 | 
			
		||||
 | 
			
		||||
            var h = iv.copyOf()
 | 
			
		||||
 | 
			
		||||
            val expansionArray = createExpansionArray(message.size)
 | 
			
		||||
            val expansionArray = createExpansionArray(inputMessage.size)
 | 
			
		||||
 | 
			
		||||
            val chunks = (
 | 
			
		||||
                        message +
 | 
			
		||||
                                expansionArray +
 | 
			
		||||
                                (message.size * 8).toULong().toPaddedByteArray()
 | 
			
		||||
                        )
 | 
			
		||||
                    .chunked(BLOCK_SIZE_IN_BYTES)
 | 
			
		||||
                    inputMessage +
 | 
			
		||||
                            expansionArray +
 | 
			
		||||
                            (inputMessage.size * 8).toULong().toPaddedByteArray()
 | 
			
		||||
                    )
 | 
			
		||||
                .chunked(BLOCK_SIZE_IN_BYTES)
 | 
			
		||||
 | 
			
		||||
            chunks.forEach { chunk ->
 | 
			
		||||
                val w = expandChunk(chunk)
 | 
			
		||||
@ -179,7 +188,7 @@ class Sha256 : Hash {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        fun createExpansionArray(originalSizeInBytes : Int) : Array<UByte> {
 | 
			
		||||
        fun createExpansionArray(originalSizeInBytes: Int): Array<UByte> {
 | 
			
		||||
            val originalMessageSizeInBits = originalSizeInBytes * 8
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -236,19 +245,19 @@ class Sha256 : Hash {
 | 
			
		||||
    var buffer = Array<UByte>(BLOCK_SIZE_IN_BYTES) { 0U }
 | 
			
		||||
 | 
			
		||||
    @ExperimentalStdlibApi
 | 
			
		||||
    fun update(message: String) {
 | 
			
		||||
        return update(message.encodeToByteArray().map { it.toUByte() }.toTypedArray())
 | 
			
		||||
    override fun update(data: String) {
 | 
			
		||||
        return update(data.encodeToByteArray().map { it.toUByte() }.toTypedArray())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun update(array: Array<UByte>) {
 | 
			
		||||
        if (array.isEmpty()) {
 | 
			
		||||
    override fun update(data: Array<UByte>) {
 | 
			
		||||
        if (data.isEmpty()) {
 | 
			
		||||
            throw RuntimeException("Updating with empty array is not allowed. If you need empty hash, just call digest without updating")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        when {
 | 
			
		||||
            bufferCounter + array.size < BLOCK_SIZE_IN_BYTES -> appendToBuffer(array, bufferCounter)
 | 
			
		||||
            bufferCounter + array.size >= BLOCK_SIZE_IN_BYTES -> {
 | 
			
		||||
                val chunked = array.chunked(BLOCK_SIZE_IN_BYTES)
 | 
			
		||||
            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, bufferCounter)
 | 
			
		||||
@ -285,10 +294,11 @@ class Sha256 : Hash {
 | 
			
		||||
        mix(h, w).copyInto(h)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun digest() : Array<UByte> {
 | 
			
		||||
    override fun digest(): Array<UByte> {
 | 
			
		||||
        val length = counter + bufferCounter
 | 
			
		||||
        val expansionArray = createExpansionArray(length)
 | 
			
		||||
        val finalBlock = buffer.copyOfRange(0, bufferCounter) + expansionArray + (length * 8).toULong().toPaddedByteArray()
 | 
			
		||||
        val finalBlock =
 | 
			
		||||
            buffer.copyOfRange(0, bufferCounter) + expansionArray + (length * 8).toULong().toPaddedByteArray()
 | 
			
		||||
        finalBlock.chunked(BLOCK_SIZE_IN_BYTES).forEach {
 | 
			
		||||
            consumeBlock(it)
 | 
			
		||||
        }
 | 
			
		||||
@ -305,11 +315,14 @@ class Sha256 : Hash {
 | 
			
		||||
        return digest
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun digestString(): String {
 | 
			
		||||
        return digest().map { it.toString(16) }.joinToString(separator = "")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun appendToBuffer(array: Array<UByte>, start: Int) {
 | 
			
		||||
        array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
 | 
			
		||||
        bufferCounter += array.size
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -14,10 +14,11 @@
 | 
			
		||||
 *    limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.ionspin.kotlin.crypto.sha
 | 
			
		||||
package com.ionspin.kotlin.crypto.hash.sha
 | 
			
		||||
 | 
			
		||||
import com.ionspin.kotlin.crypto.Hash
 | 
			
		||||
import com.ionspin.kotlin.crypto.chunked
 | 
			
		||||
import com.ionspin.kotlin.crypto.hash.StatelessHash
 | 
			
		||||
import com.ionspin.kotlin.crypto.hash.UpdateableHash
 | 
			
		||||
import com.ionspin.kotlin.crypto.rotateRight
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -27,13 +28,18 @@ import com.ionspin.kotlin.crypto.rotateRight
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
@ExperimentalUnsignedTypes
 | 
			
		||||
class Sha512 : Hash {
 | 
			
		||||
    companion object {
 | 
			
		||||
class Sha512 : UpdateableHash {
 | 
			
		||||
 | 
			
		||||
    override val MAX_HASH_BYTES: Int = 32
 | 
			
		||||
 | 
			
		||||
    companion object : StatelessHash {
 | 
			
		||||
        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,
 | 
			
		||||
@ -129,19 +135,24 @@ class Sha512 : Hash {
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        @ExperimentalStdlibApi
 | 
			
		||||
        fun digest(message: String): Array<UByte> {
 | 
			
		||||
            return digest(message.encodeToByteArray().map { it.toUByte() }.toTypedArray())
 | 
			
		||||
        override fun digest(inputString: String, key: String?, hashLength: Int): Array<UByte> {
 | 
			
		||||
            return digest(
 | 
			
		||||
                inputString.encodeToByteArray().map { it.toUByte() }.toTypedArray(),
 | 
			
		||||
                key?.run { encodeToByteArray().map { it.toUByte() }.toTypedArray() } ?: emptyArray<UByte>(),
 | 
			
		||||
                hashLength = hashLength
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun digest(message: Array<UByte>): Array<UByte> {
 | 
			
		||||
        override fun digest(inputMessage: Array<UByte>, key: Array<UByte>, hashLength: Int): Array<UByte> {
 | 
			
		||||
 | 
			
		||||
            var h = iv.copyOf()
 | 
			
		||||
 | 
			
		||||
            val expansionArray = createExpansionArray(message.size)
 | 
			
		||||
            val expansionArray = createExpansionArray(inputMessage.size)
 | 
			
		||||
 | 
			
		||||
            val chunks =
 | 
			
		||||
                (message + expansionArray + (message.size * 8).toULong().toPadded128BitByteArray()).chunked(
 | 
			
		||||
                    BLOCK_SIZE_IN_BYTES)
 | 
			
		||||
                (inputMessage + expansionArray + (inputMessage.size * 8).toULong().toPadded128BitByteArray()).chunked(
 | 
			
		||||
                    BLOCK_SIZE_IN_BYTES
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
            chunks.forEach { chunk ->
 | 
			
		||||
                val w = expandChunk(chunk)
 | 
			
		||||
@ -150,14 +161,14 @@ class Sha512 : Hash {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            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()
 | 
			
		||||
                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
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -248,7 +259,7 @@ class Sha512 : Hash {
 | 
			
		||||
            return h
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun createExpansionArray(originalSizeInBytes : Int) : Array<UByte> {
 | 
			
		||||
        fun createExpansionArray(originalSizeInBytes: Int): Array<UByte> {
 | 
			
		||||
            val originalMessageSizeInBits = originalSizeInBytes * 8
 | 
			
		||||
 | 
			
		||||
            val expandedRemainderOf1024 = (originalMessageSizeInBits + 129) % BLOCK_SIZE
 | 
			
		||||
@ -309,19 +320,19 @@ class Sha512 : Hash {
 | 
			
		||||
    var buffer = Array<UByte>(BLOCK_SIZE_IN_BYTES) { 0U }
 | 
			
		||||
 | 
			
		||||
    @ExperimentalStdlibApi
 | 
			
		||||
    fun update(message: String) {
 | 
			
		||||
        return update(message.encodeToByteArray().map { it.toUByte() }.toTypedArray())
 | 
			
		||||
    override fun update(data: String) {
 | 
			
		||||
        return update(data.encodeToByteArray().map { it.toUByte() }.toTypedArray())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun update(array: Array<UByte>) {
 | 
			
		||||
        if (array.isEmpty()) {
 | 
			
		||||
    override fun update(data: Array<UByte>) {
 | 
			
		||||
        if (data.isEmpty()) {
 | 
			
		||||
            throw RuntimeException("Updating with empty array is not allowed. If you need empty hash, just call digest without updating")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        when {
 | 
			
		||||
            bufferCounter + array.size < BLOCK_SIZE_IN_BYTES -> appendToBuffer(array, bufferCounter)
 | 
			
		||||
            bufferCounter + array.size >= BLOCK_SIZE_IN_BYTES -> {
 | 
			
		||||
                val chunked = array.chunked(BLOCK_SIZE_IN_BYTES)
 | 
			
		||||
            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, bufferCounter)
 | 
			
		||||
@ -358,10 +369,11 @@ class Sha512 : Hash {
 | 
			
		||||
        mix(h, w).copyInto(h)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun digest() : Array<UByte> {
 | 
			
		||||
    override fun digest(): Array<UByte> {
 | 
			
		||||
        val length = counter + bufferCounter
 | 
			
		||||
        val expansionArray = createExpansionArray(length)
 | 
			
		||||
        val finalBlock = buffer.copyOfRange(0, bufferCounter) + expansionArray + (length * 8).toULong().toPadded128BitByteArray()
 | 
			
		||||
        val finalBlock =
 | 
			
		||||
            buffer.copyOfRange(0, bufferCounter) + expansionArray + (length * 8).toULong().toPadded128BitByteArray()
 | 
			
		||||
        finalBlock.chunked(BLOCK_SIZE_IN_BYTES).forEach {
 | 
			
		||||
            consumeBlock(it)
 | 
			
		||||
        }
 | 
			
		||||
@ -378,6 +390,10 @@ class Sha512 : Hash {
 | 
			
		||||
        return digest
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun digestString(): String {
 | 
			
		||||
        return digest().map { it.toString(16) }.joinToString(separator = "")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun appendToBuffer(array: Array<UByte>, start: Int) {
 | 
			
		||||
        array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
 | 
			
		||||
        bufferCounter += array.size
 | 
			
		||||
@ -16,9 +16,9 @@
 | 
			
		||||
 | 
			
		||||
package com.ionspin.kotlin.crypto
 | 
			
		||||
 | 
			
		||||
import com.ionspin.kotlin.crypto.blake2b.Blake2b
 | 
			
		||||
import com.ionspin.kotlin.crypto.sha.Sha256
 | 
			
		||||
import com.ionspin.kotlin.crypto.sha.Sha512
 | 
			
		||||
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 kotlin.test.Test
 | 
			
		||||
 | 
			
		||||
import kotlin.test.assertTrue
 | 
			
		||||
@ -74,7 +74,7 @@ class ReadmeTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    fun sha256Example() {
 | 
			
		||||
        val input ="abc"
 | 
			
		||||
        val result = Sha256.digest(message = input)
 | 
			
		||||
        val result = Sha256.digest(inputString = input)
 | 
			
		||||
        val expectedResult = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
 | 
			
		||||
        assertTrue {
 | 
			
		||||
            result.contentEquals(expectedResult.chunked(2).map { it.toUByte(16) }.toTypedArray())
 | 
			
		||||
@ -87,7 +87,7 @@ class ReadmeTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    fun sha512Example() {
 | 
			
		||||
        val input ="abc"
 | 
			
		||||
        val result = Sha512.digest(message = input.encodeToByteArray().map { it.toUByte() }.toTypedArray())
 | 
			
		||||
        val result = Sha512.digest(inputMessage = input.encodeToByteArray().map { it.toUByte() }.toTypedArray())
 | 
			
		||||
        println(result.map {it.toString(16)})
 | 
			
		||||
        val expectedResult = "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a" +
 | 
			
		||||
                "2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,7 @@
 | 
			
		||||
 *    limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.ionspin.kotlin.crypto.blake2b
 | 
			
		||||
package com.ionspin.kotlin.crypto.hash.blake2b
 | 
			
		||||
 | 
			
		||||
import kotlin.test.Test
 | 
			
		||||
import kotlin.test.assertTrue
 | 
			
		||||
@ -14,11 +14,8 @@
 | 
			
		||||
 *    limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.ionspin.kotlin.crypto.blake2b
 | 
			
		||||
package com.ionspin.kotlin.crypto.hash.blake2b
 | 
			
		||||
 | 
			
		||||
import com.ionspin.kotlin.crypto.util.testBlocking
 | 
			
		||||
import kotlinx.coroutines.GlobalScope
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import kotlin.test.Test
 | 
			
		||||
import kotlin.test.assertTrue
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,7 @@
 | 
			
		||||
 *    limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.ionspin.kotlin.crypto.blake2b
 | 
			
		||||
package com.ionspin.kotlin.crypto.hash.blake2b
 | 
			
		||||
 | 
			
		||||
import kotlin.test.Test
 | 
			
		||||
import kotlin.test.assertTrue
 | 
			
		||||
@ -37,10 +37,9 @@ class Blake2bKnowAnswerTests {
 | 
			
		||||
    fun knownAnswerTest() {
 | 
			
		||||
        kat.forEach {
 | 
			
		||||
            val parsedInput = it.input.chunked(2).map { it.toUByte(16) }.toTypedArray()
 | 
			
		||||
            val chunkedInput = parsedInput.toList().chunked(128).map { it.toTypedArray() }.toTypedArray()
 | 
			
		||||
            val result = Blake2b.digest(
 | 
			
		||||
                inputMessage = chunkedInput,
 | 
			
		||||
                secretKey = it.key.chunked(2).map { it.toUByte(16) }.toTypedArray()
 | 
			
		||||
                inputMessage = parsedInput,
 | 
			
		||||
                key = it.key.chunked(2).map { it.toUByte(16) }.toTypedArray()
 | 
			
		||||
            )
 | 
			
		||||
            assertTrue {
 | 
			
		||||
                result.contentEquals(it.hash.chunked(2).map { it.toUByte(16) }.toTypedArray())
 | 
			
		||||
@ -14,7 +14,7 @@
 | 
			
		||||
 *    limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.ionspin.kotlin.crypto.sha
 | 
			
		||||
package com.ionspin.kotlin.crypto.hash.sha
 | 
			
		||||
 | 
			
		||||
import kotlin.test.Test
 | 
			
		||||
import kotlin.test.assertTrue
 | 
			
		||||
@ -31,7 +31,7 @@ class Sha256Test {
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testWellKnownValue() {
 | 
			
		||||
 | 
			
		||||
        val result = Sha256.digest(message = "abc")
 | 
			
		||||
        val result = Sha256.digest(inputString = "abc")
 | 
			
		||||
        val expectedResult = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
 | 
			
		||||
        assertTrue {
 | 
			
		||||
            result.contentEquals(expectedResult.chunked(2).map { it.toUByte(16) }.toTypedArray())
 | 
			
		||||
@ -44,7 +44,7 @@ class Sha256Test {
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testWellKnownDoubleBlock() {
 | 
			
		||||
 | 
			
		||||
        val resultDoubleBlock = Sha256.digest(message = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")
 | 
			
		||||
        val resultDoubleBlock = Sha256.digest(inputString = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")
 | 
			
		||||
        val expectedResultForDoubleBlock = "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"
 | 
			
		||||
        assertTrue {
 | 
			
		||||
            resultDoubleBlock.contentEquals(expectedResultForDoubleBlock.chunked(2).map { it.toUByte(16) }.toTypedArray())
 | 
			
		||||
@ -56,7 +56,7 @@ class Sha256Test {
 | 
			
		||||
    fun testWellKnown3() { //It's good that I'm consistent with names.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        val resultDoubleBlock = Sha256.digest(message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")
 | 
			
		||||
        val resultDoubleBlock = Sha256.digest(inputString = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")
 | 
			
		||||
        println(resultDoubleBlock.map{ it.toString(16)}.joinToString(separator = ""))
 | 
			
		||||
        val expectedResultForDoubleBlock = "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1"
 | 
			
		||||
        assertTrue {
 | 
			
		||||
@ -71,7 +71,7 @@ class Sha256Test {
 | 
			
		||||
        for (i in 0 until 1000000) {
 | 
			
		||||
            inputBuilder.append("a")
 | 
			
		||||
        }
 | 
			
		||||
        val resultDoubleBlock = Sha256.digest(message = (inputBuilder.toString()).encodeToByteArray().map { it.toUByte() }.toTypedArray())
 | 
			
		||||
        val resultDoubleBlock = Sha256.digest(inputMessage = (inputBuilder.toString()).encodeToByteArray().map { it.toUByte() }.toTypedArray())
 | 
			
		||||
        val expectedResultForDoubleBlock = "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"
 | 
			
		||||
        assertTrue {
 | 
			
		||||
            resultDoubleBlock.contentEquals(expectedResultForDoubleBlock.chunked(2).map { it.toUByte(16) }.toTypedArray())
 | 
			
		||||
@ -14,9 +14,8 @@
 | 
			
		||||
 *    limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.ionspin.kotlin.crypto.sha
 | 
			
		||||
package com.ionspin.kotlin.crypto.hash.sha
 | 
			
		||||
 | 
			
		||||
import com.ionspin.kotlin.crypto.chunked
 | 
			
		||||
import kotlin.test.Ignore
 | 
			
		||||
import kotlin.test.Test
 | 
			
		||||
import kotlin.test.assertTrue
 | 
			
		||||
@ -47,7 +46,7 @@ class Sha256UpdateableTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testWellKnownDoubleBlock() {
 | 
			
		||||
        val sha256 = Sha256()
 | 
			
		||||
        sha256.update(message = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")
 | 
			
		||||
        sha256.update(data = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")
 | 
			
		||||
        val resultDoubleBlock = sha256.digest()
 | 
			
		||||
        println(resultDoubleBlock.map{ it.toString(16)}.joinToString(separator = ""))
 | 
			
		||||
        val expectedResultForDoubleBlock = "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"
 | 
			
		||||
@ -60,7 +59,7 @@ class Sha256UpdateableTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testWellKnown3() { //It's good that I'm consistent with names.
 | 
			
		||||
        val sha256 = Sha256()
 | 
			
		||||
        sha256.update(message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")
 | 
			
		||||
        sha256.update(data = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")
 | 
			
		||||
        val resultDoubleBlock = sha256.digest()
 | 
			
		||||
        println(resultDoubleBlock.map{ it.toString(16)}.joinToString(separator = ""))
 | 
			
		||||
        val expectedResultForDoubleBlock = "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1"
 | 
			
		||||
@ -14,7 +14,7 @@
 | 
			
		||||
 *    limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.ionspin.kotlin.crypto.sha
 | 
			
		||||
package com.ionspin.kotlin.crypto.hash.sha
 | 
			
		||||
 | 
			
		||||
import kotlin.test.Test
 | 
			
		||||
import kotlin.test.assertTrue
 | 
			
		||||
@ -30,7 +30,7 @@ class Sha512Test {
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testWellKnownValue() {
 | 
			
		||||
 | 
			
		||||
        val result = Sha512.digest(message = "abc".encodeToByteArray().map { it.toUByte() }.toTypedArray())
 | 
			
		||||
        val result = Sha512.digest(inputMessage = "abc".encodeToByteArray().map { it.toUByte() }.toTypedArray())
 | 
			
		||||
        println(result.map {it.toString(16)})
 | 
			
		||||
        val expectedResult = "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a" +
 | 
			
		||||
            "2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"
 | 
			
		||||
@ -46,7 +46,7 @@ class Sha512Test {
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testWellKnown3() {
 | 
			
		||||
        val sha512 = Sha512()
 | 
			
		||||
        sha512.update(message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")
 | 
			
		||||
        sha512.update(data = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")
 | 
			
		||||
        val resultDoubleBlock = sha512.digest()
 | 
			
		||||
        val expectedResultForDoubleBlock = "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018" +
 | 
			
		||||
                "501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909"
 | 
			
		||||
@ -62,7 +62,7 @@ class Sha512Test {
 | 
			
		||||
        for (i in 0 until 1000000) {
 | 
			
		||||
            inputBuilder.append("a")
 | 
			
		||||
        }
 | 
			
		||||
        val resultDoubleBlock = Sha512.digest(message = (inputBuilder.toString()).encodeToByteArray().map { it.toUByte() }.toTypedArray())
 | 
			
		||||
        val resultDoubleBlock = Sha512.digest(inputMessage = (inputBuilder.toString()).encodeToByteArray().map { it.toUByte() }.toTypedArray())
 | 
			
		||||
        val expectedResultForDoubleBlock = "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b"
 | 
			
		||||
        assertTrue {
 | 
			
		||||
            resultDoubleBlock.contentEquals(expectedResultForDoubleBlock.chunked(2).map { it.toUByte(16) }.toTypedArray())
 | 
			
		||||
@ -14,7 +14,7 @@
 | 
			
		||||
 *    limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.ionspin.kotlin.crypto.sha
 | 
			
		||||
package com.ionspin.kotlin.crypto.hash.sha
 | 
			
		||||
 | 
			
		||||
import kotlin.test.Ignore
 | 
			
		||||
import kotlin.test.Test
 | 
			
		||||
@ -45,7 +45,7 @@ class Sha512UpdateableTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    fun testWellKnownDoubleBlock() {
 | 
			
		||||
        val sha512 = Sha512()
 | 
			
		||||
        sha512.update(message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")
 | 
			
		||||
        sha512.update(data = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")
 | 
			
		||||
        val resultDoubleBlock = sha512.digest()
 | 
			
		||||
        println(resultDoubleBlock.map{ it.toString(16)}.joinToString(separator = ""))
 | 
			
		||||
        val expectedResultForDoubleBlock = "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018" +
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user