Просмотр исходного кода

Merge pull request #293 from rod-hynes/master

Remove propagation channel ID from SLOK key derivation
Rod Hynes 9 лет назад
Родитель
Сommit
8b1ad2d943
3 измененных файлов с 110 добавлено и 140 удалено
  1. 66 78
      psiphon/common/osl/osl.go
  2. 44 61
      psiphon/common/osl/osl_test.go
  3. 0 1
      psiphon/remoteServerList_test.go

+ 66 - 78
psiphon/common/osl/osl.go

@@ -84,11 +84,12 @@ type Scheme struct {
 	Epoch string
 
 	// Regions is a list of client country codes this scheme applies to.
+	// If empty, the scheme applies to all regions.
 	Regions []string
 
-	// PropagationChannelIDs is a list of client propagtion channel IDs
-	// this scheme applies to. Propagation channel IDs are an input
-	// to SLOK key derivation.
+	// PropagationChannelIDs is a list of client propagation channel IDs
+	// this scheme applies to.
+	// If empty, the scheme applies to all propagation channels IDs.
 	PropagationChannelIDs []string
 
 	// MasterKey is the base random key used for SLOK key derivation. It
@@ -188,14 +189,13 @@ type KeySplit struct {
 
 // ClientSeedState tracks the progress of a client towards seeding SLOKs.
 type ClientSeedState struct {
-	scheme               *Scheme
-	propagationChannelID string
-	signalIssueSLOKs     chan struct{}
-	progress             []*TrafficValues
-	progressSLOKTime     int64
-	mutex                sync.Mutex
-	issuedSLOKs          map[string]*SLOK
-	payloadSLOKs         []*SLOK
+	scheme           *Scheme
+	signalIssueSLOKs chan struct{}
+	progress         []*TrafficValues
+	progressSLOKTime int64
+	mutex            sync.Mutex
+	issuedSLOKs      map[string]*SLOK
+	payloadSLOKs     []*SLOK
 }
 
 // ClientSeedPortForward map a client port forward, which is relaying
@@ -213,9 +213,8 @@ type ClientSeedPortForward struct {
 // used to derive the SLOK secret key and ID.
 // Note: SeedSpecID is not a []byte as slokReference is used as a map key.
 type slokReference struct {
-	PropagationChannelID string
-	SeedSpecID           string
-	Time                 time.Time
+	SeedSpecID string
+	Time       time.Time
 }
 
 // SLOK is a seeded SLOK issued to a client. The client will store the
@@ -335,6 +334,10 @@ func LoadConfig(configJSON []byte) (*Config, error) {
 // client progress towards seeding SLOKs. psiphond maintains one
 // ClientSeedState for each connected client.
 //
+// NewClientSeedState selects the first Scheme in config.Schemes
+// with an active Epoch and which permits the client's region and
+// propagation channel ID. Only the first matching scheme is selected.
+//
 // A signal is sent on signalIssueSLOKs when sufficient progress
 // has been made that a new SLOK *may* be issued. psiphond will
 // receive the signal and then call GetClientSeedPayload/IssueSLOKs
@@ -354,8 +357,10 @@ func (config *Config) NewClientSeedState(
 		// schemes with many propagation channel IDs or region filters, use
 		// maps for more efficient lookup.
 		if scheme.epoch.Before(time.Now().UTC()) &&
-			common.Contains(scheme.PropagationChannelIDs, propagationChannelID) &&
-			(len(scheme.Regions) == 0 || common.Contains(scheme.Regions, clientRegion)) {
+			(len(scheme.PropagationChannelIDs) == 0 ||
+				common.Contains(scheme.PropagationChannelIDs, propagationChannelID)) &&
+			(len(scheme.Regions) == 0 ||
+				common.Contains(scheme.Regions, clientRegion)) {
 
 			// Empty progress is initialized up front for all seed specs. Once
 			// created, the progress structure is read-only (the slice, not the
@@ -366,13 +371,12 @@ func (config *Config) NewClientSeedState(
 			}
 
 			return &ClientSeedState{
-				scheme:               scheme,
-				propagationChannelID: propagationChannelID,
-				signalIssueSLOKs:     signalIssueSLOKs,
-				progressSLOKTime:     getSLOKTime(scheme.SeedPeriodNanoseconds),
-				progress:             progress,
-				issuedSLOKs:          make(map[string]*SLOK),
-				payloadSLOKs:         nil,
+				scheme:           scheme,
+				signalIssueSLOKs: signalIssueSLOKs,
+				progressSLOKTime: getSLOKTime(scheme.SeedPeriodNanoseconds),
+				progress:         progress,
+				issuedSLOKs:      make(map[string]*SLOK),
+				payloadSLOKs:     nil,
 			}
 		}
 	}
@@ -528,9 +532,8 @@ func (state *ClientSeedState) issueSLOKs() {
 		if progress.exceeds(&seedSpec.Targets) {
 
 			ref := &slokReference{
-				PropagationChannelID: state.propagationChannelID,
-				SeedSpecID:           string(seedSpec.ID),
-				Time:                 progressSLOKTime,
+				SeedSpecID: string(seedSpec.ID),
+				Time:       progressSLOKTime,
 			}
 
 			state.scheme.derivedSLOKCacheMutex.RLock()
@@ -581,7 +584,6 @@ func deriveSLOK(
 
 	key := deriveKeyHKDF(
 		scheme.MasterKey,
-		[]byte(ref.PropagationChannelID),
 		[]byte(ref.SeedSpecID),
 		timeBytes)
 
@@ -681,8 +683,6 @@ type KeyShares struct {
 
 // Pave creates the full set of OSL files, for all schemes in the
 // configuration, to be dropped in an out-of-band distribution site.
-// Only OSLs for the propagation channel ID associated with the
-// distribution site are paved. This function is used by automation.
 //
 // The Name component of each file relates to the values returned by
 // the client functions GetRegistryURL and GetOSLFileURL.
@@ -701,7 +701,6 @@ type KeyShares struct {
 // to OSLs in the case where OSLs are repaved in subsequent calls.
 func (config *Config) Pave(
 	endTime time.Time,
-	propagationChannelID string,
 	signingPublicKey string,
 	signingPrivateKey string,
 	paveServerEntries []map[time.Time]string) ([]*PaveFile, error) {
@@ -724,51 +723,48 @@ func (config *Config) Pave(
 			slokTimePeriodsPerOSL *= keySplit.Total
 		}
 
-		if common.Contains(scheme.PropagationChannelIDs, propagationChannelID) {
-			oslTime := scheme.epoch
-			for !oslTime.After(endTime) {
-
-				firstSLOKTime := oslTime
-				fileKey, fileSpec, err := makeOSLFileSpec(
-					scheme, propagationChannelID, firstSLOKTime)
-				if err != nil {
-					return nil, common.ContextError(err)
-				}
+		oslTime := scheme.epoch
+		for !oslTime.After(endTime) {
 
-				registry.FileSpecs = append(registry.FileSpecs, fileSpec)
+			firstSLOKTime := oslTime
+			fileKey, fileSpec, err := makeOSLFileSpec(scheme, firstSLOKTime)
+			if err != nil {
+				return nil, common.ContextError(err)
+			}
 
-				serverEntries, ok := paveServerEntries[schemeIndex][oslTime]
-				if ok {
+			registry.FileSpecs = append(registry.FileSpecs, fileSpec)
 
-					signedServerEntries, err := common.WriteAuthenticatedDataPackage(
-						serverEntries,
-						signingPublicKey,
-						signingPrivateKey)
-					if err != nil {
-						return nil, common.ContextError(err)
-					}
+			serverEntries, ok := paveServerEntries[schemeIndex][oslTime]
+			if ok {
 
-					boxedServerEntries, err := box(fileKey, compress(signedServerEntries))
-					if err != nil {
-						return nil, common.ContextError(err)
-					}
+				signedServerEntries, err := common.WriteAuthenticatedDataPackage(
+					serverEntries,
+					signingPublicKey,
+					signingPrivateKey)
+				if err != nil {
+					return nil, common.ContextError(err)
+				}
 
-					md5sum := md5.Sum(boxedServerEntries)
-					fileSpec.MD5Sum = md5sum[:]
+				boxedServerEntries, err := box(fileKey, compress(signedServerEntries))
+				if err != nil {
+					return nil, common.ContextError(err)
+				}
 
-					fileName := fmt.Sprintf(
-						OSL_FILENAME_FORMAT, hex.EncodeToString(fileSpec.ID))
+				md5sum := md5.Sum(boxedServerEntries)
+				fileSpec.MD5Sum = md5sum[:]
 
-					paveFiles = append(paveFiles, &PaveFile{
-						Name:     fileName,
-						Contents: boxedServerEntries,
-					})
-				}
+				fileName := fmt.Sprintf(
+					OSL_FILENAME_FORMAT, hex.EncodeToString(fileSpec.ID))
 
-				oslTime = oslTime.Add(
-					time.Duration(
-						int64(slokTimePeriodsPerOSL) * scheme.SeedPeriodNanoseconds))
+				paveFiles = append(paveFiles, &PaveFile{
+					Name:     fileName,
+					Contents: boxedServerEntries,
+				})
 			}
+
+			oslTime = oslTime.Add(
+				time.Duration(
+					int64(slokTimePeriodsPerOSL) * scheme.SeedPeriodNanoseconds))
 		}
 	}
 
@@ -800,13 +796,11 @@ func (config *Config) Pave(
 // tree, given sufficient SLOKs.
 func makeOSLFileSpec(
 	scheme *Scheme,
-	propagationChannelID string,
 	firstSLOKTime time.Time) ([]byte, *OSLFileSpec, error) {
 
 	ref := &slokReference{
-		PropagationChannelID: propagationChannelID,
-		SeedSpecID:           string(scheme.SeedSpecs[0].ID),
-		Time:                 firstSLOKTime,
+		SeedSpecID: string(scheme.SeedSpecs[0].ID),
+		Time:       firstSLOKTime,
 	}
 	firstSLOK := deriveSLOK(scheme, ref)
 	oslID := firstSLOK.ID
@@ -820,7 +814,6 @@ func makeOSLFileSpec(
 		scheme,
 		fileKey,
 		scheme.SeedPeriodKeySplits,
-		propagationChannelID,
 		&firstSLOKTime)
 	if err != nil {
 		return nil, nil, common.ContextError(err)
@@ -839,7 +832,6 @@ func divideKey(
 	scheme *Scheme,
 	key []byte,
 	keySplits []KeySplit,
-	propagationChannelID string,
 	nextSLOKTime *time.Time) (*KeyShares, error) {
 
 	keySplitIndex := len(keySplits) - 1
@@ -863,7 +855,6 @@ func divideKey(
 				scheme,
 				shareKey,
 				keySplits[0:keySplitIndex],
-				propagationChannelID,
 				nextSLOKTime)
 			if err != nil {
 				return nil, common.ContextError(err)
@@ -873,7 +864,6 @@ func divideKey(
 			keyShare, err := divideKeyWithSeedSpecSLOKs(
 				scheme,
 				shareKey,
-				propagationChannelID,
 				nextSLOKTime)
 			if err != nil {
 				return nil, common.ContextError(err)
@@ -900,7 +890,6 @@ func divideKey(
 func divideKeyWithSeedSpecSLOKs(
 	scheme *Scheme,
 	key []byte,
-	propagationChannelID string,
 	nextSLOKTime *time.Time) (*KeyShares, error) {
 
 	var boxedShares [][]byte
@@ -915,9 +904,8 @@ func divideKeyWithSeedSpecSLOKs(
 	for index, seedSpec := range scheme.SeedSpecs {
 
 		ref := &slokReference{
-			PropagationChannelID: propagationChannelID,
-			SeedSpecID:           string(seedSpec.ID),
-			Time:                 *nextSLOKTime,
+			SeedSpecID: string(seedSpec.ID),
+			Time:       *nextSLOKTime,
 		}
 		slok := deriveSLOK(scheme, ref)
 

+ 44 - 61
psiphon/common/osl/osl_test.go

@@ -300,76 +300,62 @@ func TestOSL(t *testing.T) {
 		t.Fatalf("GenerateAuthenticatedDataPackageKeys failed: %s", err)
 	}
 
-	pavedDirectories := make(map[string][]byte)
-	pavedOSLFileContents := make(map[string]map[string][]byte)
+	files := make(map[string][]byte)
 
 	t.Run("pave OSLs", func(t *testing.T) {
 
 		// Pave sufficient OSLs to cover simulated elapsed time of all test cases.
 		endTime := epoch.Add(1000 * time.Millisecond)
 
-		// In actual deployment, paved files for each propagation channel ID
-		// are dropped in distinct distribution sites.
-		for _, propagationChannelID := range []string{
-			"2995DB0C968C59C4F23E87988D9C0D41",
-			"E742C25A6D8BA8C17F37E725FA628569",
-			"36F1CF2DF1250BF0C7BA0629CE3DC657"} {
+		// Dummy server entry payloads will be the OSL ID, which the following
+		// tests use to verify that the correct OSL file decrypts successfully.
+		paveServerEntries := make([]map[time.Time]string, len(config.Schemes))
+		for schemeIndex, scheme := range config.Schemes {
 
-			// Dummy server entry payloads will be the OSL ID, which the following
-			// tests use to verify that the correct OSL file decrypts successfully.
-			paveServerEntries := make([]map[time.Time]string, len(config.Schemes))
-			for schemeIndex, scheme := range config.Schemes {
+			paveServerEntries[schemeIndex] = make(map[time.Time]string)
 
-				paveServerEntries[schemeIndex] = make(map[time.Time]string)
-
-				slokTimePeriodsPerOSL := 1
-				for _, keySplit := range scheme.SeedPeriodKeySplits {
-					slokTimePeriodsPerOSL *= keySplit.Total
-				}
-
-				oslTime := scheme.epoch
-				for oslTime.Before(endTime) {
-					firstSLOKRef := &slokReference{
-						PropagationChannelID: propagationChannelID,
-						SeedSpecID:           string(scheme.SeedSpecs[0].ID),
-						Time:                 oslTime,
-					}
-					firstSLOK := deriveSLOK(scheme, firstSLOKRef)
-					oslID := firstSLOK.ID
-					paveServerEntries[schemeIndex][oslTime] =
-						base64.StdEncoding.EncodeToString(oslID)
-
-					oslTime = oslTime.Add(
-						time.Duration(
-							int64(slokTimePeriodsPerOSL) * scheme.SeedPeriodNanoseconds))
-				}
+			slokTimePeriodsPerOSL := 1
+			for _, keySplit := range scheme.SeedPeriodKeySplits {
+				slokTimePeriodsPerOSL *= keySplit.Total
 			}
 
-			paveFiles, err := config.Pave(
-				endTime,
-				propagationChannelID,
-				signingPublicKey,
-				signingPrivateKey,
-				paveServerEntries)
-			if err != nil {
-				t.Fatalf("Pave failed: %s", err)
+			oslTime := scheme.epoch
+			for oslTime.Before(endTime) {
+				firstSLOKRef := &slokReference{
+					SeedSpecID: string(scheme.SeedSpecs[0].ID),
+					Time:       oslTime,
+				}
+				firstSLOK := deriveSLOK(scheme, firstSLOKRef)
+				oslID := firstSLOK.ID
+				paveServerEntries[schemeIndex][oslTime] =
+					base64.StdEncoding.EncodeToString(oslID)
+
+				oslTime = oslTime.Add(
+					time.Duration(
+						int64(slokTimePeriodsPerOSL) * scheme.SeedPeriodNanoseconds))
 			}
+		}
 
-			// Check that the paved file name matches the name the client will look for.
-			if len(paveFiles) < 1 || paveFiles[len(paveFiles)-1].Name != GetOSLRegistryURL("") {
-				t.Fatalf("invalid registry pave file")
-			}
+		paveFiles, err := config.Pave(
+			endTime,
+			signingPublicKey,
+			signingPrivateKey,
+			paveServerEntries)
+		if err != nil {
+			t.Fatalf("Pave failed: %s", err)
+		}
 
-			pavedDirectories[propagationChannelID] = paveFiles[len(paveFiles)-1].Contents
+		// Check that the paved file name matches the name the client will look for.
+		if len(paveFiles) < 1 || paveFiles[len(paveFiles)-1].Name != GetOSLRegistryURL("") {
+			t.Fatalf("invalid registry pave file")
+		}
 
-			pavedOSLFileContents[propagationChannelID] = make(map[string][]byte)
-			for _, paveFile := range paveFiles[0:len(paveFiles)] {
-				pavedOSLFileContents[propagationChannelID][paveFile.Name] = paveFile.Contents
-			}
+		for _, paveFile := range paveFiles {
+			files[paveFile.Name] = paveFile.Contents
 		}
 	})
 
-	if len(pavedDirectories) != 3 {
+	if len(files) == 0 {
 		// Previous subtest failed. Following tests cannot be completed, so abort.
 		t.Fatalf("pave failed")
 	}
@@ -494,9 +480,8 @@ func TestOSL(t *testing.T) {
 					slok := deriveSLOK(
 						testCase.scheme,
 						&slokReference{
-							PropagationChannelID: testCase.propagationChannelID,
-							SeedSpecID:           string(testCase.scheme.SeedSpecs[seedSpecIndex].ID),
-							Time:                 epoch.Add(time.Duration(timePeriod) * time.Millisecond),
+							SeedSpecID: string(testCase.scheme.SeedSpecs[seedSpecIndex].ID),
+							Time:       epoch.Add(time.Duration(timePeriod) * time.Millisecond),
 						})
 
 					slokMap[string(slok.ID)] = slok.Key
@@ -512,13 +497,12 @@ func TestOSL(t *testing.T) {
 
 			checkRegistryStartTime := time.Now()
 
-			registry, _, err := UnpackRegistry(
-				pavedDirectories[testCase.propagationChannelID], signingPublicKey)
+			registry, _, err := UnpackRegistry(files[GetOSLRegistryURL("")], signingPublicKey)
 			if err != nil {
 				t.Fatalf("UnpackRegistry failed: %s", err)
 			}
 
-			t.Logf("registry size: %d", len(pavedDirectories[testCase.propagationChannelID]))
+			t.Logf("registry size: %d", len(files[GetOSLRegistryURL("")]))
 			t.Logf("registry OSL count: %d", len(registry.FileSpecs))
 
 			oslIDs := registry.GetSeededOSLIDs(
@@ -535,8 +519,7 @@ func TestOSL(t *testing.T) {
 			}
 
 			for _, oslID := range oslIDs {
-				oslFileContents, ok :=
-					pavedOSLFileContents[testCase.propagationChannelID][GetOSLFileURL("", oslID)]
+				oslFileContents, ok := files[GetOSLFileURL("", oslID)]
 				if !ok {
 					t.Fatalf("unknown OSL file name")
 				}

+ 0 - 1
psiphon/remoteServerList_test.go

@@ -139,7 +139,6 @@ func TestObfuscatedRemoteServerLists(t *testing.T) {
 
 	paveFiles, err := oslConfig.Pave(
 		epoch,
-		propagationChannelID,
 		signingPublicKey,
 		signingPrivateKey,
 		[]map[time.Time]string{