// Copyright 2021 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cryptoutils

import (
	"bytes"
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"errors"
	"math/big"
	"strings"
	"testing"
	"time"

	"github.com/google/go-cmp/cmp"
)

const (
	cert1PEM = `-----BEGIN CERTIFICATE-----
MIICyTCCAlCgAwIBAgITYZXhosLz4+Q/XCUwBySVDmU2jTAKBggqhkjOPQQDAzAq
MRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxETAPBgNVBAMTCHNpZ3N0b3JlMB4XDTIx
MDMwOTA0NDYwOVoXDTIxMDMwOTA1MDYwMlowOjEbMBkGA1UECgwSbG9yZW5jLmRA
Z21haWwuY29tMRswGQYDVQQDDBJsb3JlbmMuZEBnbWFpbC5jb20wdjAQBgcqhkjO
PQIBBgUrgQQAIgNiAARIA8thgk3Zext2UWP1aBE1uoIAqetevPiEDuGKtSUPYxBv
AhzrwhDTbHrj6vMQCBNE7o4AfewyJAZf6CKbee8WIakPfAjRSTQjjnZBzKvSHn4K
u8SByXjFN0rde8qDqo+jggEmMIIBIjAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAww
CgYIKwYBBQUHAwMwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUQeDktDb9QFrYxF8H
xBXkAHQmvqwwHwYDVR0jBBgwFoAUyMUdAEGaJCkyUSTrDa5K7UoG0+wwgY0GCCsG
AQUFBwEBBIGAMH4wfAYIKwYBBQUHMAKGcGh0dHA6Ly9wcml2YXRlY2EtY29udGVu
dC02MDNmZTdlNy0wMDAwLTIyMjctYmY3NS1mNGY1ZTgwZDI5NTQuc3RvcmFnZS5n
b29nbGVhcGlzLmNvbS9jYTM2YTFlOTYyNDJiOWZjYjE0Ni9jYS5jcnQwHQYDVR0R
BBYwFIESbG9yZW5jLmRAZ21haWwuY29tMAoGCCqGSM49BAMDA2cAMGQCMAgjOcjN
P3w/xB8bi/hKXJ9RdNH/DMADiusGtd1d/xxyFVj1xYosQ7y1G6y84VDBvQIwMfQG
8Tp8zsxDg5Oz4qUBZ/AKmkPJHhgmiHftwbb5I1S+1xdhzJtJ8Eg0M00/nqok
-----END CERTIFICATE-----
`

	cert2PEM = `-----BEGIN CERTIFICATE-----
MIICyzCCAlGgAwIBAgIUAMtHjhf/mTVArZNaNcBC1UDyDa0wCgYIKoZIzj0EAwMw
KjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0y
MTAzMDkwNDQ3MTFaFw0yMTAzMDkwNTA3MDNaMDoxGzAZBgNVBAoMEmxvcmVuYy5k
QGdtYWlsLmNvbTEbMBkGA1UEAwwSbG9yZW5jLmRAZ21haWwuY29tMHYwEAYHKoZI
zj0CAQYFK4EEACIDYgAERYBuXY15kRDrwW6kpfwbp/5DmrnGnaZLvUwG1QVGV4g7
/dqNNoXpOU0TstLFSsawgc5YvcgKOw52EGBg5Fi9kUslF1M6bsAarMSTeZl7pzHb
sh8B8+U/jn2HZtfCzalvo4IBJjCCASIwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQM
MAoGCCsGAQUFBwMDMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFK2qQ8zm+0Yz5KM8
6ybuUZPrAVX7MB8GA1UdIwQYMBaAFMjFHQBBmiQpMlEk6w2uSu1KBtPsMIGNBggr
BgEFBQcBAQSBgDB+MHwGCCsGAQUFBzAChnBodHRwOi8vcHJpdmF0ZWNhLWNvbnRl
bnQtNjAzZmU3ZTctMDAwMC0yMjI3LWJmNzUtZjRmNWU4MGQyOTU0LnN0b3JhZ2Uu
Z29vZ2xlYXBpcy5jb20vY2EzNmExZTk2MjQyYjlmY2IxNDYvY2EuY3J0MB0GA1Ud
EQQWMBSBEmxvcmVuYy5kQGdtYWlsLmNvbTAKBggqhkjOPQQDAwNoADBlAjA6Nk0l
eD2ey2UnPUcH6gklWo2szEobbv+uEKPYFzZ8fQn3v7nIdz8p3oSajT2oxFMCMQCQ
zisxAae2OlC3p3RbPuobGVvKI6EYmfnohdvD5OgZPRskNhGAiR2e3MDQuuexbAw=
-----END CERTIFICATE-----
`

	somethingElsePEM = `-----BEGIN OTHER THING-----
MIICyjCCAlGgAwIBAgIUAJ7YnfKI0PIi90IkP1ZGVt1hbswwCgYIKoZIzj0EAwMw
KjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0y
MTAzMDkwNDQ4MDJaFw0yMTAzMDkwNTA3NTVaMDoxGzAZBgNVBAoMEmxvcmVuYy5k
QGdtYWlsLmNvbTEbMBkGA1UEAwwSbG9yZW5jLmRAZ21haWwuY29tMHYwEAYHKoZI
zj0CAQYFK4EEACIDYgAEPFq3DlRqPtYRM2XF7eKw4pZFCIo3i1mBTFNzQRL/GufH
G+bQuF9D9qDWwD2sFLDauzTbaIUvpxEdu0ab0rYi0x1sV/RLyZhc7KmpFxGuPEkr
agrlBGDUnxXkUj53NGLCo4IBJjCCASIwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQM
MAoGCCsGAQUFBwMDMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFOYy000hj60o55n3
yX/sE3Kbk65NMB8GA1UdIwQYMBaAFMjFHQBBmiQpMlEk6w2uSu1KBtPsMIGNBggr
BgEFBQcBAQSBgDB+MHwGCCsGAQUFBzAChnBodHRwOi8vcHJpdmF0ZWNhLWNvbnRl
bnQtNjAzZmU3ZTctMDAwMC0yMjI3LWJmNzUtZjRmNWU4MGQyOTU0LnN0b3JhZ2Uu
Z29vZ2xlYXBpcy5jb20vY2EzNmExZTk2MjQyYjlmY2IxNDYvY2EuY3J0MB0GA1Ud
EQQWMBSBEmxvcmVuYy5kQGdtYWlsLmNvbTAKBggqhkjOPQQDAwNnADBkAjAYFQVj
6qsh4cQ05NDOOCBrIdwtaYuiQHCy6y/+0ujrJG8prwc7zx+mFsL0Fsd9g/QCMGZq
vn2snyjRWmm8kGr62QR0T6TKIElf07EmdrAglZodYXcHhv0b0JUmdTnn30vRDQ==
-----BEGIN OTHER THING-----
`
)

