val initstring = "Hey Bond! Beware of the tall blond man with a single black shoe."
fun String.toWords() = toByteArray().toList().chunked(4).map{it ->
val l = it.map{(it and 0xff).toLong()}
l[0] + (l[1] shl 8) + (l[2] shl 16) + (l[3] shl 24)}.toTypedArray()
// R performs a rotation to the left of s positions for
// v interpreted as a 32 bits value
fun R(v: Long, s: Int) = if (s % 32 == 0) v else (((v shl (s % 32)) and 0xFFFFFFFF) or
((v and 0xFFFFFFFF) shr (32-(s % 32))))
fun salsa20(input: Array<Long>): Array<Long> {
assert(input.size == 16)
val x = input.copyOf()
val p = listOf(
4, 0, 12, 7, 8, 4, 0, 9,
12, 8, 4, 13, 0, 12, 8, 18,
9, 5, 1, 7, 13, 9, 5, 9,
1, 13, 9, 13, 5, 1, 13, 18,
14, 10, 6, 7, 2, 14, 10, 9,
6, 2, 14, 13, 10, 6, 2, 18,
3, 15, 11, 7, 7, 3, 15, 9,
11, 7, 3, 13, 15, 11, 7, 18,
1, 0, 3, 7, 2, 1, 0, 9,
3, 2, 1, 13, 0, 3, 2, 18,
6, 5, 4, 7, 7, 6, 5, 9,
4, 7, 6, 13, 5, 4, 7, 18,
11, 10, 9, 7, 8, 11, 10, 9,
9, 8, 11, 13, 10, 9, 8, 18,
12, 15, 14, 7, 13, 12, 15, 9,
14, 13, 12, 13, 15, 14, 13, 18)
for (r in 0 until 20 step 2) {
for (o in 0 until p.size step 4) {
val (w,a,b,s) = p.subList(o, o+4)
x[w] = x[w] xor R(x[a] + x[b], s)
}
}
for (w in 0 until x.size) x[w] = (x[w] + input[w]) and 0xFFFFFFFF
return x
}
fun Array<Long>.toHexDigest() = map { it ->
"%02x%02x%02x%02x".format(it and 0xFF, (it ushr 8) and 0xFF, (it ushr 16) and 0xFF, (it ushr 24) and 0xFF)
}.joinToString("")
fun Array<Long>.toHexWords() = map {it -> "0x%08x".format(it) }.joinToString(" ")
fun Array<Long>.hash(steps: Int) = (0 until steps).fold(this) { acc, _ -> salsa20(acc)}
fun main(args: Array<String>) {
assert(R(1,0) == 1L)
assert(R(1,31) == 0x80000000L)
assert(R(1,31) == 0x80000000L)
val initial = initstring.toWords()
println("input words: " + initial.toHexWords())
println("input hex: " + initial.toHexDigest())
println("step 0: " + initial.hash(0).toHexDigest())
println("step 1: " + initial.hash(1).toHexDigest())
println("step 1000: " + initial.hash(1000).toHexDigest())
println("step 100000: " + initial.hash(100000).toHexDigest())
println("step 1000000: " + initial.hash(1000000).toHexDigest())
}