huis.noise-kotlin.1.0.1.source-code.Symmetry.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of noise-kotlin Show documentation
Show all versions of noise-kotlin Show documentation
Noise protocols based on Diffie-Hellman key agreement
The newest version!
package nl.sanderdijkhuis.noise
import nl.sanderdijkhuis.noise.cryptography.*
import nl.sanderdijkhuis.noise.data.Data
import nl.sanderdijkhuis.noise.data.Size
import nl.sanderdijkhuis.noise.data.State
/**
* Encompasses all Noise protocol symmetric cryptography state required during handshakes.
*/
data class Symmetry(val cipher: Cipher, val key: ChainingKey, val handshakeHash: HandshakeHash) {
@JvmInline
value class ChainingKey(val digest: Digest)
@JvmInline
value class HandshakeHash(val digest: Digest)
@JvmInline
internal value class InputKeyMaterial(val data: Data) {
init {
require(data.isEmpty || data.size == DEFAULT_SIZE || data.size == SharedSecret.SIZE)
}
companion object {
val DEFAULT_SIZE = Size(32u)
}
}
val cryptography get() = cipher.cryptography
fun mixKey(sharedSecret: SharedSecret) = let {
val (chainingKey, cipherKey) = deriveKeys(cryptography, key, InputKeyMaterial(sharedSecret.data))
copy(
cipher = Cipher(cryptography = cryptography, key = CipherKey(cipherKey.data)),
key = ChainingKey(chainingKey)
)
}
fun mixHash(data: Data) =
copy(handshakeHash = HandshakeHash(cryptography.hash(handshakeHash.digest.data + data)))
fun encryptAndHash(plaintext: Plaintext) =
cipher.encrypt(AssociatedData(handshakeHash.digest.data), plaintext).let {
State(copy(cipher = it.value).mixHash(it.result.data), it.result.data)
}
fun decryptAndHash(ciphertext: Ciphertext) =
cipher.decrypt(AssociatedData(handshakeHash.digest.data), ciphertext)?.let {
State(copy(cipher = it.value).mixHash(ciphertext.data), it.result)
}
fun split() = let {
val zeroLen = InputKeyMaterial(Data.empty)
val temporaryKeys = deriveKeys(cryptography, key, zeroLen)
val c1 = Cipher(cryptography, CipherKey(temporaryKeys.first.data))
val c2 = Cipher(cryptography, CipherKey(temporaryKeys.second.data))
Pair(c1, c2)
}
companion object {
fun initialize(cryptography: Cryptography, protocolName: String) = let {
val name = Data(protocolName.toByteArray())
val h = if (name.size <= Digest.SIZE)
Digest(Data(Digest.SIZE.byteArray { name.value.getOrElse(it) { 0x00 } }))
else
cryptography.hash(name)
val ck = ChainingKey(h)
val state = Cipher(cryptography)
Symmetry(state, ck, HandshakeHash(h))
}
private val BLOCK_SIZE = Size(64u)
/** [RFC 2104](https://www.rfc-editor.org/rfc/rfc2104.html) */
private fun authenticateMessage(cryptography: Cryptography, key: Digest, data: Data) = let {
fun block(init: (Int) -> Byte) = Data(ByteArray(BLOCK_SIZE.integerValue, init))
val keyData =
if (key.data.size <= BLOCK_SIZE)
block { key.data.value.getOrElse(it) { 0x00 } }
else cryptography.hash(key.data).data
val innerPadding = block { 0x36 }
val outerPadding = block { 0x5c }
cryptography.hash(keyData.xor(outerPadding) + cryptography.hash(keyData.xor(innerPadding) + data).data)
}
internal fun deriveKeys(cryptography: Cryptography, key: ChainingKey, material: InputKeyMaterial) = let {
val temporaryKey = authenticateMessage(cryptography, key.digest, material.data)
val output1 = authenticateMessage(cryptography, temporaryKey, Data(byteArrayOf(0x01)))
val output2 = authenticateMessage(cryptography, temporaryKey, output1.data + Data(byteArrayOf(0x02)))
Pair(output1, output2)
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy