diff --git a/.gitignore b/.gitignore index 0bffc857c..55068f31b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,8 @@ /nebula-darwin /nebula.exe /nebula-cert.exe -/coverage.out +**/coverage.out +**/cover.out /cpu.pprof /build /*.tar.gz diff --git a/cert/cert_v1.go b/cert/cert_v1.go index c1d74db99..7717cc58c 100644 --- a/cert/cert_v1.go +++ b/cert/cert_v1.go @@ -210,7 +210,8 @@ func (c *certificateV1) getRawDetails() *RawNebulaCertificateDetails { func (c *certificateV1) String() string { b, err := json.MarshalIndent(c.marshalJSON(), "", "\t") if err != nil { - return "" + //TODO: should we panic instead? + return fmt.Sprintf("", err) } return string(b) } @@ -392,8 +393,6 @@ func unmarshalCertificateV1(b []byte, publicKey []byte) (*certificateV1, error) } } - //do not sort the subnets field for V1 certs - return &nc, nil } diff --git a/cert/cert_test.go b/cert/cert_v1_test.go similarity index 75% rename from cert/cert_test.go rename to cert/cert_v1_test.go index 2d59a293c..f3b109233 100644 --- a/cert/cert_test.go +++ b/cert/cert_v1_test.go @@ -13,11 +13,13 @@ import ( "github.com/slackhq/nebula/test" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "golang.org/x/crypto/curve25519" "golang.org/x/crypto/ed25519" + "google.golang.org/protobuf/proto" ) -func TestMarshalingNebulaCertificate(t *testing.T) { +func TestCertificateV1_Marshal(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") @@ -50,104 +52,22 @@ func TestMarshalingNebulaCertificate(t *testing.T) { nc2, err := unmarshalCertificateV1(b, nil) assert.Nil(t, err) - assert.Equal(t, nc.signature, nc2.Signature()) - assert.Equal(t, nc.details.name, nc2.Name()) - assert.Equal(t, nc.details.notBefore, nc2.NotBefore()) - assert.Equal(t, nc.details.notAfter, nc2.NotAfter()) - assert.Equal(t, nc.details.publicKey, nc2.PublicKey()) - assert.Equal(t, nc.details.isCA, nc2.IsCA()) + assert.Equal(t, nc.Version(), Version1) + assert.Equal(t, nc.Curve(), Curve_CURVE25519) + assert.Equal(t, nc.Signature(), nc2.Signature()) + assert.Equal(t, nc.Name(), nc2.Name()) + assert.Equal(t, nc.NotBefore(), nc2.NotBefore()) + assert.Equal(t, nc.NotAfter(), nc2.NotAfter()) + assert.Equal(t, nc.PublicKey(), nc2.PublicKey()) + assert.Equal(t, nc.IsCA(), nc2.IsCA()) - assert.Equal(t, nc.details.networks, nc2.Networks()) - assert.Equal(t, nc.details.unsafeNetworks, nc2.UnsafeNetworks()) + assert.Equal(t, nc.Networks(), nc2.Networks()) + assert.Equal(t, nc.UnsafeNetworks(), nc2.UnsafeNetworks()) - assert.Equal(t, nc.details.groups, nc2.Groups()) + assert.Equal(t, nc.Groups(), nc2.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 := certificateV1{ -// details: detailsV1{ -// Name: "testing", -// Ips: []netip.Prefix{ -// mustParsePrefixUnmapped("10.1.1.1/24"), -// mustParsePrefixUnmapped("10.1.1.2/16"), -// //TODO: netip cant do it -// //{IP: net.ParseIP("10.1.1.3"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))}, -// }, -// Subnets: []netip.Prefix{ -// //TODO: netip cant do it -// //{IP: net.ParseIP("9.1.1.1"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))}, -// mustParsePrefixUnmapped("9.1.1.2/24"), -// mustParsePrefixUnmapped("9.1.1.3/24"), -// }, -// 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(Curve_CURVE25519, priv)) -// assert.True(t, nc.CheckSignature(pub)) -// -// _, err = nc.Marshal() -// assert.Nil(t, err) -// //t.Log("Cert size:", len(b)) -//} - -//func TestNebulaCertificate_SignP256(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("01234567890abcedfghij1234567890ab1234567890abcedfghij1234567890ab") -// -// nc := certificateV1{ -// details: detailsV1{ -// Name: "testing", -// Ips: []netip.Prefix{ -// mustParsePrefixUnmapped("10.1.1.1/24"), -// mustParsePrefixUnmapped("10.1.1.2/16"), -// //TODO: netip no can do -// //{IP: net.ParseIP("10.1.1.3"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))}, -// }, -// Subnets: []netip.Prefix{ -// //TODO: netip bad -// //{IP: net.ParseIP("9.1.1.1"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))}, -// mustParsePrefixUnmapped("9.1.1.2/24"), -// mustParsePrefixUnmapped("9.1.1.3/16"), -// }, -// Groups: []string{"test-group1", "test-group2", "test-group3"}, -// NotBefore: before, -// NotAfter: after, -// PublicKey: pubKey, -// IsCA: false, -// Curve: Curve_P256, -// Issuer: "1234567890abcedfghij1234567890ab", -// }, -// } -// -// priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) -// pub := elliptic.Marshal(elliptic.P256(), priv.PublicKey.X, priv.PublicKey.Y) -// rawPriv := priv.D.FillBytes(make([]byte, 32)) -// -// assert.Nil(t, err) -// assert.False(t, nc.CheckSignature(pub)) -// assert.Nil(t, nc.Sign(Curve_P256, rawPriv)) -// assert.True(t, nc.CheckSignature(pub)) -// -// _, err = nc.Marshal() -// assert.Nil(t, err) -// //t.Log("Cert size:", len(b)) -//} - -func TestNebulaCertificate_Expired(t *testing.T) { +func TestCertificateV1_Expired(t *testing.T) { nc := certificateV1{ details: detailsV1{ notBefore: time.Now().Add(time.Second * -60).Round(time.Second), @@ -160,7 +80,7 @@ func TestNebulaCertificate_Expired(t *testing.T) { assert.False(t, nc.Expired(time.Now())) } -func TestNebulaCertificate_MarshalJSON(t *testing.T) { +func TestCertificateV1_MarshalJSON(t *testing.T) { time.Local = time.UTC pubKey := []byte("1234567890abcedfghij1234567890ab") @@ -194,7 +114,7 @@ func TestNebulaCertificate_MarshalJSON(t *testing.T) { ) } -func TestNebulaCertificate_Verify(t *testing.T) { +func TestCertificateV1_Verify(t *testing.T) { ca, _, caKey, err := newTestCaCert(time.Now(), time.Now().Add(10*time.Minute), nil, nil, nil) assert.Nil(t, err) @@ -242,7 +162,7 @@ func TestNebulaCertificate_Verify(t *testing.T) { assert.Nil(t, err) } -func TestNebulaCertificate_VerifyP256(t *testing.T) { +func TestCertificateV1_VerifyP256(t *testing.T) { ca, _, caKey, err := newTestCaCertP256(time.Now(), time.Now().Add(10*time.Minute), nil, nil, nil) assert.Nil(t, err) @@ -290,7 +210,7 @@ func TestNebulaCertificate_VerifyP256(t *testing.T) { assert.Nil(t, err) } -func TestNebulaCertificate_Verify_IPs(t *testing.T) { +func TestCertificateV1_Verify_IPs(t *testing.T) { caIp1 := mustParsePrefixUnmapped("10.0.0.0/16") caIp2 := mustParsePrefixUnmapped("192.168.0.0/24") ca, _, caKey, err := newTestCaCert(time.Now(), time.Now().Add(10*time.Minute), []netip.Prefix{caIp1, caIp2}, nil, []string{"test"}) @@ -355,7 +275,7 @@ func TestNebulaCertificate_Verify_IPs(t *testing.T) { assert.Nil(t, err) } -func TestNebulaCertificate_Verify_Subnets(t *testing.T) { +func TestCertificateV1_Verify_Subnets(t *testing.T) { caIp1 := mustParsePrefixUnmapped("10.0.0.0/16") caIp2 := mustParsePrefixUnmapped("192.168.0.0/24") ca, _, caKey, err := newTestCaCert(time.Now(), time.Now().Add(10*time.Minute), nil, []netip.Prefix{caIp1, caIp2}, []string{"test"}) @@ -420,7 +340,7 @@ func TestNebulaCertificate_Verify_Subnets(t *testing.T) { assert.Nil(t, err) } -func TestNebulaCertificate_VerifyPrivateKey(t *testing.T) { +func TestCertificateV1_VerifyPrivateKey(t *testing.T) { ca, _, caKey, err := newTestCaCert(time.Time{}, time.Time{}, nil, nil, nil) assert.Nil(t, err) err = ca.VerifyPrivateKey(Curve_CURVE25519, caKey) @@ -440,7 +360,7 @@ func TestNebulaCertificate_VerifyPrivateKey(t *testing.T) { assert.NotNil(t, err) } -func TestNebulaCertificate_VerifyPrivateKeyP256(t *testing.T) { +func TestCertificateV1_VerifyPrivateKeyP256(t *testing.T) { ca, _, caKey, err := newTestCaCertP256(time.Time{}, time.Time{}, nil, nil, nil) assert.Nil(t, err) err = ca.VerifyPrivateKey(Curve_P256, caKey) @@ -470,49 +390,42 @@ func appendByteSlices(b ...[]byte) []byte { // Ensure that upgrading the protobuf library does not change how certificates // are marshalled, since this would break signature verification -//TODO: since netip cant represent 255.0.255.0 netmask we can't verify the old certs are ok -//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 := certificateV1{ -// details: detailsV1{ -// Name: "testing", -// Ips: []netip.Prefix{ -// mustParsePrefixUnmapped("10.1.1.1/24"), -// mustParsePrefixUnmapped("10.1.1.2/16"), -// //TODO: netip bad -// //{IP: net.ParseIP("10.1.1.3"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))}, -// }, -// Subnets: []netip.Prefix{ -// //TODO: netip bad -// //{IP: net.ParseIP("9.1.1.1"), Mask: net.IPMask(net.ParseIP("255.0.255.0"))}, -// mustParsePrefixUnmapped("9.1.1.2/24"), -// mustParsePrefixUnmapped("9.1.1.3/16"), -// }, -// 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 TestNebulaCertificate_Copy(t *testing.T) { +func TestMarshalingCertificateV1Consistency(t *testing.T) { + before := time.Date(1970, time.January, 1, 1, 1, 1, 1, time.UTC) + after := time.Date(9999, time.January, 1, 1, 1, 1, 1, time.UTC) + pubKey := []byte("1234567890abcedfghij1234567890ab") + + nc := certificateV1{ + details: detailsV1{ + name: "testing", + networks: []netip.Prefix{ + mustParsePrefixUnmapped("10.1.1.2/16"), + mustParsePrefixUnmapped("10.1.1.1/24"), + }, + unsafeNetworks: []netip.Prefix{ + mustParsePrefixUnmapped("9.1.1.3/16"), + mustParsePrefixUnmapped("9.1.1.2/24"), + }, + 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() + require.Nil(t, err) + assert.Equal(t, "0a8e010a0774657374696e671212828284508080fcff0f8182845080feffff0f1a12838284488080fcff0f8282844880feffff0f220b746573742d67726f757031220b746573742d67726f757032220b746573742d67726f75703328cd1c30cdb8ccf0af073a20313233343536373839306162636564666768696a3132333435363738393061624a081234567890abcedf1220313233343536373839306162636564666768696a313233343536373839306162", fmt.Sprintf("%x", b)) + + b, err = proto.Marshal(nc.getRawDetails()) + assert.Nil(t, err) + assert.Equal(t, "0a0774657374696e671212828284508080fcff0f8182845080feffff0f1a12838284488080fcff0f8282844880feffff0f220b746573742d67726f757031220b746573742d67726f757032220b746573742d67726f75703328cd1c30cdb8ccf0af073a20313233343536373839306162636564666768696a3132333435363738393061624a081234567890abcedf", fmt.Sprintf("%x", b)) +} + +func TestCertificateV1_Copy(t *testing.T) { ca, _, caKey, err := newTestCaCert(time.Now(), time.Now().Add(10*time.Minute), nil, nil, nil) assert.Nil(t, err) @@ -523,7 +436,7 @@ func TestNebulaCertificate_Copy(t *testing.T) { test.AssertDeepCopyEqual(t, c, cc) } -func TestUnmarshalNebulaCertificate(t *testing.T) { +func TestUnmarshalCertificateV1(t *testing.T) { // Test that we don't panic with an invalid certificate (#332) data := []byte("\x98\x00\x00") _, err := unmarshalCertificateV1(data, nil) diff --git a/cert/cert_v2.go b/cert/cert_v2.go index 11aa666bd..545e2a103 100644 --- a/cert/cert_v2.go +++ b/cert/cert_v2.go @@ -20,8 +20,6 @@ import ( "golang.org/x/crypto/curve25519" ) -//TODO: should we avoid hex encoding shit on output? Just let it be base64? - const ( classConstructed = 0x20 classContextSpecific = 0x80 @@ -217,6 +215,7 @@ func (c *certificateV2) VerifyPrivateKey(curve Curve, key []byte) error { func (c *certificateV2) String() string { b, err := json.MarshalIndent(c.marshalJSON(), "", "\t") if err != nil { + //TODO: should we panic instead? return "" } return string(b) diff --git a/cert/cert_v2_test.go b/cert/cert_v2_test.go new file mode 100644 index 000000000..3adb47c64 --- /dev/null +++ b/cert/cert_v2_test.go @@ -0,0 +1,119 @@ +package cert + +import ( + "net/netip" + "slices" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestCertificateV2_Marshal(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 := certificateV2{ + details: detailsV2{ + name: "testing", + networks: []netip.Prefix{ + mustParsePrefixUnmapped("10.1.1.2/16"), + mustParsePrefixUnmapped("10.1.1.1/24"), + }, + unsafeNetworks: []netip.Prefix{ + mustParsePrefixUnmapped("9.1.1.3/16"), + mustParsePrefixUnmapped("9.1.1.2/24"), + }, + groups: []string{"test-group1", "test-group2", "test-group3"}, + notBefore: before, + notAfter: after, + isCA: false, + issuer: "1234567890abcdef1234567890abcdef", + }, + signature: []byte("1234567890abcdef1234567890abcdef"), + publicKey: pubKey, + } + + db, err := nc.details.Marshal() + require.NoError(t, err) + nc.rawDetails = db + + b, err := nc.Marshal() + require.Nil(t, err) + //t.Log("Cert size:", len(b)) + + nc2, err := unmarshalCertificateV2(b, nil, Curve_CURVE25519) + assert.Nil(t, err) + + assert.Equal(t, nc.Version(), Version2) + assert.Equal(t, nc.Curve(), Curve_CURVE25519) + assert.Equal(t, nc.Signature(), nc2.Signature()) + assert.Equal(t, nc.Name(), nc2.Name()) + assert.Equal(t, nc.NotBefore(), nc2.NotBefore()) + assert.Equal(t, nc.NotAfter(), nc2.NotAfter()) + assert.Equal(t, nc.PublicKey(), nc2.PublicKey()) + assert.Equal(t, nc.IsCA(), nc2.IsCA()) + assert.Equal(t, nc.Issuer(), nc2.Issuer()) + + // unmarshalling will sort networks and unsafeNetworks, we need to do the same + // but first make sure it fails + assert.NotEqual(t, nc.Networks(), nc2.Networks()) + assert.NotEqual(t, nc.UnsafeNetworks(), nc2.UnsafeNetworks()) + + slices.SortFunc(nc.details.networks, comparePrefix) + slices.SortFunc(nc.details.unsafeNetworks, comparePrefix) + + assert.Equal(t, nc.Networks(), nc2.Networks()) + assert.Equal(t, nc.UnsafeNetworks(), nc2.UnsafeNetworks()) + + assert.Equal(t, nc.Groups(), nc2.Groups()) +} + +func TestCertificateV2_Expired(t *testing.T) { + nc := certificateV2{ + details: detailsV2{ + 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 TestCertificateV2_MarshalJSON(t *testing.T) { + time.Local = time.UTC + pubKey := []byte("1234567890abcedfghij1234567890ab") + + nc := certificateV2{ + details: detailsV2{ + name: "testing", + networks: []netip.Prefix{ + mustParsePrefixUnmapped("10.1.1.1/24"), + mustParsePrefixUnmapped("10.1.1.2/16"), + }, + unsafeNetworks: []netip.Prefix{ + mustParsePrefixUnmapped("9.1.1.2/24"), + mustParsePrefixUnmapped("9.1.1.3/16"), + }, + 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), + isCA: false, + issuer: "1234567890abcedfghij1234567890ab", + }, + publicKey: pubKey, + signature: []byte("1234567890abcedfghij1234567890ab"), + } + + b, err := nc.MarshalJSON() + assert.Nil(t, err) + assert.Equal( + t, + "{\"curve\":\"CURVE25519\",\"details\":{\"groups\":[\"test-group1\",\"test-group2\",\"test-group3\"],\"isCa\":false,\"issuer\":\"1234567890abcedfghij1234567890ab\",\"name\":\"testing\",\"networks\":[\"10.1.1.1/24\",\"10.1.1.2/16\"],\"notAfter\":\"0000-11-30T02:00:00Z\",\"notBefore\":\"0000-11-30T01:00:00Z\",\"unsafeNetworks\":[\"9.1.1.2/24\",\"9.1.1.3/16\"]},\"fingerprint\":\"a9e2984aad14d49821f86993e1c3ec9f1d51251baa7636fbca96aebb72439ade\",\"publicKey\":\"313233343536373839306162636564666768696a313233343536373839306162\",\"signature\":\"313233343536373839306162636564666768696a313233343536373839306162\",\"version\":2}", + string(b), + ) +} diff --git a/cert/sign_test.go b/cert/sign_test.go new file mode 100644 index 000000000..f87ee61bf --- /dev/null +++ b/cert/sign_test.go @@ -0,0 +1,89 @@ +package cert + +import ( + "crypto/ecdsa" + "crypto/ed25519" + "crypto/elliptic" + "crypto/rand" + "net/netip" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestCertificateV1_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") + + tbs := TBSCertificate{ + Version: Version1, + Name: "testing", + Networks: []netip.Prefix{ + mustParsePrefixUnmapped("10.1.1.1/24"), + mustParsePrefixUnmapped("10.1.1.2/16"), + }, + UnsafeNetworks: []netip.Prefix{ + mustParsePrefixUnmapped("9.1.1.2/24"), + mustParsePrefixUnmapped("9.1.1.3/24"), + }, + Groups: []string{"test-group1", "test-group2", "test-group3"}, + NotBefore: before, + NotAfter: after, + PublicKey: pubKey, + IsCA: false, + } + + pub, priv, err := ed25519.GenerateKey(rand.Reader) + c, err := tbs.Sign(&certificateV1{details: detailsV1{notBefore: before, notAfter: after}}, Curve_CURVE25519, priv) + assert.Nil(t, err) + assert.NotNil(t, c) + assert.True(t, c.CheckSignature(pub)) + + b, err := c.Marshal() + assert.Nil(t, err) + uc, err := unmarshalCertificateV1(b, nil) + assert.Nil(t, err) + assert.NotNil(t, uc) +} + +func TestCertificateV1_SignP256(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("01234567890abcedfghij1234567890ab1234567890abcedfghij1234567890ab") + + tbs := TBSCertificate{ + Version: Version1, + Name: "testing", + Networks: []netip.Prefix{ + mustParsePrefixUnmapped("10.1.1.1/24"), + mustParsePrefixUnmapped("10.1.1.2/16"), + }, + UnsafeNetworks: []netip.Prefix{ + mustParsePrefixUnmapped("9.1.1.2/24"), + mustParsePrefixUnmapped("9.1.1.3/16"), + }, + Groups: []string{"test-group1", "test-group2", "test-group3"}, + NotBefore: before, + NotAfter: after, + PublicKey: pubKey, + IsCA: false, + Curve: Curve_P256, + } + + priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + pub := elliptic.Marshal(elliptic.P256(), priv.PublicKey.X, priv.PublicKey.Y) + rawPriv := priv.D.FillBytes(make([]byte, 32)) + + c, err := tbs.Sign(&certificateV1{details: detailsV1{notBefore: before, notAfter: after}}, Curve_P256, rawPriv) + assert.Nil(t, err) + assert.NotNil(t, c) + assert.True(t, c.CheckSignature(pub)) + + b, err := c.Marshal() + assert.Nil(t, err) + uc, err := unmarshalCertificateV1(b, nil) + assert.Nil(t, err) + assert.NotNil(t, uc) +}