diff --git a/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/HkdfBenchmark.kt b/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/HkdfBenchmark.kt index bcdb1c5db..207b9e8bf 100644 --- a/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/HkdfBenchmark.kt +++ b/benchmark/src/androidTest/java/com/vitorpamplona/quartz/benchmark/HkdfBenchmark.kt @@ -46,7 +46,15 @@ class HkdfBenchmark { } @Test - fun hkdfExpand() { + fun hkdfExpandWithCheck() { + val hkdf = Hkdf() + benchmarkRule.measureRepeated { + hkdf.fastExpand(sharedKey, encrypted.nonce, encrypted.ciphertext, encrypted.mac) + } + } + + @Test + fun hkdfFastExpand() { val hkdf = Hkdf() benchmarkRule.measureRepeated { hkdf.fastExpand(sharedKey, encrypted.nonce) diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip44Encryption/Nip44v2.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip44Encryption/Nip44v2.kt index a749f0ed6..38ccddd47 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip44Encryption/Nip44v2.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip44Encryption/Nip44v2.kt @@ -113,12 +113,14 @@ class Nip44v2 { decoded: EncryptedInfo, conversationKey: ByteArray, ): String { - val messageKey = getMessageKeys(conversationKey, decoded.nonce) - - checkHMacAad(messageKey, decoded) + val messageKey = checkMessageKeys(conversationKey, decoded) return unpad( - chaCha.decrypt(decoded.ciphertext, messageKey.chachaNonce, messageKey.chachaKey), + chaCha.decrypt( + decoded.ciphertext, + messageKey.chachaNonce, + messageKey.chachaKey, + ), ) } @@ -210,6 +212,11 @@ class Nip44v2 { nonce: ByteArray, ): Hkdf.MessageKey = hkdf.fastExpand(conversationKey, nonce) + fun checkMessageKeys( + conversationKey: ByteArray, + decoded: EncryptedInfo, + ): Hkdf.MessageKey = hkdf.fastExpand(conversationKey, decoded.nonce, decoded.ciphertext, decoded.mac) + /** @return 32B shared secret */ fun computeConversationKey( privateKey: ByteArray, diff --git a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip44Encryption/crypto/Hkdf.kt b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip44Encryption/crypto/Hkdf.kt index a30c84765..35909b400 100644 --- a/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip44Encryption/crypto/Hkdf.kt +++ b/quartz/src/androidMain/kotlin/com/vitorpamplona/quartz/nip44Encryption/crypto/Hkdf.kt @@ -20,6 +20,7 @@ */ package com.vitorpamplona.quartz.nip44Encryption.crypto +import com.vitorpamplona.quartz.nip01Core.core.toHexKey import java.nio.ByteBuffer import javax.crypto.Mac import javax.crypto.spec.SecretKeySpec @@ -88,6 +89,8 @@ class Hkdf( fun fastExpand( key: ByteArray, nonce: ByteArray, + ciphertext: ByteArray? = null, + ciphertextMac: ByteArray? = null, ): MessageKey { check(key.size == hashLen) check(nonce.size == hashLen) @@ -112,10 +115,22 @@ class Hkdf( mac.update(3) val round3 = mac.doFinal() - val hmacKey = ByteArray(32) + val hmacKey = ByteArray(hashLen) System.arraycopy(round2, 12, hmacKey, 0, 20) System.arraycopy(round3, 0, hmacKey, 20, 12) + // checks the mac here to avoid building a new Mac.getInstance(algorithm) + if (ciphertext != null && ciphertextMac != null) { + mac.init(FixedKey(hmacKey, algorithm)) + mac.update(nonce) + mac.update(ciphertext) + val calculatedMac = mac.doFinal() + + check(calculatedMac.contentEquals(ciphertextMac)) { + "Invalid Mac: Calculated ${calculatedMac.toHexKey()}, decoded: ${ciphertextMac.toHexKey()}" + } + } + return MessageKey( chachaKey = round1, chachaNonce = round2.copyOfRange(0, 12),