github.com/psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/common/osl/osl.go (about)

     1  /*
     2   * Copyright (c) 2016, Psiphon Inc.
     3   * All rights reserved.
     4   *
     5   * This program is free software: you can redistribute it and/or modify
     6   * it under the terms of the GNU General Public License as published by
     7   * the Free Software Foundation, either version 3 of the License, or
     8   * (at your option) any later version.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  // Package osl implements the Obfuscated Server List (OSL) mechanism. This
    21  // mechanism is a method of distributing server lists only to clients that
    22  // demonstrate certain behavioral traits. Clients are seeded with Server
    23  // List Obfuscation Keys (SLOKs) as they meet the configured criteria. These
    24  // keys are stored and later combined to assemble keys to decrypt out-of-band
    25  // distributed OSL files that contain server lists.
    26  //
    27  // This package contains the core routines used in psiphond (to track client
    28  // traits and issue SLOKs), clients (to manage SLOKs and decrypt OSLs), and
    29  // automation (to create OSLs for distribution).
    30  package osl
    31  
    32  import (
    33  	"crypto/aes"
    34  	"crypto/cipher"
    35  	"crypto/hmac"
    36  	"crypto/md5"
    37  	"crypto/sha256"
    38  	"encoding/base64"
    39  	"encoding/binary"
    40  	"encoding/hex"
    41  	"encoding/json"
    42  	std_errors "errors"
    43  	"fmt"
    44  	"io"
    45  	"net"
    46  	"net/url"
    47  	"path"
    48  	"path/filepath"
    49  	"sort"
    50  	"strings"
    51  	"sync"
    52  	"sync/atomic"
    53  	"time"
    54  
    55  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
    56  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/nacl/secretbox"
    57  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
    58  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/sss"
    59  )
    60  
    61  const (
    62  	KEY_LENGTH_BYTES    = 32
    63  	REGISTRY_FILENAME   = "osl-registry"
    64  	OSL_FILENAME_FORMAT = "osl-%s"
    65  )
    66  
    67  // Config is an OSL configuration, which consists of a list of schemes.
    68  // The Reload function supports hot reloading of rules data while the
    69  // process is running.
    70  type Config struct {
    71  	common.ReloadableFile
    72  
    73  	Schemes []*Scheme
    74  }
    75  
    76  // Scheme defines a OSL seeding and distribution strategy. SLOKs to
    77  // decrypt OSLs are issued based on client network activity -- defined
    78  // in the SeedSpecs -- and time. OSLs are created for periods of time
    79  // and can be decrypted by clients that are seeded with a sufficient
    80  // selection of SLOKs for that time period. Distribution of server
    81  // entries to OSLs is delegated to automation.
    82  type Scheme struct {
    83  
    84  	// Epoch is the start time of the scheme, the start time of the
    85  	// first OSL and when SLOKs will first be issued. It must be
    86  	// specified in UTC and must be a multiple of SeedPeriodNanoseconds.
    87  	Epoch string
    88  
    89  	// Regions is a list of client country codes this scheme applies to.
    90  	// If empty, the scheme applies to all regions.
    91  	Regions []string
    92  
    93  	// PropagationChannelIDs is a list of client propagtion channel IDs
    94  	// this scheme applies to. Propagation channel IDs are an input
    95  	// to SLOK key derivation.
    96  	PropagationChannelIDs []string
    97  
    98  	// MasterKey is the base random key used for SLOK key derivation. It
    99  	// must be unique for each scheme. It must be 32 random bytes, base64
   100  	// encoded.
   101  	MasterKey []byte
   102  
   103  	// SeedSpecs is the set of different client network activity patterns
   104  	// that will result in issuing SLOKs. For a given time period, a distinct
   105  	// SLOK is issued for each SeedSpec.
   106  	// Duplicate subnets may appear in multiple SeedSpecs.
   107  	SeedSpecs []*SeedSpec
   108  
   109  	// SeedSpecThreshold is the threshold scheme for combining SLOKs to
   110  	// decrypt an OSL. For any fixed time period, at least K (threshold) of
   111  	// N (total) SLOKs from the N SeedSpecs must be seeded for a client to be
   112  	// able to reassemble the OSL key.
   113  	// Limitation: thresholds must be at least 2.
   114  	SeedSpecThreshold int
   115  
   116  	// SeedPeriodNanoseconds is the time period granularity of SLOKs.
   117  	// New SLOKs are issued every SeedPeriodNanoseconds. Client progress
   118  	// towards activity levels is reset at the end of each period.
   119  	SeedPeriodNanoseconds int64
   120  
   121  	// KeySplits is the time period threshold scheme layered on top of the
   122  	// SeedSpecThreshold scheme for combining SLOKs to decrypt an OSL.
   123  	// There must be at least one level. For one level, any K (threshold) of
   124  	// N (total) SeedSpec SLOK groups must be sufficiently seeded for a client
   125  	// to be able to reassemble the OSL key. When an additional level is
   126  	// specified, then K' of N' groups of N of K SeedSpec SLOK groups must be
   127  	// sufficiently seeded. And so on. The first level in the list is the
   128  	// lowest level. The time period for OSLs is determined by the totals in
   129  	// the KeySplits.
   130  	//
   131  	// Example:
   132  	//
   133  	//   SeedSpecs = <3 specs>
   134  	//   SeedSpecThreshold = 2
   135  	//   SeedPeriodNanoseconds = 100,000,000 = 100 milliseconds
   136  	//   SeedPeriodKeySplits = [{10, 7}, {60, 5}]
   137  	//
   138  	//   In these scheme, up to 3 distinct SLOKs, one per spec, are issued
   139  	//   every 100 milliseconds.
   140  	//
   141  	//   Distinct OSLs are paved for every minute (60 seconds). Each OSL
   142  	//   key is split such that, for those 60 seconds, a client must seed
   143  	//   2/3 spec SLOKs for 7 of 10 consecutive 100 ms. time periods within
   144  	//   a second, for any 5 of 60 seconds within the minute.
   145  	//
   146  	SeedPeriodKeySplits []KeySplit
   147  
   148  	// The following fields are ephemeral state.
   149  
   150  	epoch                 time.Time
   151  	subnetLookups         []common.SubnetLookup
   152  	derivedSLOKCacheMutex sync.RWMutex
   153  	derivedSLOKCache      map[slokReference]*SLOK
   154  }
   155  
   156  // SeedSpec defines a client traffic pattern that results in a seeded SLOK.
   157  // For each time period, a unique SLOK is issued to a client that meets the
   158  // traffic levels specified in Targets. All upstream port forward traffic to
   159  // UpstreamSubnets is counted towards the targets.
   160  //
   161  // ID is a SLOK key derivation component and must be 32 random bytes, base64
   162  // encoded. UpstreamSubnets is a list of CIDRs. Description is not used; it's
   163  // for JSON config file comments.
   164  type SeedSpec struct {
   165  	Description     string
   166  	ID              []byte
   167  	UpstreamSubnets []string
   168  	Targets         TrafficValues
   169  }
   170  
   171  // TrafficValues defines a client traffic level that seeds a SLOK.
   172  // BytesRead and BytesWritten are the minimum bytes transferred counts to
   173  // seed a SLOK. Both UDP and TCP data will be counted towards these totals.
   174  // PortForwardDurationNanoseconds is the duration that a TCP or UDP port
   175  // forward is active (not connected, in the UDP case). All threshold
   176  // settings must be met to seed a SLOK; any threshold may be set to 0 to
   177  // be trivially satisfied.
   178  type TrafficValues struct {
   179  	BytesRead                      int64
   180  	BytesWritten                   int64
   181  	PortForwardDurationNanoseconds int64
   182  }
   183  
   184  // KeySplit defines a secret key splitting scheme where the secret is split
   185  // into n (total) shares and any K (threshold) of N shares must be known
   186  // to recostruct the split secret.
   187  type KeySplit struct {
   188  	Total     int
   189  	Threshold int
   190  }
   191  
   192  // ClientSeedState tracks the progress of a client towards seeding SLOKs
   193  // across all schemes the client qualifies for.
   194  type ClientSeedState struct {
   195  	propagationChannelID string
   196  	seedProgress         []*ClientSeedProgress
   197  	mutex                sync.Mutex
   198  	signalIssueSLOKs     chan struct{}
   199  	issuedSLOKs          map[string]*SLOK
   200  	payloadSLOKs         []*SLOK
   201  }
   202  
   203  // ClientSeedProgress tracks client progress towards seeding SLOKs for
   204  // a particular scheme.
   205  type ClientSeedProgress struct {
   206  	// Note: 64-bit ints used with atomic operations are placed
   207  	// at the start of struct to ensure 64-bit alignment.
   208  	// (https://golang.org/pkg/sync/atomic/#pkg-note-BUG)
   209  	progressSLOKTime int64
   210  	scheme           *Scheme
   211  	trafficProgress  []*TrafficValues
   212  }
   213  
   214  // ClientSeedPortForward map a client port forward, which is relaying
   215  // traffic to a specific upstream address, to all seed state progress
   216  // counters for SeedSpecs with subnets containing the upstream address.
   217  // As traffic is relayed through the port forwards, the bytes transferred
   218  // and duration count towards the progress of these SeedSpecs and
   219  // associated SLOKs.
   220  type ClientSeedPortForward struct {
   221  	state              *ClientSeedState
   222  	progressReferences []progressReference
   223  }
   224  
   225  // progressReference points to a particular ClientSeedProgress and
   226  // TrafficValues for to update with traffic events for a
   227  // ClientSeedPortForward.
   228  type progressReference struct {
   229  	seedProgressIndex    int
   230  	trafficProgressIndex int
   231  }
   232  
   233  // slokReference uniquely identifies a SLOK by specifying all the fields
   234  // used to derive the SLOK secret key and ID.
   235  // Note: SeedSpecID is not a []byte as slokReference is used as a map key.
   236  type slokReference struct {
   237  	PropagationChannelID string
   238  	SeedSpecID           string
   239  	Time                 time.Time
   240  }
   241  
   242  // SLOK is a seeded SLOK issued to a client. The client will store the
   243  // SLOK in its local database; look it up by ID when checking which OSLs it
   244  // can reassemble keys for; and use the key material to reassemble OSL
   245  // file keys.
   246  type SLOK struct {
   247  	ID  []byte
   248  	Key []byte
   249  }
   250  
   251  // SeedPayload is the list of seeded SLOKs sent to a client.
   252  type SeedPayload struct {
   253  	SLOKs []*SLOK
   254  }
   255  
   256  // NewConfig initializes a Config with the settings in the specified
   257  // file.
   258  func NewConfig(filename string) (*Config, error) {
   259  
   260  	config := &Config{}
   261  
   262  	config.ReloadableFile = common.NewReloadableFile(
   263  		filename,
   264  		true,
   265  		func(fileContent []byte, _ time.Time) error {
   266  			newConfig, err := LoadConfig(fileContent)
   267  			if err != nil {
   268  				return errors.Trace(err)
   269  			}
   270  			// Modify actual traffic rules only after validation
   271  			config.Schemes = newConfig.Schemes
   272  			return nil
   273  		})
   274  
   275  	_, err := config.Reload()
   276  	if err != nil {
   277  		return nil, errors.Trace(err)
   278  	}
   279  
   280  	return config, nil
   281  }
   282  
   283  // LoadConfig loads, validates, and initializes a JSON encoded OSL
   284  // configuration.
   285  func LoadConfig(configJSON []byte) (*Config, error) {
   286  
   287  	var config Config
   288  	err := json.Unmarshal(configJSON, &config)
   289  	if err != nil {
   290  		return nil, errors.Trace(err)
   291  	}
   292  
   293  	var previousEpoch time.Time
   294  
   295  	for _, scheme := range config.Schemes {
   296  
   297  		if scheme == nil {
   298  			return nil, errors.TraceNew("invalid scheme")
   299  		}
   300  
   301  		epoch, err := time.Parse(time.RFC3339, scheme.Epoch)
   302  		if err != nil {
   303  			return nil, errors.Tracef("invalid epoch format: %s", err)
   304  		}
   305  
   306  		if epoch.UTC() != epoch {
   307  			return nil, errors.TraceNew("invalid epoch timezone")
   308  		}
   309  
   310  		if epoch.Round(time.Duration(scheme.SeedPeriodNanoseconds)) != epoch {
   311  			return nil, errors.TraceNew("invalid epoch period")
   312  		}
   313  
   314  		if epoch.Before(previousEpoch) {
   315  			return nil, errors.TraceNew("invalid epoch order")
   316  		}
   317  
   318  		previousEpoch = epoch
   319  
   320  		scheme.epoch = epoch
   321  		scheme.subnetLookups = make([]common.SubnetLookup, len(scheme.SeedSpecs))
   322  		scheme.derivedSLOKCache = make(map[slokReference]*SLOK)
   323  
   324  		if len(scheme.MasterKey) != KEY_LENGTH_BYTES {
   325  			return nil, errors.TraceNew("invalid master key")
   326  		}
   327  
   328  		for index, seedSpec := range scheme.SeedSpecs {
   329  
   330  			if seedSpec == nil {
   331  				return nil, errors.TraceNew("invalid seed spec")
   332  			}
   333  
   334  			if len(seedSpec.ID) != KEY_LENGTH_BYTES {
   335  				return nil, errors.TraceNew("invalid seed spec ID")
   336  			}
   337  
   338  			// TODO: check that subnets do not overlap, as required by SubnetLookup
   339  			subnetLookup, err := common.NewSubnetLookup(seedSpec.UpstreamSubnets)
   340  			if err != nil {
   341  				return nil, errors.Tracef("invalid upstream subnets: %s", err)
   342  			}
   343  
   344  			scheme.subnetLookups[index] = subnetLookup
   345  		}
   346  
   347  		if !isValidShamirSplit(len(scheme.SeedSpecs), scheme.SeedSpecThreshold) {
   348  			return nil, errors.TraceNew("invalid seed spec key split")
   349  		}
   350  
   351  		if len(scheme.SeedPeriodKeySplits) < 1 {
   352  			return nil, errors.TraceNew("invalid seed period key split count")
   353  		}
   354  
   355  		for _, keySplit := range scheme.SeedPeriodKeySplits {
   356  			if !isValidShamirSplit(keySplit.Total, keySplit.Threshold) {
   357  				return nil, errors.TraceNew("invalid seed period key split")
   358  			}
   359  		}
   360  	}
   361  
   362  	return &config, nil
   363  }
   364  
   365  // NewClientSeedState creates a new client seed state to track
   366  // client progress towards seeding SLOKs. psiphond maintains one
   367  // ClientSeedState for each connected client.
   368  //
   369  // A signal is sent on signalIssueSLOKs when sufficient progress
   370  // has been made that a new SLOK *may* be issued. psiphond will
   371  // receive the signal and then call GetClientSeedPayload/IssueSLOKs
   372  // to issue SLOKs, generate payload, and send to the client. The
   373  // sender will not block sending to signalIssueSLOKs; the channel
   374  // should be appropriately buffered.
   375  func (config *Config) NewClientSeedState(
   376  	clientRegion, propagationChannelID string,
   377  	signalIssueSLOKs chan struct{}) *ClientSeedState {
   378  
   379  	config.ReloadableFile.RLock()
   380  	defer config.ReloadableFile.RUnlock()
   381  
   382  	state := &ClientSeedState{
   383  		propagationChannelID: propagationChannelID,
   384  		signalIssueSLOKs:     signalIssueSLOKs,
   385  		issuedSLOKs:          make(map[string]*SLOK),
   386  		payloadSLOKs:         nil,
   387  	}
   388  
   389  	for _, scheme := range config.Schemes {
   390  
   391  		// All matching schemes are selected.
   392  		// Note: this implementation assumes a few simple schemes. For more
   393  		// schemes with many propagation channel IDs or region filters, use
   394  		// maps for more efficient lookup.
   395  		if scheme.epoch.Before(time.Now().UTC()) &&
   396  			common.Contains(scheme.PropagationChannelIDs, propagationChannelID) &&
   397  			(len(scheme.Regions) == 0 || common.Contains(scheme.Regions, clientRegion)) {
   398  
   399  			// Empty progress is initialized up front for all seed specs. Once
   400  			// created, the progress structure is read-only (the slice, not the
   401  			// TrafficValue fields); this permits lock-free operation.
   402  			trafficProgress := make([]*TrafficValues, len(scheme.SeedSpecs))
   403  			for index := 0; index < len(scheme.SeedSpecs); index++ {
   404  				trafficProgress[index] = &TrafficValues{}
   405  			}
   406  
   407  			seedProgress := &ClientSeedProgress{
   408  				scheme:           scheme,
   409  				progressSLOKTime: getSLOKTime(scheme.SeedPeriodNanoseconds),
   410  				trafficProgress:  trafficProgress,
   411  			}
   412  
   413  			state.seedProgress = append(state.seedProgress, seedProgress)
   414  		}
   415  	}
   416  
   417  	return state
   418  }
   419  
   420  // Hibernate clears references to short-lived objects (currently,
   421  // signalIssueSLOKs) so that a ClientSeedState can be stored for
   422  // later resumption without blocking garbage collection of the
   423  // short-lived objects.
   424  //
   425  // The ClientSeedState will still hold references to its Config;
   426  // the caller is responsible for discarding hibernated seed states
   427  // when the config changes.
   428  //
   429  // The caller should ensure that all ClientSeedPortForwards
   430  // associated with this ClientSeedState are closed before
   431  // hibernation.
   432  func (state *ClientSeedState) Hibernate() {
   433  	state.mutex.Lock()
   434  	defer state.mutex.Unlock()
   435  
   436  	state.signalIssueSLOKs = nil
   437  }
   438  
   439  // Resume resumes a hibernated ClientSeedState by resetting the required
   440  // objects (currently, signalIssueSLOKs) cleared by Hibernate.
   441  func (state *ClientSeedState) Resume(
   442  	signalIssueSLOKs chan struct{}) {
   443  
   444  	state.mutex.Lock()
   445  	defer state.mutex.Unlock()
   446  
   447  	state.signalIssueSLOKs = signalIssueSLOKs
   448  }
   449  
   450  // NewClientSeedPortForward creates a new client port forward
   451  // traffic progress tracker. Port forward progress reported to the
   452  // ClientSeedPortForward is added to seed state progress for all
   453  // seed specs containing upstreamIPAddress in their subnets.
   454  // The return value will be nil when activity for upstreamIPAddress
   455  // does not count towards any progress.
   456  // NewClientSeedPortForward may be invoked concurrently by many
   457  // psiphond port forward establishment goroutines.
   458  func (state *ClientSeedState) NewClientSeedPortForward(
   459  	upstreamIPAddress net.IP) *ClientSeedPortForward {
   460  
   461  	// Concurrency: access to ClientSeedState is unsynchronized
   462  	// but references only read-only fields.
   463  
   464  	if len(state.seedProgress) == 0 {
   465  		return nil
   466  	}
   467  
   468  	var progressReferences []progressReference
   469  
   470  	// Determine which seed spec subnets contain upstreamIPAddress
   471  	// and point to the progress for each. When progress is reported,
   472  	// it is added directly to all of these TrafficValues instances.
   473  	// Assumes state.progress entries correspond 1-to-1 with
   474  	// state.scheme.subnetLookups.
   475  	// Note: this implementation assumes a small number of schemes and
   476  	// seed specs. For larger numbers, instead of N SubnetLookups, create
   477  	// a single SubnetLookup which returns, for a given IP address, all
   478  	// matching subnets and associated seed specs.
   479  	for seedProgressIndex, seedProgress := range state.seedProgress {
   480  		for trafficProgressIndex, subnetLookup := range seedProgress.scheme.subnetLookups {
   481  			if subnetLookup.ContainsIPAddress(upstreamIPAddress) {
   482  				progressReferences = append(
   483  					progressReferences,
   484  					progressReference{
   485  						seedProgressIndex:    seedProgressIndex,
   486  						trafficProgressIndex: trafficProgressIndex,
   487  					})
   488  			}
   489  		}
   490  	}
   491  
   492  	if progressReferences == nil {
   493  		return nil
   494  	}
   495  
   496  	return &ClientSeedPortForward{
   497  		state:              state,
   498  		progressReferences: progressReferences,
   499  	}
   500  }
   501  
   502  func (state *ClientSeedState) sendIssueSLOKsSignal() {
   503  	state.mutex.Lock()
   504  	defer state.mutex.Unlock()
   505  
   506  	if state.signalIssueSLOKs != nil {
   507  		select {
   508  		case state.signalIssueSLOKs <- struct{}{}:
   509  		default:
   510  		}
   511  	}
   512  }
   513  
   514  // UpdateProgress adds port forward bytes transferred and duration to
   515  // all seed spec progresses associated with the port forward.
   516  // If UpdateProgress is invoked after the SLOK time period has rolled
   517  // over, any pending seeded SLOKs are issued and all progress is reset.
   518  // UpdateProgress may be invoked concurrently by many psiphond port
   519  // relay goroutines. The implementation of UpdateProgress prioritizes
   520  // not blocking port forward relaying; a consequence of this lock-free
   521  // design is that progress reported at the exact time of SLOK time period
   522  // rollover may be dropped.
   523  func (portForward *ClientSeedPortForward) UpdateProgress(
   524  	bytesRead, bytesWritten, durationNanoseconds int64) {
   525  
   526  	// Concurrency: non-blocking -- access to ClientSeedState is unsynchronized
   527  	// to read-only fields, atomic, or channels, except in the case of a time
   528  	// period rollover, in which case a mutex is acquired.
   529  
   530  	for _, progressReference := range portForward.progressReferences {
   531  
   532  		seedProgress := portForward.state.seedProgress[progressReference.seedProgressIndex]
   533  		trafficProgress := seedProgress.trafficProgress[progressReference.trafficProgressIndex]
   534  
   535  		slokTime := getSLOKTime(seedProgress.scheme.SeedPeriodNanoseconds)
   536  
   537  		// If the SLOK time period has changed since progress was last recorded,
   538  		// call issueSLOKs which will issue any SLOKs for that past time period
   539  		// and then clear all progress. Progress will then be recorded for the
   540  		// current time period.
   541  		// As it acquires the state mutex, issueSLOKs may stall other port
   542  		// forwards for this client. The delay is minimized by SLOK caching,
   543  		// which avoids redundant crypto operations.
   544  		if slokTime != atomic.LoadInt64(&seedProgress.progressSLOKTime) {
   545  			portForward.state.mutex.Lock()
   546  			portForward.state.issueSLOKs()
   547  			portForward.state.mutex.Unlock()
   548  
   549  			// Call to issueSLOKs may have issued new SLOKs. Note that
   550  			// this will only happen if the time period rolls over with
   551  			// sufficient progress pending while the signalIssueSLOKs
   552  			// receiver did not call IssueSLOKs soon enough.
   553  			portForward.state.sendIssueSLOKsSignal()
   554  		}
   555  
   556  		// Add directly to the permanent TrafficValues progress accumulators
   557  		// for the state's seed specs. Concurrently, other port forwards may
   558  		// be adding to the same accumulators. Also concurrently, another
   559  		// goroutine may be invoking issueSLOKs, which zeros all the accumulators.
   560  		// As a consequence, progress may be dropped at the exact time of
   561  		// time period rollover.
   562  
   563  		seedSpec := seedProgress.scheme.SeedSpecs[progressReference.trafficProgressIndex]
   564  
   565  		alreadyExceedsTargets := trafficProgress.exceeds(&seedSpec.Targets)
   566  
   567  		atomic.AddInt64(&trafficProgress.BytesRead, bytesRead)
   568  		atomic.AddInt64(&trafficProgress.BytesWritten, bytesWritten)
   569  		atomic.AddInt64(&trafficProgress.PortForwardDurationNanoseconds, durationNanoseconds)
   570  
   571  		// With the target newly met for a SeedSpec, a new
   572  		// SLOK *may* be issued.
   573  		if !alreadyExceedsTargets && trafficProgress.exceeds(&seedSpec.Targets) {
   574  			portForward.state.sendIssueSLOKsSignal()
   575  		}
   576  	}
   577  }
   578  
   579  func (lhs *TrafficValues) exceeds(rhs *TrafficValues) bool {
   580  	return atomic.LoadInt64(&lhs.BytesRead) >= atomic.LoadInt64(&rhs.BytesRead) &&
   581  		atomic.LoadInt64(&lhs.BytesWritten) >= atomic.LoadInt64(&rhs.BytesWritten) &&
   582  		atomic.LoadInt64(&lhs.PortForwardDurationNanoseconds) >=
   583  			atomic.LoadInt64(&rhs.PortForwardDurationNanoseconds)
   584  }
   585  
   586  // issueSLOKs checks client progress against each candidate seed spec
   587  // and seeds SLOKs when the client traffic levels are achieved. After
   588  // checking progress, and if the SLOK time period has changed since
   589  // progress was last recorded, progress is reset. Partial, insufficient
   590  // progress is intentionally dropped when the time period rolls over.
   591  // Derived SLOKs are cached to avoid redundant CPU intensive operations.
   592  // All issued SLOKs are retained in the client state for the duration
   593  // of the client's session.
   594  func (state *ClientSeedState) issueSLOKs() {
   595  
   596  	// Concurrency: the caller must lock state.mutex.
   597  
   598  	if len(state.seedProgress) == 0 {
   599  		return
   600  	}
   601  
   602  	for _, seedProgress := range state.seedProgress {
   603  
   604  		progressSLOKTime := time.Unix(0, seedProgress.progressSLOKTime)
   605  
   606  		for index, trafficProgress := range seedProgress.trafficProgress {
   607  
   608  			seedSpec := seedProgress.scheme.SeedSpecs[index]
   609  
   610  			if trafficProgress.exceeds(&seedSpec.Targets) {
   611  
   612  				ref := &slokReference{
   613  					PropagationChannelID: state.propagationChannelID,
   614  					SeedSpecID:           string(seedSpec.ID),
   615  					Time:                 progressSLOKTime,
   616  				}
   617  
   618  				seedProgress.scheme.derivedSLOKCacheMutex.RLock()
   619  				slok, ok := seedProgress.scheme.derivedSLOKCache[*ref]
   620  				seedProgress.scheme.derivedSLOKCacheMutex.RUnlock()
   621  				if !ok {
   622  					slok = seedProgress.scheme.deriveSLOK(ref)
   623  					seedProgress.scheme.derivedSLOKCacheMutex.Lock()
   624  					seedProgress.scheme.derivedSLOKCache[*ref] = slok
   625  					seedProgress.scheme.derivedSLOKCacheMutex.Unlock()
   626  				}
   627  
   628  				// Previously issued SLOKs are not re-added to
   629  				// the payload.
   630  				if state.issuedSLOKs[string(slok.ID)] == nil {
   631  					state.issuedSLOKs[string(slok.ID)] = slok
   632  					state.payloadSLOKs = append(state.payloadSLOKs, slok)
   633  				}
   634  			}
   635  		}
   636  
   637  		slokTime := getSLOKTime(seedProgress.scheme.SeedPeriodNanoseconds)
   638  
   639  		if slokTime != atomic.LoadInt64(&seedProgress.progressSLOKTime) {
   640  			atomic.StoreInt64(&seedProgress.progressSLOKTime, slokTime)
   641  			// The progress map structure is not reset or modifed; instead
   642  			// the mapped accumulator values are zeroed. Concurrently, port
   643  			// forward relay goroutines continue to add to these accumulators.
   644  			for _, trafficProgress := range seedProgress.trafficProgress {
   645  				atomic.StoreInt64(&trafficProgress.BytesRead, 0)
   646  				atomic.StoreInt64(&trafficProgress.BytesWritten, 0)
   647  				atomic.StoreInt64(&trafficProgress.PortForwardDurationNanoseconds, 0)
   648  			}
   649  		}
   650  	}
   651  }
   652  
   653  func getSLOKTime(seedPeriodNanoseconds int64) int64 {
   654  	return time.Now().UTC().Truncate(time.Duration(seedPeriodNanoseconds)).UnixNano()
   655  }
   656  
   657  // GetSeedPayload issues any pending SLOKs and returns the accumulated
   658  // SLOKs for a given client. psiphond will calls this when it receives
   659  // signalIssueSLOKs which is the trigger to check for new SLOKs.
   660  // Note: caller must not modify the SLOKs in SeedPayload.SLOKs
   661  // as these are shared data.
   662  func (state *ClientSeedState) GetSeedPayload() *SeedPayload {
   663  
   664  	state.mutex.Lock()
   665  	defer state.mutex.Unlock()
   666  
   667  	if len(state.seedProgress) == 0 {
   668  		return &SeedPayload{}
   669  	}
   670  
   671  	state.issueSLOKs()
   672  
   673  	sloks := make([]*SLOK, len(state.payloadSLOKs))
   674  	for index, slok := range state.payloadSLOKs {
   675  		sloks[index] = slok
   676  	}
   677  
   678  	return &SeedPayload{
   679  		SLOKs: sloks,
   680  	}
   681  }
   682  
   683  // ClearSeedPayload resets the accumulated SLOK payload (but not SLOK
   684  // progress). psiphond calls this after the client has acknowledged
   685  // receipt of a payload.
   686  func (state *ClientSeedState) ClearSeedPayload() {
   687  
   688  	state.mutex.Lock()
   689  	defer state.mutex.Unlock()
   690  
   691  	state.payloadSLOKs = nil
   692  }
   693  
   694  // deriveSLOK produces SLOK secret keys and IDs using HKDF-Expand
   695  // defined in https://tools.ietf.org/html/rfc5869.
   696  func (scheme *Scheme) deriveSLOK(ref *slokReference) *SLOK {
   697  
   698  	timeBytes := make([]byte, 8)
   699  	binary.LittleEndian.PutUint64(timeBytes, uint64(ref.Time.UnixNano()))
   700  
   701  	key := deriveKeyHKDF(
   702  		scheme.MasterKey,
   703  		[]byte(ref.PropagationChannelID),
   704  		[]byte(ref.SeedSpecID),
   705  		timeBytes)
   706  
   707  	// TODO: is ID derivation cryptographically sound?
   708  	id := deriveKeyHKDF(
   709  		scheme.MasterKey,
   710  		key)
   711  
   712  	return &SLOK{
   713  		ID:  id,
   714  		Key: key,
   715  	}
   716  }
   717  
   718  // GetOSLDuration returns the total time duration of an OSL,
   719  // which is a function of the scheme's SeedPeriodNanoSeconds,
   720  // the duration of a single SLOK, and the scheme's SeedPeriodKeySplits,
   721  // the number of SLOKs associated with an OSL.
   722  func (scheme *Scheme) GetOSLDuration() time.Duration {
   723  	slokTimePeriodsPerOSL := 1
   724  	for _, keySplit := range scheme.SeedPeriodKeySplits {
   725  		slokTimePeriodsPerOSL *= keySplit.Total
   726  	}
   727  
   728  	return time.Duration(
   729  		int64(slokTimePeriodsPerOSL) * scheme.SeedPeriodNanoseconds)
   730  }
   731  
   732  // PaveFile describes an OSL data file to be paved to an out-of-band
   733  // distribution drop site. There are two types of files: a registry,
   734  // which describes how to assemble keys for OSLs, and the encrypted
   735  // OSL files.
   736  type PaveFile struct {
   737  	Name     string
   738  	Contents []byte
   739  }
   740  
   741  // Registry describes a set of OSL files.
   742  type Registry struct {
   743  	FileSpecs []*OSLFileSpec
   744  }
   745  
   746  // An OSLFileSpec includes an ID which is used to reference the
   747  // OSL file and describes the key splits used to divide the OSL
   748  // file key along with the SLOKs required to reassemble those keys.
   749  //
   750  // The MD5Sum field is a checksum of the contents of the OSL file
   751  // to be used to skip redownloading previously downloaded files.
   752  // MD5 is not cryptographically secure and this checksum is not
   753  // relied upon for OSL verification. MD5 is used for compatibility
   754  // with out-of-band distribution hosts.
   755  type OSLFileSpec struct {
   756  	ID        []byte
   757  	KeyShares *KeyShares
   758  	MD5Sum    []byte
   759  }
   760  
   761  // KeyShares is a tree data structure which describes the
   762  // key splits used to divide a secret key. BoxedShares are encrypted
   763  // shares of the key, and #Threshold amount of decrypted BoxedShares
   764  // are required to reconstruct the secret key. The keys for BoxedShares
   765  // are either SLOKs (referenced by SLOK ID) or random keys that are
   766  // themselves split as described in child KeyShares.
   767  type KeyShares struct {
   768  	Threshold   int
   769  	BoxedShares [][]byte
   770  	SLOKIDs     [][]byte
   771  	KeyShares   []*KeyShares
   772  }
   773  
   774  type PaveLogInfo struct {
   775  	FileName             string
   776  	SchemeIndex          int
   777  	PropagationChannelID string
   778  	OSLID                string
   779  	OSLTime              time.Time
   780  	OSLDuration          time.Duration
   781  	ServerEntryCount     int
   782  }
   783  
   784  // Pave creates the full set of OSL files, for all schemes in the
   785  // configuration, to be dropped in an out-of-band distribution site.
   786  // Only OSLs for the propagation channel ID associated with the
   787  // distribution site are paved. This function is used by automation.
   788  //
   789  // The Name component of each file relates to the values returned by
   790  // the client functions GetRegistryURL and GetOSLFileURL.
   791  //
   792  // Pave returns a pave file for the entire registry of all OSLs from
   793  // epoch to endTime, and a pave file for each OSL. paveServerEntries is
   794  // a map from hex-encoded OSL IDs to server entries to pave into that OSL.
   795  // When entries are found, OSL will contain those entries, newline
   796  // separated. Otherwise the OSL will still be issued, but be empty (unless
   797  // the scheme is in omitEmptyOSLsSchemes). The server entries are paved
   798  // in string value sort order, ensuring that the OSL content remains
   799  // constant as long as the same _set_ of server entries is input.
   800  //
   801  // If startTime is specified and is after epoch, the pave file will contain
   802  // OSLs for the first period at or after startTime.
   803  //
   804  // As OSLs outside the epoch-endTime range will no longer appear in
   805  // the registry, Pave is intended to be used to create the full set
   806  // of OSLs for a distribution site; i.e., not incrementally.
   807  //
   808  // Automation is responsible for consistently distributing server entries
   809  // to OSLs in the case where OSLs are repaved in subsequent calls.
   810  func (config *Config) Pave(
   811  	startTime time.Time,
   812  	endTime time.Time,
   813  	propagationChannelID string,
   814  	signingPublicKey string,
   815  	signingPrivateKey string,
   816  	paveServerEntries map[string][]string,
   817  	omitMD5SumsSchemes []int,
   818  	omitEmptyOSLsSchemes []int,
   819  	logCallback func(*PaveLogInfo)) ([]*PaveFile, error) {
   820  
   821  	config.ReloadableFile.RLock()
   822  	defer config.ReloadableFile.RUnlock()
   823  
   824  	var paveFiles []*PaveFile
   825  
   826  	registry := &Registry{}
   827  
   828  	for schemeIndex, scheme := range config.Schemes {
   829  		if common.Contains(scheme.PropagationChannelIDs, propagationChannelID) {
   830  
   831  			omitMD5Sums := common.ContainsInt(omitMD5SumsSchemes, schemeIndex)
   832  
   833  			omitEmptyOSLs := common.ContainsInt(omitEmptyOSLsSchemes, schemeIndex)
   834  
   835  			oslDuration := scheme.GetOSLDuration()
   836  
   837  			oslTime := scheme.epoch
   838  
   839  			if !startTime.IsZero() && !startTime.Before(scheme.epoch) {
   840  				for oslTime.Before(startTime) {
   841  					oslTime = oslTime.Add(oslDuration)
   842  				}
   843  			}
   844  
   845  			for !oslTime.After(endTime) {
   846  
   847  				firstSLOKTime := oslTime
   848  				fileKey, fileSpec, err := makeOSLFileSpec(
   849  					scheme, propagationChannelID, firstSLOKTime)
   850  				if err != nil {
   851  					return nil, errors.Trace(err)
   852  				}
   853  
   854  				hexEncodedOSLID := hex.EncodeToString(fileSpec.ID)
   855  
   856  				serverEntryCount := len(paveServerEntries[hexEncodedOSLID])
   857  
   858  				if serverEntryCount > 0 || !omitEmptyOSLs {
   859  
   860  					registry.FileSpecs = append(registry.FileSpecs, fileSpec)
   861  
   862  					serverEntries := append([]string(nil), paveServerEntries[hexEncodedOSLID]...)
   863  					sort.Strings(serverEntries)
   864  
   865  					// payload will be "" when nothing is found in serverEntries
   866  					payload := strings.Join(serverEntries, "\n")
   867  
   868  					serverEntriesPackage, err := common.WriteAuthenticatedDataPackage(
   869  						payload,
   870  						signingPublicKey,
   871  						signingPrivateKey)
   872  					if err != nil {
   873  						return nil, errors.Trace(err)
   874  					}
   875  
   876  					boxedServerEntries, err := box(fileKey, serverEntriesPackage)
   877  					if err != nil {
   878  						return nil, errors.Trace(err)
   879  					}
   880  
   881  					if !omitMD5Sums {
   882  						md5sum := md5.Sum(boxedServerEntries)
   883  						fileSpec.MD5Sum = md5sum[:]
   884  					}
   885  
   886  					fileName := fmt.Sprintf(
   887  						OSL_FILENAME_FORMAT, hexEncodedOSLID)
   888  
   889  					paveFiles = append(paveFiles, &PaveFile{
   890  						Name:     fileName,
   891  						Contents: boxedServerEntries,
   892  					})
   893  
   894  					if logCallback != nil {
   895  						logCallback(&PaveLogInfo{
   896  							FileName:             fileName,
   897  							SchemeIndex:          schemeIndex,
   898  							PropagationChannelID: propagationChannelID,
   899  							OSLID:                hexEncodedOSLID,
   900  							OSLTime:              oslTime,
   901  							OSLDuration:          oslDuration,
   902  							ServerEntryCount:     serverEntryCount,
   903  						})
   904  					}
   905  				}
   906  
   907  				oslTime = oslTime.Add(oslDuration)
   908  			}
   909  		}
   910  	}
   911  
   912  	registryJSON, err := json.Marshal(registry)
   913  	if err != nil {
   914  		return nil, errors.Trace(err)
   915  	}
   916  
   917  	registryPackage, err := common.WriteAuthenticatedDataPackage(
   918  		base64.StdEncoding.EncodeToString(registryJSON),
   919  		signingPublicKey,
   920  		signingPrivateKey)
   921  	if err != nil {
   922  		return nil, errors.Trace(err)
   923  	}
   924  
   925  	paveFiles = append(paveFiles, &PaveFile{
   926  		Name:     REGISTRY_FILENAME,
   927  		Contents: registryPackage,
   928  	})
   929  
   930  	return paveFiles, nil
   931  }
   932  
   933  // CurrentOSLIDs returns a mapping from each propagation channel ID in the
   934  // specified scheme to the corresponding current time period, hex-encoded OSL ID.
   935  func (config *Config) CurrentOSLIDs(schemeIndex int) (map[string]string, error) {
   936  
   937  	config.ReloadableFile.RLock()
   938  	defer config.ReloadableFile.RUnlock()
   939  
   940  	if schemeIndex < 0 || schemeIndex >= len(config.Schemes) {
   941  		return nil, errors.TraceNew("invalid scheme index")
   942  	}
   943  
   944  	scheme := config.Schemes[schemeIndex]
   945  	now := time.Now().UTC()
   946  	oslDuration := scheme.GetOSLDuration()
   947  	oslTime := scheme.epoch.Add((now.Sub(scheme.epoch) / oslDuration) * oslDuration)
   948  
   949  	OSLIDs := make(map[string]string)
   950  	for _, propagationChannelID := range scheme.PropagationChannelIDs {
   951  		_, fileSpec, err := makeOSLFileSpec(scheme, propagationChannelID, oslTime)
   952  		if err != nil {
   953  			return nil, errors.Trace(err)
   954  		}
   955  		OSLIDs[propagationChannelID] = hex.EncodeToString(fileSpec.ID)
   956  	}
   957  
   958  	return OSLIDs, nil
   959  }
   960  
   961  // makeOSLFileSpec creates an OSL file key, splits it according to the
   962  // scheme's key splits, and sets the OSL ID as its first SLOK ID. The
   963  // returned key is used to encrypt the OSL payload and then discarded;
   964  // the key may be reassembled using the data in the KeyShares tree,
   965  // given sufficient SLOKs.
   966  func makeOSLFileSpec(
   967  	scheme *Scheme,
   968  	propagationChannelID string,
   969  	firstSLOKTime time.Time) ([]byte, *OSLFileSpec, error) {
   970  
   971  	ref := &slokReference{
   972  		PropagationChannelID: propagationChannelID,
   973  		SeedSpecID:           string(scheme.SeedSpecs[0].ID),
   974  		Time:                 firstSLOKTime,
   975  	}
   976  	firstSLOK := scheme.deriveSLOK(ref)
   977  	oslID := firstSLOK.ID
   978  
   979  	// Note: previously, fileKey was a random key. Now, the key
   980  	// is derived from the master key and OSL ID. This deterministic
   981  	// derivation ensures that repeated paves of the same OSL
   982  	// with the same ID and same content yields the same MD5Sum
   983  	// to avoid wasteful downloads.
   984  	//
   985  	// Similarly, the shareKeys generated in divideKey and the Shamir
   986  	// key splitting random polynomials are now both determinisitcally
   987  	// generated from a seeded CSPRNG. This ensures that the OSL
   988  	// registry remains identical for repeated paves of the same config
   989  	// and parameters.
   990  	//
   991  	// The split structure is added to the deterministic key
   992  	// derivation so that changes to the split configuration will not
   993  	// expose the same key material to different SLOK combinations.
   994  
   995  	splitStructure := make([]byte, 16*(1+len(scheme.SeedPeriodKeySplits)))
   996  	i := 0
   997  	binary.LittleEndian.PutUint64(splitStructure[i:], uint64(len(scheme.SeedSpecs)))
   998  	binary.LittleEndian.PutUint64(splitStructure[i+8:], uint64(scheme.SeedSpecThreshold))
   999  	i += 16
  1000  	for _, keySplit := range scheme.SeedPeriodKeySplits {
  1001  		binary.LittleEndian.PutUint64(splitStructure[i:], uint64(keySplit.Total))
  1002  		binary.LittleEndian.PutUint64(splitStructure[i+8:], uint64(keySplit.Threshold))
  1003  		i += 16
  1004  	}
  1005  
  1006  	fileKey := deriveKeyHKDF(
  1007  		scheme.MasterKey,
  1008  		splitStructure,
  1009  		[]byte("osl-file-key"),
  1010  		oslID)
  1011  
  1012  	splitKeyMaterialSeed := deriveKeyHKDF(
  1013  		scheme.MasterKey,
  1014  		splitStructure,
  1015  		[]byte("osl-file-split-key-material-seed"),
  1016  		oslID)
  1017  
  1018  	keyMaterialReader, err := newSeededKeyMaterialReader(splitKeyMaterialSeed)
  1019  	if err != nil {
  1020  		return nil, nil, errors.Trace(err)
  1021  	}
  1022  
  1023  	keyShares, err := divideKey(
  1024  		scheme,
  1025  		keyMaterialReader,
  1026  		fileKey,
  1027  		scheme.SeedPeriodKeySplits,
  1028  		propagationChannelID,
  1029  		&firstSLOKTime)
  1030  	if err != nil {
  1031  		return nil, nil, errors.Trace(err)
  1032  	}
  1033  
  1034  	fileSpec := &OSLFileSpec{
  1035  		ID:        oslID,
  1036  		KeyShares: keyShares,
  1037  	}
  1038  
  1039  	return fileKey, fileSpec, nil
  1040  }
  1041  
  1042  // divideKey recursively constructs a KeyShares tree.
  1043  func divideKey(
  1044  	scheme *Scheme,
  1045  	keyMaterialReader io.Reader,
  1046  	key []byte,
  1047  	keySplits []KeySplit,
  1048  	propagationChannelID string,
  1049  	nextSLOKTime *time.Time) (*KeyShares, error) {
  1050  
  1051  	keySplitIndex := len(keySplits) - 1
  1052  	keySplit := keySplits[keySplitIndex]
  1053  
  1054  	shares, err := shamirSplit(
  1055  		key,
  1056  		keySplit.Total,
  1057  		keySplit.Threshold,
  1058  		keyMaterialReader)
  1059  	if err != nil {
  1060  		return nil, errors.Trace(err)
  1061  	}
  1062  
  1063  	var boxedShares [][]byte
  1064  	var keyShares []*KeyShares
  1065  
  1066  	for _, share := range shares {
  1067  
  1068  		var shareKey [KEY_LENGTH_BYTES]byte
  1069  
  1070  		n, err := keyMaterialReader.Read(shareKey[:])
  1071  		if err == nil && n != len(shareKey) {
  1072  			err = std_errors.New("unexpected length")
  1073  		}
  1074  		if err != nil {
  1075  			return nil, errors.Trace(err)
  1076  		}
  1077  
  1078  		if keySplitIndex > 0 {
  1079  			keyShare, err := divideKey(
  1080  				scheme,
  1081  				keyMaterialReader,
  1082  				shareKey[:],
  1083  				keySplits[0:keySplitIndex],
  1084  				propagationChannelID,
  1085  				nextSLOKTime)
  1086  			if err != nil {
  1087  				return nil, errors.Trace(err)
  1088  			}
  1089  			keyShares = append(keyShares, keyShare)
  1090  		} else {
  1091  			keyShare, err := divideKeyWithSeedSpecSLOKs(
  1092  				scheme,
  1093  				keyMaterialReader,
  1094  				shareKey[:],
  1095  				propagationChannelID,
  1096  				nextSLOKTime)
  1097  			if err != nil {
  1098  				return nil, errors.Trace(err)
  1099  			}
  1100  			keyShares = append(keyShares, keyShare)
  1101  
  1102  			*nextSLOKTime = nextSLOKTime.Add(time.Duration(scheme.SeedPeriodNanoseconds))
  1103  		}
  1104  		boxedShare, err := box(shareKey[:], share)
  1105  		if err != nil {
  1106  			return nil, errors.Trace(err)
  1107  		}
  1108  		boxedShares = append(boxedShares, boxedShare)
  1109  	}
  1110  
  1111  	return &KeyShares{
  1112  		Threshold:   keySplit.Threshold,
  1113  		BoxedShares: boxedShares,
  1114  		SLOKIDs:     nil,
  1115  		KeyShares:   keyShares,
  1116  	}, nil
  1117  }
  1118  
  1119  func divideKeyWithSeedSpecSLOKs(
  1120  	scheme *Scheme,
  1121  	keyMaterialReader io.Reader,
  1122  	key []byte,
  1123  	propagationChannelID string,
  1124  	nextSLOKTime *time.Time) (*KeyShares, error) {
  1125  
  1126  	var boxedShares [][]byte
  1127  	var slokIDs [][]byte
  1128  
  1129  	shares, err := shamirSplit(
  1130  		key,
  1131  		len(scheme.SeedSpecs),
  1132  		scheme.SeedSpecThreshold,
  1133  		keyMaterialReader)
  1134  	if err != nil {
  1135  		return nil, errors.Trace(err)
  1136  	}
  1137  
  1138  	for index, seedSpec := range scheme.SeedSpecs {
  1139  
  1140  		ref := &slokReference{
  1141  			PropagationChannelID: propagationChannelID,
  1142  			SeedSpecID:           string(seedSpec.ID),
  1143  			Time:                 *nextSLOKTime,
  1144  		}
  1145  		slok := scheme.deriveSLOK(ref)
  1146  
  1147  		boxedShare, err := box(slok.Key, shares[index])
  1148  		if err != nil {
  1149  			return nil, errors.Trace(err)
  1150  		}
  1151  		boxedShares = append(boxedShares, boxedShare)
  1152  
  1153  		slokIDs = append(slokIDs, slok.ID)
  1154  	}
  1155  
  1156  	return &KeyShares{
  1157  		Threshold:   scheme.SeedSpecThreshold,
  1158  		BoxedShares: boxedShares,
  1159  		SLOKIDs:     slokIDs,
  1160  		KeyShares:   nil,
  1161  	}, nil
  1162  }
  1163  
  1164  // reassembleKey recursively traverses a KeyShares tree, determining
  1165  // whether there exists suffient SLOKs to reassemble the root key and
  1166  // performing the key assembly as required.
  1167  func (keyShares *KeyShares) reassembleKey(lookup SLOKLookup, unboxKey bool) (bool, []byte, error) {
  1168  
  1169  	if (len(keyShares.SLOKIDs) > 0 && len(keyShares.KeyShares) > 0) ||
  1170  		(len(keyShares.SLOKIDs) > 0 && len(keyShares.SLOKIDs) != len(keyShares.BoxedShares)) ||
  1171  		(len(keyShares.KeyShares) > 0 && len(keyShares.KeyShares) != len(keyShares.BoxedShares)) {
  1172  		return false, nil, errors.TraceNew("unexpected KeyShares format")
  1173  	}
  1174  
  1175  	shareCount := 0
  1176  	var shares [][]byte
  1177  	if unboxKey {
  1178  		// Note: shamirCombine infers share indices from slice offset, so the full
  1179  		// keyShares.Total slots are allocated and missing shares are left nil.
  1180  		shares = make([][]byte, len(keyShares.BoxedShares))
  1181  	}
  1182  	if len(keyShares.SLOKIDs) > 0 {
  1183  		for i := 0; i < len(keyShares.SLOKIDs) && shareCount < keyShares.Threshold; i++ {
  1184  			slokKey := lookup(keyShares.SLOKIDs[i])
  1185  			if slokKey == nil {
  1186  				continue
  1187  			}
  1188  			shareCount += 1
  1189  			if unboxKey {
  1190  				share, err := unbox(slokKey, keyShares.BoxedShares[i])
  1191  				if err != nil {
  1192  					return false, nil, errors.Trace(err)
  1193  				}
  1194  				shares[i] = share
  1195  			}
  1196  		}
  1197  	} else {
  1198  		for i := 0; i < len(keyShares.KeyShares) && shareCount < keyShares.Threshold; i++ {
  1199  			ok, key, err := keyShares.KeyShares[i].reassembleKey(lookup, unboxKey)
  1200  			if err != nil {
  1201  				return false, nil, errors.Trace(err)
  1202  			}
  1203  			if !ok {
  1204  				continue
  1205  			}
  1206  			shareCount += 1
  1207  			if unboxKey {
  1208  				share, err := unbox(key, keyShares.BoxedShares[i])
  1209  				if err != nil {
  1210  					return false, nil, errors.Trace(err)
  1211  				}
  1212  				shares[i] = share
  1213  			}
  1214  		}
  1215  	}
  1216  
  1217  	if shareCount < keyShares.Threshold {
  1218  		return false, nil, nil
  1219  	}
  1220  
  1221  	if !unboxKey {
  1222  		return true, nil, nil
  1223  	}
  1224  
  1225  	joinedKey := shamirCombine(shares)
  1226  
  1227  	return true, joinedKey, nil
  1228  }
  1229  
  1230  // GetOSLRegistryURL returns the URL for an OSL registry. Clients
  1231  // call this when fetching the registry from out-of-band
  1232  // distribution sites.
  1233  // Clients are responsible for tracking whether the remote file has
  1234  // changed or not before downloading.
  1235  func GetOSLRegistryURL(baseURL string) string {
  1236  	u, err := url.Parse(baseURL)
  1237  	if err != nil {
  1238  		return ""
  1239  	}
  1240  	u.Path = path.Join(u.Path, REGISTRY_FILENAME)
  1241  	return u.String()
  1242  }
  1243  
  1244  // GetOSLRegistryFilename returns an appropriate filename for
  1245  // the resumable download destination for the OSL registry.
  1246  func GetOSLRegistryFilename(baseDirectory string) string {
  1247  	return filepath.Join(baseDirectory, REGISTRY_FILENAME)
  1248  }
  1249  
  1250  // GetOSLFileURL returns the URL for an OSL file. Once the client
  1251  // has determined, from GetSeededOSLIDs, which OSLs it has sufficiently
  1252  // seeded, it calls this to fetch the OSLs for download and decryption.
  1253  // Clients are responsible for tracking whether the remote file has
  1254  // changed or not before downloading.
  1255  func GetOSLFileURL(baseURL string, oslID []byte) string {
  1256  	u, err := url.Parse(baseURL)
  1257  	if err != nil {
  1258  		return ""
  1259  	}
  1260  	u.Path = path.Join(
  1261  		u.Path, fmt.Sprintf(OSL_FILENAME_FORMAT, hex.EncodeToString(oslID)))
  1262  	return u.String()
  1263  }
  1264  
  1265  // GetOSLFilename returns an appropriate filename for the resumable
  1266  // download destination for the OSL file.
  1267  func GetOSLFilename(baseDirectory string, oslID []byte) string {
  1268  	return filepath.Join(
  1269  		baseDirectory, fmt.Sprintf(OSL_FILENAME_FORMAT, hex.EncodeToString(oslID)))
  1270  }
  1271  
  1272  // SLOKLookup is a callback to lookup SLOK keys by ID.
  1273  type SLOKLookup func([]byte) []byte
  1274  
  1275  // RegistryStreamer authenticates and processes a JSON encoded OSL registry.
  1276  // The streamer processes the registry without loading the entire file
  1277  // into memory, parsing each OSL file spec in turn and returning those
  1278  // OSL file specs for which the client has sufficient SLOKs to reassemble
  1279  // the OSL key and decrypt.
  1280  //
  1281  // At this stage, SLOK reassembly simply does SLOK ID lookups and threshold
  1282  // counting and does not derive keys for every OSL. This allows the client
  1283  // to defer key derivation until NewOSLReader for cases where it has not
  1284  // already imported the OSL.
  1285  //
  1286  // The client's propagation channel ID is used implicitly: it determines the
  1287  // base URL used to download the registry and OSL files. If the client has
  1288  // seeded SLOKs from a propagation channel ID different than the one associated
  1289  // with its present base URL, they will not appear in the registry and not
  1290  // be used.
  1291  type RegistryStreamer struct {
  1292  	jsonDecoder *json.Decoder
  1293  	lookup      SLOKLookup
  1294  }
  1295  
  1296  // NewRegistryStreamer creates a new RegistryStreamer.
  1297  func NewRegistryStreamer(
  1298  	registryFileContent io.ReadSeeker,
  1299  	signingPublicKey string,
  1300  	lookup SLOKLookup) (*RegistryStreamer, error) {
  1301  
  1302  	payloadReader, err := common.NewAuthenticatedDataPackageReader(
  1303  		registryFileContent, signingPublicKey)
  1304  	if err != nil {
  1305  		return nil, errors.Trace(err)
  1306  	}
  1307  
  1308  	base64Decoder := base64.NewDecoder(base64.StdEncoding, payloadReader)
  1309  
  1310  	// A json.Decoder is used to stream the JSON payload, which
  1311  	// is expected to be of the following form, corresponding
  1312  	// to the Registry struct type:
  1313  	//
  1314  	// {"FileSpecs" : [{...}, {...}, ..., {...}]}
  1315  
  1316  	jsonDecoder := json.NewDecoder(base64Decoder)
  1317  
  1318  	err = expectJSONDelimiter(jsonDecoder, "{")
  1319  	if err != nil {
  1320  		return nil, errors.Trace(err)
  1321  	}
  1322  
  1323  	token, err := jsonDecoder.Token()
  1324  	if err != nil {
  1325  		return nil, errors.Trace(err)
  1326  	}
  1327  
  1328  	name, ok := token.(string)
  1329  
  1330  	if !ok {
  1331  		return nil, errors.Trace(
  1332  			fmt.Errorf("unexpected token type: %T", token))
  1333  	}
  1334  
  1335  	if name != "FileSpecs" {
  1336  		return nil, errors.Trace(
  1337  			fmt.Errorf("unexpected field name: %s", name))
  1338  	}
  1339  
  1340  	err = expectJSONDelimiter(jsonDecoder, "[")
  1341  	if err != nil {
  1342  		return nil, errors.Trace(err)
  1343  	}
  1344  
  1345  	return &RegistryStreamer{
  1346  		jsonDecoder: jsonDecoder,
  1347  		lookup:      lookup,
  1348  	}, nil
  1349  }
  1350  
  1351  // Next returns the next OSL file spec that the client
  1352  // has sufficient SLOKs to decrypt. The client calls
  1353  // NewOSLReader with the file spec to process that OSL.
  1354  // Next returns nil at EOF.
  1355  func (s *RegistryStreamer) Next() (*OSLFileSpec, error) {
  1356  
  1357  	for {
  1358  		if s.jsonDecoder.More() {
  1359  
  1360  			var fileSpec OSLFileSpec
  1361  			err := s.jsonDecoder.Decode(&fileSpec)
  1362  			if err != nil {
  1363  				return nil, errors.Trace(err)
  1364  			}
  1365  
  1366  			ok, _, err := fileSpec.KeyShares.reassembleKey(s.lookup, false)
  1367  			if err != nil {
  1368  				return nil, errors.Trace(err)
  1369  			}
  1370  
  1371  			if ok {
  1372  				return &fileSpec, nil
  1373  			}
  1374  
  1375  		} else {
  1376  
  1377  			// Expect the end of the FileSpecs array.
  1378  			err := expectJSONDelimiter(s.jsonDecoder, "]")
  1379  			if err != nil {
  1380  				return nil, errors.Trace(err)
  1381  			}
  1382  
  1383  			// Expect the end of the Registry object.
  1384  			err = expectJSONDelimiter(s.jsonDecoder, "}")
  1385  			if err != nil {
  1386  				return nil, errors.Trace(err)
  1387  			}
  1388  
  1389  			// Expect the end of the registry content.
  1390  			_, err = s.jsonDecoder.Token()
  1391  			if err != io.EOF {
  1392  				return nil, errors.Trace(err)
  1393  			}
  1394  
  1395  			return nil, nil
  1396  		}
  1397  	}
  1398  }
  1399  
  1400  func expectJSONDelimiter(jsonDecoder *json.Decoder, delimiter string) error {
  1401  	token, err := jsonDecoder.Token()
  1402  	if err != nil {
  1403  		return errors.Trace(err)
  1404  	}
  1405  
  1406  	delim, ok := token.(json.Delim)
  1407  
  1408  	if !ok {
  1409  		return errors.Tracef("unexpected token type: %T", token)
  1410  	}
  1411  
  1412  	if delim.String() != delimiter {
  1413  		return errors.Tracef("unexpected delimiter: %s", delim.String())
  1414  	}
  1415  
  1416  	return nil
  1417  }
  1418  
  1419  // NewOSLReader decrypts, authenticates and streams an OSL payload.
  1420  func NewOSLReader(
  1421  	oslFileContent io.ReadSeeker,
  1422  	fileSpec *OSLFileSpec,
  1423  	lookup SLOKLookup,
  1424  	signingPublicKey string) (io.Reader, error) {
  1425  
  1426  	ok, fileKey, err := fileSpec.KeyShares.reassembleKey(lookup, true)
  1427  	if err != nil {
  1428  		return nil, errors.Trace(err)
  1429  	}
  1430  	if !ok {
  1431  		return nil, errors.TraceNew("unseeded OSL")
  1432  	}
  1433  
  1434  	if len(fileKey) != KEY_LENGTH_BYTES {
  1435  		return nil, errors.TraceNew("invalid key length")
  1436  	}
  1437  
  1438  	var nonce [24]byte
  1439  	var key [KEY_LENGTH_BYTES]byte
  1440  	copy(key[:], fileKey)
  1441  
  1442  	unboxer, err := secretbox.NewOpenReadSeeker(oslFileContent, &nonce, &key)
  1443  	if err != nil {
  1444  		return nil, errors.Trace(err)
  1445  	}
  1446  
  1447  	return common.NewAuthenticatedDataPackageReader(
  1448  		unboxer,
  1449  		signingPublicKey)
  1450  }
  1451  
  1452  // zeroReader reads an unlimited stream of zeroes.
  1453  type zeroReader struct {
  1454  }
  1455  
  1456  func (z *zeroReader) Read(p []byte) (int, error) {
  1457  	for i := 0; i < len(p); i++ {
  1458  		p[i] = 0
  1459  	}
  1460  	return len(p), nil
  1461  }
  1462  
  1463  // newSeededKeyMaterialReader constructs a CSPRNG using AES-CTR.
  1464  // The seed is the AES key and the IV is fixed and constant.
  1465  // Using same seed will always produce the same output stream.
  1466  // The data stream is intended to be used to deterministically
  1467  // generate key material and is not intended as a general
  1468  // purpose CSPRNG.
  1469  func newSeededKeyMaterialReader(seed []byte) (io.Reader, error) {
  1470  
  1471  	if len(seed) != KEY_LENGTH_BYTES {
  1472  		return nil, errors.TraceNew("invalid key length")
  1473  	}
  1474  
  1475  	aesCipher, err := aes.NewCipher(seed)
  1476  	if err != nil {
  1477  		return nil, errors.Trace(err)
  1478  	}
  1479  
  1480  	var iv [aes.BlockSize]byte
  1481  
  1482  	return &cipher.StreamReader{
  1483  		S: cipher.NewCTR(aesCipher, iv[:]),
  1484  		R: new(zeroReader),
  1485  	}, nil
  1486  }
  1487  
  1488  // deriveKeyHKDF implements HKDF-Expand as defined in https://tools.ietf.org/html/rfc5869
  1489  // where masterKey = PRK, context = info, and L = 32; SHA-256 is used so HashLen = 32
  1490  func deriveKeyHKDF(masterKey []byte, context ...[]byte) []byte {
  1491  
  1492  	// TODO: use golang.org/x/crypto/hkdf?
  1493  
  1494  	mac := hmac.New(sha256.New, masterKey)
  1495  	for _, item := range context {
  1496  		mac.Write([]byte(item))
  1497  	}
  1498  	mac.Write([]byte{byte(0x01)})
  1499  	return mac.Sum(nil)
  1500  }
  1501  
  1502  // isValidShamirSplit checks sss.Split constraints
  1503  func isValidShamirSplit(total, threshold int) bool {
  1504  	if total < 1 || total > 254 || threshold < 1 || threshold > total {
  1505  		return false
  1506  	}
  1507  	return true
  1508  }
  1509  
  1510  // shamirSplit is a helper wrapper for sss.Split
  1511  func shamirSplit(
  1512  	secret []byte,
  1513  	total, threshold int,
  1514  	randReader io.Reader) ([][]byte, error) {
  1515  
  1516  	if !isValidShamirSplit(total, threshold) {
  1517  		return nil, errors.TraceNew("invalid parameters")
  1518  	}
  1519  
  1520  	if threshold == 1 {
  1521  		// Special case: each share is simply the secret
  1522  		shares := make([][]byte, total)
  1523  		for i := 0; i < total; i++ {
  1524  			shares[i] = secret
  1525  		}
  1526  		return shares, nil
  1527  	}
  1528  
  1529  	shareMap, err := sss.SplitUsingReader(
  1530  		byte(total), byte(threshold), secret, randReader)
  1531  	if err != nil {
  1532  		return nil, errors.Trace(err)
  1533  	}
  1534  
  1535  	shares := make([][]byte, total)
  1536  	for i := 0; i < total; i++ {
  1537  		// Note: sss.Combine index starts at 1
  1538  		shares[i] = shareMap[byte(i)+1]
  1539  	}
  1540  
  1541  	return shares, nil
  1542  }
  1543  
  1544  // shamirCombine is a helper wrapper for sss.Combine
  1545  func shamirCombine(shares [][]byte) []byte {
  1546  
  1547  	if len(shares) == 1 {
  1548  		// Special case: each share is simply the secret
  1549  		return shares[0]
  1550  	}
  1551  
  1552  	// Convert a sparse list into a map
  1553  	shareMap := make(map[byte][]byte)
  1554  	for index, share := range shares {
  1555  		if share != nil {
  1556  			// Note: sss.Combine index starts at 1
  1557  			shareMap[byte(index)+1] = share
  1558  		}
  1559  	}
  1560  
  1561  	return sss.Combine(shareMap)
  1562  }
  1563  
  1564  // box is a helper wrapper for secretbox.Seal.
  1565  // A constant nonce is used, which is secure so long as
  1566  // each key is used to encrypt only one message.
  1567  func box(key, plaintext []byte) ([]byte, error) {
  1568  	if len(key) != KEY_LENGTH_BYTES {
  1569  		return nil, errors.TraceNew("invalid key length")
  1570  	}
  1571  	var nonce [24]byte
  1572  	var secretboxKey [KEY_LENGTH_BYTES]byte
  1573  	copy(secretboxKey[:], key)
  1574  	box := secretbox.Seal(nil, plaintext, &nonce, &secretboxKey)
  1575  	return box, nil
  1576  }
  1577  
  1578  // unbox is a helper wrapper for secretbox.Open
  1579  func unbox(key, box []byte) ([]byte, error) {
  1580  	if len(key) != KEY_LENGTH_BYTES {
  1581  		return nil, errors.TraceNew("invalid key length")
  1582  	}
  1583  	var nonce [24]byte
  1584  	var secretboxKey [KEY_LENGTH_BYTES]byte
  1585  	copy(secretboxKey[:], key)
  1586  	plaintext, ok := secretbox.Open(nil, box, &nonce, &secretboxKey)
  1587  	if !ok {
  1588  		return nil, errors.TraceNew("unbox failed")
  1589  	}
  1590  	return plaintext, nil
  1591  }