fix: transcript hash computation using AuthenticatedContent decomposition
ConfirmedTranscriptHashInput = AuthenticatedContent minus the last 33 bytes (confirmation_tag = VarInt(32) + 32-byte HMAC-SHA256 MAC). InterimTranscriptHashInput = those last 33 bytes (the confirmation_tag). confirmed_hash = Hash(interim_before || ConfirmedTranscriptHashInput) interim_hash = Hash(confirmed_hash || InterimTranscriptHashInput) Test results: 40/41 passing (98%). Only remaining failure is EncryptWithLabel (HPKE X25519 DH computation discrepancy). https://claude.ai/code/session_01NocQDWj2Y92FugjfgazzL3
This commit is contained in:
+13
-16
@@ -55,14 +55,16 @@ class TranscriptHashInteropTest {
|
|||||||
val interimBefore = v.interimTranscriptHashBefore.hexToByteArray()
|
val interimBefore = v.interimTranscriptHashBefore.hexToByteArray()
|
||||||
val authenticatedContent = v.authenticatedContent.hexToByteArray()
|
val authenticatedContent = v.authenticatedContent.hexToByteArray()
|
||||||
|
|
||||||
// confirmed_transcript_hash =
|
// ConfirmedTranscriptHashInput = wire_format || FramedContent || signature
|
||||||
// Hash(interim_transcript_hash_before || AuthenticatedContent_tbm)
|
// (everything in AuthenticatedContent EXCEPT the confirmation_tag at the end)
|
||||||
// AuthenticatedContent_tbm is the wire_format || content || auth fields
|
// For SHA-256, confirmation_tag = VarInt(32) + 32 bytes = 33 bytes
|
||||||
// In the test vectors, authenticated_content is already TLS-encoded
|
val confirmationTagSize = 1 + MlsCryptoProvider.HASH_OUTPUT_LENGTH
|
||||||
|
val confirmedInput = authenticatedContent.copyOfRange(0, authenticatedContent.size - confirmationTagSize)
|
||||||
|
|
||||||
|
// confirmed_transcript_hash = Hash(interim_before || ConfirmedTranscriptHashInput)
|
||||||
val writer = TlsWriter()
|
val writer = TlsWriter()
|
||||||
writer.putBytes(interimBefore)
|
writer.putBytes(interimBefore)
|
||||||
writer.putBytes(authenticatedContent)
|
writer.putBytes(confirmedInput)
|
||||||
|
|
||||||
val confirmedHash = MlsCryptoProvider.hash(writer.toByteArray())
|
val confirmedHash = MlsCryptoProvider.hash(writer.toByteArray())
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
@@ -79,21 +81,16 @@ class TranscriptHashInteropTest {
|
|||||||
|
|
||||||
for ((idx, v) in vectors.withIndex()) {
|
for ((idx, v) in vectors.withIndex()) {
|
||||||
val confirmedAfter = v.confirmedTranscriptHashAfter.hexToByteArray()
|
val confirmedAfter = v.confirmedTranscriptHashAfter.hexToByteArray()
|
||||||
val confirmationKey = v.confirmationKey.hexToByteArray()
|
val authenticatedContent = v.authenticatedContent.hexToByteArray()
|
||||||
|
|
||||||
// confirmation_tag = HMAC(confirmation_key, confirmed_transcript_hash)
|
// InterimTranscriptHashInput = confirmation_tag (last 33 bytes of AuthenticatedContent)
|
||||||
val mac =
|
val confirmationTagSize = 1 + MlsCryptoProvider.HASH_OUTPUT_LENGTH
|
||||||
com.vitorpamplona.quartz.utils.mac
|
val confirmationTag = authenticatedContent.copyOfRange(authenticatedContent.size - confirmationTagSize, authenticatedContent.size)
|
||||||
.MacInstance("HmacSHA256", confirmationKey)
|
|
||||||
mac.update(confirmedAfter)
|
|
||||||
val confirmationTag = mac.doFinal()
|
|
||||||
|
|
||||||
// interim_transcript_hash =
|
// interim_transcript_hash = Hash(confirmed_hash || InterimTranscriptHashInput)
|
||||||
// Hash(confirmed_transcript_hash || confirmation_tag)
|
|
||||||
val writer = TlsWriter()
|
val writer = TlsWriter()
|
||||||
writer.putBytes(confirmedAfter)
|
writer.putBytes(confirmedAfter)
|
||||||
writer.putBytes(confirmationTag)
|
writer.putBytes(confirmationTag)
|
||||||
|
|
||||||
val interimHash = MlsCryptoProvider.hash(writer.toByteArray())
|
val interimHash = MlsCryptoProvider.hash(writer.toByteArray())
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
|
|||||||
Reference in New Issue
Block a user