var (
	// `x509.Certificate.Equal` only compares `Raw` fields.
	cert1 = &x509.Certificate{
		Raw: []byte{0x30, 0x82, 0x2, 0xc9, 0x30, 0x82, 0x2, 0x50, 0xa0, 0x3, 0x2, 0x1, 0x2, 0x2, 0x13, 0x61, 0x95, 0xe1, 0xa2, 0xc2, 0xf3, 0xe3, 0xe4, 0x3f, 0x5c, 0x25, 0x30, 0x7, 0x24, 0x95, 0xe, 0x65, 0x36, 0x8d, 0x30, 0xa, 0x6, 0x8, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x4, 0x3, 0x3, 0x30, 0x2a, 0x31, 0x15, 0x30, 0x13, 0x6, 0x3, 0x55, 0x4, 0xa, 0x13, 0xc, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x64, 0x65, 0x76, 0x31, 0x11, 0x30, 0xf, 0x6, 0x3, 0x55, 0x4, 0x3, 0x13, 0x8, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x30, 0x1e, 0x17, 0xd, 0x32, 0x31, 0x30, 0x33, 0x30, 0x39, 0x30, 0x34, 0x34, 0x36, 0x30, 0x39, 0x5a, 0x17, 0xd, 0x32, 0x31, 0x30, 0x33, 0x30, 0x39, 0x30, 0x35, 0x30, 0x36, 0x30, 0x32, 0x5a, 0x30, 0x3a, 0x31, 0x1b, 0x30, 0x19, 0x6, 0x3, 0x55, 0x4, 0xa, 0xc, 0x12, 0x6c, 0x6f, 0x72, 0x65, 0x6e, 0x63, 0x2e, 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x31, 0x1b, 0x30, 0x19, 0x6, 0x3, 0x55, 0x4, 0x3, 0xc, 0x12, 0x6c, 0x6f, 0x72, 0x65, 0x6e, 0x63, 0x2e, 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x76, 0x30, 0x10, 0x6, 0x7, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x2, 0x1, 0x6, 0x5, 0x2b, 0x81, 0x4, 0x0, 0x22, 0x3, 0x62, 0x0, 0x4, 0x48, 0x3, 0xcb, 0x61, 0x82, 0x4d, 0xd9, 0x7b, 0x1b, 0x76, 0x51, 0x63, 0xf5, 0x68, 0x11, 0x35, 0xba, 0x82, 0x0, 0xa9, 0xeb, 0x5e, 0xbc, 0xf8, 0x84, 0xe, 0xe1, 0x8a, 0xb5, 0x25, 0xf, 0x63, 0x10, 0x6f, 0x2, 0x1c, 0xeb, 0xc2, 0x10, 0xd3, 0x6c, 0x7a, 0xe3, 0xea, 0xf3, 0x10, 0x8, 0x13, 0x44, 0xee, 0x8e, 0x0, 0x7d, 0xec, 0x32, 0x24, 0x6, 0x5f, 0xe8, 0x22, 0x9b, 0x79, 0xef, 0x16, 0x21, 0xa9, 0xf, 0x7c, 0x8, 0xd1, 0x49, 0x34, 0x23, 0x8e, 0x76, 0x41, 0xcc, 0xab, 0xd2, 0x1e, 0x7e, 0xa, 0xbb, 0xc4, 0x81, 0xc9, 0x78, 0xc5, 0x37, 0x4a, 0xdd, 0x7b, 0xca, 0x83, 0xaa, 0x8f, 0xa3, 0x82, 0x1, 0x26, 0x30, 0x82, 0x1, 0x22, 0x30, 0xe, 0x6, 0x3, 0x55, 0x1d, 0xf, 0x1, 0x1, 0xff, 0x4, 0x4, 0x3, 0x2, 0x7, 0x80, 0x30, 0x13, 0x6, 0x3, 0x55, 0x1d, 0x25, 0x4, 0xc, 0x30, 0xa, 0x6, 0x8, 0x2b, 0x6, 0x1, 0x5, 0x5, 0x7, 0x3, 0x3, 0x30, 0xc, 0x6, 0x3, 0x55, 0x1d, 0x13, 0x1, 0x1, 0xff, 0x4, 0x2, 0x30, 0x0, 0x30, 0x1d, 0x6, 0x3, 0x55, 0x1d, 0xe, 0x4, 0x16, 0x4, 0x14, 0x41, 0xe0, 0xe4, 0xb4, 0x36, 0xfd, 0x40, 0x5a, 0xd8, 0xc4, 0x5f, 0x7, 0xc4, 0x15, 0xe4, 0x0, 0x74, 0x26, 0xbe, 0xac, 0x30, 0x1f, 0x6, 0x3, 0x55, 0x1d, 0x23, 0x4, 0x18, 0x30, 0x16, 0x80, 0x14, 0xc8, 0xc5, 0x1d, 0x0, 0x41, 0x9a, 0x24, 0x29, 0x32, 0x51, 0x24, 0xeb, 0xd, 0xae, 0x4a, 0xed, 0x4a, 0x6, 0xd3, 0xec, 0x30, 0x81, 0x8d, 0x6, 0x8, 0x2b, 0x6, 0x1, 0x5, 0x5, 0x7, 0x1, 0x1, 0x4, 0x81, 0x80, 0x30, 0x7e, 0x30, 0x7c, 0x6, 0x8, 0x2b, 0x6, 0x1, 0x5, 0x5, 0x7, 0x30, 0x2, 0x86, 0x70, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x63, 0x61, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x36, 0x30, 0x33, 0x66, 0x65, 0x37, 0x65, 0x37, 0x2d, 0x30, 0x30, 0x30, 0x30, 0x2d, 0x32, 0x32, 0x32, 0x37, 0x2d, 0x62, 0x66, 0x37, 0x35, 0x2d, 0x66, 0x34, 0x66, 0x35, 0x65, 0x38, 0x30, 0x64, 0x32, 0x39, 0x35, 0x34, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x61, 0x33, 0x36, 0x61, 0x31, 0x65, 0x39, 0x36, 0x32, 0x34, 0x32, 0x62, 0x39, 0x66, 0x63, 0x62, 0x31, 0x34, 0x36, 0x2f, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x1d, 0x6, 0x3, 0x55, 0x1d, 0x11, 0x4, 0x16, 0x30, 0x14, 0x81, 0x12, 0x6c, 0x6f, 0x72, 0x65, 0x6e, 0x63, 0x2e, 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0xa, 0x6, 0x8, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x4, 0x3, 0x3, 0x3, 0x67, 0x0, 0x30, 0x64, 0x2, 0x30, 0x8, 0x23, 0x39, 0xc8, 0xcd, 0x3f, 0x7c, 0x3f, 0xc4, 0x1f, 0x1b, 0x8b, 0xf8, 0x4a, 0x5c, 0x9f, 0x51, 0x74, 0xd1, 0xff, 0xc, 0xc0, 0x3, 0x8a, 0xeb, 0x6, 0xb5, 0xdd, 0x5d, 0xff, 0x1c, 0x72, 0x15, 0x58, 0xf5, 0xc5, 0x8a, 0x2c, 0x43, 0xbc, 0xb5, 0x1b, 0xac, 0xbc, 0xe1, 0x50, 0xc1, 0xbd, 0x2, 0x30, 0x31, 0xf4, 0x6, 0xf1, 0x3a, 0x7c, 0xce, 0xcc, 0x43, 0x83, 0x93, 0xb3, 0xe2, 0xa5, 0x1, 0x67, 0xf0, 0xa, 0x9a, 0x43, 0xc9, 0x1e, 0x18, 0x26, 0x88, 0x77, 0xed, 0xc1, 0xb6, 0xf9, 0x23, 0x54, 0xbe, 0xd7, 0x17, 0x61, 0xcc, 0x9b, 0x49, 0xf0, 0x48, 0x34, 0x33, 0x4d, 0x3f, 0x9e, 0xaa, 0x24},
	}
	cert2 = &x509.Certificate{
		Raw: []byte{0x30, 0x82, 0x2, 0xcb, 0x30, 0x82, 0x2, 0x51, 0xa0, 0x3, 0x2, 0x1, 0x2, 0x2, 0x14, 0x0, 0xcb, 0x47, 0x8e, 0x17, 0xff, 0x99, 0x35, 0x40, 0xad, 0x93, 0x5a, 0x35, 0xc0, 0x42, 0xd5, 0x40, 0xf2, 0xd, 0xad, 0x30, 0xa, 0x6, 0x8, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x4, 0x3, 0x3, 0x30, 0x2a, 0x31, 0x15, 0x30, 0x13, 0x6, 0x3, 0x55, 0x4, 0xa, 0x13, 0xc, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x64, 0x65, 0x76, 0x31, 0x11, 0x30, 0xf, 0x6, 0x3, 0x55, 0x4, 0x3, 0x13, 0x8, 0x73, 0x69, 0x67, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x30, 0x1e, 0x17, 0xd, 0x32, 0x31, 0x30, 0x33, 0x30, 0x39, 0x30, 0x34, 0x34, 0x37, 0x31, 0x31, 0x5a, 0x17, 0xd, 0x32, 0x31, 0x30, 0x33, 0x30, 0x39, 0x30, 0x35, 0x30, 0x37, 0x30, 0x33, 0x5a, 0x30, 0x3a, 0x31, 0x1b, 0x30, 0x19, 0x6, 0x3, 0x55, 0x4, 0xa, 0xc, 0x12, 0x6c, 0x6f, 0x72, 0x65, 0x6e, 0x63, 0x2e, 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x31, 0x1b, 0x30, 0x19, 0x6, 0x3, 0x55, 0x4, 0x3, 0xc, 0x12, 0x6c, 0x6f, 0x72, 0x65, 0x6e, 0x63, 0x2e, 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x76, 0x30, 0x10, 0x6, 0x7, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x2, 0x1, 0x6, 0x5, 0x2b, 0x81, 0x4, 0x0, 0x22, 0x3, 0x62, 0x0, 0x4, 0x45, 0x80, 0x6e, 0x5d, 0x8d, 0x79, 0x91, 0x10, 0xeb, 0xc1, 0x6e, 0xa4, 0xa5, 0xfc, 0x1b, 0xa7, 0xfe, 0x43, 0x9a, 0xb9, 0xc6, 0x9d, 0xa6, 0x4b, 0xbd, 0x4c, 0x6, 0xd5, 0x5, 0x46, 0x57, 0x88, 0x3b, 0xfd, 0xda, 0x8d, 0x36, 0x85, 0xe9, 0x39, 0x4d, 0x13, 0xb2, 0xd2, 0xc5, 0x4a, 0xc6, 0xb0, 0x81, 0xce, 0x58, 0xbd, 0xc8, 0xa, 0x3b, 0xe, 0x76, 0x10, 0x60, 0x60, 0xe4, 0x58, 0xbd, 0x91, 0x4b, 0x25, 0x17, 0x53, 0x3a, 0x6e, 0xc0, 0x1a, 0xac, 0xc4, 0x93, 0x79, 0x99, 0x7b, 0xa7, 0x31, 0xdb, 0xb2, 0x1f, 0x1, 0xf3, 0xe5, 0x3f, 0x8e, 0x7d, 0x87, 0x66, 0xd7, 0xc2, 0xcd, 0xa9, 0x6f, 0xa3, 0x82, 0x1, 0x26, 0x30, 0x82, 0x1, 0x22, 0x30, 0xe, 0x6, 0x3, 0x55, 0x1d, 0xf, 0x1, 0x1, 0xff, 0x4, 0x4, 0x3, 0x2, 0x7, 0x80, 0x30, 0x13, 0x6, 0x3, 0x55, 0x1d, 0x25, 0x4, 0xc, 0x30, 0xa, 0x6, 0x8, 0x2b, 0x6, 0x1, 0x5, 0x5, 0x7, 0x3, 0x3, 0x30, 0xc, 0x6, 0x3, 0x55, 0x1d, 0x13, 0x1, 0x1, 0xff, 0x4, 0x2, 0x30, 0x0, 0x30, 0x1d, 0x6, 0x3, 0x55, 0x1d, 0xe, 0x4, 0x16, 0x4, 0x14, 0xad, 0xaa, 0x43, 0xcc, 0xe6, 0xfb, 0x46, 0x33, 0xe4, 0xa3, 0x3c, 0xeb, 0x26, 0xee, 0x51, 0x93, 0xeb, 0x1, 0x55, 0xfb, 0x30, 0x1f, 0x6, 0x3, 0x55, 0x1d, 0x23, 0x4, 0x18, 0x30, 0x16, 0x80, 0x14, 0xc8, 0xc5, 0x1d, 0x0, 0x41, 0x9a, 0x24, 0x29, 0x32, 0x51, 0x24, 0xeb, 0xd, 0xae, 0x4a, 0xed, 0x4a, 0x6, 0xd3, 0xec, 0x30, 0x81, 0x8d, 0x6, 0x8, 0x2b, 0x6, 0x1, 0x5, 0x5, 0x7, 0x1, 0x1, 0x4, 0x81, 0x80, 0x30, 0x7e, 0x30, 0x7c, 0x6, 0x8, 0x2b, 0x6, 0x1, 0x5, 0x5, 0x7, 0x30, 0x2, 0x86, 0x70, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x63, 0x61, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x36, 0x30, 0x33, 0x66, 0x65, 0x37, 0x65, 0x37, 0x2d, 0x30, 0x30, 0x30, 0x30, 0x2d, 0x32, 0x32, 0x32, 0x37, 0x2d, 0x62, 0x66, 0x37, 0x35, 0x2d, 0x66, 0x34, 0x66, 0x35, 0x65, 0x38, 0x30, 0x64, 0x32, 0x39, 0x35, 0x34, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x61, 0x33, 0x36, 0x61, 0x31, 0x65, 0x39, 0x36, 0x32, 0x34, 0x32, 0x62, 0x39, 0x66, 0x63, 0x62, 0x31, 0x34, 0x36, 0x2f, 0x63, 0x61, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x1d, 0x6, 0x3, 0x55, 0x1d, 0x11, 0x4, 0x16, 0x30, 0x14, 0x81, 0x12, 0x6c, 0x6f, 0x72, 0x65, 0x6e, 0x63, 0x2e, 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0xa, 0x6, 0x8, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x4, 0x3, 0x3, 0x3, 0x68, 0x0, 0x30, 0x65, 0x2, 0x30, 0x3a, 0x36, 0x4d, 0x25, 0x78, 0x3d, 0x9e, 0xcb, 0x65, 0x27, 0x3d, 0x47, 0x7, 0xea, 0x9, 0x25, 0x5a, 0x8d, 0xac, 0xcc, 0x4a, 0x1b, 0x6e, 0xff, 0xae, 0x10, 0xa3, 0xd8, 0x17, 0x36, 0x7c, 0x7d, 0x9, 0xf7, 0xbf, 0xb9, 0xc8, 0x77, 0x3f, 0x29, 0xde, 0x84, 0x9a, 0x8d, 0x3d, 0xa8, 0xc4, 0x53, 0x2, 0x31, 0x0, 0x90, 0xce, 0x2b, 0x31, 0x1, 0xa7, 0xb6, 0x3a, 0x50, 0xb7, 0xa7, 0x74, 0x5b, 0x3e, 0xea, 0x1b, 0x19, 0x5b, 0xca, 0x23, 0xa1, 0x18, 0x99, 0xf9, 0xe8, 0x85, 0xdb, 0xc3, 0xe4, 0xe8, 0x19, 0x3d, 0x1b, 0x24, 0x36, 0x11, 0x80, 0x89, 0x1d, 0x9e, 0xdc, 0xc0, 0xd0, 0xba, 0xe7, 0xb1, 0x6c, 0xc},
	}
)

