osl_test.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723
  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
  20. import (
  21. "bytes"
  22. "encoding/base64"
  23. "encoding/hex"
  24. "fmt"
  25. "io/ioutil"
  26. "net"
  27. "testing"
  28. "time"
  29. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  30. )
  31. func TestOSL(t *testing.T) {
  32. configJSONTemplate := `
  33. {
  34. "Schemes" : [
  35. {
  36. "Epoch" : "%s",
  37. "Regions" : ["US", "CA"],
  38. "PropagationChannelIDs" : ["2995DB0C968C59C4F23E87988D9C0D41", "E742C25A6D8BA8C17F37E725FA628569", "B4A780E67695595FA486E9B900EA7335"],
  39. "MasterKey" : "wFuSbqU/pJ/35vRmoM8T9ys1PgDa8uzJps1Y+FNKa5U=",
  40. "SeedSpecs" : [
  41. {
  42. "Description": "spec1",
  43. "ID" : "IXHWfVgWFkEKvgqsjmnJuN3FpaGuCzQMETya+DSQvsk=",
  44. "UpstreamSubnets" : ["192.168.0.0/16", "172.16.0.0/12"],
  45. "Targets" :
  46. {
  47. "BytesRead" : 1,
  48. "BytesWritten" : 1,
  49. "PortForwardDurationNanoseconds" : 1
  50. }
  51. },
  52. {
  53. "Description": "spec2",
  54. "ID" : "qvpIcORLE2Pi5TZmqRtVkEp+OKov0MhfsYPLNV7FYtI=",
  55. "UpstreamSubnets" : ["192.168.0.0/16", "10.0.0.0/8"],
  56. "UpstreamASNs" : ["0000"],
  57. "Targets" :
  58. {
  59. "BytesRead" : 10,
  60. "BytesWritten" : 10,
  61. "PortForwardDurationNanoseconds" : 10
  62. }
  63. },
  64. {
  65. "Description": "spec3",
  66. "ID" : "ts5LInjFHbVKX+/C5/bSJqUh+cLT5kJy92TZGLvAtPU=",
  67. "UpstreamSubnets" : ["100.64.0.0/10"],
  68. "Targets" :
  69. {
  70. "BytesRead" : 100,
  71. "BytesWritten" : 100,
  72. "PortForwardDurationNanoseconds" : 100
  73. }
  74. }
  75. ],
  76. "SeedSpecThreshold" : 2,
  77. "SeedPeriodNanoseconds" : 5000000,
  78. "SeedPeriodKeySplits": [
  79. {
  80. "Total": 10,
  81. "Threshold": 5
  82. },
  83. {
  84. "Total": 10,
  85. "Threshold": 5
  86. }
  87. ]
  88. },
  89. {
  90. "Epoch" : "%s",
  91. "Regions" : ["US", "CA"],
  92. "PropagationChannelIDs" : ["36F1CF2DF1250BF0C7BA0629CE3DC657", "B4A780E67695595FA486E9B900EA7335"],
  93. "MasterKey" : "fcyQy8JSxLXHt/Iom9Qj9wMnSjrsccTiiSPEsJicet4=",
  94. "SeedSpecs" : [
  95. {
  96. "Description": "spec1",
  97. "ID" : "NXY0/4lqMxx5XIszIhMbwHobH/qb2Gl0Bw/OGndc1vM=",
  98. "UpstreamSubnets" : ["192.168.0.0/16", "172.16.0.0/12"],
  99. "Targets" :
  100. {
  101. "BytesRead" : 1,
  102. "BytesWritten" : 1,
  103. "PortForwardDurationNanoseconds" : 1
  104. }
  105. },
  106. {
  107. "Description": "spec2",
  108. "ID" : "o78G6muv3idtbQKXoU05tF6gTlQj1LHmNe0eUWkZGxs=",
  109. "UpstreamSubnets" : ["192.168.0.0/16", "10.0.0.0/8"],
  110. "Targets" :
  111. {
  112. "BytesRead" : 10,
  113. "BytesWritten" : 10,
  114. "PortForwardDurationNanoseconds" : 10
  115. }
  116. },
  117. {
  118. "Description": "spec3",
  119. "ID" : "1DlAvJYpoSEfcqMXYBV7bDEtYu3LCQO39ISD5tmi8Uo=",
  120. "UpstreamSubnets" : ["100.64.0.0/10"],
  121. "Targets" :
  122. {
  123. "BytesRead" : 0,
  124. "BytesWritten" : 0,
  125. "PortForwardDurationNanoseconds" : 0
  126. }
  127. }
  128. ],
  129. "SeedSpecThreshold" : 2,
  130. "SeedPeriodNanoseconds" : 5000000,
  131. "SeedPeriodKeySplits": [
  132. {
  133. "Total": 100,
  134. "Threshold": 25
  135. }
  136. ]
  137. }
  138. ]
  139. }
  140. `
  141. seedPeriod := 5 * time.Millisecond // "SeedPeriodNanoseconds" : 5000000
  142. now := time.Now().UTC()
  143. epoch := now.Add(-seedPeriod).Truncate(seedPeriod)
  144. epochStr := epoch.Format(time.RFC3339Nano)
  145. configJSON := fmt.Sprintf(configJSONTemplate, epochStr, epochStr)
  146. // The first scheme requires sufficient activity within 5/10 5 millisecond
  147. // periods and 5/10 50 millisecond longer periods. The second scheme requires
  148. // sufficient activity within 25/100 5 millisecond periods.
  149. config, err := LoadConfig([]byte(configJSON))
  150. if err != nil {
  151. t.Fatalf("LoadConfig failed: %s", err)
  152. }
  153. portForwardASN := new(string)
  154. lookupASN := func(net.IP) string {
  155. return *portForwardASN
  156. }
  157. t.Run("ineligible client, sufficient transfer", func(t *testing.T) {
  158. clientSeedState := config.NewClientSeedState("US", "C5E8D2EDFD093B50D8D65CF59D0263CA", nil)
  159. seedPortForward := clientSeedState.NewClientSeedPortForward(net.ParseIP("192.168.0.1"), lookupASN)
  160. if seedPortForward != nil {
  161. t.Fatalf("expected nil client seed port forward")
  162. }
  163. })
  164. // This clientSeedState is used across multiple tests.
  165. signalIssueSLOKs := make(chan struct{}, 1)
  166. clientSeedState := config.NewClientSeedState("US", "2995DB0C968C59C4F23E87988D9C0D41", signalIssueSLOKs)
  167. t.Run("eligible client, no transfer", func(t *testing.T) {
  168. if len(clientSeedState.GetSeedPayload().SLOKs) != 0 {
  169. t.Fatalf("expected 0 SLOKs, got %d", len(clientSeedState.GetSeedPayload().SLOKs))
  170. }
  171. })
  172. t.Run("eligible client, insufficient transfer", func(t *testing.T) {
  173. clientSeedState.NewClientSeedPortForward(net.ParseIP("10.0.0.1"), lookupASN).UpdateProgress(5, 5, 5)
  174. if len(clientSeedState.GetSeedPayload().SLOKs) != 0 {
  175. t.Fatalf("expected 0 SLOKs, got %d", len(clientSeedState.GetSeedPayload().SLOKs))
  176. }
  177. })
  178. rolloverToNextSLOKTime := func() {
  179. // Rollover to the next SLOK time, so accrued data transfer will be reset.
  180. now := time.Now().UTC()
  181. time.Sleep(now.Add(seedPeriod).Truncate(seedPeriod).Sub(now))
  182. }
  183. t.Run("eligible client, insufficient transfer after rollover", func(t *testing.T) {
  184. rolloverToNextSLOKTime()
  185. clientSeedState.NewClientSeedPortForward(net.ParseIP("10.0.0.1"), lookupASN).UpdateProgress(5, 5, 5)
  186. if len(clientSeedState.GetSeedPayload().SLOKs) != 0 {
  187. t.Fatalf("expected 0 SLOKs, got %d", len(clientSeedState.GetSeedPayload().SLOKs))
  188. }
  189. })
  190. t.Run("eligible client, sufficient transfer, one port forward, match by ip", func(t *testing.T) {
  191. rolloverToNextSLOKTime()
  192. clientSeedPortForward := clientSeedState.NewClientSeedPortForward(net.ParseIP("10.0.0.1"), lookupASN)
  193. clientSeedPortForward.UpdateProgress(5, 5, 5)
  194. clientSeedPortForward.UpdateProgress(5, 5, 5)
  195. select {
  196. case <-signalIssueSLOKs:
  197. default:
  198. t.Fatalf("expected issue SLOKs signal")
  199. }
  200. if len(clientSeedState.GetSeedPayload().SLOKs) != 1 {
  201. t.Fatalf("expected 1 SLOKs, got %d", len(clientSeedState.GetSeedPayload().SLOKs))
  202. }
  203. })
  204. t.Run("eligible client, sufficient transfer, one port forward, match by asn", func(t *testing.T) {
  205. rolloverToNextSLOKTime()
  206. *portForwardASN = "0000"
  207. clientSeedPortForward := clientSeedState.NewClientSeedPortForward(net.ParseIP("11.0.0.1"), lookupASN)
  208. clientSeedPortForward.UpdateProgress(5, 5, 5)
  209. clientSeedPortForward.UpdateProgress(5, 5, 5)
  210. *portForwardASN = ""
  211. select {
  212. case <-signalIssueSLOKs:
  213. default:
  214. t.Fatalf("expected issue SLOKs signal")
  215. }
  216. // Expect 2 SLOKS: 1 new, and 1 remaining in payload.
  217. if len(clientSeedState.GetSeedPayload().SLOKs) != 2 {
  218. t.Fatalf("expected 2 SLOKs, got %d", len(clientSeedState.GetSeedPayload().SLOKs))
  219. }
  220. })
  221. t.Run("eligible client, sufficient transfer, one port forward, match by ip and asn", func(t *testing.T) {
  222. rolloverToNextSLOKTime()
  223. *portForwardASN = "0000"
  224. clientSeedPortForward := clientSeedState.NewClientSeedPortForward(net.ParseIP("10.0.0.1"), lookupASN)
  225. clientSeedPortForward.UpdateProgress(5, 5, 5)
  226. // Check that progress is not double counted.
  227. if len(clientSeedState.GetSeedPayload().SLOKs) != 2 {
  228. t.Fatalf("expected 2 SLOKs, got %d", len(clientSeedState.GetSeedPayload().SLOKs))
  229. }
  230. clientSeedPortForward.UpdateProgress(5, 5, 5)
  231. *portForwardASN = ""
  232. select {
  233. case <-signalIssueSLOKs:
  234. default:
  235. t.Fatalf("expected issue SLOKs signal")
  236. }
  237. // Expect 3 SLOKS: 1 new, and 2 remaining in payload.
  238. if len(clientSeedState.GetSeedPayload().SLOKs) != 3 {
  239. t.Fatalf("expected 3 SLOKs, got %d", len(clientSeedState.GetSeedPayload().SLOKs))
  240. }
  241. })
  242. t.Run("eligible client, sufficient transfer, multiple port forwards", func(t *testing.T) {
  243. rolloverToNextSLOKTime()
  244. clientSeedState.NewClientSeedPortForward(net.ParseIP("10.0.0.1"), lookupASN).UpdateProgress(5, 5, 5)
  245. clientSeedState.NewClientSeedPortForward(net.ParseIP("10.0.0.1"), lookupASN).UpdateProgress(5, 5, 5)
  246. select {
  247. case <-signalIssueSLOKs:
  248. default:
  249. t.Fatalf("expected issue SLOKs signal")
  250. }
  251. // Expect 4 SLOKS: 1 new, and 3 remaining in payload.
  252. if len(clientSeedState.GetSeedPayload().SLOKs) != 4 {
  253. t.Fatalf("expected 4 SLOKs, got %d", len(clientSeedState.GetSeedPayload().SLOKs))
  254. }
  255. })
  256. t.Run("eligible client, sufficient transfer multiple SLOKs", func(t *testing.T) {
  257. rolloverToNextSLOKTime()
  258. clientSeedState.NewClientSeedPortForward(net.ParseIP("192.168.0.1"), lookupASN).UpdateProgress(5, 5, 5)
  259. clientSeedState.NewClientSeedPortForward(net.ParseIP("10.0.0.1"), lookupASN).UpdateProgress(5, 5, 5)
  260. select {
  261. case <-signalIssueSLOKs:
  262. default:
  263. t.Fatalf("expected issue SLOKs signal")
  264. }
  265. // Expect 6 SLOKS: 2 new, and 4 remaining in payload.
  266. if len(clientSeedState.GetSeedPayload().SLOKs) != 6 {
  267. t.Fatalf("expected 6 SLOKs, got %d", len(clientSeedState.GetSeedPayload().SLOKs))
  268. }
  269. })
  270. t.Run("clear payload", func(t *testing.T) {
  271. clientSeedState.ClearSeedPayload()
  272. if len(clientSeedState.GetSeedPayload().SLOKs) != 0 {
  273. t.Fatalf("expected 0 SLOKs, got %d", len(clientSeedState.GetSeedPayload().SLOKs))
  274. }
  275. })
  276. t.Run("no transfer required", func(t *testing.T) {
  277. rolloverToNextSLOKTime()
  278. clientSeedState := config.NewClientSeedState("US", "36F1CF2DF1250BF0C7BA0629CE3DC657", nil)
  279. if len(clientSeedState.GetSeedPayload().SLOKs) != 1 {
  280. t.Fatalf("expected 1 SLOKs, got %d", len(clientSeedState.GetSeedPayload().SLOKs))
  281. }
  282. })
  283. t.Run("concurrent schemes", func(t *testing.T) {
  284. rolloverToNextSLOKTime()
  285. clientSeedState := config.NewClientSeedState("US", "B4A780E67695595FA486E9B900EA7335", nil)
  286. clientSeedPortForward := clientSeedState.NewClientSeedPortForward(net.ParseIP("192.168.0.1"), lookupASN)
  287. clientSeedPortForward.UpdateProgress(10, 10, 10)
  288. if len(clientSeedState.GetSeedPayload().SLOKs) != 5 {
  289. t.Fatalf("expected 5 SLOKs, got %d", len(clientSeedState.GetSeedPayload().SLOKs))
  290. }
  291. })
  292. signingPublicKey, signingPrivateKey, err := common.GenerateAuthenticatedDataPackageKeys()
  293. if err != nil {
  294. t.Fatalf("GenerateAuthenticatedDataPackageKeys failed: %s", err)
  295. }
  296. pavedRegistries := make(map[string][]byte)
  297. pavedOSLFileContents := make(map[string]map[string][]byte)
  298. t.Run("pave OSLs", func(t *testing.T) {
  299. // Pave sufficient OSLs to cover simulated elapsed time of all test cases.
  300. endTime := epoch.Add(1000 * seedPeriod)
  301. // In actual deployment, paved files for each propagation channel ID
  302. // are dropped in distinct distribution sites.
  303. for _, propagationChannelID := range []string{
  304. "2995DB0C968C59C4F23E87988D9C0D41",
  305. "E742C25A6D8BA8C17F37E725FA628569",
  306. "36F1CF2DF1250BF0C7BA0629CE3DC657"} {
  307. // Dummy server entry payloads will be the OSL ID, which the following
  308. // tests use to verify that the correct OSL file decrypts successfully.
  309. paveServerEntries := make(map[string][]string)
  310. for _, scheme := range config.Schemes {
  311. oslDuration := scheme.GetOSLDuration()
  312. oslTime := scheme.epoch
  313. for oslTime.Before(endTime) {
  314. firstSLOKRef := &slokReference{
  315. PropagationChannelID: propagationChannelID,
  316. SeedSpecID: string(scheme.SeedSpecs[0].ID),
  317. Time: oslTime,
  318. }
  319. firstSLOK := scheme.deriveSLOK(firstSLOKRef)
  320. oslID := firstSLOK.ID
  321. paveServerEntries[hex.EncodeToString(oslID)] =
  322. []string{base64.StdEncoding.EncodeToString(oslID)}
  323. oslTime = oslTime.Add(oslDuration)
  324. }
  325. }
  326. // Note: these options are exercised in remoteServerList_test.go
  327. omitMD5SumsSchemes := []int{}
  328. omitEmptyOSLsSchemes := []int{}
  329. firstPaveFiles, err := config.Pave(
  330. time.Time{},
  331. endTime,
  332. propagationChannelID,
  333. signingPublicKey,
  334. signingPrivateKey,
  335. paveServerEntries,
  336. omitMD5SumsSchemes,
  337. omitEmptyOSLsSchemes,
  338. nil)
  339. if err != nil {
  340. t.Fatalf("Pave failed: %s", err)
  341. }
  342. offsetPaveFiles, err := config.Pave(
  343. epoch.Add(500*seedPeriod+seedPeriod/2),
  344. endTime,
  345. propagationChannelID,
  346. signingPublicKey,
  347. signingPrivateKey,
  348. paveServerEntries,
  349. omitMD5SumsSchemes,
  350. omitEmptyOSLsSchemes,
  351. nil)
  352. if err != nil {
  353. t.Fatalf("Pave failed: %s", err)
  354. }
  355. paveFiles, err := config.Pave(
  356. time.Time{},
  357. endTime,
  358. propagationChannelID,
  359. signingPublicKey,
  360. signingPrivateKey,
  361. paveServerEntries,
  362. omitMD5SumsSchemes,
  363. omitEmptyOSLsSchemes,
  364. nil)
  365. if err != nil {
  366. t.Fatalf("Pave failed: %s", err)
  367. }
  368. // Check that the paved file name matches the name the client will look for.
  369. if len(paveFiles) < 1 || paveFiles[len(paveFiles)-1].Name != GetOSLRegistryURL("") {
  370. t.Fatalf("invalid registry pave file")
  371. }
  372. // Check that the content of two paves is the same: all the crypto should be
  373. // deterministic.
  374. for index, paveFile := range paveFiles {
  375. if paveFile.Name != firstPaveFiles[index].Name {
  376. t.Fatalf("pave name mismatch")
  377. }
  378. if !bytes.Equal(paveFile.Contents, firstPaveFiles[index].Contents) {
  379. t.Fatalf("pave content mismatch")
  380. }
  381. }
  382. // Check that the output of a pave using an unaligned offset from epoch
  383. // produces a subset of OSLs with the same IDs and content: the OSL and
  384. // SLOK time slots must align.
  385. if len(offsetPaveFiles) >= len(paveFiles) {
  386. t.Fatalf("unexpected pave size")
  387. }
  388. for _, offsetPaveFile := range offsetPaveFiles {
  389. found := false
  390. for _, paveFile := range paveFiles {
  391. if offsetPaveFile.Name == paveFile.Name {
  392. if offsetPaveFile.Name != GetOSLRegistryURL("") &&
  393. !bytes.Equal(offsetPaveFile.Contents, paveFile.Contents) {
  394. t.Fatalf("pave content mismatch")
  395. }
  396. found = true
  397. break
  398. }
  399. }
  400. if !found {
  401. t.Fatalf("pave name missing")
  402. }
  403. }
  404. // Use the paved content in the following tests.
  405. pavedRegistries[propagationChannelID] = paveFiles[len(paveFiles)-1].Contents
  406. pavedOSLFileContents[propagationChannelID] = make(map[string][]byte)
  407. for _, paveFile := range paveFiles[0:] {
  408. pavedOSLFileContents[propagationChannelID][paveFile.Name] = paveFile.Contents
  409. }
  410. }
  411. })
  412. if len(pavedRegistries) != 3 {
  413. // Previous subtest failed. Following tests cannot be completed, so abort.
  414. t.Fatalf("pave failed")
  415. }
  416. // To ensure SLOKs are issued at precise time periods, the following tests
  417. // bypass ClientSeedState and derive SLOKs directly.
  418. expandRanges := func(ranges ...[2]int) []int {
  419. a := make([]int, 0)
  420. for _, r := range ranges {
  421. for n := r[0]; n <= r[1]; n++ {
  422. a = append(a, n)
  423. }
  424. }
  425. return a
  426. }
  427. singleSplitPropagationChannelID := "36F1CF2DF1250BF0C7BA0629CE3DC657"
  428. singleSplitScheme := config.Schemes[1]
  429. doubleSplitPropagationChannelID := "2995DB0C968C59C4F23E87988D9C0D41"
  430. doubleSplitScheme := config.Schemes[0]
  431. keySplitTestCases := []struct {
  432. description string
  433. propagationChannelID string
  434. scheme *Scheme
  435. issueSLOKTimePeriods []int
  436. issueSLOKSeedSpecIndexes []int
  437. expectedOSLCount int
  438. }{
  439. {
  440. "single split scheme: insufficient SLOK periods",
  441. singleSplitPropagationChannelID,
  442. singleSplitScheme,
  443. expandRanges([2]int{0, 23}),
  444. []int{0, 1},
  445. 0,
  446. },
  447. {
  448. "single split scheme: insufficient SLOK seed specs",
  449. singleSplitPropagationChannelID,
  450. singleSplitScheme,
  451. expandRanges([2]int{0, 23}),
  452. []int{0},
  453. 0,
  454. },
  455. {
  456. "single split scheme: sufficient SLOKs",
  457. singleSplitPropagationChannelID,
  458. singleSplitScheme,
  459. expandRanges([2]int{0, 24}),
  460. []int{0, 1},
  461. 1,
  462. },
  463. {
  464. "single split scheme: sufficient SLOKs (alternative seed specs)",
  465. singleSplitPropagationChannelID,
  466. singleSplitScheme,
  467. expandRanges([2]int{0, 24}),
  468. []int{1, 2},
  469. 1,
  470. },
  471. {
  472. "single split scheme: more than sufficient SLOKs",
  473. singleSplitPropagationChannelID,
  474. singleSplitScheme,
  475. expandRanges([2]int{0, 49}),
  476. []int{0, 1},
  477. 1,
  478. },
  479. {
  480. "double split scheme: insufficient SLOK periods",
  481. doubleSplitPropagationChannelID,
  482. doubleSplitScheme,
  483. expandRanges([2]int{0, 4}, [2]int{10, 14}, [2]int{20, 24}, [2]int{30, 34}, [2]int{40, 43}),
  484. []int{0, 1},
  485. 0,
  486. },
  487. {
  488. "double split scheme: insufficient SLOK period spread",
  489. doubleSplitPropagationChannelID,
  490. doubleSplitScheme,
  491. expandRanges([2]int{0, 25}),
  492. []int{0, 1},
  493. 0,
  494. },
  495. {
  496. "double split scheme: insufficient SLOK seed specs",
  497. doubleSplitPropagationChannelID,
  498. doubleSplitScheme,
  499. expandRanges([2]int{0, 4}, [2]int{10, 14}, [2]int{20, 24}, [2]int{30, 34}, [2]int{40, 44}),
  500. []int{0},
  501. 0,
  502. },
  503. {
  504. "double split scheme: sufficient SLOKs",
  505. doubleSplitPropagationChannelID,
  506. doubleSplitScheme,
  507. expandRanges([2]int{0, 4}, [2]int{10, 14}, [2]int{20, 24}, [2]int{30, 34}, [2]int{40, 44}),
  508. []int{0, 1},
  509. 1,
  510. },
  511. {
  512. "double split scheme: sufficient SLOKs (alternative seed specs)",
  513. doubleSplitPropagationChannelID,
  514. doubleSplitScheme,
  515. expandRanges([2]int{0, 4}, [2]int{10, 14}, [2]int{20, 24}, [2]int{30, 34}, [2]int{40, 44}),
  516. []int{1, 2},
  517. 1,
  518. },
  519. }
  520. for _, testCase := range keySplitTestCases {
  521. t.Run(testCase.description, func(t *testing.T) {
  522. slokMap := make(map[string][]byte)
  523. for _, timePeriod := range testCase.issueSLOKTimePeriods {
  524. for _, seedSpecIndex := range testCase.issueSLOKSeedSpecIndexes {
  525. slok := testCase.scheme.deriveSLOK(
  526. &slokReference{
  527. PropagationChannelID: testCase.propagationChannelID,
  528. SeedSpecID: string(testCase.scheme.SeedSpecs[seedSpecIndex].ID),
  529. Time: epoch.Add(time.Duration(timePeriod) * seedPeriod),
  530. })
  531. slokMap[string(slok.ID)] = slok.Key
  532. }
  533. }
  534. startTime := time.Now()
  535. lookupSLOKs := func(slokID []byte) []byte {
  536. return slokMap[string(slokID)]
  537. }
  538. registryStreamer, err := NewRegistryStreamer(
  539. bytes.NewReader(pavedRegistries[testCase.propagationChannelID]),
  540. signingPublicKey,
  541. lookupSLOKs)
  542. if err != nil {
  543. t.Fatalf("NewRegistryStreamer failed: %s", err)
  544. }
  545. seededOSLCount := 0
  546. for {
  547. fileSpec, err := registryStreamer.Next()
  548. if err != nil {
  549. t.Fatalf("Next failed: %s", err)
  550. }
  551. if fileSpec == nil {
  552. break
  553. }
  554. seededOSLCount += 1
  555. oslFileContents, ok :=
  556. pavedOSLFileContents[testCase.propagationChannelID][GetOSLFileURL("", fileSpec.ID)]
  557. if !ok {
  558. t.Fatalf("unknown OSL file name")
  559. }
  560. payloadReader, err := NewOSLReader(
  561. bytes.NewReader(oslFileContents),
  562. fileSpec,
  563. lookupSLOKs,
  564. signingPublicKey)
  565. if err != nil {
  566. t.Fatalf("NewOSLReader failed: %s", err)
  567. }
  568. payload, err := ioutil.ReadAll(payloadReader)
  569. if err != nil {
  570. t.Fatalf("ReadAll failed: %s", err)
  571. }
  572. // The decrypted OSL should contain its own ID.
  573. if string(payload) != base64.StdEncoding.EncodeToString(fileSpec.ID) {
  574. t.Fatalf("unexpected OSL file contents")
  575. }
  576. }
  577. t.Logf("registry size: %d", len(pavedRegistries[testCase.propagationChannelID]))
  578. t.Logf("SLOK count: %d", len(slokMap))
  579. t.Logf("seeded OSL count: %d", seededOSLCount)
  580. t.Logf("elapsed time: %s", time.Since(startTime))
  581. if seededOSLCount != testCase.expectedOSLCount {
  582. t.Fatalf("expected %d OSLs got %d", testCase.expectedOSLCount, seededOSLCount)
  583. }
  584. })
  585. }
  586. }