osl.go 47 KB

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