package com.example.notepad.sync.zurich
internal object HChaCha20 {
private val CONSTANTS = intArrayOf(0x61707865, 0x3320646e, 0x79622d32, 0x6b206574)
private const val ROUNDS = 20
fun deriveSubkey(key: ByteArray, nonce16: ByteArray): ByteArray {
require(key.size == 32) { "HChaCha20 key must be 32 bytes" }
require(nonce16.size == 16) { "HChaCha20 nonce must be 16 bytes" }
val state = IntArray(16)
CONSTANTS.copyInto(state, 0)
for (i in 0 until 8) state[4 + i] = leWordAt(key, i * 4)
for (i in 0 until 4) state[12 + i] = leWordAt(nonce16, i * 4)
var round = 0
while (round < ROUNDS) {
quarterRound(state, 0, 4, 8, 12)
quarterRound(state, 1, 5, 9, 13)
quarterRound(state, 2, 6, 10, 14)
quarterRound(state, 3, 7, 11, 15)
quarterRound(state, 0, 5, 10, 15)
quarterRound(state, 1, 6, 11, 12)
quarterRound(state, 2, 7, 8, 13)
quarterRound(state, 3, 4, 9, 14)
round += 2
}
val output = ByteArray(32)
for (i in 0 until 4) writeLeWord(output, i * 4, state[i])
for (i in 0 until 4) writeLeWord(output, 16 + i * 4, state[12 + i])
return output
}
private fun quarterRound(s: IntArray, a: Int, b: Int, c: Int, d: Int) {
s[a] += s[b]; s[d] = (s[d] xor s[a]).rotateLeft(16)
s[c] += s[d]; s[b] = (s[b] xor s[c]).rotateLeft(12)
s[a] += s[b]; s[d] = (s[d] xor s[a]).rotateLeft(8)
s[c] += s[d]; s[b] = (s[b] xor s[c]).rotateLeft(7)
}
}