forked from sergeych/crypto2
missing initial files + publishing
This commit is contained in:
1 parent
aaa8c436b0
commit
f429cfe418
25 files changed
+870
No files matched your search
@@ -0,0 +1,183 @@
|
||||
package org.komputing.khash.keccak
|
||||
|
||||
import com.ionspin.kotlin.bignum.integer.BigInteger
|
||||
import org.komputing.khash.keccak.extensions.fillWith
|
||||
import kotlin.math.min
|
||||
|
||||
object Keccak {
|
||||
|
||||
private val BIT_65 = BigInteger.ONE shl (64)
|
||||
private val MAX_64_BITS = BIT_65 - BigInteger.ONE
|
||||
|
||||
fun digest(value: ByteArray, parameter: KeccakParameter): ByteArray {
|
||||
val uState = IntArray(200)
|
||||
val uMessage = convertToUInt(value)
|
||||
|
||||
var blockSize = 0
|
||||
var inputOffset = 0
|
||||
|
||||
// Absorbing phase
|
||||
while (inputOffset < uMessage.size) {
|
||||
blockSize = min(uMessage.size - inputOffset, parameter.rateInBytes)
|
||||
for (i in 0 until blockSize) {
|
||||
uState[i] = uState[i] xor uMessage[i + inputOffset]
|
||||
}
|
||||
|
||||
inputOffset += blockSize
|
||||
|
||||
if (blockSize == parameter.rateInBytes) {
|
||||
doF(uState)
|
||||
blockSize = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Padding phase
|
||||
uState[blockSize] = uState[blockSize] xor parameter.d
|
||||
if (parameter.d and 0x80 != 0 && blockSize == parameter.rateInBytes - 1) {
|
||||
doF(uState)
|
||||
}
|
||||
|
||||
uState[parameter.rateInBytes - 1] = uState[parameter.rateInBytes - 1] xor 0x80
|
||||
doF(uState)
|
||||
|
||||
// Squeezing phase
|
||||
val byteResults = mutableListOf<Byte>()
|
||||
var tOutputLen = parameter.outputLengthInBytes
|
||||
while (tOutputLen > 0) {
|
||||
blockSize = min(tOutputLen, parameter.rateInBytes)
|
||||
for (i in 0 until blockSize) {
|
||||
byteResults.add(uState[i].toByte().toInt().toByte())
|
||||
}
|
||||
|
||||
tOutputLen -= blockSize
|
||||
if (tOutputLen > 0) {
|
||||
doF(uState)
|
||||
}
|
||||
}
|
||||
|
||||
return byteResults.toByteArray()
|
||||
}
|
||||
|
||||
private fun doF(uState: IntArray) {
|
||||
val lState = Array(5) { Array(5) { BigInteger.ZERO } }
|
||||
|
||||
for (i in 0..4) {
|
||||
for (j in 0..4) {
|
||||
val data = IntArray(8)
|
||||
val index = 8 * (i + 5 * j)
|
||||
uState.copyInto(data, 0, index, index + data.size)
|
||||
lState[i][j] = convertFromLittleEndianTo64(data)
|
||||
}
|
||||
}
|
||||
roundB(lState)
|
||||
|
||||
uState.fillWith(0)
|
||||
for (i in 0..4) {
|
||||
for (j in 0..4) {
|
||||
val data = convertFrom64ToLittleEndian(lState[i][j])
|
||||
data.copyInto(uState, 8 * (i + 5 * j))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Permutation on the given state.
|
||||
*/
|
||||
private fun roundB(state: Array<Array<BigInteger>>) {
|
||||
var lfsrState = 1
|
||||
for (round in 0..23) {
|
||||
val c = arrayOfNulls<BigInteger>(5)
|
||||
val d = arrayOfNulls<BigInteger>(5)
|
||||
|
||||
// θ step
|
||||
for (i in 0..4) {
|
||||
c[i] = state[i][0].xor(state[i][1]).xor(state[i][2]).xor(state[i][3]).xor(state[i][4])
|
||||
}
|
||||
|
||||
for (i in 0..4) {
|
||||
d[i] = c[(i + 4) % 5]!!.xor(c[(i + 1) % 5]!!.leftRotate64(1))
|
||||
}
|
||||
|
||||
for (i in 0..4) {
|
||||
for (j in 0..4) {
|
||||
state[i][j] = state[i][j].xor(d[i]!!)
|
||||
}
|
||||
}
|
||||
|
||||
// ρ and π steps
|
||||
var x = 1
|
||||
var y = 0
|
||||
var current = state[x][y]
|
||||
for (i in 0..23) {
|
||||
val tX = x
|
||||
x = y
|
||||
y = (2 * tX + 3 * y) % 5
|
||||
|
||||
val shiftValue = current
|
||||
current = state[x][y]
|
||||
|
||||
state[x][y] = shiftValue.leftRotate64Safely((i + 1) * (i + 2) / 2)
|
||||
}
|
||||
|
||||
// χ step
|
||||
for (j in 0..4) {
|
||||
val t = arrayOfNulls<BigInteger>(5)
|
||||
for (i in 0..4) {
|
||||
t[i] = state[i][j]
|
||||
}
|
||||
|
||||
for (i in 0..4) {
|
||||
// ~t[(i + 1) % 5]
|
||||
val invertVal = t[(i + 1) % 5]!!.xor(MAX_64_BITS)
|
||||
// t[i] ^ ((~t[(i + 1) % 5]) & t[(i + 2) % 5])
|
||||
state[i][j] = t[i]!!.xor(invertVal.and(t[(i + 2) % 5]!!))
|
||||
}
|
||||
}
|
||||
|
||||
// ι step
|
||||
for (i in 0..6) {
|
||||
lfsrState = (lfsrState shl 1 xor (lfsrState shr 7) * 0x71) % 256
|
||||
// pow(2, i) - 1
|
||||
val bitPosition = (1 shl i) - 1
|
||||
if (lfsrState and 2 != 0) {
|
||||
state[0][0] = state[0][0].xor(BigInteger.ONE shl bitPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given [data] array to an [IntArray] containing UInt values.
|
||||
*/
|
||||
private fun convertToUInt(data: ByteArray) = IntArray(data.size) {
|
||||
data[it].toInt() and 0xFF
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given [data] array containing the little endian representation of a number to a [BigInteger].
|
||||
*/
|
||||
private fun convertFromLittleEndianTo64(data: IntArray): BigInteger {
|
||||
val value = data.map { it.toString(16) }
|
||||
.map { if (it.length == 2) it else "0$it" }
|
||||
.reversed()
|
||||
.joinToString("")
|
||||
return BigInteger.parseString(value, 16)
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given [BigInteger] to a little endian representation as an [IntArray].
|
||||
*/
|
||||
private fun convertFrom64ToLittleEndian(uLong: BigInteger): IntArray {
|
||||
val asHex = uLong.toString(16)
|
||||
val asHexPadded = "0".repeat((8 * 2) - asHex.length) + asHex
|
||||
return IntArray(8) {
|
||||
((7 - it) * 2).let { pos ->
|
||||
asHexPadded.substring(pos, pos + 2).toInt(16)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun BigInteger.leftRotate64Safely(rotate: Int) = leftRotate64(rotate % 64)
|
||||
|
||||
private fun BigInteger.leftRotate64(rotate: Int) = (this shr (64 - rotate)).add(this shl rotate).mod(BIT_65)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
@file:Suppress("unused")
|
||||
|
||||
package org.komputing.khash.keccak
|
||||
|
||||
/**
|
||||
* Parameters defining the FIPS 202 standard.
|
||||
*/
|
||||
enum class KeccakParameter(val rateInBytes: Int,val outputLengthInBytes: Int, val d: Int) {
|
||||
|
||||
KECCAK_224(144, 28, 0x01),
|
||||
KECCAK_256(136, 32, 0x01),
|
||||
KECCAK_384(104, 48, 0x01),
|
||||
KECCAK_512(72, 64, 0x01),
|
||||
|
||||
SHA3_224(144, 28, 0x06),
|
||||
SHA3_256(136, 32, 0x06),
|
||||
SHA3_384(104, 48, 0x06),
|
||||
SHA3_512(72, 64, 0x06),
|
||||
|
||||
SHAKE128(168, 32, 0x1F),
|
||||
SHAKE256(136, 64, 0x1F)
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.komputing.khash.keccak.extensions
|
||||
|
||||
/**
|
||||
* Assigns the specified int value to each element of the specified
|
||||
* range in the specified array of ints. The range to be filled
|
||||
* extends from index <tt>fromIndex</tt>, inclusive, to index
|
||||
* <tt>toIndex</tt>, exclusive. (If <tt>fromIndex==toIndex</tt>, the
|
||||
* range to be filled is empty.)
|
||||
*
|
||||
* @param fromIndex the index of the first element (inclusive) to be
|
||||
* filled with the specified value
|
||||
* @param toIndex the index of the last element (exclusive) to be
|
||||
* filled with the specified value
|
||||
* @param value the value to be stored in all elements of the array
|
||||
* @throws IllegalArgumentException if <tt>fromIndex > toIndex</tt>
|
||||
* @throws ArrayIndexOutOfBoundsException if <tt>fromIndex < 0</tt> or
|
||||
* <tt>toIndex > a.length</tt>
|
||||
*/
|
||||
internal fun IntArray.fillWith(value: Int, fromIndex: Int = 0, toIndex: Int = this.size) {
|
||||
if (fromIndex > toIndex) {
|
||||
throw IllegalArgumentException(
|
||||
"fromIndex($fromIndex) > toIndex($toIndex)"
|
||||
)
|
||||
}
|
||||
|
||||
if (fromIndex < 0) {
|
||||
throw ArrayIndexOutOfBoundsException(fromIndex)
|
||||
}
|
||||
if (toIndex > this.size) {
|
||||
throw ArrayIndexOutOfBoundsException(toIndex)
|
||||
}
|
||||
|
||||
for (i in fromIndex until toIndex)
|
||||
this[i] = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new [ArrayIndexOutOfBoundsException]
|
||||
* class with an argument indicating the illegal index.
|
||||
* @param index the illegal index.
|
||||
*/
|
||||
internal class ArrayIndexOutOfBoundsException(index: Int) : Throwable("Array index out of range: $index")
|
||||
@@ -0,0 +1,19 @@
|
||||
@file:Suppress("unused")
|
||||
package org.komputing.khash.keccak.extensions
|
||||
|
||||
import org.komputing.khash.keccak.Keccak
|
||||
import org.komputing.khash.keccak.KeccakParameter
|
||||
|
||||
/**
|
||||
* Computes the proper Keccak digest of [this] byte array based on the given [parameter]
|
||||
*/
|
||||
fun ByteArray.digestKeccak(parameter: KeccakParameter): ByteArray {
|
||||
return Keccak.digest(this, parameter)
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the proper Keccak digest of [this] string based on the given [parameter]
|
||||
*/
|
||||
fun String.digestKeccak(parameter: KeccakParameter): ByteArray {
|
||||
return Keccak.digest(encodeToByteArray(), parameter)
|
||||
}
|
||||
Reference in new issue
Block a user