func assertCertsEqual(t *testing.T, wanted, got []*x509.Certificate) {
	t.Helper()
	if len(wanted) != len(got) {
		t.Errorf("got %d certs, wanted %d", len(got), len(wanted))
	}
	for i, cert := range wanted {
		if d := cmp.Diff(cert, got[i]); d != "" {
			t.Errorf("wanted[%d] != got[%d] (-want +got): %s", i, i, d)
		}
	}
}

func TestCertificatesFromPEM(t *testing.T) {
	testCases := []struct {
		name     string
		pemBytes []byte

		expected  []*x509.Certificate
		expectErr bool
	}{
		{
			name:     "one cert",
			pemBytes: []byte(cert1PEM),

			expected: []*x509.Certificate{cert1},
		},
		{
			name:     "one cert with trailing newline",
			pemBytes: append([]byte(cert1PEM), '\n'),

			expected: []*x509.Certificate{cert1},
		},
		{
			name:     "two certs",
			pemBytes: []byte(cert1PEM + cert2PEM),
			expected: []*x509.Certificate{cert1, cert2},
		},
		{
			name:     "two certs with newline between and after certificates",
			pemBytes: []byte(cert1PEM + "\n" + cert2PEM + "\n"),
			expected: []*x509.Certificate{cert1, cert2},
		},
		{
			name:     "one cert and one unknown PEM",
			pemBytes: []byte(cert1PEM + somethingElsePEM),

			expectErr: true,
		},
		{
			name:     "unknown PEM",
			pemBytes: []byte(somethingElsePEM),

			expectErr: true,
		},
	}
	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			t.Run("Unmarshal", func(t *testing.T) {
				got, err := UnmarshalCertificatesFromPEM(tc.pemBytes)
				if err != nil {
					if !tc.expectErr {
						t.Fatalf("UnmarshalCertificatesFromPEM() returned unexpected error: %v", err)
					}
				} else if tc.expectErr {
					t.Fatalf("UnmarshalCertificatesFromPEM() should have returned an error, got: %v", got)
				}
				assertCertsEqual(t, tc.expected, got)
			})
			t.Run("UnmarshalLimited", func(t *testing.T) {
				got, err := UnmarshalCertificatesFromPEMLimited(tc.pemBytes, 10 /*iterations*/)
				if err != nil {
					if !tc.expectErr {
						t.Fatalf("UnmarshalCertificatesFromPEM() returned unexpected error: %v", err)
					}
				} else if tc.expectErr {
					t.Fatalf("UnmarshalCertificatesFromPEM() should have returned an error, got: %v", got)
				}
				assertCertsEqual(t, tc.expected, got)
			})
			t.Run("Load", func(t *testing.T) {
				got, err := LoadCertificatesFromPEM(bytes.NewReader(tc.pemBytes))
				if err != nil {
					if !tc.expectErr {
						t.Fatalf("LoadCertificatesFromPEM() returned unexpected error: %v", err)
					}
				} else if tc.expectErr {
					t.Fatalf("LoadCertificatesFromPEM() should have returned an error, got: %v", got)
				}
				assertCertsEqual(t, tc.expected, got)
			})
		})
	}
}

