ca_pool_test.go 25 KB

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