framing.mx raw

   1  package mls
   2  
   3  // MLS framing types (RFC 9420 ยง6).
   4  // Pure serialization - crypto operations (sign/encrypt/decrypt) are
   5  // in framing_crypto.mx once the cipher suite layer is available.
   6  
   7  var (
   8  	errInvalidContentType error
   9  	errInvalidSenderType  error
  10  	errInvalidWireFormat  error
  11  	errInvalidVersion     error
  12  	errNonZeroPadding     error
  13  )
  14  
  15  // --- Protocol version ---
  16  
  17  type protocolVersion uint16
  18  
  19  const (
  20  	protocolVersionMLS10 protocolVersion = 1
  21  )
  22  
  23  // --- Content type ---
  24  
  25  type contentType uint8
  26  
  27  const (
  28  	contentTypeApplication contentType = 1
  29  	contentTypeProposal    contentType = 2
  30  	contentTypeCommit      contentType = 3
  31  )
  32  
  33  func (ct *contentType) unmarshal(r *Reader) (err error) {
  34  	b, ok := r.readByte()
  35  	if !ok {
  36  		return errUnexpectedEOF
  37  	}
  38  	*ct = contentType(b)
  39  	switch *ct {
  40  	case contentTypeApplication, contentTypeProposal, contentTypeCommit:
  41  		return nil
  42  	default:
  43  		return errInvalidContentType
  44  	}
  45  }
  46  
  47  func (ct *contentType) marshal(w *Writer) {
  48  	w.addByte(byte(ct))
  49  }
  50  
  51  // --- Sender type ---
  52  
  53  type senderType uint8
  54  
  55  const (
  56  	senderTypeMember            senderType = 1
  57  	senderTypeExternal          senderType = 2
  58  	senderTypeNewMemberProposal senderType = 3
  59  	senderTypeNewMemberCommit   senderType = 4
  60  )
  61  
  62  func (st *senderType) unmarshal(r *Reader) (err error) {
  63  	b, ok := r.readByte()
  64  	if !ok {
  65  		return errUnexpectedEOF
  66  	}
  67  	*st = senderType(b)
  68  	switch *st {
  69  	case senderTypeMember, senderTypeExternal, senderTypeNewMemberProposal, senderTypeNewMemberCommit:
  70  		return nil
  71  	default:
  72  		return errInvalidSenderType
  73  	}
  74  }
  75  
  76  func (st *senderType) marshal(w *Writer) {
  77  	w.addByte(byte(st))
  78  }
  79  
  80  // --- Sender ---
  81  
  82  type sender struct {
  83  	senderType  senderType
  84  	leafIndex   leafIndex // for senderTypeMember
  85  	senderIndex uint32    // for senderTypeExternal
  86  }
  87  
  88  func (snd *sender) unmarshal(r *Reader) (err error) {
  89  	*snd = sender{}
  90  	if e := snd.senderType.unmarshal(r); e != nil {
  91  		return e
  92  	}
  93  	switch snd.senderType {
  94  	case senderTypeMember:
  95  		v, ok := r.readUint32()
  96  		if !ok {
  97  			return errUnexpectedEOF
  98  		}
  99  		snd.leafIndex = leafIndex(v)
 100  	case senderTypeExternal:
 101  		v, ok := r.readUint32()
 102  		if !ok {
 103  			return errUnexpectedEOF
 104  		}
 105  		snd.senderIndex = v
 106  	}
 107  	return nil
 108  }
 109  
 110  func (snd *sender) marshal(w *Writer) {
 111  	snd.senderType.marshal(w)
 112  	switch snd.senderType {
 113  	case senderTypeMember:
 114  		w.addUint32(uint32(snd.leafIndex))
 115  	case senderTypeExternal:
 116  		w.addUint32(snd.senderIndex)
 117  	}
 118  }
 119  
 120  // --- Wire format ---
 121  
 122  type wireFormat uint16
 123  
 124  // http://www.iana.org/assignments/mls/mls.xhtml#mls-wire-formats
 125  const (
 126  	wireFormatMLSPublicMessage  wireFormat = 0x0001
 127  	wireFormatMLSPrivateMessage wireFormat = 0x0002
 128  	wireFormatMLSWelcome        wireFormat = 0x0003
 129  	wireFormatMLSGroupInfo      wireFormat = 0x0004
 130  	wireFormatMLSKeyPackage     wireFormat = 0x0005
 131  )
 132  
 133  func (wf *wireFormat) unmarshal(r *Reader) (err error) {
 134  	v, ok := r.readUint16()
 135  	if !ok {
 136  		return errUnexpectedEOF
 137  	}
 138  	*wf = wireFormat(v)
 139  	switch *wf {
 140  	case wireFormatMLSPublicMessage, wireFormatMLSPrivateMessage,
 141  		wireFormatMLSWelcome, wireFormatMLSGroupInfo, wireFormatMLSKeyPackage:
 142  		return nil
 143  	default:
 144  		return errInvalidWireFormat
 145  	}
 146  }
 147  
 148  func (wf *wireFormat) marshal(w *Writer) {
 149  	w.addUint16(uint16(wf))
 150  }
 151  
 152  // --- Group identifier ---
 153  
 154  // groupIDEqual reports whether two application-specific group identifiers
 155  // are equal.
 156  func groupIDEqual(ref []byte, other []byte) (ok bool) {
 157  	if len(ref) != len(other) {
 158  		return false
 159  	}
 160  	for i := range ref {
 161  		if ref[i] != other[i] {
 162  			return false
 163  		}
 164  	}
 165  	return true
 166  }
 167  
 168  // --- FramedContent ---
 169  
 170  type framedContent struct {
 171  	groupID           []byte
 172  	epoch             uint64
 173  	sender            sender
 174  	authenticatedData []byte
 175  
 176  	contentType     contentType
 177  	applicationData []byte    // for contentTypeApplication
 178  	proposal        *proposal // for contentTypeProposal
 179  	commit          *commit   // for contentTypeCommit
 180  }
 181  
 182  func (content *framedContent) unmarshal(r *Reader) (err error) {
 183  	*content = framedContent{}
 184  
 185  	var ok bool
 186  	content.groupID, ok = r.readOpaqueVec()
 187  	if !ok {
 188  		return errUnexpectedEOF
 189  	}
 190  	content.epoch, ok = r.readUint64()
 191  	if !ok {
 192  		return errUnexpectedEOF
 193  	}
 194  	if e := content.sender.unmarshal(r); e != nil {
 195  		return e
 196  	}
 197  	content.authenticatedData, ok = r.readOpaqueVec()
 198  	if !ok {
 199  		return errUnexpectedEOF
 200  	}
 201  	if e := content.contentType.unmarshal(r); e != nil {
 202  		return e
 203  	}
 204  
 205  	switch content.contentType {
 206  	case contentTypeApplication:
 207  		content.applicationData, ok = r.readOpaqueVec()
 208  		if !ok {
 209  			return errUnexpectedEOF
 210  		}
 211  		return nil
 212  	case contentTypeProposal:
 213  		content.proposal = &proposal{}
 214  		return content.proposal.unmarshal(r)
 215  	case contentTypeCommit:
 216  		content.commit = &commit{}
 217  		return content.commit.unmarshal(r)
 218  	default:
 219  		panic("unreachable")
 220  	}
 221  }
 222  
 223  func (content *framedContent) marshal(w *Writer) {
 224  	w.writeOpaqueVec([]byte(content.groupID))
 225  	w.addUint64(content.epoch)
 226  	content.sender.marshal(w)
 227  	w.writeOpaqueVec(content.authenticatedData)
 228  	content.contentType.marshal(w)
 229  	switch content.contentType {
 230  	case contentTypeApplication:
 231  		w.writeOpaqueVec(content.applicationData)
 232  	case contentTypeProposal:
 233  		content.proposal.marshal(w)
 234  	case contentTypeCommit:
 235  		content.commit.marshal(w)
 236  	default:
 237  		panic("unreachable")
 238  	}
 239  }
 240  
 241  // --- MLSMessage (top-level wire message) ---
 242  
 243  type mlsMessage struct {
 244  	version        protocolVersion
 245  	wireFormat     wireFormat
 246  	publicMessage  *publicMessage  // for wireFormatMLSPublicMessage
 247  	privateMessage *privateMessage // for wireFormatMLSPrivateMessage
 248  	welcome        *Welcome        // for wireFormatMLSWelcome
 249  	groupInfo      *groupInfo      // for wireFormatMLSGroupInfo
 250  	keyPackage     *KeyPackage     // for wireFormatMLSKeyPackage
 251  }
 252  
 253  func (msg *mlsMessage) unmarshal(r *Reader) (err error) {
 254  	*msg = mlsMessage{}
 255  
 256  	v, ok := r.readUint16()
 257  	if !ok {
 258  		return errUnexpectedEOF
 259  	}
 260  	msg.version = protocolVersion(v)
 261  	if msg.version != protocolVersionMLS10 {
 262  		return errInvalidVersion
 263  	}
 264  
 265  	if e := msg.wireFormat.unmarshal(r); e != nil {
 266  		return e
 267  	}
 268  
 269  	switch msg.wireFormat {
 270  	case wireFormatMLSPublicMessage:
 271  		msg.publicMessage = &publicMessage{}
 272  		return msg.publicMessage.unmarshal(r)
 273  	case wireFormatMLSPrivateMessage:
 274  		msg.privateMessage = &privateMessage{}
 275  		return msg.privateMessage.unmarshal(r)
 276  	case wireFormatMLSWelcome:
 277  		msg.welcome = &Welcome{}
 278  		return msg.welcome.unmarshal(r)
 279  	case wireFormatMLSGroupInfo:
 280  		msg.groupInfo = &groupInfo{}
 281  		return msg.groupInfo.unmarshal(r)
 282  	case wireFormatMLSKeyPackage:
 283  		msg.keyPackage = &KeyPackage{}
 284  		return msg.keyPackage.unmarshal(r)
 285  	default:
 286  		panic("unreachable")
 287  	}
 288  }
 289  
 290  func (msg *mlsMessage) marshal(w *Writer) {
 291  	w.addUint16(uint16(msg.version))
 292  	msg.wireFormat.marshal(w)
 293  	switch msg.wireFormat {
 294  	case wireFormatMLSPublicMessage:
 295  		msg.publicMessage.marshal(w)
 296  	case wireFormatMLSPrivateMessage:
 297  		msg.privateMessage.marshal(w)
 298  	case wireFormatMLSWelcome:
 299  		msg.welcome.marshal(w)
 300  	case wireFormatMLSGroupInfo:
 301  		msg.groupInfo.marshal(w)
 302  	case wireFormatMLSKeyPackage:
 303  		msg.keyPackage.marshal(w)
 304  	default:
 305  		panic("unreachable")
 306  	}
 307  }
 308  
 309  // --- FramedContentAuthData ---
 310  
 311  type framedContentAuthData struct {
 312  	signature       []byte
 313  	confirmationTag []byte // for contentTypeCommit
 314  }
 315  
 316  func (authData *framedContentAuthData) unmarshal(r *Reader, ct contentType) (err error) {
 317  	*authData = framedContentAuthData{}
 318  
 319  	var ok bool
 320  	authData.signature, ok = r.readOpaqueVec()
 321  	if !ok {
 322  		return errUnexpectedEOF
 323  	}
 324  	if ct == contentTypeCommit {
 325  		authData.confirmationTag, ok = r.readOpaqueVec()
 326  		if !ok {
 327  			return errUnexpectedEOF
 328  		}
 329  	}
 330  	return nil
 331  }
 332  
 333  func (authData *framedContentAuthData) marshal(w *Writer, ct contentType) {
 334  	w.writeOpaqueVec(authData.signature)
 335  	if ct == contentTypeCommit {
 336  		w.writeOpaqueVec(authData.confirmationTag)
 337  	}
 338  }
 339  
 340  // --- AuthenticatedContent ---
 341  
 342  type authenticatedContent struct {
 343  	wireFormat wireFormat
 344  	content    framedContent
 345  	auth       framedContentAuthData
 346  }
 347  
 348  func (authContent *authenticatedContent) unmarshal(r *Reader) (err error) {
 349  	if e := authContent.wireFormat.unmarshal(r); e != nil {
 350  		return e
 351  	}
 352  	if e := authContent.content.unmarshal(r); e != nil {
 353  		return e
 354  	}
 355  	return authContent.auth.unmarshal(r, authContent.content.contentType)
 356  }
 357  
 358  func (authContent *authenticatedContent) marshal(w *Writer) {
 359  	authContent.wireFormat.marshal(w)
 360  	authContent.content.marshal(w)
 361  	authContent.auth.marshal(w, authContent.content.contentType)
 362  }
 363  
 364  func (authContent *authenticatedContent) confirmedTranscriptHashInput() (p *confirmedTranscriptHashInput) {
 365  	return &confirmedTranscriptHashInput{
 366  		wireFormat: authContent.wireFormat,
 367  		content:    authContent.content,
 368  		signature:  authContent.auth.signature,
 369  	}
 370  }
 371  
 372  func (authContent *authenticatedContent) framedContentTBS(ctx *groupContext) (p *framedContentTBS) {
 373  	return &framedContentTBS{
 374  		version:    protocolVersionMLS10,
 375  		wireFormat: authContent.wireFormat,
 376  		content:    authContent.content,
 377  		context:    ctx,
 378  	}
 379  }
 380  
 381  // --- FramedContentTBS (to-be-signed) ---
 382  
 383  type framedContentTBS struct {
 384  	version    protocolVersion
 385  	wireFormat wireFormat
 386  	content    framedContent
 387  	context    *groupContext // for senderTypeMember and senderTypeNewMemberCommit
 388  }
 389  
 390  func (content *framedContentTBS) marshal(w *Writer) {
 391  	w.addUint16(uint16(content.version))
 392  	content.wireFormat.marshal(w)
 393  	content.content.marshal(w)
 394  	switch content.content.sender.senderType {
 395  	case senderTypeMember, senderTypeNewMemberCommit:
 396  		content.context.marshal(w)
 397  	}
 398  }
 399  
 400  // --- PublicMessage ---
 401  
 402  type publicMessage struct {
 403  	content       framedContent
 404  	auth          framedContentAuthData
 405  	membershipTag []byte // for senderTypeMember
 406  }
 407  
 408  func (msg *publicMessage) unmarshal(r *Reader) (err error) {
 409  	*msg = publicMessage{}
 410  
 411  	if e := msg.content.unmarshal(r); e != nil {
 412  		return e
 413  	}
 414  	if e := msg.auth.unmarshal(r, msg.content.contentType); e != nil {
 415  		return e
 416  	}
 417  
 418  	if msg.content.sender.senderType == senderTypeMember {
 419  		var ok bool
 420  		msg.membershipTag, ok = r.readOpaqueVec()
 421  		if !ok {
 422  			return errUnexpectedEOF
 423  		}
 424  	}
 425  	return nil
 426  }
 427  
 428  func (msg *publicMessage) marshal(w *Writer) {
 429  	msg.content.marshal(w)
 430  	msg.auth.marshal(w, msg.content.contentType)
 431  	if msg.content.sender.senderType == senderTypeMember {
 432  		w.writeOpaqueVec(msg.membershipTag)
 433  	}
 434  }
 435  
 436  func (msg *publicMessage) authenticatedContent() (p *authenticatedContent) {
 437  	return &authenticatedContent{
 438  		wireFormat: wireFormatMLSPublicMessage,
 439  		content:    msg.content,
 440  		auth:       msg.auth,
 441  	}
 442  }
 443  
 444  func (msg *publicMessage) authenticatedContentTBM(ctx *groupContext) (p *authenticatedContentTBM) {
 445  	return &authenticatedContentTBM{
 446  		contentTBS: *msg.authenticatedContent().framedContentTBS(ctx),
 447  		auth:       msg.auth,
 448  	}
 449  }
 450  
 451  // --- AuthenticatedContentTBM (to-be-MACed) ---
 452  
 453  type authenticatedContentTBM struct {
 454  	contentTBS framedContentTBS
 455  	auth       framedContentAuthData
 456  }
 457  
 458  func (tbm *authenticatedContentTBM) marshal(w *Writer) {
 459  	tbm.contentTBS.marshal(w)
 460  	tbm.auth.marshal(w, tbm.contentTBS.content.contentType)
 461  }
 462  
 463  // --- PrivateMessage ---
 464  
 465  type privateMessage struct {
 466  	groupID             []byte
 467  	epoch               uint64
 468  	contentType         contentType
 469  	authenticatedData   []byte
 470  	encryptedSenderData []byte
 471  	ciphertext          []byte
 472  }
 473  
 474  func (msg *privateMessage) unmarshal(r *Reader) (err error) {
 475  	*msg = privateMessage{}
 476  
 477  	var ok bool
 478  	msg.groupID, ok = r.readOpaqueVec()
 479  	if !ok {
 480  		return errUnexpectedEOF
 481  	}
 482  	msg.epoch, ok = r.readUint64()
 483  	if !ok {
 484  		return errUnexpectedEOF
 485  	}
 486  	if e := msg.contentType.unmarshal(r); e != nil {
 487  		return e
 488  	}
 489  	msg.authenticatedData, ok = r.readOpaqueVec()
 490  	if !ok {
 491  		return errUnexpectedEOF
 492  	}
 493  	msg.encryptedSenderData, ok = r.readOpaqueVec()
 494  	if !ok {
 495  		return errUnexpectedEOF
 496  	}
 497  	msg.ciphertext, ok = r.readOpaqueVec()
 498  	if !ok {
 499  		return errUnexpectedEOF
 500  	}
 501  	return nil
 502  }
 503  
 504  func (msg *privateMessage) marshal(w *Writer) {
 505  	w.writeOpaqueVec([]byte(msg.groupID))
 506  	w.addUint64(msg.epoch)
 507  	msg.contentType.marshal(w)
 508  	w.writeOpaqueVec(msg.authenticatedData)
 509  	w.writeOpaqueVec(msg.encryptedSenderData)
 510  	w.writeOpaqueVec(msg.ciphertext)
 511  }
 512  
 513  func (msg *privateMessage) authenticatedContent(sd *senderData, content *privateMessageContent) (p *authenticatedContent) {
 514  	return content.authenticatedContent(&framedContent{
 515  		groupID: msg.groupID,
 516  		epoch:   msg.epoch,
 517  		sender: sender{
 518  			senderType: senderTypeMember,
 519  			leafIndex:  sd.leafIndex,
 520  		},
 521  		authenticatedData: msg.authenticatedData,
 522  		contentType:       msg.contentType,
 523  		applicationData:   content.applicationData,
 524  		proposal:          content.proposal,
 525  		commit:            content.commit,
 526  	})
 527  }
 528  
 529  // --- Sender data AAD ---
 530  
 531  type senderDataAAD struct {
 532  	groupID     []byte
 533  	epoch       uint64
 534  	contentType contentType
 535  }
 536  
 537  func (aad *senderDataAAD) marshal(w *Writer) {
 538  	w.writeOpaqueVec([]byte(aad.groupID))
 539  	w.addUint64(aad.epoch)
 540  	aad.contentType.marshal(w)
 541  }
 542  
 543  // --- Private content AAD ---
 544  
 545  type privateContentAAD struct {
 546  	groupID           []byte
 547  	epoch             uint64
 548  	contentType       contentType
 549  	authenticatedData []byte
 550  }
 551  
 552  func (aad *privateContentAAD) marshal(w *Writer) {
 553  	w.writeOpaqueVec([]byte(aad.groupID))
 554  	w.addUint64(aad.epoch)
 555  	aad.contentType.marshal(w)
 556  	w.writeOpaqueVec(aad.authenticatedData)
 557  }
 558  
 559  // --- PrivateMessageContent ---
 560  
 561  type privateMessageContent struct {
 562  	applicationData []byte    // for contentTypeApplication
 563  	proposal        *proposal // for contentTypeProposal
 564  	commit          *commit   // for contentTypeCommit
 565  
 566  	auth framedContentAuthData
 567  }
 568  
 569  func (content *privateMessageContent) unmarshal(r *Reader, ct contentType) (err error) {
 570  	*content = privateMessageContent{}
 571  
 572  	var err error
 573  	switch ct {
 574  	case contentTypeApplication:
 575  		var ok bool
 576  		content.applicationData, ok = r.readOpaqueVec()
 577  		if !ok {
 578  			err = errUnexpectedEOF
 579  		}
 580  	case contentTypeProposal:
 581  		content.proposal = &proposal{}
 582  		err = content.proposal.unmarshal(r)
 583  	case contentTypeCommit:
 584  		content.commit = &commit{}
 585  		err = content.commit.unmarshal(r)
 586  	default:
 587  		panic("unreachable")
 588  	}
 589  	if err != nil {
 590  		return err
 591  	}
 592  	return content.auth.unmarshal(r, ct)
 593  }
 594  
 595  func (content *privateMessageContent) marshal(w *Writer, ct contentType) {
 596  	switch ct {
 597  	case contentTypeApplication:
 598  		w.writeOpaqueVec(content.applicationData)
 599  	case contentTypeProposal:
 600  		content.proposal.marshal(w)
 601  	case contentTypeCommit:
 602  		content.commit.marshal(w)
 603  	default:
 604  		panic("unreachable")
 605  	}
 606  	content.auth.marshal(w, ct)
 607  }
 608  
 609  func (content *privateMessageContent) authenticatedContent(fc *framedContent) (p *authenticatedContent) {
 610  	return &authenticatedContent{
 611  		wireFormat: wireFormatMLSPrivateMessage,
 612  		content:    *fc,
 613  		auth:       content.auth,
 614  	}
 615  }
 616  
 617  // --- SenderData ---
 618  
 619  type senderData struct {
 620  	leafIndex  leafIndex
 621  	generation uint32
 622  	reuseGuard [4]byte
 623  }
 624  
 625  func (data *senderData) unmarshal(r *Reader) (err error) {
 626  	v, ok := r.readUint32()
 627  	if !ok {
 628  		return errUnexpectedEOF
 629  	}
 630  	data.leafIndex = leafIndex(v)
 631  	data.generation, ok = r.readUint32()
 632  	if !ok {
 633  		return errUnexpectedEOF
 634  	}
 635  	guard, ok := r.readN(4)
 636  	if !ok {
 637  		return errUnexpectedEOF
 638  	}
 639  	data.reuseGuard[0] = guard[0]
 640  	data.reuseGuard[1] = guard[1]
 641  	data.reuseGuard[2] = guard[2]
 642  	data.reuseGuard[3] = guard[3]
 643  	return nil
 644  }
 645  
 646  func (data *senderData) marshal(w *Writer) {
 647  	w.addUint32(uint32(data.leafIndex))
 648  	w.addUint32(data.generation)
 649  	w.addBytes(data.reuseGuard[:])
 650  }
 651