func TestUnmarshalCertificatesFromPEMLimited(t *testing.T) {
	iterations := 10
	pemCerts := make([]byte, 0, len(cert1PEM)*iterations+len(cert1PEM))
	for range iterations {
		pemCerts = append(pemCerts, []byte(cert1PEM)...)
	}
	certs, err := UnmarshalCertificatesFromPEMLimited(pemCerts, iterations)
	if err != nil {
		t.Fatalf("expected no error, got %v", err)
	}
	if len(certs) != iterations {
		t.Fatalf("unexpected number of certificates, expected %d, got %d", iterations, len(certs))
	}

	// append one more certificate to cause a failure
	pemCerts = append(pemCerts, []byte(cert1PEM)...)
	_, err = UnmarshalCertificatesFromPEMLimited(pemCerts, iterations)
	if err == nil || err.Error() != "too many certificates specified in PEM block" {
		t.Fatalf("expected error with too many certificates, got %v", err)
	}
}

func TestMarshalCertificateToPEM(t *testing.T) {
	testCases := []struct {
		name string
		cert *x509.Certificate

		expected  []byte
		expectErr bool
	}{
		{
			name:     "cert1",
			cert:     cert1,
			expected: []byte(cert1PEM),
		},
		{
			name:     "cert2",
			cert:     cert2,
			expected: []byte(cert2PEM),
		},
		{
			name:      "nil",
			cert:      nil,
			expectErr: true,
		},
	}
	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			got, err := MarshalCertificateToPEM(tc.cert)
			if err != nil {
				if !tc.expectErr {
					t.Fatalf("MarshalCertificateToPEM() returned unexpected error: %v", err)
				}
			} else if tc.expectErr {
				t.Fatalf("MarshalCertificateToPEM() should have returned an error, got: %v", got)
			}
			if d := cmp.Diff(tc.expected, got); d != "" {
				t.Errorf("MarshalCertificateToPEM() returned unexpected PEM (-want +got): %s", d)
			}
		})
	}
}

