ca_pool_test.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. package cert
  2. import (
  3. "net/netip"
  4. "testing"
  5. "time"
  6. "github.com/slackhq/nebula/cert/p256"
  7. "github.com/stretchr/testify/assert"
  8. "github.com/stretchr/testify/require"
  9. )
  10. func TestNewCAPoolFromBytes(t *testing.T) {
  11. noNewLines := `
  12. # Current provisional, Remove once everything moves over to the real root.
  13. -----BEGIN NEBULA CERTIFICATE-----
  14. Cj4KDm5lYnVsYSByb290IGNhKM0cMM24zPCvBzogV24YEw5YiqeI/oYo8XXFsoo+
  15. PBmiOafNJhLacf9rsspAARJAz9OAnh8TKAUKix1kKVMyQU4iM3LsFfZRf6ODWXIf
  16. 2qWMpB6fpd3PSoVYziPoOt2bIHIFLlgRLPJz3I3xBEdBCQ==
  17. -----END NEBULA CERTIFICATE-----
  18. # root-ca01
  19. -----BEGIN NEBULA CERTIFICATE-----
  20. CkEKEW5lYnVsYSByb290IGNhIDAxKM0cMM24zPCvBzogPzbWTxt8ZgXPQEwup7Br
  21. BrtIt1O0q5AuTRT3+t2x1VJAARJAZ+2ib23qBXjdy49oU1YysrwuKkWWKrtJ7Jye
  22. rFBQpDXikOukhQD/mfkloFwJ+Yjsfru7IpTN4ZfjXL+kN/2sCA==
  23. -----END NEBULA CERTIFICATE-----
  24. `
  25. withNewLines := `
  26. # Current provisional, Remove once everything moves over to the real root.
  27. -----BEGIN NEBULA CERTIFICATE-----
  28. Cj4KDm5lYnVsYSByb290IGNhKM0cMM24zPCvBzogV24YEw5YiqeI/oYo8XXFsoo+
  29. PBmiOafNJhLacf9rsspAARJAz9OAnh8TKAUKix1kKVMyQU4iM3LsFfZRf6ODWXIf
  30. 2qWMpB6fpd3PSoVYziPoOt2bIHIFLlgRLPJz3I3xBEdBCQ==
  31. -----END NEBULA CERTIFICATE-----
  32. # root-ca01
  33. -----BEGIN NEBULA CERTIFICATE-----
  34. CkEKEW5lYnVsYSByb290IGNhIDAxKM0cMM24zPCvBzogPzbWTxt8ZgXPQEwup7Br
  35. BrtIt1O0q5AuTRT3+t2x1VJAARJAZ+2ib23qBXjdy49oU1YysrwuKkWWKrtJ7Jye
  36. rFBQpDXikOukhQD/mfkloFwJ+Yjsfru7IpTN4ZfjXL+kN/2sCA==
  37. -----END NEBULA CERTIFICATE-----
  38. `
  39. expired := `
  40. # expired certificate
  41. -----BEGIN NEBULA CERTIFICATE-----
  42. CjMKB2V4cGlyZWQozRwwzRw6ICJSG94CqX8wn5I65Pwn25V6HftVfWeIySVtp2DA
  43. 7TY/QAESQMaAk5iJT5EnQwK524ZaaHGEJLUqqbh5yyOHhboIGiVTWkFeH3HccTW8
  44. Tq5a8AyWDQdfXbtEZ1FwabeHfH5Asw0=
  45. -----END NEBULA CERTIFICATE-----
  46. `
  47. p256 := `
  48. # p256 certificate
  49. -----BEGIN NEBULA CERTIFICATE-----
  50. CmQKEG5lYnVsYSBQMjU2IHRlc3QozRwwzbjM8K8HOkEEdrmmg40zQp44AkMq6DZp
  51. k+coOv04r+zh33ISyhbsafnYduN17p2eD7CmHvHuerguXD9f32gcxo/KsFCKEjMe
  52. +0ABoAYBEkcwRQIgVoTg38L7uWku9xQgsr06kxZ/viQLOO/w1Qj1vFUEnhcCIQCq
  53. 75SjTiV92kv/1GcbT3wWpAZQQDBiUHVMVmh1822szA==
  54. -----END NEBULA CERTIFICATE-----
  55. `
  56. rootCA := certificateV1{
  57. details: detailsV1{
  58. name: "nebula root ca",
  59. },
  60. }
  61. rootCA01 := certificateV1{
  62. details: detailsV1{
  63. name: "nebula root ca 01",
  64. },
  65. }
  66. rootCAP256 := certificateV1{
  67. details: detailsV1{
  68. name: "nebula P256 test",
  69. },
  70. }
  71. p, err := NewCAPoolFromPEM([]byte(noNewLines))
  72. require.NoError(t, err)
  73. assert.Equal(t, p.CAs["ce4e6c7a596996eb0d82a8875f0f0137a4b53ce22d2421c9fd7150e7a26f6300"].Certificate.Name(), rootCA.details.name)
  74. assert.Equal(t, p.CAs["04c585fcd9a49b276df956a22b7ebea3bf23f1fca5a17c0b56ce2e626631969e"].Certificate.Name(), rootCA01.details.name)
  75. pp, err := NewCAPoolFromPEM([]byte(withNewLines))
  76. require.NoError(t, err)
  77. assert.Equal(t, pp.CAs["ce4e6c7a596996eb0d82a8875f0f0137a4b53ce22d2421c9fd7150e7a26f6300"].Certificate.Name(), rootCA.details.name)
  78. assert.Equal(t, pp.CAs["04c585fcd9a49b276df956a22b7ebea3bf23f1fca5a17c0b56ce2e626631969e"].Certificate.Name(), rootCA01.details.name)
  79. // expired cert, no valid certs
  80. ppp, err := NewCAPoolFromPEM([]byte(expired))
  81. assert.Equal(t, ErrExpired, err)
  82. assert.Equal(t, "expired", ppp.CAs["c39b35a0e8f246203fe4f32b9aa8bfd155f1ae6a6be9d78370641e43397f48f5"].Certificate.Name())
  83. // expired cert, with valid certs
  84. pppp, err := NewCAPoolFromPEM(append([]byte(expired), noNewLines...))
  85. assert.Equal(t, ErrExpired, err)
  86. assert.Equal(t, pppp.CAs["ce4e6c7a596996eb0d82a8875f0f0137a4b53ce22d2421c9fd7150e7a26f6300"].Certificate.Name(), rootCA.details.name)
  87. assert.Equal(t, pppp.CAs["04c585fcd9a49b276df956a22b7ebea3bf23f1fca5a17c0b56ce2e626631969e"].Certificate.Name(), rootCA01.details.name)
  88. assert.Equal(t, "expired", pppp.CAs["c39b35a0e8f246203fe4f32b9aa8bfd155f1ae6a6be9d78370641e43397f48f5"].Certificate.Name())
  89. assert.Len(t, pppp.CAs, 3)
  90. ppppp, err := NewCAPoolFromPEM([]byte(p256))
  91. require.NoError(t, err)
  92. assert.Equal(t, ppppp.CAs["552bf7d99bec1fc775a0e4c324bf6d8f789b3078f1919c7960d2e5e0c351ee97"].Certificate.Name(), rootCAP256.details.name)
  93. assert.Len(t, ppppp.CAs, 1)
  94. }
  95. func TestCertificateV1_Verify(t *testing.T) {
  96. ca, _, caKey, _ := NewTestCaCert(Version1, Curve_CURVE25519, time.Now(), time.Now().Add(10*time.Minute), nil, nil, nil)
  97. c, _, _, _ := NewTestCert(Version1, Curve_CURVE25519, ca, caKey, "test cert", time.Now(), time.Now().Add(5*time.Minute), nil, nil, nil)
  98. caPool := NewCAPool()
  99. require.NoError(t, caPool.AddCA(ca))
  100. f, err := c.Fingerprint()
  101. require.NoError(t, err)
  102. caPool.BlocklistFingerprint(f)
  103. _, err = caPool.VerifyCertificate(time.Now(), c)
  104. require.EqualError(t, err, "certificate is in the block list")
  105. caPool.ResetCertBlocklist()
  106. _, err = caPool.VerifyCertificate(time.Now(), c)
  107. require.NoError(t, err)
  108. _, err = caPool.VerifyCertificate(time.Now().Add(time.Hour*1000), c)
  109. require.EqualError(t, err, "root certificate is expired")
  110. assert.PanicsWithError(t, "certificate is valid before the signing certificate", func() {
  111. NewTestCert(Version1, Curve_CURVE25519, ca, caKey, "test cert2", time.Time{}, time.Time{}, nil, nil, nil)
  112. })
  113. // Test group assertion
  114. ca, _, caKey, _ = NewTestCaCert(Version1, Curve_CURVE25519, time.Now(), time.Now().Add(10*time.Minute), nil, nil, []string{"test1", "test2"})
  115. caPem, err := ca.MarshalPEM()
  116. require.NoError(t, err)
  117. caPool = NewCAPool()
  118. b, err := caPool.AddCAFromPEM(caPem)
  119. require.NoError(t, err)
  120. assert.Empty(t, b)
  121. assert.PanicsWithError(t, "certificate contained a group not present on the signing ca: bad", func() {
  122. NewTestCert(Version1, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, nil, []string{"test1", "bad"})
  123. })
  124. c, _, _, _ = NewTestCert(Version1, Curve_CURVE25519, ca, caKey, "test2", time.Now(), time.Now().Add(5*time.Minute), nil, nil, []string{"test1"})
  125. require.NoError(t, err)
  126. _, err = caPool.VerifyCertificate(time.Now(), c)
  127. require.NoError(t, err)
  128. }
  129. func TestCertificateV1_VerifyP256(t *testing.T) {
  130. ca, _, caKey, _ := NewTestCaCert(Version1, Curve_P256, time.Now(), time.Now().Add(10*time.Minute), nil, nil, nil)
  131. c, _, _, _ := NewTestCert(Version1, Curve_P256, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, nil, nil)
  132. caPool := NewCAPool()
  133. require.NoError(t, caPool.AddCA(ca))
  134. f, err := c.Fingerprint()
  135. require.NoError(t, err)
  136. caPool.BlocklistFingerprint(f)
  137. _, err = caPool.VerifyCertificate(time.Now(), c)
  138. require.EqualError(t, err, "certificate is in the block list")
  139. // Create a copy of the cert and swap to the alternate form for the signature
  140. nc := c.Copy()
  141. b, err := p256.Swap(c.Signature())
  142. require.NoError(t, err)
  143. require.NoError(t, nc.(*certificateV1).setSignature(b))
  144. _, err = caPool.VerifyCertificate(time.Now(), nc)
  145. require.EqualError(t, err, "certificate is in the block list")
  146. caPool.ResetCertBlocklist()
  147. _, err = caPool.VerifyCertificate(time.Now(), c)
  148. require.NoError(t, err)
  149. _, err = caPool.VerifyCertificate(time.Now().Add(time.Hour*1000), c)
  150. require.EqualError(t, err, "root certificate is expired")
  151. assert.PanicsWithError(t, "certificate is valid before the signing certificate", func() {
  152. NewTestCert(Version1, Curve_P256, ca, caKey, "test", time.Time{}, time.Time{}, nil, nil, nil)
  153. })
  154. // Test group assertion
  155. ca, _, caKey, _ = NewTestCaCert(Version1, Curve_P256, time.Now(), time.Now().Add(10*time.Minute), nil, nil, []string{"test1", "test2"})
  156. caPem, err := ca.MarshalPEM()
  157. require.NoError(t, err)
  158. caPool = NewCAPool()
  159. b, err = caPool.AddCAFromPEM(caPem)
  160. require.NoError(t, err)
  161. assert.Empty(t, b)
  162. assert.PanicsWithError(t, "certificate contained a group not present on the signing ca: bad", func() {
  163. NewTestCert(Version1, Curve_P256, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, nil, []string{"test1", "bad"})
  164. })
  165. c, _, _, _ = NewTestCert(Version1, Curve_P256, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, nil, []string{"test1"})
  166. cc, err := caPool.VerifyCertificate(time.Now(), c)
  167. require.NoError(t, err)
  168. // Reset the blocklist and block the alternate form fingerprint
  169. caPool.ResetCertBlocklist()
  170. caPool.BlocklistFingerprint(cc.fingerprint2)
  171. err = caPool.VerifyCachedCertificate(time.Now(), cc)
  172. require.EqualError(t, err, "certificate is in the block list")
  173. caPool.ResetCertBlocklist()
  174. err = caPool.VerifyCachedCertificate(time.Now(), cc)
  175. require.NoError(t, err)
  176. }
  177. func TestCertificateV1_Verify_IPs(t *testing.T) {
  178. caIp1 := mustParsePrefixUnmapped("10.0.0.0/16")
  179. caIp2 := mustParsePrefixUnmapped("192.168.0.0/24")
  180. ca, _, caKey, _ := NewTestCaCert(Version1, Curve_CURVE25519, time.Now(), time.Now().Add(10*time.Minute), []netip.Prefix{caIp1, caIp2}, nil, []string{"test"})
  181. caPem, err := ca.MarshalPEM()
  182. require.NoError(t, err)
  183. caPool := NewCAPool()
  184. b, err := caPool.AddCAFromPEM(caPem)
  185. require.NoError(t, err)
  186. assert.Empty(t, b)
  187. // ip is outside the network
  188. cIp1 := mustParsePrefixUnmapped("10.1.0.0/24")
  189. cIp2 := mustParsePrefixUnmapped("192.168.0.1/16")
  190. assert.PanicsWithError(t, "certificate contained a network assignment outside the limitations of the signing ca: 10.1.0.0/24", func() {
  191. NewTestCert(Version1, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), []netip.Prefix{cIp1, cIp2}, nil, []string{"test"})
  192. })
  193. // ip is outside the network reversed order of above
  194. cIp1 = mustParsePrefixUnmapped("192.168.0.1/24")
  195. cIp2 = mustParsePrefixUnmapped("10.1.0.0/24")
  196. assert.PanicsWithError(t, "certificate contained a network assignment outside the limitations of the signing ca: 10.1.0.0/24", func() {
  197. NewTestCert(Version1, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), []netip.Prefix{cIp1, cIp2}, nil, []string{"test"})
  198. })
  199. // ip is within the network but mask is outside
  200. cIp1 = mustParsePrefixUnmapped("10.0.1.0/15")
  201. cIp2 = mustParsePrefixUnmapped("192.168.0.1/24")
  202. assert.PanicsWithError(t, "certificate contained a network assignment outside the limitations of the signing ca: 10.0.1.0/15", func() {
  203. NewTestCert(Version1, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), []netip.Prefix{cIp1, cIp2}, nil, []string{"test"})
  204. })
  205. // ip is within the network but mask is outside reversed order of above
  206. cIp1 = mustParsePrefixUnmapped("192.168.0.1/24")
  207. cIp2 = mustParsePrefixUnmapped("10.0.1.0/15")
  208. assert.PanicsWithError(t, "certificate contained a network assignment outside the limitations of the signing ca: 10.0.1.0/15", func() {
  209. NewTestCert(Version1, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), []netip.Prefix{cIp1, cIp2}, nil, []string{"test"})
  210. })
  211. // ip and mask are within the network
  212. cIp1 = mustParsePrefixUnmapped("10.0.1.0/16")
  213. cIp2 = mustParsePrefixUnmapped("192.168.0.1/25")
  214. c, _, _, _ := NewTestCert(Version1, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), []netip.Prefix{cIp1, cIp2}, nil, []string{"test"})
  215. _, err = caPool.VerifyCertificate(time.Now(), c)
  216. require.NoError(t, err)
  217. // Exact matches
  218. c, _, _, _ = NewTestCert(Version1, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), []netip.Prefix{caIp1, caIp2}, nil, []string{"test"})
  219. require.NoError(t, err)
  220. _, err = caPool.VerifyCertificate(time.Now(), c)
  221. require.NoError(t, err)
  222. // Exact matches reversed
  223. c, _, _, _ = NewTestCert(Version1, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), []netip.Prefix{caIp2, caIp1}, nil, []string{"test"})
  224. require.NoError(t, err)
  225. _, err = caPool.VerifyCertificate(time.Now(), c)
  226. require.NoError(t, err)
  227. // Exact matches reversed with just 1
  228. c, _, _, _ = NewTestCert(Version1, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), []netip.Prefix{caIp1}, nil, []string{"test"})
  229. require.NoError(t, err)
  230. _, err = caPool.VerifyCertificate(time.Now(), c)
  231. require.NoError(t, err)
  232. }
  233. func TestCertificateV1_Verify_Subnets(t *testing.T) {
  234. caIp1 := mustParsePrefixUnmapped("10.0.0.0/16")
  235. caIp2 := mustParsePrefixUnmapped("192.168.0.0/24")
  236. ca, _, caKey, _ := NewTestCaCert(Version1, Curve_CURVE25519, time.Now(), time.Now().Add(10*time.Minute), nil, []netip.Prefix{caIp1, caIp2}, []string{"test"})
  237. caPem, err := ca.MarshalPEM()
  238. require.NoError(t, err)
  239. caPool := NewCAPool()
  240. b, err := caPool.AddCAFromPEM(caPem)
  241. require.NoError(t, err)
  242. assert.Empty(t, b)
  243. // ip is outside the network
  244. cIp1 := mustParsePrefixUnmapped("10.1.0.0/24")
  245. cIp2 := mustParsePrefixUnmapped("192.168.0.1/16")
  246. assert.PanicsWithError(t, "certificate contained an unsafe network assignment outside the limitations of the signing ca: 10.1.0.0/24", func() {
  247. NewTestCert(Version1, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, []netip.Prefix{cIp1, cIp2}, []string{"test"})
  248. })
  249. // ip is outside the network reversed order of above
  250. cIp1 = mustParsePrefixUnmapped("192.168.0.1/24")
  251. cIp2 = mustParsePrefixUnmapped("10.1.0.0/24")
  252. assert.PanicsWithError(t, "certificate contained an unsafe network assignment outside the limitations of the signing ca: 10.1.0.0/24", func() {
  253. NewTestCert(Version1, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, []netip.Prefix{cIp1, cIp2}, []string{"test"})
  254. })
  255. // ip is within the network but mask is outside
  256. cIp1 = mustParsePrefixUnmapped("10.0.1.0/15")
  257. cIp2 = mustParsePrefixUnmapped("192.168.0.1/24")
  258. assert.PanicsWithError(t, "certificate contained an unsafe network assignment outside the limitations of the signing ca: 10.0.1.0/15", func() {
  259. NewTestCert(Version1, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, []netip.Prefix{cIp1, cIp2}, []string{"test"})
  260. })
  261. // ip is within the network but mask is outside reversed order of above
  262. cIp1 = mustParsePrefixUnmapped("192.168.0.1/24")
  263. cIp2 = mustParsePrefixUnmapped("10.0.1.0/15")
  264. assert.PanicsWithError(t, "certificate contained an unsafe network assignment outside the limitations of the signing ca: 10.0.1.0/15", func() {
  265. NewTestCert(Version1, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, []netip.Prefix{cIp1, cIp2}, []string{"test"})
  266. })
  267. // ip and mask are within the network
  268. cIp1 = mustParsePrefixUnmapped("10.0.1.0/16")
  269. cIp2 = mustParsePrefixUnmapped("192.168.0.1/25")
  270. c, _, _, _ := NewTestCert(Version1, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, []netip.Prefix{cIp1, cIp2}, []string{"test"})
  271. require.NoError(t, err)
  272. _, err = caPool.VerifyCertificate(time.Now(), c)
  273. require.NoError(t, err)
  274. // Exact matches
  275. c, _, _, _ = NewTestCert(Version1, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, []netip.Prefix{caIp1, caIp2}, []string{"test"})
  276. require.NoError(t, err)
  277. _, err = caPool.VerifyCertificate(time.Now(), c)
  278. require.NoError(t, err)
  279. // Exact matches reversed
  280. c, _, _, _ = NewTestCert(Version1, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, []netip.Prefix{caIp2, caIp1}, []string{"test"})
  281. require.NoError(t, err)
  282. _, err = caPool.VerifyCertificate(time.Now(), c)
  283. require.NoError(t, err)
  284. // Exact matches reversed with just 1
  285. c, _, _, _ = NewTestCert(Version1, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, []netip.Prefix{caIp1}, []string{"test"})
  286. require.NoError(t, err)
  287. _, err = caPool.VerifyCertificate(time.Now(), c)
  288. require.NoError(t, err)
  289. }
  290. func TestCertificateV2_Verify(t *testing.T) {
  291. ca, _, caKey, _ := NewTestCaCert(Version2, Curve_CURVE25519, time.Now(), time.Now().Add(10*time.Minute), nil, nil, nil)
  292. c, _, _, _ := NewTestCert(Version2, Curve_CURVE25519, ca, caKey, "test cert", time.Now(), time.Now().Add(5*time.Minute), nil, nil, nil)
  293. caPool := NewCAPool()
  294. require.NoError(t, caPool.AddCA(ca))
  295. f, err := c.Fingerprint()
  296. require.NoError(t, err)
  297. caPool.BlocklistFingerprint(f)
  298. _, err = caPool.VerifyCertificate(time.Now(), c)
  299. require.EqualError(t, err, "certificate is in the block list")
  300. caPool.ResetCertBlocklist()
  301. _, err = caPool.VerifyCertificate(time.Now(), c)
  302. require.NoError(t, err)
  303. _, err = caPool.VerifyCertificate(time.Now().Add(time.Hour*1000), c)
  304. require.EqualError(t, err, "root certificate is expired")
  305. assert.PanicsWithError(t, "certificate is valid before the signing certificate", func() {
  306. NewTestCert(Version2, Curve_CURVE25519, ca, caKey, "test cert2", time.Time{}, time.Time{}, nil, nil, nil)
  307. })
  308. // Test group assertion
  309. ca, _, caKey, _ = NewTestCaCert(Version2, Curve_CURVE25519, time.Now(), time.Now().Add(10*time.Minute), nil, nil, []string{"test1", "test2"})
  310. caPem, err := ca.MarshalPEM()
  311. require.NoError(t, err)
  312. caPool = NewCAPool()
  313. b, err := caPool.AddCAFromPEM(caPem)
  314. require.NoError(t, err)
  315. assert.Empty(t, b)
  316. assert.PanicsWithError(t, "certificate contained a group not present on the signing ca: bad", func() {
  317. NewTestCert(Version2, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, nil, []string{"test1", "bad"})
  318. })
  319. c, _, _, _ = NewTestCert(Version2, Curve_CURVE25519, ca, caKey, "test2", time.Now(), time.Now().Add(5*time.Minute), nil, nil, []string{"test1"})
  320. require.NoError(t, err)
  321. _, err = caPool.VerifyCertificate(time.Now(), c)
  322. require.NoError(t, err)
  323. }
  324. func TestCertificateV2_VerifyP256(t *testing.T) {
  325. ca, _, caKey, _ := NewTestCaCert(Version2, Curve_P256, time.Now(), time.Now().Add(10*time.Minute), nil, nil, nil)
  326. c, _, _, _ := NewTestCert(Version2, Curve_P256, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, nil, nil)
  327. caPool := NewCAPool()
  328. require.NoError(t, caPool.AddCA(ca))
  329. f, err := c.Fingerprint()
  330. require.NoError(t, err)
  331. caPool.BlocklistFingerprint(f)
  332. _, err = caPool.VerifyCertificate(time.Now(), c)
  333. require.EqualError(t, err, "certificate is in the block list")
  334. // Create a copy of the cert and swap to the alternate form for the signature
  335. nc := c.Copy()
  336. b, err := p256.Swap(c.Signature())
  337. require.NoError(t, err)
  338. require.NoError(t, nc.(*certificateV2).setSignature(b))
  339. _, err = caPool.VerifyCertificate(time.Now(), nc)
  340. require.EqualError(t, err, "certificate is in the block list")
  341. caPool.ResetCertBlocklist()
  342. _, err = caPool.VerifyCertificate(time.Now(), c)
  343. require.NoError(t, err)
  344. _, err = caPool.VerifyCertificate(time.Now().Add(time.Hour*1000), c)
  345. require.EqualError(t, err, "root certificate is expired")
  346. assert.PanicsWithError(t, "certificate is valid before the signing certificate", func() {
  347. NewTestCert(Version2, Curve_P256, ca, caKey, "test", time.Time{}, time.Time{}, nil, nil, nil)
  348. })
  349. // Test group assertion
  350. ca, _, caKey, _ = NewTestCaCert(Version2, Curve_P256, time.Now(), time.Now().Add(10*time.Minute), nil, nil, []string{"test1", "test2"})
  351. caPem, err := ca.MarshalPEM()
  352. require.NoError(t, err)
  353. caPool = NewCAPool()
  354. b, err = caPool.AddCAFromPEM(caPem)
  355. require.NoError(t, err)
  356. assert.Empty(t, b)
  357. assert.PanicsWithError(t, "certificate contained a group not present on the signing ca: bad", func() {
  358. NewTestCert(Version2, Curve_P256, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, nil, []string{"test1", "bad"})
  359. })
  360. c, _, _, _ = NewTestCert(Version2, Curve_P256, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, nil, []string{"test1"})
  361. cc, err := caPool.VerifyCertificate(time.Now(), c)
  362. require.NoError(t, err)
  363. // Reset the blocklist and block the alternate form fingerprint
  364. caPool.ResetCertBlocklist()
  365. caPool.BlocklistFingerprint(cc.fingerprint2)
  366. err = caPool.VerifyCachedCertificate(time.Now(), cc)
  367. require.EqualError(t, err, "certificate is in the block list")
  368. caPool.ResetCertBlocklist()
  369. err = caPool.VerifyCachedCertificate(time.Now(), cc)
  370. require.NoError(t, err)
  371. }
  372. func TestCertificateV2_Verify_IPs(t *testing.T) {
  373. caIp1 := mustParsePrefixUnmapped("10.0.0.0/16")
  374. caIp2 := mustParsePrefixUnmapped("192.168.0.0/24")
  375. ca, _, caKey, _ := NewTestCaCert(Version2, Curve_CURVE25519, time.Now(), time.Now().Add(10*time.Minute), []netip.Prefix{caIp1, caIp2}, nil, []string{"test"})
  376. caPem, err := ca.MarshalPEM()
  377. require.NoError(t, err)
  378. caPool := NewCAPool()
  379. b, err := caPool.AddCAFromPEM(caPem)
  380. require.NoError(t, err)
  381. assert.Empty(t, b)
  382. // ip is outside the network
  383. cIp1 := mustParsePrefixUnmapped("10.1.0.0/24")
  384. cIp2 := mustParsePrefixUnmapped("192.168.0.1/16")
  385. assert.PanicsWithError(t, "certificate contained a network assignment outside the limitations of the signing ca: 10.1.0.0/24", func() {
  386. NewTestCert(Version2, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), []netip.Prefix{cIp1, cIp2}, nil, []string{"test"})
  387. })
  388. // ip is outside the network reversed order of above
  389. cIp1 = mustParsePrefixUnmapped("192.168.0.1/24")
  390. cIp2 = mustParsePrefixUnmapped("10.1.0.0/24")
  391. assert.PanicsWithError(t, "certificate contained a network assignment outside the limitations of the signing ca: 10.1.0.0/24", func() {
  392. NewTestCert(Version2, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), []netip.Prefix{cIp1, cIp2}, nil, []string{"test"})
  393. })
  394. // ip is within the network but mask is outside
  395. cIp1 = mustParsePrefixUnmapped("10.0.1.0/15")
  396. cIp2 = mustParsePrefixUnmapped("192.168.0.1/24")
  397. assert.PanicsWithError(t, "certificate contained a network assignment outside the limitations of the signing ca: 10.0.1.0/15", func() {
  398. NewTestCert(Version2, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), []netip.Prefix{cIp1, cIp2}, nil, []string{"test"})
  399. })
  400. // ip is within the network but mask is outside reversed order of above
  401. cIp1 = mustParsePrefixUnmapped("192.168.0.1/24")
  402. cIp2 = mustParsePrefixUnmapped("10.0.1.0/15")
  403. assert.PanicsWithError(t, "certificate contained a network assignment outside the limitations of the signing ca: 10.0.1.0/15", func() {
  404. NewTestCert(Version2, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), []netip.Prefix{cIp1, cIp2}, nil, []string{"test"})
  405. })
  406. // ip and mask are within the network
  407. cIp1 = mustParsePrefixUnmapped("10.0.1.0/16")
  408. cIp2 = mustParsePrefixUnmapped("192.168.0.1/25")
  409. c, _, _, _ := NewTestCert(Version2, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), []netip.Prefix{cIp1, cIp2}, nil, []string{"test"})
  410. _, err = caPool.VerifyCertificate(time.Now(), c)
  411. require.NoError(t, err)
  412. // Exact matches
  413. c, _, _, _ = NewTestCert(Version2, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), []netip.Prefix{caIp1, caIp2}, nil, []string{"test"})
  414. require.NoError(t, err)
  415. _, err = caPool.VerifyCertificate(time.Now(), c)
  416. require.NoError(t, err)
  417. // Exact matches reversed
  418. c, _, _, _ = NewTestCert(Version2, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), []netip.Prefix{caIp2, caIp1}, nil, []string{"test"})
  419. require.NoError(t, err)
  420. _, err = caPool.VerifyCertificate(time.Now(), c)
  421. require.NoError(t, err)
  422. // Exact matches reversed with just 1
  423. c, _, _, _ = NewTestCert(Version2, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), []netip.Prefix{caIp1}, nil, []string{"test"})
  424. require.NoError(t, err)
  425. _, err = caPool.VerifyCertificate(time.Now(), c)
  426. require.NoError(t, err)
  427. }
  428. func TestCertificateV2_Verify_Subnets(t *testing.T) {
  429. caIp1 := mustParsePrefixUnmapped("10.0.0.0/16")
  430. caIp2 := mustParsePrefixUnmapped("192.168.0.0/24")
  431. ca, _, caKey, _ := NewTestCaCert(Version2, Curve_CURVE25519, time.Now(), time.Now().Add(10*time.Minute), nil, []netip.Prefix{caIp1, caIp2}, []string{"test"})
  432. caPem, err := ca.MarshalPEM()
  433. require.NoError(t, err)
  434. caPool := NewCAPool()
  435. b, err := caPool.AddCAFromPEM(caPem)
  436. require.NoError(t, err)
  437. assert.Empty(t, b)
  438. // ip is outside the network
  439. cIp1 := mustParsePrefixUnmapped("10.1.0.0/24")
  440. cIp2 := mustParsePrefixUnmapped("192.168.0.1/16")
  441. assert.PanicsWithError(t, "certificate contained an unsafe network assignment outside the limitations of the signing ca: 10.1.0.0/24", func() {
  442. NewTestCert(Version2, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, []netip.Prefix{cIp1, cIp2}, []string{"test"})
  443. })
  444. // ip is outside the network reversed order of above
  445. cIp1 = mustParsePrefixUnmapped("192.168.0.1/24")
  446. cIp2 = mustParsePrefixUnmapped("10.1.0.0/24")
  447. assert.PanicsWithError(t, "certificate contained an unsafe network assignment outside the limitations of the signing ca: 10.1.0.0/24", func() {
  448. NewTestCert(Version2, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, []netip.Prefix{cIp1, cIp2}, []string{"test"})
  449. })
  450. // ip is within the network but mask is outside
  451. cIp1 = mustParsePrefixUnmapped("10.0.1.0/15")
  452. cIp2 = mustParsePrefixUnmapped("192.168.0.1/24")
  453. assert.PanicsWithError(t, "certificate contained an unsafe network assignment outside the limitations of the signing ca: 10.0.1.0/15", func() {
  454. NewTestCert(Version2, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, []netip.Prefix{cIp1, cIp2}, []string{"test"})
  455. })
  456. // ip is within the network but mask is outside reversed order of above
  457. cIp1 = mustParsePrefixUnmapped("192.168.0.1/24")
  458. cIp2 = mustParsePrefixUnmapped("10.0.1.0/15")
  459. assert.PanicsWithError(t, "certificate contained an unsafe network assignment outside the limitations of the signing ca: 10.0.1.0/15", func() {
  460. NewTestCert(Version2, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, []netip.Prefix{cIp1, cIp2}, []string{"test"})
  461. })
  462. // ip and mask are within the network
  463. cIp1 = mustParsePrefixUnmapped("10.0.1.0/16")
  464. cIp2 = mustParsePrefixUnmapped("192.168.0.1/25")
  465. c, _, _, _ := NewTestCert(Version2, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, []netip.Prefix{cIp1, cIp2}, []string{"test"})
  466. require.NoError(t, err)
  467. _, err = caPool.VerifyCertificate(time.Now(), c)
  468. require.NoError(t, err)
  469. // Exact matches
  470. c, _, _, _ = NewTestCert(Version2, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, []netip.Prefix{caIp1, caIp2}, []string{"test"})
  471. require.NoError(t, err)
  472. _, err = caPool.VerifyCertificate(time.Now(), c)
  473. require.NoError(t, err)
  474. // Exact matches reversed
  475. c, _, _, _ = NewTestCert(Version2, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, []netip.Prefix{caIp2, caIp1}, []string{"test"})
  476. require.NoError(t, err)
  477. _, err = caPool.VerifyCertificate(time.Now(), c)
  478. require.NoError(t, err)
  479. // Exact matches reversed with just 1
  480. c, _, _, _ = NewTestCert(Version2, Curve_CURVE25519, ca, caKey, "test", time.Now(), time.Now().Add(5*time.Minute), nil, []netip.Prefix{caIp1}, []string{"test"})
  481. require.NoError(t, err)
  482. _, err = caPool.VerifyCertificate(time.Now(), c)
  483. require.NoError(t, err)
  484. }