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