func TestMarshalCertificatesToPEM(t *testing.T) {
	testCases := []struct {
		name  string
		certs []*x509.Certificate

		expected  []byte
		expectErr bool
	}{
		{
			name:     "one cert",
			certs:    []*x509.Certificate{cert1},
			expected: []byte(cert1PEM),
		},
		{
			name:     "two certs",
			certs:    []*x509.Certificate{cert1, cert2},
			expected: []byte(cert1PEM + cert2PEM),
		},
		{
			name:      "nil cert",
			certs:     []*x509.Certificate{cert1, nil},
			expectErr: true,
		},
	}
	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			got, err := MarshalCertificatesToPEM(tc.certs)
			if err != nil {
				if !tc.expectErr {
					t.Fatalf("MarshalCertificatesToPEM() returned unexpected error: %v", err)
				}
			} else if tc.expectErr {
				t.Fatalf("MarshalCertificatesToPEM() should have returned an error, got: %v", got)
			}
			if d := cmp.Diff(tc.expected, got); d != "" {
				t.Errorf("MarshalCertificatesToPEM() returned unexpected PEM (-want +got): %s", d)
			}
		})
	}
}

func errorsEqual(a, b error) bool {
	if errors.Is(a, b) {
		// both are nil
		return true
	}
	return a != nil && b != nil && a.Error() == b.Error()
}

