passive_client_test.go raw

   1  package mls
   2  
   3  import (
   4  	"bytes"
   5  	"fmt"
   6  	"testing"
   7  	"time"
   8  )
   9  
  10  type passiveClientTest struct {
  11  	CipherSuite CipherSuite `json:"cipher_suite"`
  12  
  13  	ExternalPSKs []struct {
  14  		PSKID testBytes `json:"psk_id"`
  15  		PSK   testBytes `json:"psk"`
  16  	} `json:"external_psks"`
  17  	KeyPackage     testBytes `json:"key_package"`
  18  	SignaturePriv  testBytes `json:"signature_priv"`
  19  	EncryptionPriv testBytes `json:"encryption_priv"`
  20  	InitPriv       testBytes `json:"init_priv"`
  21  
  22  	Welcome                   testBytes `json:"welcome"`
  23  	RatchetTree               testBytes `json:"ratchet_tree"`
  24  	InitialEpochAuthenticator testBytes `json:"initial_epoch_authenticator"`
  25  
  26  	Epochs []struct {
  27  		Proposals          []testBytes `json:"proposals"`
  28  		Commit             testBytes   `json:"commit"`
  29  		EpochAuthenticator testBytes   `json:"epoch_authenticator"`
  30  	} `json:"epochs"`
  31  }
  32  
  33  func testPassiveClient(t *testing.T, tc *passiveClientTest) {
  34  	cs := tc.CipherSuite
  35  	initPriv := []byte(tc.InitPriv)
  36  	encryptionPriv := []byte(tc.EncryptionPriv)
  37  	signaturePriv := []byte(tc.SignaturePriv)
  38  
  39  	if !cs.Supported() {
  40  		t.Skipf("unsupported cipher suite %v", cs)
  41  	}
  42  
  43  	welcome, err := UnmarshalWelcome(tc.Welcome)
  44  	if err != nil {
  45  		t.Fatalf("UnmarshalWelcome() = %v", err)
  46  	}
  47  	if welcome.cipherSuite != cs {
  48  		t.Fatalf("welcome.cipherSuite = %v, want %v", welcome.cipherSuite, cs)
  49  	}
  50  
  51  	keyPkg, err := UnmarshalKeyPackage([]byte(tc.KeyPackage))
  52  	if err != nil {
  53  		t.Fatalf("UnmarshalKeyPackage() = %v", err)
  54  	}
  55  	if keyPkg.cipherSuite != welcome.cipherSuite {
  56  		t.Fatalf("keyPkg.cipherSuite = %v, want %v", keyPkg.cipherSuite, welcome.cipherSuite)
  57  	}
  58  
  59  	if err := checkEncryptionKeyPair(cs, keyPkg.initKey, initPriv); err != nil {
  60  		t.Errorf("invalid init keypair: %v", err)
  61  	}
  62  	if err := checkEncryptionKeyPair(cs, keyPkg.leafNode.encryptionKey, encryptionPriv); err != nil {
  63  		t.Errorf("invalid encryption keypair: %v", err)
  64  	}
  65  	if err := checkSignatureKeyPair(cs, []byte(keyPkg.leafNode.signatureKey), signaturePriv); err != nil {
  66  		t.Errorf("invalid signature keypair: %v", err)
  67  	}
  68  
  69  	keyPkgRef, err := keyPkg.GenerateRef()
  70  	if err != nil {
  71  		t.Fatalf("keyPackage.generateRef() = %v", err)
  72  	}
  73  
  74  	groupSecrets, err := welcome.decryptGroupSecrets(keyPkgRef, initPriv)
  75  	if err != nil {
  76  		t.Fatalf("welcome.decryptGroupSecrets() = %v", err)
  77  	}
  78  
  79  	if !groupSecrets.verifySingleReinitOrBranchPSK() {
  80  		t.Errorf("groupSecrets.verifySingleReinitOrBranchPSK() failed")
  81  	}
  82  
  83  	var psks [][]byte
  84  	for _, pskID := range groupSecrets.psks {
  85  		if pskID.pskType != pskTypeExternal {
  86  			t.Fatalf("group secrets contain a non-external PSK ID")
  87  		}
  88  
  89  		found := false
  90  		for _, epsk := range tc.ExternalPSKs {
  91  			if bytes.Equal([]byte(epsk.PSKID), pskID.pskID) {
  92  				psks = append(psks, []byte(epsk.PSK))
  93  				found = true
  94  				break
  95  			}
  96  		}
  97  		if !found {
  98  			t.Fatalf("PSK ID %v not found", pskID.pskID)
  99  		}
 100  	}
 101  
 102  	keyPairPkg := KeyPairPackage{
 103  		Public: *keyPkg,
 104  		Private: PrivateKeyPackage{
 105  			InitKey:       initPriv,
 106  			EncryptionKey: encryptionPriv,
 107  			SignatureKey:  signaturePriv,
 108  		},
 109  	}
 110  	disableLifetimeCheck := func() time.Time { return time.Time{} }
 111  	group, err := groupFromSecrets(welcome, &keyPairPkg, groupSecrets, &groupFromSecretsOptions{
 112  		rawTree: []byte(tc.RatchetTree),
 113  		psks:    psks,
 114  		now:     disableLifetimeCheck,
 115  	})
 116  	if err != nil {
 117  		t.Errorf("groupFromWelcomeAndSecrets() = %v", err)
 118  	}
 119  
 120  	epochAuthenticator, err := cs.deriveSecret(group.epochSecret, secretLabelAuthentication)
 121  	if err != nil {
 122  		t.Errorf("deriveSecret(authentication) = %v", err)
 123  	} else if !bytes.Equal(epochAuthenticator, []byte(tc.InitialEpochAuthenticator)) {
 124  		t.Errorf("deriveSecret(authentication) = %v, want %v", epochAuthenticator, tc.InitialEpochAuthenticator)
 125  	}
 126  
 127  	for i, epoch := range tc.Epochs {
 128  		t.Logf("epoch %v", i)
 129  
 130  		for i, rawProposal := range epoch.Proposals {
 131  			if _, _, err := group.UnmarshalAndProcessMessage(rawProposal); err != nil {
 132  				t.Fatalf("UnmarshalAndProcessMessage(proposal[%v]) = %v", i, err)
 133  			}
 134  		}
 135  
 136  		var msg mlsMessage
 137  		if err := unmarshal([]byte(epoch.Commit), &msg); err != nil {
 138  			t.Fatalf("unmarshal(commit) = %v", err)
 139  		} else if msg.wireFormat != wireFormatMLSPublicMessage {
 140  			t.Fatalf("TODO: wireFormat = %v", msg.wireFormat)
 141  		}
 142  		pubMsg := msg.publicMessage
 143  
 144  		authContent, err := group.verifyPublicMessage(pubMsg)
 145  		if err != nil {
 146  			t.Errorf("verifyPublicMessage() = %v", err)
 147  		}
 148  		senderLeafIndex := pubMsg.content.sender.leafIndex
 149  
 150  		if authContent.content.contentType != contentTypeCommit {
 151  			t.Errorf("contentType = %v, want %v", authContent.content.contentType, contentTypeCommit)
 152  		}
 153  		commit := authContent.content.commit
 154  
 155  		proposals, _, err := resolveProposals(commit.proposals, senderLeafIndex, group.pendingProposals)
 156  		for _, prop := range proposals {
 157  			switch prop.proposalType {
 158  			case proposalTypeAdd, proposalTypeRemove, proposalTypeUpdate:
 159  				// handled by ratchetTree.apply
 160  			case proposalTypePSK:
 161  				// handled below
 162  			default:
 163  				t.Skipf("TODO: proposal type = %v", prop.proposalType)
 164  			}
 165  		}
 166  
 167  		var (
 168  			pskIDs []preSharedKeyID
 169  			psks   [][]byte
 170  		)
 171  		for _, prop := range proposals {
 172  			if prop.proposalType != proposalTypePSK {
 173  				continue
 174  			}
 175  
 176  			pskID := prop.preSharedKey.psk
 177  			if pskID.pskType != pskTypeExternal {
 178  				t.Skipf("TODO: PSK ID type = %v", pskID.pskType)
 179  			}
 180  
 181  			found := false
 182  			for _, epsk := range tc.ExternalPSKs {
 183  				if bytes.Equal([]byte(epsk.PSKID), pskID.pskID) {
 184  					pskIDs = append(pskIDs, pskID)
 185  					psks = append(psks, []byte(epsk.PSK))
 186  					found = true
 187  					break
 188  				}
 189  			}
 190  			if !found {
 191  				t.Fatalf("PSK ID %v not found", pskID.pskID)
 192  			}
 193  		}
 194  
 195  		err = group.processCommit(authContent, pskIDs, psks, disableLifetimeCheck)
 196  		if err != nil {
 197  			t.Errorf("processCommit() = %v", err)
 198  		}
 199  	}
 200  }
 201  
 202  func checkEncryptionKeyPair(cs CipherSuite, pub, priv []byte) error {
 203  	wantPlaintext := []byte("foo")
 204  	label := []byte("bar")
 205  
 206  	kemOutput, ciphertext, err := cs.encryptWithLabel(pub, label, nil, wantPlaintext)
 207  	if err != nil {
 208  		return err
 209  	}
 210  
 211  	plaintext, err := cs.decryptWithLabel(priv, label, nil, kemOutput, ciphertext)
 212  	if err != nil {
 213  		return err
 214  	}
 215  
 216  	if !bytes.Equal(plaintext, wantPlaintext) {
 217  		return fmt.Errorf("got plaintext %v, want %v", plaintext, wantPlaintext)
 218  	}
 219  
 220  	return nil
 221  }
 222  
 223  func checkSignatureKeyPair(cs CipherSuite, pub, priv []byte) error {
 224  	content := []byte("foo")
 225  	label := []byte("bar")
 226  
 227  	signature, err := cs.signWithLabel(priv, label, content)
 228  	if err != nil {
 229  		return err
 230  	}
 231  
 232  	if !cs.verifyWithLabel(pub, label, content, signature) {
 233  		return fmt.Errorf("signature verification failed")
 234  	}
 235  
 236  	return nil
 237  }
 238  
 239  func TestPassiveClientWelcome(t *testing.T) {
 240  	var tests []passiveClientTest
 241  	loadTestVector(t, "testdata/passive-client-welcome.json", &tests)
 242  
 243  	for i, tc := range tests {
 244  		t.Run(fmt.Sprintf("[%v]", i), func(t *testing.T) {
 245  			testPassiveClient(t, &tc)
 246  		})
 247  	}
 248  }
 249  
 250  func TestPassiveClientCommit(t *testing.T) {
 251  	var tests []passiveClientTest
 252  	loadTestVector(t, "testdata/passive-client-handling-commit.json", &tests)
 253  
 254  	for i, tc := range tests {
 255  		t.Run(fmt.Sprintf("[%v]", i), func(t *testing.T) {
 256  			testPassiveClient(t, &tc)
 257  		})
 258  	}
 259  }
 260  
 261  func TestPassiveClientRandom(t *testing.T) {
 262  	var tests []passiveClientTest
 263  	loadTestVector(t, "testdata/passive-client-random.json", &tests)
 264  
 265  	for i, tc := range tests {
 266  		t.Run(fmt.Sprintf("[%v]", i), func(t *testing.T) {
 267  			testPassiveClient(t, &tc)
 268  		})
 269  	}
 270  }
 271