key_schedule_test.go raw
1 package mls
2
3 import (
4 "bytes"
5 "fmt"
6 "testing"
7 )
8
9 type pskSecretTest struct {
10 CipherSuite CipherSuite `json:"cipher_suite"`
11
12 PSKs []struct {
13 PSKID testBytes `json:"psk_id"`
14 PSK testBytes `json:"psk"`
15 PSKNonce testBytes `json:"psk_nonce"`
16 } `json:"psks"`
17
18 PSKSecret testBytes `json:"psk_secret"`
19 }
20
21 func testPSKSecret(t *testing.T, tc *pskSecretTest) {
22 var (
23 pskIDs []preSharedKeyID
24 psks [][]byte
25 )
26 for _, psk := range tc.PSKs {
27 pskIDs = append(pskIDs, preSharedKeyID{
28 pskType: pskTypeExternal,
29 pskID: []byte(psk.PSKID),
30 pskNonce: []byte(psk.PSKNonce),
31 })
32 psks = append(psks, []byte(psk.PSK))
33 }
34
35 pskSecret, err := extractPSKSecret(tc.CipherSuite, pskIDs, psks)
36 if err != nil {
37 t.Fatalf("extractPSKSecret() = %v", err)
38 }
39 if !bytes.Equal(pskSecret, []byte(tc.PSKSecret)) {
40 t.Errorf("extractPSKSecret() = %v, want %v", pskSecret, tc.PSKSecret)
41 }
42 }
43
44 func TestPSKSecret(t *testing.T) {
45 var tests []pskSecretTest
46 loadTestVector(t, "testdata/psk_secret.json", &tests)
47
48 for i, tc := range tests {
49 t.Run(fmt.Sprintf("[%v]", i), func(t *testing.T) {
50 testPSKSecret(t, &tc)
51 })
52 }
53 }
54
55 type keyScheduleTest struct {
56 CipherSuite CipherSuite `json:"cipher_suite"`
57
58 GroupID testBytes `json:"group_id"`
59 InitialInitSecret testBytes `json:"initial_init_secret"`
60
61 Epochs []struct {
62 TreeHash testBytes `json:"tree_hash"`
63 CommitSecret testBytes `json:"commit_secret"`
64 PSKSecret testBytes `json:"psk_secret"`
65 ConfirmedTranscriptHash testBytes `json:"confirmed_transcript_hash"`
66
67 GroupContext testBytes `json:"group_context"`
68
69 JoinerSecret testBytes `json:"joiner_secret"`
70 WelcomeSecret testBytes `json:"welcome_secret"`
71 InitSecret testBytes `json:"init_secret"`
72
73 SenderDataSecret testBytes `json:"sender_data_secret"`
74 EncryptionSecret testBytes `json:"encryption_secret"`
75 ExporterSecret testBytes `json:"exporter_secret"`
76 EpochAuthenticator testBytes `json:"epoch_authenticator"`
77 ExternalSecret testBytes `json:"external_secret"`
78 ConfirmationKey testBytes `json:"confirmation_key"`
79 MembershipKey testBytes `json:"membership_key"`
80 ResumptionPSK testBytes `json:"resumption_psk"`
81
82 ExternalPub testBytes `json:"external_pub"`
83 Exporter struct {
84 Label string `json:"label"`
85 Context testBytes `json:"context"`
86 Length uint16 `json:"length"`
87 Secret testBytes `json:"secret"`
88 } `json:"exporter"`
89 } `json:"epochs"`
90 }
91
92 func testKeySchedule(t *testing.T, tc *keyScheduleTest) {
93 initSecret := []byte(tc.InitialInitSecret)
94 for i, epoch := range tc.Epochs {
95 t.Logf("epoch %d", i)
96
97 ctx := groupContext{
98 version: protocolVersionMLS10,
99 cipherSuite: tc.CipherSuite,
100 groupID: GroupID(tc.GroupID),
101 epoch: uint64(i),
102 treeHash: []byte(epoch.TreeHash),
103 confirmedTranscriptHash: []byte(epoch.ConfirmedTranscriptHash),
104 }
105 rawCtx, err := marshal(&ctx)
106 if err != nil {
107 t.Fatalf("marshal(groupContext) = %v", err)
108 } else if !bytes.Equal(rawCtx, []byte(epoch.GroupContext)) {
109 t.Errorf("marshal(groupContext) = %v, want %v", rawCtx, epoch.GroupContext)
110 }
111
112 joinerSecret, err := ctx.extractJoinerSecret(initSecret, []byte(epoch.CommitSecret))
113 if err != nil {
114 t.Errorf("extractJoinerSecret() = %v", err)
115 } else if !bytes.Equal(joinerSecret, []byte(epoch.JoinerSecret)) {
116 t.Errorf("extractJoinerSecret() = %v, want %v", joinerSecret, epoch.JoinerSecret)
117 }
118
119 welcomeSecret, err := extractWelcomeSecret(ctx.cipherSuite, joinerSecret, []byte(epoch.PSKSecret))
120 if err != nil {
121 t.Errorf("extractWelcomeSecret() = %v", err)
122 } else if !bytes.Equal(welcomeSecret, []byte(epoch.WelcomeSecret)) {
123 t.Errorf("extractWelcomeSecret() = %v, want %v", welcomeSecret, epoch.WelcomeSecret)
124 }
125
126 epochSecret, err := ctx.extractEpochSecret(joinerSecret, []byte(epoch.PSKSecret))
127 if err != nil {
128 t.Fatalf("extractEpochSecret() = %v", err)
129 }
130
131 initSecret, err = ctx.cipherSuite.deriveSecret(epochSecret, secretLabelInit)
132 if err != nil {
133 t.Errorf("deriveSecret(init) = %v", err)
134 } else if !bytes.Equal(initSecret, []byte(epoch.InitSecret)) {
135 t.Errorf("deriveSecret(init) = %v, want %v", initSecret, epoch.InitSecret)
136 }
137
138 secrets := []struct {
139 label []byte
140 want testBytes
141 }{
142 {secretLabelSenderData, epoch.SenderDataSecret},
143 {secretLabelEncryption, epoch.EncryptionSecret},
144 {secretLabelExporter, epoch.ExporterSecret},
145 {secretLabelExternal, epoch.ExternalSecret},
146 {secretLabelConfirm, epoch.ConfirmationKey},
147 {secretLabelMembership, epoch.MembershipKey},
148 {secretLabelResumption, epoch.ResumptionPSK},
149 }
150 for _, secret := range secrets {
151 sec, err := ctx.cipherSuite.deriveSecret(epochSecret, secret.label)
152 if err != nil {
153 t.Errorf("deriveSecret(%v) = %v", string(secret.label), err)
154 } else if !bytes.Equal(sec, []byte(secret.want)) {
155 t.Errorf("deriveSecret(%v) = %v, want %v", string(secret.label), sec, secret.want)
156 }
157 }
158
159 externalSecret := []byte(epoch.ExternalSecret)
160 if ctx.cipherSuite.Supported() {
161 kem, _, _ := ctx.cipherSuite.hpke().Params()
162 pub, _ := kem.Scheme().DeriveKeyPair(externalSecret)
163 if b, err := pub.MarshalBinary(); err != nil {
164 t.Errorf("kem.PublicKey.MarshalBinary() = %v", err)
165 } else if !bytes.Equal(b, epoch.ExternalPub) {
166 t.Errorf("kem.PublicKey.MarshalBinary() = %v, want %v", b, epoch.ExternalPub)
167 }
168 }
169
170 exporterSecret := []byte(epoch.ExporterSecret)
171 b, err := deriveExporter(ctx.cipherSuite, exporterSecret, []byte(epoch.Exporter.Label), []byte(epoch.Exporter.Context), epoch.Exporter.Length)
172 if err != nil {
173 t.Errorf("deriveExporter() = %v", err)
174 } else if !bytes.Equal(b, epoch.Exporter.Secret) {
175 t.Errorf("deriveExporter() = %v, want %v", b, epoch.Exporter.Secret)
176 }
177 }
178 }
179
180 func TestKeySchedule(t *testing.T) {
181 var tests []keyScheduleTest
182 loadTestVector(t, "testdata/key-schedule.json", &tests)
183
184 for i, tc := range tests {
185 t.Run(fmt.Sprintf("[%v]", i), func(t *testing.T) {
186 testKeySchedule(t, &tc)
187 })
188 }
189 }
190
191 type transcriptHashesTest struct {
192 CipherSuite CipherSuite `json:"cipher_suite"`
193
194 ConfirmationKey testBytes `json:"confirmation_key"`
195 AuthenticatedContent testBytes `json:"authenticated_content"`
196 InterimTranscriptHashBefore testBytes `json:"interim_transcript_hash_before"`
197
198 ConfirmedTranscriptHashAfter testBytes `json:"confirmed_transcript_hash_after"`
199 InterimTranscriptHashAfter testBytes `json:"interim_transcript_hash_after"`
200 }
201
202 func testTranscriptHashes(t *testing.T, tc *transcriptHashesTest) {
203 cs := tc.CipherSuite
204
205 var authContent authenticatedContent
206 if err := unmarshal([]byte(tc.AuthenticatedContent), &authContent); err != nil {
207 t.Fatalf("unmarshal() = %v", err)
208 } else if authContent.content.contentType != contentTypeCommit {
209 t.Fatalf("contentType = %v, want %v", authContent.content.contentType, contentTypeCommit)
210 }
211
212 if !authContent.auth.verifyConfirmationTag(cs, []byte(tc.ConfirmationKey), []byte(tc.ConfirmedTranscriptHashAfter)) {
213 t.Errorf("verifyConfirmationTag() failed")
214 }
215
216 confirmedTranscriptHashAfter, err := authContent.confirmedTranscriptHashInput().hash(cs, []byte(tc.InterimTranscriptHashBefore))
217 if err != nil {
218 t.Fatalf("confirmedTranscriptHashInput.hash() = %v", err)
219 } else if !bytes.Equal(confirmedTranscriptHashAfter, []byte(tc.ConfirmedTranscriptHashAfter)) {
220 t.Errorf("confirmedTranscriptHashInput.hash() = %v, want %v", confirmedTranscriptHashAfter, tc.ConfirmedTranscriptHashAfter)
221 }
222
223 interimTranscriptHashAfter, err := nextInterimTranscriptHash(cs, confirmedTranscriptHashAfter, authContent.auth.confirmationTag)
224 if err != nil {
225 t.Fatalf("nextInterimTranscriptHash() = %v", err)
226 } else if !bytes.Equal(interimTranscriptHashAfter, []byte(tc.InterimTranscriptHashAfter)) {
227 t.Errorf("nextInterimTranscriptHash() = %v, want %v", interimTranscriptHashAfter, tc.InterimTranscriptHashAfter)
228 }
229 }
230
231 func TestTranscriptHashes(t *testing.T) {
232 var tests []transcriptHashesTest
233 loadTestVector(t, "testdata/transcript-hashes.json", &tests)
234
235 for i, tc := range tests {
236 t.Run(fmt.Sprintf("[%v]", i), func(t *testing.T) {
237 testTranscriptHashes(t, &tc)
238 })
239 }
240 }
241