Skip to content

Commit

Permalink
Create class Blake2 to inherit
Browse files Browse the repository at this point in the history
  • Loading branch information
HoangNguyen219 committed Jun 28, 2024
1 parent 8b34a23 commit 527da62
Show file tree
Hide file tree
Showing 5 changed files with 331 additions and 300 deletions.
34 changes: 32 additions & 2 deletions library/blake2/api/blake2.api
Original file line number Diff line number Diff line change
@@ -1,4 +1,34 @@
public final class org/kotlincrypto/hash/blake2/Blake2b : org/kotlincrypto/core/digest/Digest {
public abstract class org/kotlincrypto/hash/blake2/Blake2 : org/kotlincrypto/core/digest/Digest {
protected field f0 Lorg/kotlincrypto/hash/blake2/Blake2Word;
protected field t0 Lorg/kotlincrypto/hash/blake2/Blake2Word;
public fun <init> (Ljava/lang/String;II)V
protected fun compress ([BI)V
protected abstract fun copy (Lorg/kotlincrypto/core/digest/internal/DigestState;)Lorg/kotlincrypto/core/digest/Digest;
protected abstract fun createM ([BI)[Lorg/kotlincrypto/hash/blake2/Blake2Word;
protected abstract fun createWord (J)Lorg/kotlincrypto/hash/blake2/Blake2Word;
protected fun digest (JI[B)[B
protected abstract fun getBlakeIV ()[Lorg/kotlincrypto/hash/blake2/Blake2Word;
protected abstract fun getBlakeSigma ()[[B
protected final fun getChainValue ()[Lorg/kotlincrypto/hash/blake2/Blake2Word;
protected final fun getF0 ()Lorg/kotlincrypto/hash/blake2/Blake2Word;
protected abstract fun getR1 ()I
protected abstract fun getR2 ()I
protected abstract fun getR3 ()I
protected abstract fun getR4 ()I
protected abstract fun getRoundsInCompress ()I
protected abstract fun getSizeBytes ()I
protected final fun getT0 ()Lorg/kotlincrypto/hash/blake2/Blake2Word;
protected final fun initChainValue ()V
protected fun resetDigest ()V
protected final fun setChainValue ([Lorg/kotlincrypto/hash/blake2/Blake2Word;)V
protected final fun setF0 (Lorg/kotlincrypto/hash/blake2/Blake2Word;)V
protected final fun setT0 (Lorg/kotlincrypto/hash/blake2/Blake2Word;)V
}

public abstract class org/kotlincrypto/hash/blake2/Blake2Word {
}

public final class org/kotlincrypto/hash/blake2/Blake2b : org/kotlincrypto/hash/blake2/Blake2 {
public static final field Companion Lorg/kotlincrypto/hash/blake2/Blake2b$Companion;
public fun <init> (I)V
public synthetic fun <init> (IILkotlin/jvm/internal/DefaultConstructorMarker;)V
Expand All @@ -11,7 +41,7 @@ public final class org/kotlincrypto/hash/blake2/Blake2b$Companion {
public final fun blake2bHash256 ([B)[B
}

public final class org/kotlincrypto/hash/blake2/Blake2s : org/kotlincrypto/core/digest/Digest {
public final class org/kotlincrypto/hash/blake2/Blake2s : org/kotlincrypto/hash/blake2/Blake2 {
public static final field Companion Lorg/kotlincrypto/hash/blake2/Blake2s$Companion;
public fun <init> (I)V
public synthetic fun <init> (IILkotlin/jvm/internal/DefaultConstructorMarker;)V
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package org.kotlincrypto.hash.blake2

import org.kotlincrypto.core.InternalKotlinCryptoApi
import org.kotlincrypto.core.digest.Digest
import org.kotlincrypto.core.digest.internal.DigestState

@OptIn(InternalKotlinCryptoApi::class)
public abstract class Blake2(
algorithmName: String,
private val blockLengthBytes: Int,
digestBits: Int
) : Digest(algorithmName, blockLengthBytes, digestBits / 8) {
// Internal state, in the BLAKE2 paper it is called v
private val internalState: Array<Blake2Word> = Array(16) { createWord(0L) }

// To use for Catenas H'
protected abstract val roundsInCompress: Int
protected abstract val sizeBytes: Int
protected abstract val r1: Int
protected abstract val r2: Int
protected abstract val r3: Int
protected abstract val r4: Int

// BLAKE Initialization Vector:
protected abstract val blakeIV: Array<Blake2Word>

// Message word permutations:
protected abstract val blakeSigma: Array<ByteArray>

// State vector, in the BLAKE2 paper it is called h
protected var chainValue: Array<Blake2Word>? = null

// holds last significant bits of counter (counts bytes)
protected lateinit var t0: Blake2Word

// finalization flag, for last block: ~0
protected lateinit var f0: Blake2Word

override fun digest(bitLength: Long, bufferOffset: Int, buffer: ByteArray): ByteArray {
val digestLength = digestLength()
val out = ByteArray(digestLength)
f0 = createWord(-0x1L)
t0 = createWord(bitLength / 8)

compress(buffer, 0)
internalState.fill(createWord(0L))
var i = 0
while (i < chainValue!!.size && i * sizeBytes < digestLength) {
val bytes = chainValue!![i].toLittleEndian()
if (i * sizeBytes < digestLength - sizeBytes) {
bytes.copyInto(out, i * sizeBytes, 0, sizeBytes)
} else {
bytes.copyInto(out, i * sizeBytes, 0, digestLength - i * sizeBytes)
}
i++
}
chainValue?.fill(createWord(0L))
reset()
return out
}

override fun resetDigest() {
f0 = createWord(0L)
t0 = createWord(0L)
chainValue = null
initChainValue()
}

abstract override fun copy(state: DigestState): Digest

override fun compress(input: ByteArray, offset: Int) {
if (input.size > blockLengthBytes) t0 = createWord(blockLengthBytes.toLong())
initInternalState()
val m = createM(input, offset)
for (round in 0 until roundsInCompress) {
// G apply to columns of internalState
// :m[blake2b_sigma[round][2 * blockPos]] /+1
g(m[blakeSigma[round][0].toInt()], m[blakeSigma[round][1].toInt()], 0, 4, 8, 12)
g(m[blakeSigma[round][2].toInt()], m[blakeSigma[round][3].toInt()], 1, 5, 9, 13)
g(m[blakeSigma[round][4].toInt()], m[blakeSigma[round][5].toInt()], 2, 6, 10, 14)
g(m[blakeSigma[round][6].toInt()], m[blakeSigma[round][7].toInt()], 3, 7, 11, 15)
// G apply to diagonals of internalState:
g(m[blakeSigma[round][8].toInt()], m[blakeSigma[round][9].toInt()], 0, 5, 10, 15)
g(m[blakeSigma[round][10].toInt()], m[blakeSigma[round][11].toInt()], 1, 6, 11, 12)
g(m[blakeSigma[round][12].toInt()], m[blakeSigma[round][13].toInt()], 2, 7, 8, 13)
g(m[blakeSigma[round][14].toInt()], m[blakeSigma[round][15].toInt()], 3, 4, 9, 14)
}

// update chain values:
for (position in chainValue!!.indices) {
chainValue!![position] = chainValue!![position] xor internalState[position] xor internalState[position + 8]
}
}

protected fun initChainValue() {
if (chainValue == null) {
chainValue = Array(8) { createWord(0L) }
blakeIV.copyInto(chainValue!!)
chainValue!![0] = (blakeIV[0] xor (createWord(digestLength().toLong()) or createWord(0x1010000L)))
}
}

private fun initInternalState() {
// initialize v:
chainValue!!.copyInto(internalState, 0, 0, chainValue!!.size)
blakeIV.copyInto(internalState, chainValue!!.size, 0, 4)

internalState[12] = t0 xor blakeIV[4]
internalState[13] = createWord(0L) xor blakeIV[5]
internalState[14] = f0 xor blakeIV[6]
internalState[15] = blakeIV[7]
}

private fun g(m1: Blake2Word, m2: Blake2Word, posA: Int, posB: Int, posC: Int, posD: Int) {
internalState[posA] = internalState[posA] + internalState[posB] + m1
internalState[posD] = (internalState[posD] xor internalState[posA]).rotateRight(r1)
internalState[posC] = internalState[posC] + internalState[posD]
internalState[posB] = (internalState[posB] xor internalState[posC]).rotateRight(r2)
internalState[posA] = internalState[posA] + internalState[posB] + m2
internalState[posD] = (internalState[posD] xor internalState[posA]).rotateRight(r3)
internalState[posC] = internalState[posC] + internalState[posD]
internalState[posB] = (internalState[posB] xor internalState[posC]).rotateRight(r4)
}

protected abstract fun createM(input: ByteArray, offset: Int): Array<Blake2Word>

protected abstract fun createWord(value: Long): Blake2Word
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package org.kotlincrypto.hash.blake2

import org.kotlincrypto.endians.LittleEndian
import org.kotlincrypto.endians.LittleEndian.Companion.toLittleEndian

public sealed class Blake2Word {
internal abstract operator fun plus(other: Blake2Word): Blake2Word
internal abstract infix fun or(other: Blake2Word): Blake2Word
internal abstract infix fun xor(other: Blake2Word): Blake2Word
internal abstract fun rotateRight(bits: Int): Blake2Word
internal abstract fun toLittleEndian(): LittleEndian

internal data class Blake2sWord(val value: Int) : Blake2Word() {
override operator fun plus(other: Blake2Word): Blake2Word = when (other) {
is Blake2sWord -> Blake2sWord(this.value + other.value)
else -> throw IllegalArgumentException("Incompatible types")
}

override infix fun or(other: Blake2Word): Blake2Word = when (other) {
is Blake2sWord -> Blake2sWord(this.value or other.value)
else -> throw IllegalArgumentException("Incompatible types")
}

override infix fun xor(other: Blake2Word): Blake2Word = when (other) {
is Blake2sWord -> Blake2sWord(this.value xor other.value)
else -> throw IllegalArgumentException("Incompatible types")
}

override fun rotateRight(bits: Int): Blake2Word = Blake2sWord(value.rotateRight(bits))
override fun toLittleEndian(): LittleEndian = value.toLittleEndian()
}

internal data class Blake2bWord(val value: Long) : Blake2Word() {
override operator fun plus(other: Blake2Word): Blake2Word = when (other) {
is Blake2bWord -> Blake2bWord(this.value + other.value)
else -> throw IllegalArgumentException("Incompatible types")
}

override infix fun or(other: Blake2Word): Blake2Word = when (other) {
is Blake2bWord -> Blake2bWord(this.value or other.value)
else -> throw IllegalArgumentException("Incompatible types")
}

override infix fun xor(other: Blake2Word): Blake2Word = when (other) {
is Blake2bWord -> Blake2bWord(this.value xor other.value)
else -> throw IllegalArgumentException("Incompatible types")
}

override fun rotateRight(bits: Int): Blake2Word = Blake2bWord(value.rotateRight(bits))
override fun toLittleEndian(): LittleEndian = value.toLittleEndian()
}
}
Loading

0 comments on commit 527da62

Please sign in to comment.