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