osl.go 49 KB

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