framing_crypto.mx raw
1 package mls
2
3 // MLS framing crypto operations (RFC 9420 ยง6).
4 // Sign/verify/encrypt/decrypt methods for message framing.
5
6 import "smesh.lol/web/common/jsbridge/subtle"
7
8 // --- Signing ---
9
10 func signAuthenticatedContent(cs CipherSuite, signKey signaturePrivateKey, wf wireFormat, content *framedContent, ctx *groupContext) (*authenticatedContent, error) {
11 authContent := authenticatedContent{
12 wireFormat: wf,
13 content: *content,
14 }
15 tbs := authContent.framedContentTBS(ctx)
16 sig, err := signFramedContent(cs, signKey, tbs)
17 if err != nil {
18 return nil, err
19 }
20 authContent.auth.signature = sig
21 return &authContent, nil
22 }
23
24 func signFramedContent(cs CipherSuite, signKey signaturePrivateKey, content *framedContentTBS) ([]byte, error) {
25 raw, err := marshalRaw(content)
26 if err != nil {
27 return nil, err
28 }
29 return cs.signWithLabel(signKey, []byte("FramedContentTBS"), raw)
30 }
31
32 func (authContent *authenticatedContent) verifySignature(verifKey signaturePublicKey, ctx *groupContext) bool {
33 raw, err := marshalRaw(authContent.framedContentTBS(ctx))
34 if err != nil {
35 return false
36 }
37 return ctx.cipherSuite.verifyWithLabel(verifKey, []byte("FramedContentTBS"), raw, authContent.auth.signature)
38 }
39
40 func (authContent *authenticatedContent) generateProposalRef(cs CipherSuite) (proposalRef, error) {
41 if authContent.content.contentType != contentTypeProposal {
42 panic("mls: not a proposal")
43 }
44 raw, err := marshalRaw(authContent)
45 if err != nil {
46 return nil, err
47 }
48 h, err := cs.refHash([]byte("MLS 1.0 Proposal Reference"), raw)
49 if err != nil {
50 return nil, err
51 }
52 return proposalRef(h), nil
53 }
54
55 // --- FramedContentAuthData ---
56
57 func (authData *framedContentAuthData) verifyConfirmationTag(cs CipherSuite, confirmationKey, confirmedTranscriptHash []byte) bool {
58 if len(authData.confirmationTag) == 0 {
59 return false
60 }
61 return cs.verifyMAC(confirmationKey, confirmedTranscriptHash, authData.confirmationTag)
62 }
63
64 func (authData *framedContentAuthData) verifySignature(cs CipherSuite, verifKey signaturePublicKey, content *framedContentTBS) bool {
65 raw, err := marshalRaw(content)
66 if err != nil {
67 return false
68 }
69 return cs.verifyWithLabel(verifKey, []byte("FramedContentTBS"), raw, authData.signature)
70 }
71
72 // --- PublicMessage ---
73
74 func signPublicMessage(cs CipherSuite, signKey signaturePrivateKey, content *framedContent, ctx *groupContext) (*publicMessage, error) {
75 authContent, err := signAuthenticatedContent(cs, signKey, wireFormatMLSPublicMessage, content, ctx)
76 if err != nil {
77 return nil, err
78 }
79 return &publicMessage{
80 content: authContent.content,
81 auth: authContent.auth,
82 }, nil
83 }
84
85 func (msg *publicMessage) signMembershipTag(cs CipherSuite, membershipKey []byte, ctx *groupContext) error {
86 if msg.content.sender.senderType != senderTypeMember {
87 return nil
88 }
89 raw, err := marshalRaw(msg.authenticatedContentTBM(ctx))
90 if err != nil {
91 return err
92 }
93 msg.membershipTag = cs.signMAC(membershipKey, raw)
94 return nil
95 }
96
97 func (msg *publicMessage) verifyMembershipTag(membershipKey []byte, ctx *groupContext) bool {
98 if msg.content.sender.senderType != senderTypeMember {
99 return true
100 }
101 raw, err := marshalRaw(msg.authenticatedContentTBM(ctx))
102 if err != nil {
103 return false
104 }
105 return ctx.cipherSuite.verifyMAC(membershipKey, raw, msg.membershipTag)
106 }
107
108 // --- PrivateMessage ---
109
110 func newSenderData(li leafIndex, generation uint32) (*senderData, error) {
111 data := senderData{
112 leafIndex: li,
113 generation: generation,
114 }
115 var rg [4]byte
116 subtle.RandomBytes(rg[:])
117 data.reuseGuard = rg
118 return &data, nil
119 }
120
121 func sampleCiphertext(cs CipherSuite, ciphertext []byte) []byte {
122 n := cs.ExtractSize()
123 if len(ciphertext) < n {
124 return ciphertext
125 }
126 return ciphertext[:n]
127 }
128
129 func expandSenderDataKey(cs CipherSuite, senderDataSecret, ciphertext []byte) ([]byte, error) {
130 sample := sampleCiphertext(cs, ciphertext)
131 return cs.expandWithLabel(senderDataSecret, []byte("key"), sample, uint16(cs.AEADKeySize()))
132 }
133
134 func expandSenderDataNonce(cs CipherSuite, senderDataSecret, ciphertext []byte) ([]byte, error) {
135 sample := sampleCiphertext(cs, ciphertext)
136 return cs.expandWithLabel(senderDataSecret, []byte("nonce"), sample, uint16(cs.AEADNonceSize()))
137 }
138
139 func derivePrivateMessageKeyAndNonce(cs CipherSuite, secret ratchetSecret, reuseGuard [4]byte) (key, nonce []byte, err error) {
140 key, err = secret.deriveKey(cs)
141 if err != nil {
142 return nil, nil, err
143 }
144 nonce, err = secret.deriveNonce(cs)
145 if err != nil {
146 return nil, nil, err
147 }
148 for i := range reuseGuard {
149 nonce[i] = nonce[i] ^ reuseGuard[i]
150 }
151 return key, nonce, nil
152 }
153
154 func signPrivateMessageContent(cs CipherSuite, signKey signaturePrivateKey, content *framedContent, ctx *groupContext) (*privateMessageContent, error) {
155 authContent, err := signAuthenticatedContent(cs, signKey, wireFormatMLSPrivateMessage, content, ctx)
156 if err != nil {
157 return nil, err
158 }
159 return &privateMessageContent{
160 applicationData: content.applicationData,
161 proposal: content.proposal,
162 commit: content.commit,
163 auth: authContent.auth,
164 }, nil
165 }
166
167 func encryptPrivateMessageContent(cs CipherSuite, secret ratchetSecret, content *framedContent, privContent *privateMessageContent, reuseGuard [4]byte) ([]byte, error) {
168 var w Writer
169 privContent.marshal(&w, content.contentType)
170 plaintext, err := w.bytes()
171 if err != nil {
172 return nil, err
173 }
174
175 key, nonce, err := derivePrivateMessageKeyAndNonce(cs, secret, reuseGuard)
176 if err != nil {
177 return nil, err
178 }
179
180 aad := privateContentAAD{
181 groupID: content.groupID,
182 epoch: content.epoch,
183 contentType: content.contentType,
184 authenticatedData: content.authenticatedData,
185 }
186 rawAAD, err := marshalRaw(&aad)
187 if err != nil {
188 return nil, err
189 }
190
191 return cs.aeadSeal(key, nonce, plaintext, rawAAD)
192 }
193
194 func encryptSenderData(cs CipherSuite, senderDataSecret []byte, sd *senderData, content *framedContent, ciphertext []byte) ([]byte, error) {
195 key, err := expandSenderDataKey(cs, senderDataSecret, ciphertext)
196 if err != nil {
197 return nil, err
198 }
199 nonce, err := expandSenderDataNonce(cs, senderDataSecret, ciphertext)
200 if err != nil {
201 return nil, err
202 }
203
204 aad := senderDataAAD{
205 groupID: content.groupID,
206 epoch: content.epoch,
207 contentType: content.contentType,
208 }
209 rawAAD, err := marshalRaw(&aad)
210 if err != nil {
211 return nil, err
212 }
213
214 rawSD, err := marshalRaw(sd)
215 if err != nil {
216 return nil, err
217 }
218
219 return cs.aeadSeal(key, nonce, rawSD, rawAAD)
220 }
221
222 func encryptPrivateMessage(cs CipherSuite, secret ratchetSecret, senderDataSecret []byte, content *framedContent, privContent *privateMessageContent, sd *senderData) (*privateMessage, error) {
223 ct, err := encryptPrivateMessageContent(cs, secret, content, privContent, sd.reuseGuard)
224 if err != nil {
225 return nil, err
226 }
227 encSD, err := encryptSenderData(cs, senderDataSecret, sd, content, ct)
228 if err != nil {
229 return nil, err
230 }
231 return &privateMessage{
232 groupID: content.groupID,
233 epoch: content.epoch,
234 contentType: content.contentType,
235 authenticatedData: content.authenticatedData,
236 encryptedSenderData: encSD,
237 ciphertext: ct,
238 }, nil
239 }
240
241 func (msg *privateMessage) decryptSenderData(cs CipherSuite, senderDataSecret []byte) (*senderData, error) {
242 key, err := expandSenderDataKey(cs, senderDataSecret, msg.ciphertext)
243 if err != nil {
244 return nil, err
245 }
246 nonce, err := expandSenderDataNonce(cs, senderDataSecret, msg.ciphertext)
247 if err != nil {
248 return nil, err
249 }
250
251 aad := senderDataAAD{
252 groupID: msg.groupID,
253 epoch: msg.epoch,
254 contentType: msg.contentType,
255 }
256 rawAAD, err := marshalRaw(&aad)
257 if err != nil {
258 return nil, err
259 }
260
261 rawSD, err := cs.aeadOpen(key, nonce, msg.encryptedSenderData, rawAAD)
262 if err != nil {
263 return nil, err
264 }
265
266 var sd senderData
267 if err := unmarshalRaw(rawSD, &sd); err != nil {
268 return nil, err
269 }
270 return &sd, nil
271 }
272
273 func (msg *privateMessage) decryptContent(cs CipherSuite, secret ratchetSecret, reuseGuard [4]byte) (*privateMessageContent, error) {
274 key, nonce, err := derivePrivateMessageKeyAndNonce(cs, secret, reuseGuard)
275 if err != nil {
276 return nil, err
277 }
278
279 aad := privateContentAAD{
280 groupID: msg.groupID,
281 epoch: msg.epoch,
282 contentType: msg.contentType,
283 authenticatedData: msg.authenticatedData,
284 }
285 rawAAD, err := marshalRaw(&aad)
286 if err != nil {
287 return nil, err
288 }
289
290 rawContent, err := cs.aeadOpen(key, nonce, msg.ciphertext, rawAAD)
291 if err != nil {
292 return nil, err
293 }
294
295 r := newReader(rawContent)
296 var content privateMessageContent
297 if err := content.unmarshal(&r, msg.contentType); err != nil {
298 return nil, err
299 }
300
301 // Verify remaining bytes are zero-padding
302 for !r.empty() {
303 b, _ := r.readByte()
304 if b != 0 {
305 return nil, errNonZeroPadding
306 }
307 }
308
309 return &content, nil
310 }
311