cert_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. package cert
  2. import (
  3. "crypto/rand"
  4. "fmt"
  5. "io"
  6. "net"
  7. "testing"
  8. "time"
  9. "github.com/golang/protobuf/proto"
  10. "github.com/stretchr/testify/assert"
  11. "golang.org/x/crypto/curve25519"
  12. "golang.org/x/crypto/ed25519"
  13. )
  14. func TestMarshalingNebulaCertificate(t *testing.T) {
  15. before := time.Now().Add(time.Second * -60).Round(time.Second)
  16. after := time.Now().Add(time.Second * 60).Round(time.Second)
  17. pubKey := []byte("1234567890abcedfghij1234567890ab")
  18. nc := NebulaCertificate{
  19. Details: NebulaCertificateDetails{
  20. Name: "testing",
  21. Ips: []*net.IPNet{
  22. {IP: net.ParseIP("10.1.1.1"), Mask: net.IPMask(net.ParseIP("255.255.255.0"))},
  23. {IP: net.ParseIP("10.1.1.2"), Mask: net.IPMask(net.ParseIP("255.255.0.0"))},
  24. {IP: net.ParseIP("10.1.1.3"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))},
  25. },
  26. Subnets: []*net.IPNet{
  27. {IP: net.ParseIP("9.1.1.1"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))},
  28. {IP: net.ParseIP("9.1.1.2"), Mask: net.IPMask(net.ParseIP("255.255.255.0"))},
  29. {IP: net.ParseIP("9.1.1.3"), Mask: net.IPMask(net.ParseIP("255.255.0.0"))},
  30. },
  31. Groups: []string{"test-group1", "test-group2", "test-group3"},
  32. NotBefore: before,
  33. NotAfter: after,
  34. PublicKey: pubKey,
  35. IsCA: false,
  36. Issuer: "1234567890abcedfghij1234567890ab",
  37. },
  38. Signature: []byte("1234567890abcedfghij1234567890ab"),
  39. }
  40. b, err := nc.Marshal()
  41. assert.Nil(t, err)
  42. t.Log("Cert size:", len(b))
  43. nc2, err := UnmarshalNebulaCertificate(b)
  44. assert.Nil(t, err)
  45. assert.Equal(t, nc.Signature, nc2.Signature)
  46. assert.Equal(t, nc.Details.Name, nc2.Details.Name)
  47. assert.Equal(t, nc.Details.NotBefore, nc2.Details.NotBefore)
  48. assert.Equal(t, nc.Details.NotAfter, nc2.Details.NotAfter)
  49. assert.Equal(t, nc.Details.PublicKey, nc2.Details.PublicKey)
  50. assert.Equal(t, nc.Details.IsCA, nc2.Details.IsCA)
  51. // IP byte arrays can be 4 or 16 in length so we have to go this route
  52. assert.Equal(t, len(nc.Details.Ips), len(nc2.Details.Ips))
  53. for i, wIp := range nc.Details.Ips {
  54. assert.Equal(t, wIp.String(), nc2.Details.Ips[i].String())
  55. }
  56. assert.Equal(t, len(nc.Details.Subnets), len(nc2.Details.Subnets))
  57. for i, wIp := range nc.Details.Subnets {
  58. assert.Equal(t, wIp.String(), nc2.Details.Subnets[i].String())
  59. }
  60. assert.EqualValues(t, nc.Details.Groups, nc2.Details.Groups)
  61. }
  62. func TestNebulaCertificate_Sign(t *testing.T) {
  63. before := time.Now().Add(time.Second * -60).Round(time.Second)
  64. after := time.Now().Add(time.Second * 60).Round(time.Second)
  65. pubKey := []byte("1234567890abcedfghij1234567890ab")
  66. nc := NebulaCertificate{
  67. Details: NebulaCertificateDetails{
  68. Name: "testing",
  69. Ips: []*net.IPNet{
  70. {IP: net.ParseIP("10.1.1.1"), Mask: net.IPMask(net.ParseIP("255.255.255.0"))},
  71. {IP: net.ParseIP("10.1.1.2"), Mask: net.IPMask(net.ParseIP("255.255.0.0"))},
  72. {IP: net.ParseIP("10.1.1.3"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))},
  73. },
  74. Subnets: []*net.IPNet{
  75. {IP: net.ParseIP("9.1.1.1"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))},
  76. {IP: net.ParseIP("9.1.1.2"), Mask: net.IPMask(net.ParseIP("255.255.255.0"))},
  77. {IP: net.ParseIP("9.1.1.3"), Mask: net.IPMask(net.ParseIP("255.255.0.0"))},
  78. },
  79. Groups: []string{"test-group1", "test-group2", "test-group3"},
  80. NotBefore: before,
  81. NotAfter: after,
  82. PublicKey: pubKey,
  83. IsCA: false,
  84. Issuer: "1234567890abcedfghij1234567890ab",
  85. },
  86. }
  87. pub, priv, err := ed25519.GenerateKey(rand.Reader)
  88. assert.Nil(t, err)
  89. assert.False(t, nc.CheckSignature(pub))
  90. assert.Nil(t, nc.Sign(priv))
  91. assert.True(t, nc.CheckSignature(pub))
  92. b, err := nc.Marshal()
  93. assert.Nil(t, err)
  94. t.Log("Cert size:", len(b))
  95. }
  96. func TestNebulaCertificate_Expired(t *testing.T) {
  97. nc := NebulaCertificate{
  98. Details: NebulaCertificateDetails{
  99. NotBefore: time.Now().Add(time.Second * -60).Round(time.Second),
  100. NotAfter: time.Now().Add(time.Second * 60).Round(time.Second),
  101. },
  102. }
  103. assert.True(t, nc.Expired(time.Now().Add(time.Hour)))
  104. assert.True(t, nc.Expired(time.Now().Add(-time.Hour)))
  105. assert.False(t, nc.Expired(time.Now()))
  106. }
  107. func TestNebulaCertificate_MarshalJSON(t *testing.T) {
  108. time.Local = time.UTC
  109. pubKey := []byte("1234567890abcedfghij1234567890ab")
  110. nc := NebulaCertificate{
  111. Details: NebulaCertificateDetails{
  112. Name: "testing",
  113. Ips: []*net.IPNet{
  114. {IP: net.ParseIP("10.1.1.1"), Mask: net.IPMask(net.ParseIP("255.255.255.0"))},
  115. {IP: net.ParseIP("10.1.1.2"), Mask: net.IPMask(net.ParseIP("255.255.0.0"))},
  116. {IP: net.ParseIP("10.1.1.3"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))},
  117. },
  118. Subnets: []*net.IPNet{
  119. {IP: net.ParseIP("9.1.1.1"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))},
  120. {IP: net.ParseIP("9.1.1.2"), Mask: net.IPMask(net.ParseIP("255.255.255.0"))},
  121. {IP: net.ParseIP("9.1.1.3"), Mask: net.IPMask(net.ParseIP("255.255.0.0"))},
  122. },
  123. Groups: []string{"test-group1", "test-group2", "test-group3"},
  124. NotBefore: time.Date(1, 0, 0, 1, 0, 0, 0, time.UTC),
  125. NotAfter: time.Date(1, 0, 0, 2, 0, 0, 0, time.UTC),
  126. PublicKey: pubKey,
  127. IsCA: false,
  128. Issuer: "1234567890abcedfghij1234567890ab",
  129. },
  130. Signature: []byte("1234567890abcedfghij1234567890ab"),
  131. }
  132. b, err := nc.MarshalJSON()
  133. assert.Nil(t, err)
  134. assert.Equal(
  135. t,
  136. "{\"details\":{\"groups\":[\"test-group1\",\"test-group2\",\"test-group3\"],\"ips\":[\"10.1.1.1/24\",\"10.1.1.2/16\",\"10.1.1.3/ff00ff00\"],\"isCa\":false,\"issuer\":\"1234567890abcedfghij1234567890ab\",\"name\":\"testing\",\"notAfter\":\"0000-11-30T02:00:00Z\",\"notBefore\":\"0000-11-30T01:00:00Z\",\"publicKey\":\"313233343536373839306162636564666768696a313233343536373839306162\",\"subnets\":[\"9.1.1.1/ff00ff00\",\"9.1.1.2/24\",\"9.1.1.3/16\"]},\"fingerprint\":\"26cb1c30ad7872c804c166b5150fa372f437aa3856b04edb4334b4470ec728e4\",\"signature\":\"313233343536373839306162636564666768696a313233343536373839306162\"}",
  137. string(b),
  138. )
  139. }
  140. func TestNebulaCertificate_Verify(t *testing.T) {
  141. ca, _, caKey, err := newTestCaCert()
  142. assert.Nil(t, err)
  143. c, _, _, err := newTestCert(ca, caKey)
  144. assert.Nil(t, err)
  145. h, err := ca.Sha256Sum()
  146. assert.Nil(t, err)
  147. caPool := NewCAPool()
  148. caPool.CAs[h] = ca
  149. f, err := c.Sha256Sum()
  150. assert.Nil(t, err)
  151. caPool.BlacklistFingerprint(f)
  152. v, err := c.Verify(time.Now(), caPool)
  153. assert.False(t, v)
  154. assert.EqualError(t, err, "certificate has been blacklisted")
  155. caPool.ResetCertBlacklist()
  156. v, err = c.Verify(time.Now(), caPool)
  157. assert.True(t, v)
  158. assert.Nil(t, err)
  159. v, err = c.Verify(time.Now().Add(time.Hour*1000), caPool)
  160. assert.False(t, v)
  161. assert.EqualError(t, err, "root certificate is expired")
  162. }
  163. func TestNebulaVerifyPrivateKey(t *testing.T) {
  164. ca, _, caKey, err := newTestCaCert()
  165. assert.Nil(t, err)
  166. c, _, priv, err := newTestCert(ca, caKey)
  167. err = c.VerifyPrivateKey(priv)
  168. assert.Nil(t, err)
  169. _, priv2 := x25519Keypair()
  170. err = c.VerifyPrivateKey(priv2)
  171. assert.NotNil(t, err)
  172. }
  173. func TestNewCAPoolFromBytes(t *testing.T) {
  174. noNewLines := `
  175. # Current provisional, Remove once everything moves over to the real root.
  176. -----BEGIN NEBULA CERTIFICATE-----
  177. CkAKDm5lYnVsYSByb290IGNhKJfap9AFMJfg1+YGOiCUQGByMuNRhIlQBOyzXWbL
  178. vcKBwDhov900phEfJ5DN3kABEkDCq5R8qBiu8sl54yVfgRcQXEDt3cHr8UTSLszv
  179. bzBEr00kERQxxTzTsH8cpYEgRoipvmExvg8WP8NdAJEYJosB
  180. -----END NEBULA CERTIFICATE-----
  181. # root-ca01
  182. -----BEGIN NEBULA CERTIFICATE-----
  183. CkMKEW5lYnVsYSByb290IGNhIDAxKJL2u9EFMJL86+cGOiDPXMH4oU6HZTk/CqTG
  184. BVG+oJpAoqokUBbI4U0N8CSfpUABEkB/Pm5A2xyH/nc8mg/wvGUWG3pZ7nHzaDMf
  185. 8/phAUt+FLzqTECzQKisYswKvE3pl9mbEYKbOdIHrxdIp95mo4sF
  186. -----END NEBULA CERTIFICATE-----
  187. `
  188. withNewLines := `
  189. # Current provisional, Remove once everything moves over to the real root.
  190. -----BEGIN NEBULA CERTIFICATE-----
  191. CkAKDm5lYnVsYSByb290IGNhKJfap9AFMJfg1+YGOiCUQGByMuNRhIlQBOyzXWbL
  192. vcKBwDhov900phEfJ5DN3kABEkDCq5R8qBiu8sl54yVfgRcQXEDt3cHr8UTSLszv
  193. bzBEr00kERQxxTzTsH8cpYEgRoipvmExvg8WP8NdAJEYJosB
  194. -----END NEBULA CERTIFICATE-----
  195. # root-ca01
  196. -----BEGIN NEBULA CERTIFICATE-----
  197. CkMKEW5lYnVsYSByb290IGNhIDAxKJL2u9EFMJL86+cGOiDPXMH4oU6HZTk/CqTG
  198. BVG+oJpAoqokUBbI4U0N8CSfpUABEkB/Pm5A2xyH/nc8mg/wvGUWG3pZ7nHzaDMf
  199. 8/phAUt+FLzqTECzQKisYswKvE3pl9mbEYKbOdIHrxdIp95mo4sF
  200. -----END NEBULA CERTIFICATE-----
  201. `
  202. rootCA := NebulaCertificate{
  203. Details: NebulaCertificateDetails{
  204. Name: "nebula root ca",
  205. },
  206. }
  207. rootCA01 := NebulaCertificate{
  208. Details: NebulaCertificateDetails{
  209. Name: "nebula root ca 01",
  210. },
  211. }
  212. p, err := NewCAPoolFromBytes([]byte(noNewLines))
  213. assert.Nil(t, err)
  214. assert.Equal(t, p.CAs[string("c9bfaf7ce8e84b2eeda2e27b469f4b9617bde192efd214b68891ecda6ed49522")].Details.Name, rootCA.Details.Name)
  215. assert.Equal(t, p.CAs[string("5c9c3f23e7ee7fe97637cbd3a0a5b854154d1d9aaaf7b566a51f4a88f76b64cd")].Details.Name, rootCA01.Details.Name)
  216. pp, err := NewCAPoolFromBytes([]byte(withNewLines))
  217. assert.Nil(t, err)
  218. assert.Equal(t, pp.CAs[string("c9bfaf7ce8e84b2eeda2e27b469f4b9617bde192efd214b68891ecda6ed49522")].Details.Name, rootCA.Details.Name)
  219. assert.Equal(t, pp.CAs[string("5c9c3f23e7ee7fe97637cbd3a0a5b854154d1d9aaaf7b566a51f4a88f76b64cd")].Details.Name, rootCA01.Details.Name)
  220. }
  221. // Ensure that upgrading the protobuf library does not change how certificates
  222. // are marshalled, since this would break signature verification
  223. func TestMarshalingNebulaCertificateConsistency(t *testing.T) {
  224. before := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
  225. after := time.Date(2017, time.January, 18, 28, 40, 0, 0, time.UTC)
  226. pubKey := []byte("1234567890abcedfghij1234567890ab")
  227. nc := NebulaCertificate{
  228. Details: NebulaCertificateDetails{
  229. Name: "testing",
  230. Ips: []*net.IPNet{
  231. {IP: net.ParseIP("10.1.1.1"), Mask: net.IPMask(net.ParseIP("255.255.255.0"))},
  232. {IP: net.ParseIP("10.1.1.2"), Mask: net.IPMask(net.ParseIP("255.255.0.0"))},
  233. {IP: net.ParseIP("10.1.1.3"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))},
  234. },
  235. Subnets: []*net.IPNet{
  236. {IP: net.ParseIP("9.1.1.1"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))},
  237. {IP: net.ParseIP("9.1.1.2"), Mask: net.IPMask(net.ParseIP("255.255.255.0"))},
  238. {IP: net.ParseIP("9.1.1.3"), Mask: net.IPMask(net.ParseIP("255.255.0.0"))},
  239. },
  240. Groups: []string{"test-group1", "test-group2", "test-group3"},
  241. NotBefore: before,
  242. NotAfter: after,
  243. PublicKey: pubKey,
  244. IsCA: false,
  245. Issuer: "1234567890abcedfghij1234567890ab",
  246. },
  247. Signature: []byte("1234567890abcedfghij1234567890ab"),
  248. }
  249. b, err := nc.Marshal()
  250. assert.Nil(t, err)
  251. t.Log("Cert size:", len(b))
  252. assert.Equal(t, "0aa2010a0774657374696e67121b8182845080feffff0f828284508080fcff0f8382845080fe83f80f1a1b8182844880fe83f80f8282844880feffff0f838284488080fcff0f220b746573742d67726f757031220b746573742d67726f757032220b746573742d67726f75703328f0e0e7d70430a08681c4053a20313233343536373839306162636564666768696a3132333435363738393061624a081234567890abcedf1220313233343536373839306162636564666768696a313233343536373839306162", fmt.Sprintf("%x", b))
  253. b, err = proto.Marshal(nc.getRawDetails())
  254. assert.Nil(t, err)
  255. t.Log("Raw cert size:", len(b))
  256. assert.Equal(t, "0a0774657374696e67121b8182845080feffff0f828284508080fcff0f8382845080fe83f80f1a1b8182844880fe83f80f8282844880feffff0f838284488080fcff0f220b746573742d67726f757031220b746573742d67726f757032220b746573742d67726f75703328f0e0e7d70430a08681c4053a20313233343536373839306162636564666768696a3132333435363738393061624a081234567890abcedf", fmt.Sprintf("%x", b))
  257. }
  258. func newTestCaCert() (*NebulaCertificate, []byte, []byte, error) {
  259. pub, priv, err := ed25519.GenerateKey(rand.Reader)
  260. before := time.Now().Add(time.Second * -60).Round(time.Second)
  261. after := time.Now().Add(time.Second * 60).Round(time.Second)
  262. nc := &NebulaCertificate{
  263. Details: NebulaCertificateDetails{
  264. Name: "test ca",
  265. NotBefore: before,
  266. NotAfter: after,
  267. PublicKey: pub,
  268. IsCA: true,
  269. },
  270. }
  271. err = nc.Sign(priv)
  272. if err != nil {
  273. return nil, nil, nil, err
  274. }
  275. return nc, pub, priv, nil
  276. }
  277. func newTestCert(ca *NebulaCertificate, key []byte) (*NebulaCertificate, []byte, []byte, error) {
  278. issuer, err := ca.Sha256Sum()
  279. if err != nil {
  280. return nil, nil, nil, err
  281. }
  282. before := time.Now().Add(time.Second * -60).Round(time.Second)
  283. after := time.Now().Add(time.Second * 60).Round(time.Second)
  284. pub, rawPriv := x25519Keypair()
  285. nc := &NebulaCertificate{
  286. Details: NebulaCertificateDetails{
  287. Name: "testing",
  288. Ips: []*net.IPNet{
  289. {IP: net.ParseIP("10.1.1.1"), Mask: net.IPMask(net.ParseIP("255.255.255.0"))},
  290. {IP: net.ParseIP("10.1.1.2"), Mask: net.IPMask(net.ParseIP("255.255.0.0"))},
  291. {IP: net.ParseIP("10.1.1.3"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))},
  292. },
  293. Subnets: []*net.IPNet{
  294. {IP: net.ParseIP("9.1.1.1"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))},
  295. {IP: net.ParseIP("9.1.1.2"), Mask: net.IPMask(net.ParseIP("255.255.255.0"))},
  296. {IP: net.ParseIP("9.1.1.3"), Mask: net.IPMask(net.ParseIP("255.255.0.0"))},
  297. },
  298. Groups: []string{"test-group1", "test-group2", "test-group3"},
  299. NotBefore: before,
  300. NotAfter: after,
  301. PublicKey: pub,
  302. IsCA: false,
  303. Issuer: issuer,
  304. },
  305. }
  306. err = nc.Sign(key)
  307. if err != nil {
  308. return nil, nil, nil, err
  309. }
  310. return nc, pub, rawPriv, nil
  311. }
  312. func x25519Keypair() ([]byte, []byte) {
  313. var pubkey, privkey [32]byte
  314. if _, err := io.ReadFull(rand.Reader, privkey[:]); err != nil {
  315. panic(err)
  316. }
  317. curve25519.ScalarBaseMult(&pubkey, &privkey)
  318. return pubkey[:], privkey[:]
  319. }