test_transcript_hashes.mx raw
1 //go:build !wasm
2
3 package mls
4
5 import "errors"
6
7 // TestTranscriptHashes validates transcript hash chain against RFC 9420 test
8 // vectors (cipher_suite 3, transcript-hashes.json index 2).
9 //
10 // Flow:
11 // 1. Unmarshal authenticated_content (must be a commit).
12 // 2. Verify authContent.confirmedTranscriptHashInput().hashValue(cs, interimBefore)
13 // equals confirmed_transcript_hash_after.
14 // 3. Verify nextInterimTranscriptHash(cs, confirmedAfter, confirmationTag)
15 // equals interim_transcript_hash_after.
16 // 4. Verify authContent.auth.verifyConfirmationTag(cs, confirmationKey, confirmedAfter).
17 func TestTranscriptHashes() error {
18 cs := CipherSuite0x0003
19
20 confirmationKey := hexb("45eb25e0f6024d7dca9a319bbb5d86d22d156613e6319507b54f4844f71bc858")
21 authContentBytes := hexb("00010567726f7570000000000000345601000000000003220220e740a6faf2db65f5853148d75d9a335d7c4b94ab106fe5f237bc34fdcfc745840040406d0a302c5106ea9d31f3aec1d43df3fff47c1c0b059f4a0b1884798c5f6c973b4e3933b927b7e5f841813e1c14d8513c579b3662a3ce5e4c50adf53c4d60df022043646bfab3e0513b627b89997a2c3192afec8c9b6e9ef839dbb5fc7c0b90c9cd")
22 interimBefore := hexb("0a56a7ac12b2fdab74b9356d100f5458dbce550c1fe69d43283001496fd08901")
23 confirmedAfter := hexb("93e90c899f8a485dad5521123de058cefde35d5659521efd9b19c16fbdd8b42c")
24 interimAfter := hexb("4184f0b59dc75aa1771efb5601124785ed6d590fe8cd58c398f4ccdbef46f3f0")
25
26 // Unmarshal authenticated content.
27 authContent := &authenticatedContent{}
28 if err := unmarshalRaw(authContentBytes, authContent); err != nil {
29 return errors.New("unmarshal authContent: " | err.Error())
30 }
31
32 // Must be a commit for confirmation tag to apply.
33 if authContent.content.contentType != contentTypeCommit {
34 return errors.New("authContent is not a commit")
35 }
36
37 // Confirmed transcript hash chain.
38 gotConfirmed, err := authContent.confirmedTranscriptHashInput().hashValue(cs, interimBefore)
39 if err != nil {
40 return errors.New("confirmedTranscriptHashInput.hashValue: " | err.Error())
41 }
42 if !bytesEqual(gotConfirmed, confirmedAfter) {
43 return errors.New("confirmed_transcript_hash_after mismatch: got " | hexenc(gotConfirmed) | " want " | hexenc(confirmedAfter))
44 }
45
46 // Interim transcript hash chain.
47 gotInterim, err := nextInterimTranscriptHash(cs, confirmedAfter, authContent.auth.confirmationTag)
48 if err != nil {
49 return errors.New("nextInterimTranscriptHash: " | err.Error())
50 }
51 if !bytesEqual(gotInterim, interimAfter) {
52 return errors.New("interim_transcript_hash_after mismatch: got " | hexenc(gotInterim) | " want " | hexenc(interimAfter))
53 }
54
55 // Verify confirmation tag with the given confirmation_key over confirmed_after.
56 if !authContent.auth.verifyConfirmationTag(cs, confirmationKey, confirmedAfter) {
57 return errors.New("verifyConfirmationTag failed")
58 }
59
60 return nil
61 }