123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 |
- package cert
- import (
- "crypto/rand"
- "fmt"
- "io"
- "net"
- "testing"
- "time"
- "github.com/golang/protobuf/proto"
- "github.com/stretchr/testify/assert"
- "golang.org/x/crypto/curve25519"
- "golang.org/x/crypto/ed25519"
- )
- func TestMarshalingNebulaCertificate(t *testing.T) {
- before := time.Now().Add(time.Second * -60).Round(time.Second)
- after := time.Now().Add(time.Second * 60).Round(time.Second)
- pubKey := []byte("1234567890abcedfghij1234567890ab")
- nc := NebulaCertificate{
- Details: NebulaCertificateDetails{
- Name: "testing",
- Ips: []*net.IPNet{
- {IP: net.ParseIP("10.1.1.1"), Mask: net.IPMask(net.ParseIP("255.255.255.0"))},
- {IP: net.ParseIP("10.1.1.2"), Mask: net.IPMask(net.ParseIP("255.255.0.0"))},
- {IP: net.ParseIP("10.1.1.3"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))},
- },
- Subnets: []*net.IPNet{
- {IP: net.ParseIP("9.1.1.1"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))},
- {IP: net.ParseIP("9.1.1.2"), Mask: net.IPMask(net.ParseIP("255.255.255.0"))},
- {IP: net.ParseIP("9.1.1.3"), Mask: net.IPMask(net.ParseIP("255.255.0.0"))},
- },
- Groups: []string{"test-group1", "test-group2", "test-group3"},
- NotBefore: before,
- NotAfter: after,
- PublicKey: pubKey,
- IsCA: false,
- Issuer: "1234567890abcedfghij1234567890ab",
- },
- Signature: []byte("1234567890abcedfghij1234567890ab"),
- }
- b, err := nc.Marshal()
- assert.Nil(t, err)
- t.Log("Cert size:", len(b))
- nc2, err := UnmarshalNebulaCertificate(b)
- assert.Nil(t, err)
- assert.Equal(t, nc.Signature, nc2.Signature)
- assert.Equal(t, nc.Details.Name, nc2.Details.Name)
- assert.Equal(t, nc.Details.NotBefore, nc2.Details.NotBefore)
- assert.Equal(t, nc.Details.NotAfter, nc2.Details.NotAfter)
- assert.Equal(t, nc.Details.PublicKey, nc2.Details.PublicKey)
- assert.Equal(t, nc.Details.IsCA, nc2.Details.IsCA)
- // IP byte arrays can be 4 or 16 in length so we have to go this route
- assert.Equal(t, len(nc.Details.Ips), len(nc2.Details.Ips))
- for i, wIp := range nc.Details.Ips {
- assert.Equal(t, wIp.String(), nc2.Details.Ips[i].String())
- }
- assert.Equal(t, len(nc.Details.Subnets), len(nc2.Details.Subnets))
- for i, wIp := range nc.Details.Subnets {
- assert.Equal(t, wIp.String(), nc2.Details.Subnets[i].String())
- }
- assert.EqualValues(t, nc.Details.Groups, nc2.Details.Groups)
- }
- func TestNebulaCertificate_Sign(t *testing.T) {
- before := time.Now().Add(time.Second * -60).Round(time.Second)
- after := time.Now().Add(time.Second * 60).Round(time.Second)
- pubKey := []byte("1234567890abcedfghij1234567890ab")
- nc := NebulaCertificate{
- Details: NebulaCertificateDetails{
- Name: "testing",
- Ips: []*net.IPNet{
- {IP: net.ParseIP("10.1.1.1"), Mask: net.IPMask(net.ParseIP("255.255.255.0"))},
- {IP: net.ParseIP("10.1.1.2"), Mask: net.IPMask(net.ParseIP("255.255.0.0"))},
- {IP: net.ParseIP("10.1.1.3"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))},
- },
- Subnets: []*net.IPNet{
- {IP: net.ParseIP("9.1.1.1"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))},
- {IP: net.ParseIP("9.1.1.2"), Mask: net.IPMask(net.ParseIP("255.255.255.0"))},
- {IP: net.ParseIP("9.1.1.3"), Mask: net.IPMask(net.ParseIP("255.255.0.0"))},
- },
- Groups: []string{"test-group1", "test-group2", "test-group3"},
- NotBefore: before,
- NotAfter: after,
- PublicKey: pubKey,
- IsCA: false,
- Issuer: "1234567890abcedfghij1234567890ab",
- },
- }
- pub, priv, err := ed25519.GenerateKey(rand.Reader)
- assert.Nil(t, err)
- assert.False(t, nc.CheckSignature(pub))
- assert.Nil(t, nc.Sign(priv))
- assert.True(t, nc.CheckSignature(pub))
- b, err := nc.Marshal()
- assert.Nil(t, err)
- t.Log("Cert size:", len(b))
- }
- func TestNebulaCertificate_Expired(t *testing.T) {
- nc := NebulaCertificate{
- Details: NebulaCertificateDetails{
- NotBefore: time.Now().Add(time.Second * -60).Round(time.Second),
- NotAfter: time.Now().Add(time.Second * 60).Round(time.Second),
- },
- }
- assert.True(t, nc.Expired(time.Now().Add(time.Hour)))
- assert.True(t, nc.Expired(time.Now().Add(-time.Hour)))
- assert.False(t, nc.Expired(time.Now()))
- }
- func TestNebulaCertificate_MarshalJSON(t *testing.T) {
- time.Local = time.UTC
- pubKey := []byte("1234567890abcedfghij1234567890ab")
- nc := NebulaCertificate{
- Details: NebulaCertificateDetails{
- Name: "testing",
- Ips: []*net.IPNet{
- {IP: net.ParseIP("10.1.1.1"), Mask: net.IPMask(net.ParseIP("255.255.255.0"))},
- {IP: net.ParseIP("10.1.1.2"), Mask: net.IPMask(net.ParseIP("255.255.0.0"))},
- {IP: net.ParseIP("10.1.1.3"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))},
- },
- Subnets: []*net.IPNet{
- {IP: net.ParseIP("9.1.1.1"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))},
- {IP: net.ParseIP("9.1.1.2"), Mask: net.IPMask(net.ParseIP("255.255.255.0"))},
- {IP: net.ParseIP("9.1.1.3"), Mask: net.IPMask(net.ParseIP("255.255.0.0"))},
- },
- Groups: []string{"test-group1", "test-group2", "test-group3"},
- NotBefore: time.Date(1, 0, 0, 1, 0, 0, 0, time.UTC),
- NotAfter: time.Date(1, 0, 0, 2, 0, 0, 0, time.UTC),
- PublicKey: pubKey,
- IsCA: false,
- Issuer: "1234567890abcedfghij1234567890ab",
- },
- Signature: []byte("1234567890abcedfghij1234567890ab"),
- }
- b, err := nc.MarshalJSON()
- assert.Nil(t, err)
- assert.Equal(
- t,
- "{\"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\"}",
- string(b),
- )
- }
- func TestNebulaCertificate_Verify(t *testing.T) {
- ca, _, caKey, err := newTestCaCert()
- assert.Nil(t, err)
- c, _, _, err := newTestCert(ca, caKey)
- assert.Nil(t, err)
- h, err := ca.Sha256Sum()
- assert.Nil(t, err)
- caPool := NewCAPool()
- caPool.CAs[h] = ca
- f, err := c.Sha256Sum()
- assert.Nil(t, err)
- caPool.BlacklistFingerprint(f)
- v, err := c.Verify(time.Now(), caPool)
- assert.False(t, v)
- assert.EqualError(t, err, "certificate has been blacklisted")
- caPool.ResetCertBlacklist()
- v, err = c.Verify(time.Now(), caPool)
- assert.True(t, v)
- assert.Nil(t, err)
- v, err = c.Verify(time.Now().Add(time.Hour*1000), caPool)
- assert.False(t, v)
- assert.EqualError(t, err, "root certificate is expired")
- }
- func TestNebulaVerifyPrivateKey(t *testing.T) {
- ca, _, caKey, err := newTestCaCert()
- assert.Nil(t, err)
- c, _, priv, err := newTestCert(ca, caKey)
- err = c.VerifyPrivateKey(priv)
- assert.Nil(t, err)
- _, priv2 := x25519Keypair()
- err = c.VerifyPrivateKey(priv2)
- assert.NotNil(t, err)
- }
- func TestNewCAPoolFromBytes(t *testing.T) {
- noNewLines := `
- # Current provisional, Remove once everything moves over to the real root.
- -----BEGIN NEBULA CERTIFICATE-----
- CkAKDm5lYnVsYSByb290IGNhKJfap9AFMJfg1+YGOiCUQGByMuNRhIlQBOyzXWbL
- vcKBwDhov900phEfJ5DN3kABEkDCq5R8qBiu8sl54yVfgRcQXEDt3cHr8UTSLszv
- bzBEr00kERQxxTzTsH8cpYEgRoipvmExvg8WP8NdAJEYJosB
- -----END NEBULA CERTIFICATE-----
- # root-ca01
- -----BEGIN NEBULA CERTIFICATE-----
- CkMKEW5lYnVsYSByb290IGNhIDAxKJL2u9EFMJL86+cGOiDPXMH4oU6HZTk/CqTG
- BVG+oJpAoqokUBbI4U0N8CSfpUABEkB/Pm5A2xyH/nc8mg/wvGUWG3pZ7nHzaDMf
- 8/phAUt+FLzqTECzQKisYswKvE3pl9mbEYKbOdIHrxdIp95mo4sF
- -----END NEBULA CERTIFICATE-----
- `
- withNewLines := `
- # Current provisional, Remove once everything moves over to the real root.
- -----BEGIN NEBULA CERTIFICATE-----
- CkAKDm5lYnVsYSByb290IGNhKJfap9AFMJfg1+YGOiCUQGByMuNRhIlQBOyzXWbL
- vcKBwDhov900phEfJ5DN3kABEkDCq5R8qBiu8sl54yVfgRcQXEDt3cHr8UTSLszv
- bzBEr00kERQxxTzTsH8cpYEgRoipvmExvg8WP8NdAJEYJosB
- -----END NEBULA CERTIFICATE-----
- # root-ca01
- -----BEGIN NEBULA CERTIFICATE-----
- CkMKEW5lYnVsYSByb290IGNhIDAxKJL2u9EFMJL86+cGOiDPXMH4oU6HZTk/CqTG
- BVG+oJpAoqokUBbI4U0N8CSfpUABEkB/Pm5A2xyH/nc8mg/wvGUWG3pZ7nHzaDMf
- 8/phAUt+FLzqTECzQKisYswKvE3pl9mbEYKbOdIHrxdIp95mo4sF
- -----END NEBULA CERTIFICATE-----
- `
- rootCA := NebulaCertificate{
- Details: NebulaCertificateDetails{
- Name: "nebula root ca",
- },
- }
- rootCA01 := NebulaCertificate{
- Details: NebulaCertificateDetails{
- Name: "nebula root ca 01",
- },
- }
- p, err := NewCAPoolFromBytes([]byte(noNewLines))
- assert.Nil(t, err)
- assert.Equal(t, p.CAs[string("c9bfaf7ce8e84b2eeda2e27b469f4b9617bde192efd214b68891ecda6ed49522")].Details.Name, rootCA.Details.Name)
- assert.Equal(t, p.CAs[string("5c9c3f23e7ee7fe97637cbd3a0a5b854154d1d9aaaf7b566a51f4a88f76b64cd")].Details.Name, rootCA01.Details.Name)
- pp, err := NewCAPoolFromBytes([]byte(withNewLines))
- assert.Nil(t, err)
- assert.Equal(t, pp.CAs[string("c9bfaf7ce8e84b2eeda2e27b469f4b9617bde192efd214b68891ecda6ed49522")].Details.Name, rootCA.Details.Name)
- assert.Equal(t, pp.CAs[string("5c9c3f23e7ee7fe97637cbd3a0a5b854154d1d9aaaf7b566a51f4a88f76b64cd")].Details.Name, rootCA01.Details.Name)
- }
- // Ensure that upgrading the protobuf library does not change how certificates
- // are marshalled, since this would break signature verification
- func TestMarshalingNebulaCertificateConsistency(t *testing.T) {
- before := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
- after := time.Date(2017, time.January, 18, 28, 40, 0, 0, time.UTC)
- pubKey := []byte("1234567890abcedfghij1234567890ab")
- nc := NebulaCertificate{
- Details: NebulaCertificateDetails{
- Name: "testing",
- Ips: []*net.IPNet{
- {IP: net.ParseIP("10.1.1.1"), Mask: net.IPMask(net.ParseIP("255.255.255.0"))},
- {IP: net.ParseIP("10.1.1.2"), Mask: net.IPMask(net.ParseIP("255.255.0.0"))},
- {IP: net.ParseIP("10.1.1.3"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))},
- },
- Subnets: []*net.IPNet{
- {IP: net.ParseIP("9.1.1.1"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))},
- {IP: net.ParseIP("9.1.1.2"), Mask: net.IPMask(net.ParseIP("255.255.255.0"))},
- {IP: net.ParseIP("9.1.1.3"), Mask: net.IPMask(net.ParseIP("255.255.0.0"))},
- },
- Groups: []string{"test-group1", "test-group2", "test-group3"},
- NotBefore: before,
- NotAfter: after,
- PublicKey: pubKey,
- IsCA: false,
- Issuer: "1234567890abcedfghij1234567890ab",
- },
- Signature: []byte("1234567890abcedfghij1234567890ab"),
- }
- b, err := nc.Marshal()
- assert.Nil(t, err)
- t.Log("Cert size:", len(b))
- assert.Equal(t, "0aa2010a0774657374696e67121b8182845080feffff0f828284508080fcff0f8382845080fe83f80f1a1b8182844880fe83f80f8282844880feffff0f838284488080fcff0f220b746573742d67726f757031220b746573742d67726f757032220b746573742d67726f75703328f0e0e7d70430a08681c4053a20313233343536373839306162636564666768696a3132333435363738393061624a081234567890abcedf1220313233343536373839306162636564666768696a313233343536373839306162", fmt.Sprintf("%x", b))
- b, err = proto.Marshal(nc.getRawDetails())
- assert.Nil(t, err)
- t.Log("Raw cert size:", len(b))
- assert.Equal(t, "0a0774657374696e67121b8182845080feffff0f828284508080fcff0f8382845080fe83f80f1a1b8182844880fe83f80f8282844880feffff0f838284488080fcff0f220b746573742d67726f757031220b746573742d67726f757032220b746573742d67726f75703328f0e0e7d70430a08681c4053a20313233343536373839306162636564666768696a3132333435363738393061624a081234567890abcedf", fmt.Sprintf("%x", b))
- }
- func newTestCaCert() (*NebulaCertificate, []byte, []byte, error) {
- pub, priv, err := ed25519.GenerateKey(rand.Reader)
- before := time.Now().Add(time.Second * -60).Round(time.Second)
- after := time.Now().Add(time.Second * 60).Round(time.Second)
- nc := &NebulaCertificate{
- Details: NebulaCertificateDetails{
- Name: "test ca",
- NotBefore: before,
- NotAfter: after,
- PublicKey: pub,
- IsCA: true,
- },
- }
- err = nc.Sign(priv)
- if err != nil {
- return nil, nil, nil, err
- }
- return nc, pub, priv, nil
- }
- func newTestCert(ca *NebulaCertificate, key []byte) (*NebulaCertificate, []byte, []byte, error) {
- issuer, err := ca.Sha256Sum()
- if err != nil {
- return nil, nil, nil, err
- }
- before := time.Now().Add(time.Second * -60).Round(time.Second)
- after := time.Now().Add(time.Second * 60).Round(time.Second)
- pub, rawPriv := x25519Keypair()
- nc := &NebulaCertificate{
- Details: NebulaCertificateDetails{
- Name: "testing",
- Ips: []*net.IPNet{
- {IP: net.ParseIP("10.1.1.1"), Mask: net.IPMask(net.ParseIP("255.255.255.0"))},
- {IP: net.ParseIP("10.1.1.2"), Mask: net.IPMask(net.ParseIP("255.255.0.0"))},
- {IP: net.ParseIP("10.1.1.3"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))},
- },
- Subnets: []*net.IPNet{
- {IP: net.ParseIP("9.1.1.1"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))},
- {IP: net.ParseIP("9.1.1.2"), Mask: net.IPMask(net.ParseIP("255.255.255.0"))},
- {IP: net.ParseIP("9.1.1.3"), Mask: net.IPMask(net.ParseIP("255.255.0.0"))},
- },
- Groups: []string{"test-group1", "test-group2", "test-group3"},
- NotBefore: before,
- NotAfter: after,
- PublicKey: pub,
- IsCA: false,
- Issuer: issuer,
- },
- }
- err = nc.Sign(key)
- if err != nil {
- return nil, nil, nil, err
- }
- return nc, pub, rawPriv, nil
- }
- func x25519Keypair() ([]byte, []byte) {
- var pubkey, privkey [32]byte
- if _, err := io.ReadFull(rand.Reader, privkey[:]); err != nil {
- panic(err)
- }
- curve25519.ScalarBaseMult(&pubkey, &privkey)
- return pubkey[:], privkey[:]
- }
|