osl.go 48 KB

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