group_test.go raw
1 package mls
2
3 import (
4 "bytes"
5 "fmt"
6 "log"
7 "testing"
8 )
9
10 type welcomeTest struct {
11 CipherSuite CipherSuite `json:"cipher_suite"`
12
13 InitPriv testBytes `json:"init_priv"`
14 SignerPub testBytes `json:"signer_pub"`
15
16 KeyPackage testBytes `json:"key_package"`
17 Welcome testBytes `json:"welcome"`
18 }
19
20 func testWelcome(t *testing.T, tc *welcomeTest) {
21 var welcomeMsg mlsMessage
22 if err := welcomeMsg.unmarshal(tc.Welcome.ByteString()); err != nil {
23 t.Fatalf("unmarshal(welcome) = %v", err)
24 } else if welcomeMsg.wireFormat != wireFormatMLSWelcome {
25 t.Fatalf("wireFormat = %v, want %v", welcomeMsg.wireFormat, wireFormatMLSWelcome)
26 }
27 welcome := welcomeMsg.welcome
28
29 var keyPackageMsg mlsMessage
30 if err := keyPackageMsg.unmarshal(tc.KeyPackage.ByteString()); err != nil {
31 t.Fatalf("unmarshal(keyPackage) = %v", err)
32 } else if keyPackageMsg.wireFormat != wireFormatMLSKeyPackage {
33 t.Fatalf("wireFormat = %v, want %v", keyPackageMsg.wireFormat, wireFormatMLSKeyPackage)
34 }
35 keyPackage := keyPackageMsg.keyPackage
36
37 keyPackageRef, err := keyPackage.GenerateRef()
38 if err != nil {
39 t.Fatalf("keyPackage.generateRef() = %v", err)
40 }
41
42 groupSecrets, err := welcome.decryptGroupSecrets(keyPackageRef, []byte(tc.InitPriv))
43 if err != nil {
44 t.Fatalf("welcome.decryptGroupSecrets() = %v", err)
45 }
46
47 groupInfo, err := welcome.decryptGroupInfo(groupSecrets.joinerSecret, nil)
48 if err != nil {
49 t.Fatalf("welcome.decryptGroupInfo() = %v", err)
50 }
51 if !groupInfo.verifySignature(signaturePublicKey(tc.SignerPub)) {
52 t.Errorf("groupInfo.verifySignature() failed")
53 }
54 if !groupInfo.verifyConfirmationTag(groupSecrets.joinerSecret, nil) {
55 t.Errorf("groupInfo.verifyConfirmationTag() failed")
56 }
57 }
58
59 func TestWelcome(t *testing.T) {
60 var tests []welcomeTest
61 loadTestVector(t, "testdata/welcome.json", &tests)
62
63 for i, tc := range tests {
64 t.Run(fmt.Sprintf("[%v]", i), func(t *testing.T) {
65 testWelcome(t, &tc)
66 })
67 }
68 }
69
70 type messageProtectionTest struct {
71 CipherSuite CipherSuite `json:"cipher_suite"`
72
73 GroupID testBytes `json:"group_id"`
74 Epoch uint64 `json:"epoch"`
75 TreeHash testBytes `json:"tree_hash"`
76 ConfirmedTranscriptHash testBytes `json:"confirmed_transcript_hash"`
77
78 SignaturePriv testBytes `json:"signature_priv"`
79 SignaturePub testBytes `json:"signature_pub"`
80
81 EncryptionSecret testBytes `json:"encryption_secret"`
82 SenderDataSecret testBytes `json:"sender_data_secret"`
83 MembershipKey testBytes `json:"membership_key"`
84
85 Proposal testBytes `json:"proposal"`
86 ProposalPub testBytes `json:"proposal_pub"`
87 ProposalPriv testBytes `json:"proposal_priv"`
88
89 Commit testBytes `json:"commit"`
90 CommitPub testBytes `json:"commit_pub"`
91 CommitPriv testBytes `json:"commit_priv"`
92
93 Application testBytes `json:"application"`
94 ApplicationPriv testBytes `json:"application_priv"`
95 }
96
97 func testMessageProtectionPub(t *testing.T, tc *messageProtectionTest, ctx *groupContext, wantRaw, rawPub []byte) {
98 var msg mlsMessage
99 if err := unmarshal(rawPub, &msg); err != nil {
100 t.Fatalf("unmarshal() = %v", err)
101 } else if msg.wireFormat != wireFormatMLSPublicMessage {
102 t.Fatalf("unmarshal(): wireFormat = %v, want %v", msg.wireFormat, wireFormatMLSPublicMessage)
103 }
104 pubMsg := msg.publicMessage
105
106 verifyPublicMessage(t, tc, ctx, pubMsg, wantRaw)
107
108 pubMsg, err := signPublicMessage(tc.CipherSuite, []byte(tc.SignaturePriv), &pubMsg.content, ctx)
109 if err != nil {
110 t.Errorf("signPublicMessage() = %v", err)
111 }
112 if err := pubMsg.signMembershipTag(tc.CipherSuite, []byte(tc.MembershipKey), ctx); err != nil {
113 t.Errorf("signMembershipTag() = %v", err)
114 }
115 verifyPublicMessage(t, tc, ctx, pubMsg, wantRaw)
116 }
117
118 func verifyPublicMessage(t *testing.T, tc *messageProtectionTest, ctx *groupContext, pubMsg *publicMessage, wantRaw []byte) {
119 authContent := pubMsg.authenticatedContent()
120 if !authContent.verifySignature([]byte(tc.SignaturePub), ctx) {
121 t.Errorf("verifySignature() failed")
122 }
123 if !pubMsg.verifyMembershipTag([]byte(tc.MembershipKey), ctx) {
124 t.Errorf("verifyMembershipTag() failed")
125 }
126
127 var (
128 raw []byte
129 err error
130 )
131 switch pubMsg.content.contentType {
132 case contentTypeApplication:
133 raw = pubMsg.content.applicationData
134 case contentTypeProposal:
135 raw, err = marshal(pubMsg.content.proposal)
136 case contentTypeCommit:
137 raw, err = marshal(pubMsg.content.commit)
138 default:
139 t.Errorf("unexpected content type %v", pubMsg.content.contentType)
140 }
141 if err != nil {
142 t.Errorf("marshal() = %v", err)
143 } else if !bytes.Equal(raw, wantRaw) {
144 t.Errorf("marshal() = %v, want %v", raw, wantRaw)
145 }
146 }
147
148 func testMessageProtectionPriv(t *testing.T, tc *messageProtectionTest, ctx *groupContext, wantRaw, rawPriv []byte) {
149 var msg mlsMessage
150 if err := unmarshal(rawPriv, &msg); err != nil {
151 t.Fatalf("unmarshal() = %v", err)
152 } else if msg.wireFormat != wireFormatMLSPrivateMessage {
153 t.Fatalf("unmarshal(): wireFormat = %v, want %v", msg.wireFormat, wireFormatMLSPrivateMessage)
154 }
155 privMsg := msg.privateMessage
156
157 tree, err := deriveSecretTree(tc.CipherSuite, numLeaves(2), []byte(tc.EncryptionSecret))
158 if err != nil {
159 t.Fatalf("deriveSecretTree() = %v", err)
160 }
161
162 label := ratchetLabelFromContentType(privMsg.contentType)
163 li := leafIndex(1)
164 secret, err := tree.deriveRatchetRoot(tc.CipherSuite, li.nodeIndex(), label)
165 if err != nil {
166 t.Fatalf("deriveRatchetRoot() = %v", err)
167 }
168
169 content := decryptPrivateMessage(t, tc, ctx, secret, privMsg, wantRaw)
170
171 senderData, err := newSenderData(li, 0) // TODO: set generation > 0
172 if err != nil {
173 t.Fatalf("newSenderData() = %v", err)
174 }
175 framedContent := framedContent{
176 groupID: GroupID(tc.GroupID),
177 epoch: tc.Epoch,
178 sender: sender{
179 senderType: senderTypeMember,
180 leafIndex: li,
181 },
182 contentType: privMsg.contentType,
183 applicationData: content.applicationData,
184 proposal: content.proposal,
185 commit: content.commit,
186 }
187 privContent, err := signPrivateMessageContent(tc.CipherSuite, []byte(tc.SignaturePriv), &framedContent, ctx)
188 if err != nil {
189 t.Fatalf("signPrivateMessageContent() = %v", err)
190 }
191
192 privMsg, err = encryptPrivateMessage(tc.CipherSuite, secret, []byte(tc.SenderDataSecret), &framedContent, privContent, senderData)
193 if err != nil {
194 t.Fatalf("encryptPrivateMessage() = %v", err)
195 }
196 decryptPrivateMessage(t, tc, ctx, secret, privMsg, wantRaw)
197 }
198
199 func decryptPrivateMessage(t *testing.T, tc *messageProtectionTest, ctx *groupContext, secret ratchetSecret, privMsg *privateMessage, wantRaw []byte) *privateMessageContent {
200 senderData, err := privMsg.decryptSenderData(tc.CipherSuite, []byte(tc.SenderDataSecret))
201 if err != nil {
202 t.Fatalf("decryptSenderData() = %v", err)
203 }
204
205 for secret.generation != senderData.generation {
206 secret, err = secret.deriveNext(tc.CipherSuite)
207 if err != nil {
208 t.Fatalf("deriveNext() = %v", err)
209 }
210 }
211
212 content, err := privMsg.decryptContent(tc.CipherSuite, secret, senderData.reuseGuard)
213 if err != nil {
214 t.Fatalf("decryptContent() = %v", err)
215 }
216
217 authContent := privMsg.authenticatedContent(senderData, content)
218 if !authContent.verifySignature([]byte(tc.SignaturePub), ctx) {
219 t.Errorf("verifySignature() failed")
220 }
221
222 var raw []byte
223 switch privMsg.contentType {
224 case contentTypeApplication:
225 raw = content.applicationData
226 case contentTypeProposal:
227 raw, err = marshal(content.proposal)
228 case contentTypeCommit:
229 raw, err = marshal(content.commit)
230 default:
231 t.Errorf("unexpected content type %v", privMsg.contentType)
232 }
233 if err != nil {
234 t.Errorf("marshal() = %v", err)
235 } else if !bytes.Equal(raw, wantRaw) {
236 t.Errorf("marshal() = %v, want %v", raw, wantRaw)
237 }
238
239 return content
240 }
241
242 func testMessageProtection(t *testing.T, tc *messageProtectionTest) {
243 ctx := groupContext{
244 version: protocolVersionMLS10,
245 cipherSuite: tc.CipherSuite,
246 groupID: GroupID(tc.GroupID),
247 epoch: tc.Epoch,
248 treeHash: []byte(tc.TreeHash),
249 confirmedTranscriptHash: []byte(tc.ConfirmedTranscriptHash),
250 }
251
252 wireFormats := []struct {
253 name string
254 raw, pub, priv testBytes
255 }{
256 {"proposal", tc.Proposal, tc.ProposalPub, tc.ProposalPriv},
257 {"commit", tc.Commit, tc.CommitPub, tc.CommitPriv},
258 {"application", tc.Application, nil, tc.ApplicationPriv},
259 }
260 for _, wireFormat := range wireFormats {
261 t.Run(wireFormat.name, func(t *testing.T) {
262 raw := []byte(wireFormat.raw)
263 pub := []byte(wireFormat.pub)
264 priv := []byte(wireFormat.priv)
265 if wireFormat.pub != nil {
266 t.Run("pub", func(t *testing.T) {
267 testMessageProtectionPub(t, tc, &ctx, raw, pub)
268 })
269 }
270 t.Run("priv", func(t *testing.T) {
271 testMessageProtectionPriv(t, tc, &ctx, raw, priv)
272 })
273 })
274 }
275 }
276
277 func TestMessageProtection(t *testing.T) {
278 var tests []messageProtectionTest
279 loadTestVector(t, "testdata/message-protection.json", &tests)
280
281 for i, tc := range tests {
282 t.Run(fmt.Sprintf("[%v]", i), func(t *testing.T) {
283 testMessageProtection(t, &tc)
284 })
285 }
286 }
287
288 func ExampleGroup() {
289 cs := CipherSuiteMLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519
290
291 aliceCredential := NewBasicCredential([]byte("앨리스"))
292 aliceKeyPairPkg, err := GenerateKeyPairPackage(cs, aliceCredential)
293 if err != nil {
294 log.Fatalf("GenerateKeyPairPackage() = %v", err)
295 }
296
297 bobCredential := NewBasicCredential([]byte("밥"))
298 bobKeyPairPkg, err := GenerateKeyPairPackage(cs, bobCredential)
299 if err != nil {
300 log.Fatalf("GenerateKeyPairPackage() = %v", err)
301 }
302
303 groupID := GroupID("비밀")
304 aliceGroup, err := CreateGroup(groupID, aliceKeyPairPkg)
305 if err != nil {
306 log.Fatalf("CreateGroup() = %v", err)
307 }
308
309 bobWelcome, addMemberMsg, err := aliceGroup.CreateWelcome([]KeyPackage{bobKeyPairPkg.Public})
310 if err != nil {
311 log.Fatalf("CreateWelcome() = %v", err)
312 }
313
314 if _, _, err := aliceGroup.UnmarshalAndProcessMessage(addMemberMsg); err != nil {
315 log.Fatalf("UnmarshalAndProcessMessage() = %v", err)
316 }
317
318 bobGroup, err := GroupFromWelcome(bobWelcome, bobKeyPairPkg)
319 if err != nil {
320 log.Fatalf("GroupFromWelcome() = %v", err)
321 }
322
323 data := []byte("안녕하세요")
324 appMsg, err := aliceGroup.CreateApplicationMessage(data)
325 if err != nil {
326 log.Fatalf("CreateApplicationMessage() = %v", err)
327 }
328
329 plaintext, _, err := bobGroup.UnmarshalAndProcessMessage(appMsg)
330 if err != nil {
331 log.Fatalf("UnmarshalAndProcessMessage() = %v", err)
332 }
333
334 fmt.Println(string(plaintext))
335 // Output: 안녕하세요
336 }
337