func TestCheckExpiration(t *testing.T) {
	testCases := []struct {
		name  string
		cert  *x509.Certificate
		epoch time.Time

		expected error
	}{
		{
			name: "valid",
			cert: &x509.Certificate{
				NotAfter:  time.Unix(4444, 0),
				NotBefore: time.Unix(2222, 0),
			},
			epoch:    time.Unix(3333, 0),
			expected: nil,
		},
		{
			name: "expired",
			cert: &x509.Certificate{
				NotAfter:  time.Unix(4444, 0),
				NotBefore: time.Unix(2222, 0),
			},
			epoch:    time.Unix(5555, 0),
			expected: errors.New("certificate expiration time 1970-01-01T01:14:04Z is before 1970-01-01T01:32:35Z"),
		},
		{
			name: "not valid yet",
			cert: &x509.Certificate{
				NotAfter:  time.Unix(4444, 0),
				NotBefore: time.Unix(2222, 0),
			},
			epoch:    time.Unix(1111, 0),
			expected: errors.New("certificate issued time 1970-01-01T00:37:02Z is before 1970-01-01T00:18:31Z"),
		},
		{
			name:     "nil",
			cert:     nil,
			expected: errors.New("certificate is nil"),
		},
	}
	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			if err := CheckExpiration(tc.cert, tc.epoch); !errorsEqual(err, tc.expected) {
				t.Errorf("CheckExpiration() should have returned: %v, got: %v", tc.expected, err)
			}
		})
	}
}

