From af74e7538249e6c3a96fa3315c42751cd0aef44d Mon Sep 17 00:00:00 2001 From: John Gardiner Myers Date: Sat, 17 Apr 2021 22:12:53 -0700 Subject: [PATCH] Allow adding secondary keyset items --- cmd/kops/create_keypair_ca.go | 15 +- cmd/kops/integration_test.go | 1 + docs/cli/kops_create_keypair_ca.md | 1 + docs/custom_ca.md | 2 +- upup/pkg/fi/ca.go | 24 ++- upup/pkg/fi/ca_test.go | 246 +++++++++++++++++++++++++---- 6 files changed, 248 insertions(+), 41 deletions(-) diff --git a/cmd/kops/create_keypair_ca.go b/cmd/kops/create_keypair_ca.go index 7534ad23bd..a81ecf831e 100644 --- a/cmd/kops/create_keypair_ca.go +++ b/cmd/kops/create_keypair_ca.go @@ -21,6 +21,7 @@ import ( "fmt" "io" "io/ioutil" + "os" "github.com/spf13/cobra" "k8s.io/klog/v2" @@ -52,6 +53,7 @@ type CreateKeypairCaOptions struct { ClusterName string PrivateKeyPath string CertPath string + Primary bool } // NewCmdCreateKeypairCa returns create ca certificate command @@ -82,6 +84,7 @@ func NewCmdCreateKeypairCa(f *util.Factory, out io.Writer) *cobra.Command { cmd.Flags().StringVar(&options.CertPath, "cert", options.CertPath, "Path to CA certificate") cmd.Flags().StringVar(&options.PrivateKeyPath, "key", options.PrivateKeyPath, "Path to CA private key") + cmd.Flags().BoolVar(&options.Primary, "primary", options.Primary, "Make the CA used to issue certificates") return cmd } @@ -132,10 +135,16 @@ func RunCreateKeypairCa(ctx context.Context, f *util.Factory, out io.Writer, opt return fmt.Errorf("error loading certificate %q: %v", options.CertPath, err) } - keyset := &fi.Keyset{ - Items: map[string]*fi.KeysetItem{}, + keyset, err := keyStore.FindKeyset(fi.CertificateIDCA) + if os.IsNotExist(err) || (err == nil && keyset == nil) { + keyset = &fi.Keyset{ + Items: map[string]*fi.KeysetItem{}, + } + } else if err != nil { + return fmt.Errorf("reading existing keyset: %v", err) } - err = keyset.AddItem(cert, privateKey) + + err = keyset.AddItem(cert, privateKey, options.Primary) if err != nil { return err } diff --git a/cmd/kops/integration_test.go b/cmd/kops/integration_test.go index 8058ee455f..be790b840a 100644 --- a/cmd/kops/integration_test.go +++ b/cmd/kops/integration_test.go @@ -484,6 +484,7 @@ func (i *integrationTest) runTest(t *testing.T, h *testutils.IntegrationTestHarn options.ClusterName = i.clusterName options.PrivateKeyPath = path.Join(i.srcDir, "ca.key") options.CertPath = path.Join(i.srcDir, "ca.crt") + options.Primary = true err := RunCreateKeypairCa(ctx, factory, &stdout, options) if err != nil { diff --git a/docs/cli/kops_create_keypair_ca.md b/docs/cli/kops_create_keypair_ca.md index befb8f44f0..c7b0d956d9 100644 --- a/docs/cli/kops_create_keypair_ca.md +++ b/docs/cli/kops_create_keypair_ca.md @@ -28,6 +28,7 @@ kops create keypair ca [flags] --cert string Path to CA certificate -h, --help help for ca --key string Path to CA private key + --primary Make the CA used to issue certificates ``` ### Options inherited from parent commands diff --git a/docs/custom_ca.md b/docs/custom_ca.md index 925894de71..eb64c9a096 100644 --- a/docs/custom_ca.md +++ b/docs/custom_ca.md @@ -17,7 +17,7 @@ The following procedure will allow you to override the CA when creating a cluste ```bash kops create -f cluster.yaml -kops create secret ca --cert ca.crt --key ca.key --name cluster-name.com +kops create secret ca --primary --cert ca.crt --key ca.key --name cluster-name.com kops update cluster --yes ``` diff --git a/upup/pkg/fi/ca.go b/upup/pkg/fi/ca.go index 822d09501e..14bc847a4e 100644 --- a/upup/pkg/fi/ca.go +++ b/upup/pkg/fi/ca.go @@ -229,7 +229,7 @@ func (k *Keyset) ToPublicKeyBytes() ([]byte, error) { } // AddItem adds an item to the keyset -func (k *Keyset) AddItem(cert *pki.Certificate, privateKey *pki.PrivateKey) error { +func (k *Keyset) AddItem(cert *pki.Certificate, privateKey *pki.PrivateKey, primary bool) error { if cert == nil { return fmt.Errorf("no certificate provided") } @@ -237,9 +237,23 @@ func (k *Keyset) AddItem(cert *pki.Certificate, privateKey *pki.PrivateKey) erro return fmt.Errorf("no private key provided") } - // Make sure any subsequently created certificates will have ids that compare higher. + if !primary && k.Primary == nil { + return fmt.Errorf("cannot add secondary item when no existing primary item") + } + + highestId := big.NewInt(0) + for id := range k.Items { + itemId, ok := big.NewInt(0).SetString(id, 10) + if ok && highestId.Cmp(itemId) < 0 { + highestId = itemId + } + } + + // Make sure any subsequently created items will have ids that compare higher. + // If setting a primary, make sure its id doesn't compare lower than existing items. idNumber := pki.BuildPKISerial(time.Now().UnixNano()) - if cert.Certificate.SerialNumber.Cmp(idNumber) <= 0 { + if cert.Certificate.SerialNumber.Cmp(idNumber) <= 0 && + (!primary || cert.Certificate.SerialNumber.Cmp(highestId) > 0) { idNumber = cert.Certificate.SerialNumber } ki := &KeysetItem{ @@ -248,7 +262,9 @@ func (k *Keyset) AddItem(cert *pki.Certificate, privateKey *pki.PrivateKey) erro PrivateKey: privateKey, } k.Items[ki.Id] = ki - k.Primary = ki + if primary { + k.Primary = ki + } return nil } diff --git a/upup/pkg/fi/ca_test.go b/upup/pkg/fi/ca_test.go index 289fa061d3..baf7df9657 100644 --- a/upup/pkg/fi/ca_test.go +++ b/upup/pkg/fi/ca_test.go @@ -30,25 +30,33 @@ import ( const certData = "-----BEGIN CERTIFICATE-----\nMIIC+DCCAeCgAwIBAgIMFnbTyckBfEgW0qf0MA0GCSqGSIb3DQEBCwUAMBgxFjAU\nBgNVBAMTDWNuPWt1YmVybmV0ZXMwHhcNMjEwNDE2MDI0NjE5WhcNMzEwNDE2MDI0\nNjE5WjAYMRYwFAYDVQQDEw1jbj1rdWJlcm5ldGVzMIIBIjANBgkqhkiG9w0BAQEF\nAAOCAQ8AMIIBCgKCAQEA4JwpEprZ5n8RIEt6jT2lAh+UDgRgx/4px21gjgywQivY\nHVxHAZexVb/E9pBa9Q2G9B1Q7TCO7YsUVRQy4JMDZVt+McFnWVwexnqBYFNcVjkE\nmDgAgvCYGE0P9d/RwRL4KuLHo+u6fv7P0jXMN+CpOxyLhYZZNa0ZOZDHsSiJSQSj\n9WGFGHrbCf0KVDpKieR1uBqHrRO+mLR5zkX2L58m74kjK4dsBhmjeq/7OAoTmiG2\nQgJ/P2IjyhiA2mRqY+hl55lwEUV/0yHYEkJC8LdGkwwZz2eF77aSPGmi/A2CSKgM\nwDTx9m+P7jcpWreYw6NG9BueGoDIve/tgFKwvVFF6QIDAQABo0IwQDAOBgNVHQ8B\nAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVOJ/Z21wnbF5Kjsi\nJFdZL/rKHJ4wDQYJKoZIhvcNAQELBQADggEBABWm3zNEiAO+HxfT+WYUhS/ot1Rf\nwzQUPmFR+1GGdXxOam4dMQ0X9uJsE5PtNw7kg9ja+jkUuharSXraueu6IbcRCXQ8\nycnjmbhcPdvH8dhQAB+2Y1xyf/olWan7NU98fwwWk3ubX1aUaoBbXJW9IqkmRG6K\nZVTnz0yGdQfqZyZbl48WfIQ3W7qsAnsSYSrhUWNNOHgy6NQRxpgzGohYFAUprFqc\n8Dm02rzpNwe+ZDOInm05UUOblsdeHYrRetfvhnYJl/CEPGAdJGnOjLMjr7V85y4a\n41XKFsjMo2ztvNdLmYiw0dfar4WSdK/AKFzUXEPRwjCe5xMtsMOIkyJtvxw=\n-----END CERTIFICATE-----\n" const tooBigSerialCertData = "-----BEGIN CERTIFICATE-----\nMIIC2DCCAcCgAwIBAgIRALJXAkVj964tq67wMSI8oJQwDQYJKoZIhvcNAQELBQAw\nFTETMBEGA1UEAxMKa3ViZXJuZXRlczAeFw0xNzEyMjcyMzUyNDBaFw0yNzEyMjcy\nMzUyNDBaMBUxEzARBgNVBAMTCmt1YmVybmV0ZXMwggEiMA0GCSqGSIb3DQEBAQUA\nA4IBDwAwggEKAoIBAQDgnCkSmtnmfxEgS3qNPaUCH5QOBGDH/inHbWCODLBCK9gd\nXEcBl7FVv8T2kFr1DYb0HVDtMI7tixRVFDLgkwNlW34xwWdZXB7GeoFgU1xWOQSY\nOACC8JgYTQ/139HBEvgq4sej67p+/s/SNcw34Kk7HIuFhlk1rRk5kMexKIlJBKP1\nYYUYetsJ/QpUOkqJ5HW4GoetE76YtHnORfYvnybviSMrh2wGGaN6r/s4ChOaIbZC\nAn8/YiPKGIDaZGpj6GXnmXARRX/TIdgSQkLwt0aTDBnPZ4XvtpI8aaL8DYJIqAzA\nNPH2b4/uNylat5jDo0b0G54agMi97+2AUrC9UUXpAgMBAAGjIzAhMA4GA1UdDwEB\n/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBVGR2r\nhzXzRMU5wriPQAJScszNORvoBpXfZoZ09FIupudFxBVU3d4hV9StKnQgPSGA5XQO\nHE97+BxJDuA/rB5oBUsMBjc7y1cde/T6hmi3rLoEYBSnSudCOXJE4G9/0f8byAJe\nrN8+No1r2VgZvZh6p74TEkXv/l3HBPWM7IdUV0HO9JDhSgOVF1fyQKJxRuLJR8jt\nO6mPH2UX0vMwVa4jvwtkddqk2OAdYQvH9rbDjjbzaiW0KnmdueRo92KHAN7BsDZy\nVpXHpqo1Kzg7D3fpaXCf5si7lqqrdJVXH4JC72zxsPehqgi8eIuqOBkiDWmRxAxh\n8yGeRx9AbknHh4Ia\n-----END CERTIFICATE-----\n" const privatekeyData = "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA4JwpEprZ5n8RIEt6jT2lAh+UDgRgx/4px21gjgywQivYHVxH\nAZexVb/E9pBa9Q2G9B1Q7TCO7YsUVRQy4JMDZVt+McFnWVwexnqBYFNcVjkEmDgA\ngvCYGE0P9d/RwRL4KuLHo+u6fv7P0jXMN+CpOxyLhYZZNa0ZOZDHsSiJSQSj9WGF\nGHrbCf0KVDpKieR1uBqHrRO+mLR5zkX2L58m74kjK4dsBhmjeq/7OAoTmiG2QgJ/\nP2IjyhiA2mRqY+hl55lwEUV/0yHYEkJC8LdGkwwZz2eF77aSPGmi/A2CSKgMwDTx\n9m+P7jcpWreYw6NG9BueGoDIve/tgFKwvVFF6QIDAQABAoIBAA0ktjaTfyrAxsTI\nBezb7Zr5NBW55dvuII299cd6MJo+rI/TRYhvUv48kY8IFXp/hyUjzgeDLunxmIf9\n/Zgsoic9Ol44/g45mMduhcGYPzAAeCdcJ5OB9rR9VfDCXyjYLlN8H8iU0734tTqM\n0V13tQ9zdSqkGPZOIcq/kR/pylbOZaQMe97BTlsAnOMSMKDgnftY4122Lq3GYy+t\nvpr+bKVaQZwvkLoSU3rECCaKaghgwCyX7jft9aEkhdJv+KlwbsGY6WErvxOaLWHd\ncuMQjGapY1Fa/4UD00mvrA260NyKfzrp6+P46RrVMwEYRJMIQ8YBAk6N6Hh7dc0G\n8Z6i1m0CgYEA9HeCJR0TSwbIQ1bDXUrzpftHuidG5BnSBtax/ND9qIPhR/FBW5nj\n22nwLc48KkyirlfIULd0ae4qVXJn7wfYcuX/cJMLDmSVtlM5Dzmi/91xRiFgIzx1\nAsbBzaFjISP2HpSgL+e9FtSXaaqeZVrflitVhYKUpI/AKV31qGHf04sCgYEA6zTV\n99Sb49Wdlns5IgsfnXl6ToRttB18lfEKcVfjAM4frnkk06JpFAZeR+9GGKUXZHqs\nz2qcplw4d/moCC6p3rYPBMLXsrGNEUFZqBlgz72QA6BBq3X0Cg1Bc2ZbK5VIzwkg\nST2SSux6ccROfgULmN5ZiLOtdUKNEZpFF3i3qtsCgYADT/s7dYFlatobz3kmMnXK\nsfTu2MllHdRys0YGHu7Q8biDuQkhrJwhxPW0KS83g4JQym+0aEfzh36bWcl+u6R7\nKhKj+9oSf9pndgk345gJz35RbPJYh+EuAHNvzdgCAvK6x1jETWeKf6btj5pF1U1i\nQ4QNIw/QiwIXjWZeubTGsQKBgQCbduLu2rLnlyyAaJZM8DlHZyH2gAXbBZpxqU8T\nt9mtkJDUS/KRiEoYGFV9CqS0aXrayVMsDfXY6B/S/UuZjO5u7LtklDzqOf1aKG3Q\ndGXPKibknqqJYH+bnUNjuYYNerETV57lijMGHuSYCf8vwLn3oxBfERRX61M/DU8Z\nworz/QKBgQDCTJI2+jdXg26XuYUmM4XXfnocfzAXhXBULt1nENcogNf1fcptAVtu\nBAiz4/HipQKqoWVUYmxfgbbLRKKLK0s0lOWKbYdVjhEm/m2ZU8wtXTagNwkIGoyq\nY/C1Lox4f1ROJnCjc/hfcOjcxX5M8A8peecHWlVtUPKTJgxQ7oMKcw==\n-----END RSA PRIVATE KEY-----\n" +const afterCertData = "-----BEGIN CERTIFICATE-----\nMIIBbjCCARigAwIBAgIMFnbWaYo6t3AwKQtWMA0GCSqGSIb3DQEBCwUAMBgxFjAU\nBgNVBAMTDWNuPWt1YmVybmV0ZXMwHhcNMjEwNDE2MDMzNDI0WhcNMzEwNDE2MDMz\nNDI0WjAYMRYwFAYDVQQDEw1jbj1rdWJlcm5ldGVzMFwwDQYJKoZIhvcNAQEBBQAD\nSwAwSAJBANLVh1dSDxJ5EcCd36av7++6+sDKqEm2GAzKIwOlfvPsm+pT+pClr51s\nd1m7V16nhWE6lhWjtsiMF8Q32+P5XZkCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEG\nMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIaNS7TlHC6K0r8yWYM1wExengDq\nMA0GCSqGSIb3DQEBCwUAA0EAoxha8yD6JLJcog/EOMdc5BpVPupQ/0FyO38Mb3l9\n0N7uZle0Tz1FQuadRtouySj37iq9nIxEeTh03Q52hNcl3A==\n-----END CERTIFICATE-----\n" +const afterPrivateKeyData = "-----BEGIN RSA PRIVATE KEY-----\nMIIBOQIBAAJBANLVh1dSDxJ5EcCd36av7++6+sDKqEm2GAzKIwOlfvPsm+pT+pCl\nr51sd1m7V16nhWE6lhWjtsiMF8Q32+P5XZkCAwEAAQJASaGZKr3V1bXCpWp9eVFo\nkmjSuhIMw/F8ZLsTj2p08+rEFIBEpTwmimO9TJEWfPB/yFb3dJpxdLKn7oLcGjSM\noQIhAOJGCrXPRZJsizw3iR3J9KaYIx07EMkqOYZf4gjWULcjAiEA7og7B0+jcxt4\nAcYj38QLZt++cUww+F2VRIQom7LUghMCICGDtFRnheuBLkJWC3YhEp6WTCUpOXxC\nR9DyZL1gWQY3AiABvHIZioXZB6Em+ic2sLmYhRZgwro0hJHajs+w2mtbiwIgN5or\n6t9M/Gb5+3SPwfbsT0ySzuVLobmPYv+Sul+MQn8=\n-----END RSA PRIVATE KEY-----\n" func TestAddItem(t *testing.T) { cert, _ := pki.ParsePEMCertificate([]byte(certData)) tooBigSerialCert, _ := pki.ParsePEMCertificate([]byte(tooBigSerialCertData)) privateKey, _ := pki.ParsePEMPrivateKey([]byte(privatekeyData)) + afterCert, _ := pki.ParsePEMCertificate([]byte(afterCertData)) + afterPrivateKey, _ := pki.ParsePEMPrivateKey([]byte(afterPrivateKeyData)) type expectedItems []struct { validateId func(t *testing.T, id string) cert *pki.Certificate privateKey *pki.PrivateKey } + type expected struct { + items expectedItems + primary *pki.PrivateKey + error string + } tests := []struct { - name string - keyset fi.Keyset - cert *pki.Certificate - privateKey *pki.PrivateKey - expectedItems expectedItems - expectedPrimary *pki.PrivateKey - expectedError string + name string + keyset fi.Keyset + cert *pki.Certificate + privateKey *pki.PrivateKey + expectedPrimary expected + expectedSecondary expected }{ { name: "first", @@ -57,50 +65,200 @@ func TestAddItem(t *testing.T) { }, cert: cert, privateKey: privateKey, - expectedItems: expectedItems{ - { - cert: cert, - privateKey: privateKey, + expectedPrimary: expected{ + items: expectedItems{ + { + cert: cert, + privateKey: privateKey, + }, }, + primary: privateKey, + }, + expectedSecondary: expected{ + error: "cannot add secondary item when no existing primary item", }, - expectedPrimary: privateKey, }, { - name: "BigSerial", + name: "firstBigSerial", keyset: fi.Keyset{ Items: map[string]*fi.KeysetItem{}, }, cert: tooBigSerialCert, privateKey: privateKey, - expectedItems: expectedItems{ - { - validateId: func(t *testing.T, id string) { - version, ok := big.NewInt(0).SetString(id, 10) - require.True(t, ok, "parses as integer") - if version.Cmp(pki.BuildPKISerial(time.Now().UnixNano())) > 0 { - t.Errorf("id %q larger than serial for current time", id) - } + expectedPrimary: expected{ + items: expectedItems{ + { + validateId: assertSerialNotInFuture, + cert: tooBigSerialCert, + privateKey: privateKey, }, - cert: tooBigSerialCert, - privateKey: privateKey, + }, + primary: privateKey, + }, + expectedSecondary: expected{ + error: "cannot add secondary item when no existing primary item", + }, + }, + { + name: "after", + keyset: fi.Keyset{ + Items: map[string]*fi.KeysetItem{ + "6952323604391556590983096308": { + Id: "6952323604391556590983096308", + Certificate: cert, + PrivateKey: privateKey, + }, + }, + Primary: &fi.KeysetItem{ + Id: "6952323604391556590983096308", + Certificate: cert, + PrivateKey: privateKey, }, }, - expectedPrimary: privateKey, + cert: afterCert, + privateKey: afterPrivateKey, + expectedPrimary: expected{ + items: expectedItems{ + { + cert: cert, + privateKey: privateKey, + }, + { + cert: afterCert, + privateKey: afterPrivateKey, + }, + }, + primary: afterPrivateKey, + }, + expectedSecondary: expected{ + items: expectedItems{ + { + cert: cert, + privateKey: privateKey, + }, + { + cert: afterCert, + privateKey: afterPrivateKey, + }, + }, + primary: privateKey, + }, + }, + { + name: "bigSerialAfter", + keyset: fi.Keyset{ + Items: map[string]*fi.KeysetItem{ + "6952335996080054816494652246": { + Id: "6952335996080054816494652246", + Certificate: afterCert, + PrivateKey: afterPrivateKey, + }, + }, + Primary: &fi.KeysetItem{ + Id: "6952335996080054816494652246", + Certificate: afterCert, + PrivateKey: afterPrivateKey, + }, + }, + cert: tooBigSerialCert, + privateKey: privateKey, + expectedPrimary: expected{ + items: expectedItems{ + { + cert: afterCert, + privateKey: afterPrivateKey, + }, + { + validateId: assertSerialNotInFuture, + cert: tooBigSerialCert, + privateKey: privateKey, + }, + }, + primary: privateKey, + }, + expectedSecondary: expected{ + items: expectedItems{ + { + cert: afterCert, + privateKey: afterPrivateKey, + }, + { + validateId: assertSerialNotInFuture, + cert: tooBigSerialCert, + privateKey: privateKey, + }, + }, + primary: afterPrivateKey, + }, + }, + { + name: "before", + keyset: fi.Keyset{ + Items: map[string]*fi.KeysetItem{ + "6952335996080054816494652246": { + Id: "6952335996080054816494652246", + Certificate: afterCert, + PrivateKey: afterPrivateKey, + }, + }, + Primary: &fi.KeysetItem{ + Id: "6952335996080054816494652246", + Certificate: afterCert, + PrivateKey: afterPrivateKey, + }, + }, + cert: cert, + privateKey: privateKey, + expectedPrimary: expected{ + items: expectedItems{ + { + cert: afterCert, + privateKey: afterPrivateKey, + }, + { + validateId: func(t *testing.T, id string) { + assertSerialNewerThan(t, id, afterCert) + }, + cert: cert, + privateKey: privateKey, + }, + }, + primary: privateKey, + }, + expectedSecondary: expected{ + items: expectedItems{ + { + cert: afterCert, + privateKey: afterPrivateKey, + }, + { + cert: cert, + privateKey: privateKey, + }, + }, + primary: afterPrivateKey, + }, }, } for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - err := tc.keyset.AddItem(tc.cert, tc.privateKey) - if tc.expectedError != "" { - assert.EqualError(t, err, tc.expectedError) + runTestcase := func(t *testing.T, primary bool, tcExpected expected) { + keyset := tc.keyset + keyset.Items = make(map[string]*fi.KeysetItem, len(tc.keyset.Items)) + for k, v := range tc.keyset.Items { + keyset.Items[k] = v + } + + err := keyset.AddItem(tc.cert, tc.privateKey, primary) + if tcExpected.error != "" { + assert.EqualError(t, err, tcExpected.error) return } assert.NoError(t, err) - assert.Equal(t, tc.expectedPrimary, tc.keyset.Primary.PrivateKey, "primary key") + assert.Equal(t, tcExpected.primary, keyset.Primary.PrivateKey, "primary key") expected: - for _, expected := range tc.expectedItems { - for id, item := range tc.keyset.Items { + for _, expected := range tcExpected.items { + for id, item := range keyset.Items { if (expected.cert != nil && expected.cert == item.Certificate) || expected.privateKey != nil && expected.privateKey == item.PrivateKey { assert.Same(t, expected.cert, item.Certificate, "item %s", id) @@ -110,7 +268,7 @@ func TestAddItem(t *testing.T) { } else { expected.validateId(t, id) } - delete(tc.keyset.Items, id) + delete(keyset.Items, id) continue expected } } @@ -120,10 +278,32 @@ func TestAddItem(t *testing.T) { t.Errorf("did not find expected key %q", expected.privateKey) } } - for id := range tc.keyset.Items { + for id := range keyset.Items { t.Errorf("unexpected item %q", id) } + } + t.Run(tc.name+" primary", func(t *testing.T) { + runTestcase(t, true, tc.expectedPrimary) + }) + t.Run(tc.name+" secondary", func(t *testing.T) { + runTestcase(t, false, tc.expectedSecondary) }) } } + +func assertSerialNotInFuture(t *testing.T, id string) { + version, ok := big.NewInt(0).SetString(id, 10) + require.True(t, ok, "parses as integer") + if version.Cmp(pki.BuildPKISerial(time.Now().UnixNano())) > 0 { + t.Errorf("id %q larger than serial for current time", id) + } +} + +func assertSerialNewerThan(t *testing.T, id string, cert *pki.Certificate) { + version, ok := big.NewInt(0).SetString(id, 10) + require.True(t, ok, "parses as integer") + if version.Cmp(cert.Certificate.SerialNumber) <= 0 { + t.Errorf("id %q not larger than %q", id, cert.Certificate.SerialNumber.String()) + } +}