osl_test.go 21 KB

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