osl.go 53 KB

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