func TestParseCSR(t *testing.T) {
	priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if err != nil {
		t.Fatal(err)
	}
	csrTmpl := &x509.CertificateRequest{Subject: pkix.Name{CommonName: "test"}}
	derCSR, err := x509.CreateCertificateRequest(rand.Reader, csrTmpl, priv)
	if err != nil {
		t.Fatal(err)
	}

	// success with type CERTIFICATE REQUEST
	pemCSR := pem.EncodeToMemory(&pem.Block{
		Type:  "CERTIFICATE REQUEST",
		Bytes: derCSR,
	})
	parsedCSR, err := ParseCSR(pemCSR)
	if err != nil {
		t.Fatal(err)
	}
	if parsedCSR.Subject.CommonName != "test" {
		t.Fatalf("unexpected CSR common name")
	}

	// success with type NEW CERTIFICATE REQUEST
	pemCSR = pem.EncodeToMemory(&pem.Block{
		Type:  "NEW CERTIFICATE REQUEST",
		Bytes: derCSR,
	})
	parsedCSR, err = ParseCSR(pemCSR)
	if err != nil {
		t.Fatal(err)
	}
	if parsedCSR.Subject.CommonName != "test" {
		t.Fatalf("unexpected CSR common name")
	}

	// fails with invalid PEM encoded block
	_, err = ParseCSR([]byte{1, 2, 3})
	if err == nil || !strings.Contains(err.Error(), "no CSR found while decoding") {
		t.Fatalf("expected error parsing invalid CSR, got %v", err)
	}

	// fails with invalid DER type
	pemCSR = pem.EncodeToMemory(&pem.Block{
		Type:  "BEGIN CERTIFICATE",
		Bytes: derCSR,
	})
	_, err = ParseCSR(pemCSR)
	if err == nil || !strings.Contains(err.Error(), "DER type BEGIN CERTIFICATE is not of any type") {
		t.Fatalf("expected error parsing invalid CSR, got %v", err)
	}
}

func TestGenerateSerialNumber(t *testing.T) {
	serialNumber, err := GenerateSerialNumber()
	if err != nil {
		t.Fatalf("unexpected error generating serial number: %v", err)
	}
	if serialNumber.Cmp(big.NewInt(0)) == -1 {
		t.Fatalf("serial number is negative: %v", serialNumber)
	}
	if serialNumber.Cmp(big.NewInt(0)) == 0 {
		t.Fatalf("serial number is 0: %v", serialNumber)
	}
	maxSerial := (&big.Int{}).Exp(big.NewInt(2), big.NewInt(159), nil)
	// Serial number must be less than max serial number.
	if serialNumber.Cmp(maxSerial) >= 0 {
		t.Fatalf("serial number is too large: %v", serialNumber)
	}
}
