// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package certutil

import (
	"bytes"
	"crypto"
	"crypto/dsa"
	"crypto/ecdsa"
	"crypto/ed25519"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha1"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/asn1"
	"encoding/hex"
	"encoding/pem"
	"errors"
	"fmt"
	"io"
	"math/big"
	"net"
	"net/url"
	"strconv"
	"strings"
	"time"

	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/vault/sdk/helper/errutil"
	"github.com/hashicorp/vault/sdk/helper/jsonutil"
	"github.com/mitchellh/mapstructure"
	"golang.org/x/crypto/cryptobyte"
	cbasn1 "golang.org/x/crypto/cryptobyte/asn1"
)

const rsaMinimumSecureKeySize = 2048

// Mapping of key types to default key lengths
var defaultAlgorithmKeyBits = map[string]int{
	"rsa": 2048,
	"ec":  256,
}

// Mapping of NIST P-Curve's key length to expected signature bits.
var expectedNISTPCurveHashBits = map[int]int{
	224: 256,
	256: 256,
	384: 384,
	521: 512,
}

// Mapping of constant names<->constant values for SignatureAlgorithm
var SignatureAlgorithmNames = map[string]x509.SignatureAlgorithm{
	"sha256withrsa":    x509.SHA256WithRSA,
	"sha384withrsa":    x509.SHA384WithRSA,
	"sha512withrsa":    x509.SHA512WithRSA,
	"ecdsawithsha256":  x509.ECDSAWithSHA256,
	"ecdsawithsha384":  x509.ECDSAWithSHA384,
	"ecdsawithsha512":  x509.ECDSAWithSHA512,
	"sha256withrsapss": x509.SHA256WithRSAPSS,
	"sha384withrsapss": x509.SHA384WithRSAPSS,
	"sha512withrsapss": x509.SHA512WithRSAPSS,
	"pureed25519":      x509.PureEd25519,
	"ed25519":          x509.PureEd25519, // Duplicated for clarity; most won't expect the "Pure" prefix.
}

// Mapping of constant values<->constant names for SignatureAlgorithm
var InvSignatureAlgorithmNames = map[x509.SignatureAlgorithm]string{
	x509.SHA256WithRSA:    "SHA256WithRSA",
	x509.SHA384WithRSA:    "SHA384WithRSA",
	x509.SHA512WithRSA:    "SHA512WithRSA",
	x509.ECDSAWithSHA256:  "ECDSAWithSHA256",
	x509.ECDSAWithSHA384:  "ECDSAWithSHA384",
	x509.ECDSAWithSHA512:  "ECDSAWithSHA512",
	x509.SHA256WithRSAPSS: "SHA256WithRSAPSS",
	x509.SHA384WithRSAPSS: "SHA384WithRSAPSS",
	x509.SHA512WithRSAPSS: "SHA512WithRSAPSS",
	x509.PureEd25519:      "Ed25519",
}

// OIDs for X.509 SAN Extension
var OidExtensionSubjectAltName = asn1.ObjectIdentifier([]int{2, 5, 29, 17})

// OID for RFC 5280 CRL Number extension.
//
// > id-ce-cRLNumber OBJECT IDENTIFIER ::= { id-ce 20 }
var CRLNumberOID = asn1.ObjectIdentifier([]int{2, 5, 29, 20})

// OID for RFC 5280 Delta CRL Indicator CRL extension.
//
// > id-ce-deltaCRLIndicator OBJECT IDENTIFIER ::= { id-ce 27 }
var DeltaCRLIndicatorOID = asn1.ObjectIdentifier([]int{2, 5, 29, 27})

// OID for KeyUsage from RFC 2459 : https://www.rfc-editor.org/rfc/rfc2459.html#section-4.2.1.3
//
// > id-ce-keyUsage OBJECT IDENTIFIER ::=  { id-ce 15 }
var KeyUsageOID = asn1.ObjectIdentifier([]int{2, 5, 29, 15})

// OID for Extended Key Usage from RFC 5280 : https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.12
//
// id-ce-extKeyUsage OBJECT IDENTIFIER ::= { id-ce 37 }
var ExtendedKeyUsageOID = asn1.ObjectIdentifier([]int{2, 5, 29, 37})

// GetHexFormatted returns the byte buffer formatted in hex with
// the specified separator between bytes.
func GetHexFormatted(buf []byte, sep string) string {
	var ret bytes.Buffer
	for _, cur := range buf {
		if ret.Len() > 0 {
			fmt.Fprintf(&ret, sep)
		}
		fmt.Fprintf(&ret, "%02x", cur)
	}
	return ret.String()
}

// ParseHexFormatted returns the raw bytes from a formatted hex string
func ParseHexFormatted(in, sep string) []byte {
	var ret bytes.Buffer
	var err error
	var inBits uint64
	inBytes := strings.Split(in, sep)
	for _, inByte := range inBytes {
		if inBits, err = strconv.ParseUint(inByte, 16, 8); err != nil {
			return nil
		}
		ret.WriteByte(uint8(inBits))
	}
	return ret.Bytes()
}

// GetSubjKeyID returns the subject key ID. The computed ID is the SHA-1 hash of
// the marshaled public key according to
// https://tools.ietf.org/html/rfc5280#section-4.2.1.2 (1)
func GetSubjKeyID(privateKey crypto.Signer) ([]byte, error) {
	if privateKey == nil {
		return nil, errutil.InternalError{Err: "passed-in private key is nil"}
	}
	return GetSubjectKeyID(privateKey.Public())
}

// Returns the explicit SKID when used for cross-signing, else computes a new
// SKID from the key itself.
func getSubjectKeyIDFromBundle(data *CreationBundle) ([]byte, error) {
	if len(data.Params.SKID) > 0 {
		return data.Params.SKID, nil
	}

	return GetSubjectKeyID(data.CSR.PublicKey)
}

func GetSubjectKeyID(pub interface{}) ([]byte, error) {
	var publicKeyBytes []byte
	switch pub := pub.(type) {
	case *rsa.PublicKey:
		type pkcs1PublicKey struct {
			N *big.Int
			E int
		}

		var err error
		publicKeyBytes, err = asn1.Marshal(pkcs1PublicKey{
			N: pub.N,
			E: pub.E,
		})
		if err != nil {
			return nil, errutil.InternalError{Err: fmt.Sprintf("error marshalling public key: %s", err)}
		}
	case *ecdsa.PublicKey:
		publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
	case ed25519.PublicKey:
		publicKeyBytes = pub
	default:
		return nil, errutil.InternalError{Err: fmt.Sprintf("unsupported public key type: %T", pub)}
	}
	skid := sha1.Sum(publicKeyBytes)
	return skid[:], nil
}

// ParsePKIMap takes a map (for instance, the Secret.Data
// returned from the PKI backend) and returns a ParsedCertBundle.
func ParsePKIMap(data map[string]interface{}) (*ParsedCertBundle, error) {
	result := &CertBundle{}
	err := mapstructure.Decode(data, result)
	if err != nil {
		return nil, errutil.UserError{Err: err.Error()}
	}

	return result.ToParsedCertBundle()
}

// ParsePKIJSON takes a JSON-encoded string and returns a ParsedCertBundle.
//
// This can be either the output of an
// issue call from the PKI backend or just its data member; or,
// JSON not coming from the PKI backend.
func ParsePKIJSON(input []byte) (*ParsedCertBundle, error) {
	result := &CertBundle{}
	err := jsonutil.DecodeJSON(input, &result)

	if err == nil {
		return result.ToParsedCertBundle()
	}

	var secret Secret
	err = jsonutil.DecodeJSON(input, &secret)

	if err == nil {
		return ParsePKIMap(secret.Data)
	}

	return nil, errutil.UserError{Err: "unable to parse out of either secret data or a secret object"}
}

func ParseDERKey(privateKeyBytes []byte) (signer crypto.Signer, format BlockType, err error) {
	var firstError error
	if signer, firstError = x509.ParseECPrivateKey(privateKeyBytes); firstError == nil {
		format = ECBlock
		return
	}

	var secondError error
	if signer, secondError = x509.ParsePKCS1PrivateKey(privateKeyBytes); secondError == nil {
		format = PKCS1Block
		return
	}

	var thirdError error
	var rawKey interface{}
	if rawKey, thirdError = x509.ParsePKCS8PrivateKey(privateKeyBytes); thirdError == nil {
		switch rawSigner := rawKey.(type) {
		case *rsa.PrivateKey:
			signer = rawSigner
		case *ecdsa.PrivateKey:
			signer = rawSigner
		case ed25519.PrivateKey:
			signer = rawSigner
		default:
			return nil, UnknownBlock, errutil.InternalError{Err: "unknown type for parsed PKCS8 Private Key"}
		}

		format = PKCS8Block
		return
	}

	return nil, UnknownBlock, fmt.Errorf("got errors attempting to parse DER private key:\n1. %v\n2. %v\n3. %v", firstError, secondError, thirdError)
}

func ParsePEMKey(keyPem string) (crypto.Signer, BlockType, error) {
	pemBlock, _ := pem.Decode([]byte(keyPem))
	if pemBlock == nil {
		return nil, UnknownBlock, errutil.UserError{Err: "no data found in PEM block"}
	}

	return ParseDERKey(pemBlock.Bytes)
}

// ParsePEMBundle takes a string of concatenated PEM-format certificate
// and private key values and decodes/parses them, checking validity along
// the way. The first certificate must be the subject certificate and issuing
// certificates may follow.  There must be at most one private key.
func ParsePEMBundle(pemBundle string) (*ParsedCertBundle, error) {
	if len(pemBundle) == 0 {
		return nil, errutil.UserError{Err: "empty pem bundle"}
	}

	pemBytes := []byte(pemBundle)
	var pemBlock *pem.Block
	parsedBundle := &ParsedCertBundle{}
	var certPath []*CertBlock

	for len(pemBytes) > 0 {
		pemBlock, pemBytes = pem.Decode(pemBytes)
		if pemBlock == nil {
			return nil, errutil.UserError{Err: "no data found in PEM block"}
		}

		if signer, format, err := ParseDERKey(pemBlock.Bytes); err == nil {
			if parsedBundle.PrivateKeyType != UnknownPrivateKey {
				return nil, errutil.UserError{Err: "more than one private key given; provide only one private key in the bundle"}
			}

			parsedBundle.PrivateKeyFormat = format
			parsedBundle.PrivateKeyType = GetPrivateKeyTypeFromSigner(signer)
			if parsedBundle.PrivateKeyType == UnknownPrivateKey {
				return nil, errutil.UserError{Err: "Unknown type of private key included in the bundle: %v"}
			}

			parsedBundle.PrivateKeyBytes = pemBlock.Bytes
			parsedBundle.PrivateKey = signer
		} else if certificates, err := x509.ParseCertificates(pemBlock.Bytes); err == nil {
			certPath = append(certPath, &CertBlock{
				Certificate: certificates[0],
				Bytes:       pemBlock.Bytes,
			})
		} else if x509.IsEncryptedPEMBlock(pemBlock) {
			return nil, errutil.UserError{Err: "Encrypted private key given; provide only decrypted private key in the bundle"}
		}
	}

	for i, certBlock := range certPath {
		if i == 0 {
			parsedBundle.Certificate = certBlock.Certificate
			parsedBundle.CertificateBytes = certBlock.Bytes
		} else {
			parsedBundle.CAChain = append(parsedBundle.CAChain, certBlock)
		}
	}

	if err := parsedBundle.Verify(); err != nil {
		return nil, errutil.UserError{Err: fmt.Sprintf("verification of parsed bundle failed: %s", err)}
	}

	return parsedBundle, nil
}

func (p *ParsedCertBundle) ToTLSCertificate() tls.Certificate {
	var cert tls.Certificate
	cert.Certificate = append(cert.Certificate, p.CertificateBytes)
	cert.Leaf = p.Certificate
	cert.PrivateKey = p.PrivateKey
	for _, ca := range p.CAChain {
		cert.Certificate = append(cert.Certificate, ca.Bytes)
	}

	return cert
}

// GeneratePrivateKey generates a private key with the specified type and key bits.
func GeneratePrivateKey(keyType string, keyBits int, container ParsedPrivateKeyContainer) error {
	return generatePrivateKey(keyType, keyBits, container, nil)
}

// GeneratePrivateKeyWithRandomSource generates a private key with the specified type and key bits.
// GeneratePrivateKeyWithRandomSource uses randomness from the entropyReader to generate the private key.
func GeneratePrivateKeyWithRandomSource(keyType string, keyBits int, container ParsedPrivateKeyContainer, entropyReader io.Reader) error {
	return generatePrivateKey(keyType, keyBits, container, entropyReader)
}

// generatePrivateKey generates a private key with the specified type and key bits.
// generatePrivateKey uses randomness from the entropyReader to generate the private key.
func generatePrivateKey(keyType string, keyBits int, container ParsedPrivateKeyContainer, entropyReader io.Reader) error {
	var err error
	var privateKeyType PrivateKeyType
	var privateKeyBytes []byte
	var privateKey crypto.Signer

	var randReader io.Reader = rand.Reader
	if entropyReader != nil {
		randReader = entropyReader
	}

	switch keyType {
	case "rsa":
		// XXX: there is a false-positive CodeQL path here around keyBits;
		// because of a default zero value in the TypeDurationSecond and
		// TypeSignedDurationSecond cases of schema.DefaultOrZero(), it
		// thinks it is possible to end up with < 2048 bit RSA Key here.
		// While this is true for SSH keys, it isn't true for PKI keys
		// due to ValidateKeyTypeLength(...) below. While we could close
		// the report as a false-positive, enforcing a minimum keyBits size
		// here of 2048 would ensure no other paths exist.
		if keyBits < 2048 {
			return errutil.InternalError{Err: fmt.Sprintf("insecure bit length for RSA private key: %d", keyBits)}
		}
		privateKeyType = RSAPrivateKey
		privateKey, err = rsa.GenerateKey(randReader, keyBits)
		if err != nil {
			return errutil.InternalError{Err: fmt.Sprintf("error generating RSA private key: %v", err)}
		}
		privateKeyBytes = x509.MarshalPKCS1PrivateKey(privateKey.(*rsa.PrivateKey))
	case "ec":
		privateKeyType = ECPrivateKey
		var curve elliptic.Curve
		switch keyBits {
		case 224:
			curve = elliptic.P224()
		case 256:
			curve = elliptic.P256()
		case 384:
			curve = elliptic.P384()
		case 521:
			curve = elliptic.P521()
		default:
			return errutil.UserError{Err: fmt.Sprintf("unsupported bit length for EC key: %d", keyBits)}
		}
		privateKey, err = ecdsa.GenerateKey(curve, randReader)
		if err != nil {
			return errutil.InternalError{Err: fmt.Sprintf("error generating EC private key: %v", err)}
		}
		privateKeyBytes, err = x509.MarshalECPrivateKey(privateKey.(*ecdsa.PrivateKey))
		if err != nil {
			return errutil.InternalError{Err: fmt.Sprintf("error marshalling EC private key: %v", err)}
		}
	case "ed25519":
		privateKeyType = Ed25519PrivateKey
		_, privateKey, err = ed25519.GenerateKey(randReader)
		if err != nil {
			return errutil.InternalError{Err: fmt.Sprintf("error generating ed25519 private key: %v", err)}
		}
		privateKeyBytes, err = x509.MarshalPKCS8PrivateKey(privateKey.(ed25519.PrivateKey))
		if err != nil {
			return errutil.InternalError{Err: fmt.Sprintf("error marshalling Ed25519 private key: %v", err)}
		}
	default:
		return errutil.UserError{Err: fmt.Sprintf("unknown key type: %s", keyType)}
	}

	container.SetParsedPrivateKey(privateKey, privateKeyType, privateKeyBytes)
	return nil
}

// GenerateSerialNumber generates a serial number suitable for a certificate
func GenerateSerialNumber() (*big.Int, error) {
	return generateSerialNumber(rand.Reader)
}

// GenerateSerialNumberWithRandomSource generates a serial number suitable
// for a certificate with custom entropy.
func GenerateSerialNumberWithRandomSource(randReader io.Reader) (*big.Int, error) {
	return generateSerialNumber(randReader)
}

func generateSerialNumber(randReader io.Reader) (*big.Int, error) {
	serial, err := rand.Int(randReader, (&big.Int{}).Exp(big.NewInt(2), big.NewInt(159), nil))
	if err != nil {
		return nil, errutil.InternalError{Err: fmt.Sprintf("error generating serial number: %v", err)}
	}
	return serial, nil
}

// ComparePublicKeysAndType compares two public keys and returns true if they match,
// false if their types or contents differ, and an error on unsupported key types.
func ComparePublicKeysAndType(key1Iface, key2Iface crypto.PublicKey) (bool, error) {
	equal, err := ComparePublicKeys(key1Iface, key2Iface)
	if err != nil {
		if strings.Contains(err.Error(), "key types do not match:") {
			return false, nil
		}
	}

	return equal, err
}

// ComparePublicKeys compares two public keys and returns true if they match,
// returns an error if public key types are mismatched, or they are an unsupported key type.
func ComparePublicKeys(key1Iface, key2Iface crypto.PublicKey) (bool, error) {
	switch key1Iface.(type) {
	case *rsa.PublicKey:
		key1 := key1Iface.(*rsa.PublicKey)
		key2, ok := key2Iface.(*rsa.PublicKey)
		if !ok {
			return false, fmt.Errorf("key types do not match: %T and %T", key1Iface, key2Iface)
		}
		if key1.N.Cmp(key2.N) != 0 ||
			key1.E != key2.E {
			return false, nil
		}
		return true, nil

	case *ecdsa.PublicKey:
		key1 := key1Iface.(*ecdsa.PublicKey)
		key2, ok := key2Iface.(*ecdsa.PublicKey)
		if !ok {
			return false, fmt.Errorf("key types do not match: %T and %T", key1Iface, key2Iface)
		}
		if key1.X.Cmp(key2.X) != 0 ||
			key1.Y.Cmp(key2.Y) != 0 {
			return false, nil
		}
		key1Params := key1.Params()
		key2Params := key2.Params()
		if key1Params.P.Cmp(key2Params.P) != 0 ||
			key1Params.N.Cmp(key2Params.N) != 0 ||
			key1Params.B.Cmp(key2Params.B) != 0 ||
			key1Params.Gx.Cmp(key2Params.Gx) != 0 ||
			key1Params.Gy.Cmp(key2Params.Gy) != 0 ||
			key1Params.BitSize != key2Params.BitSize {
			return false, nil
		}
		return true, nil
	case ed25519.PublicKey:
		key1 := key1Iface.(ed25519.PublicKey)
		key2, ok := key2Iface.(ed25519.PublicKey)
		if !ok {
			return false, fmt.Errorf("key types do not match: %T and %T", key1Iface, key2Iface)
		}
		if !key1.Equal(key2) {
			return false, nil
		}
		return true, nil
	default:
		return false, fmt.Errorf("cannot compare key with type %T", key1Iface)
	}
}

// ParsePublicKeyPEM is used to parse RSA and ECDSA public keys from PEMs
func ParsePublicKeyPEM(data []byte) (interface{}, error) {
	block, data := pem.Decode(data)
	if block != nil {
		if len(bytes.TrimSpace(data)) > 0 {
			return nil, errutil.UserError{Err: "unexpected trailing data after parsed PEM block"}
		}
		var rawKey interface{}
		var err error
		if rawKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil {
			if cert, err := x509.ParseCertificate(block.Bytes); err == nil {
				rawKey = cert.PublicKey
			} else {
				return nil, err
			}
		}

		switch key := rawKey.(type) {
		case *rsa.PublicKey:
			return key, nil
		case *ecdsa.PublicKey:
			return key, nil
		case ed25519.PublicKey:
			return key, nil
		}
	}
	return nil, errors.New("data does not contain any valid public keys")
}

// AddPolicyIdentifiers adds certificate policies extension, based on CreationBundle
func AddPolicyIdentifiers(data *CreationBundle, certTemplate *x509.Certificate) {
	oidOnly := true
	for _, oidStr := range data.Params.PolicyIdentifiers {
		oid, err := StringToOid(oidStr)
		if err == nil {
			certTemplate.PolicyIdentifiers = append(certTemplate.PolicyIdentifiers, oid)
		}
		if err != nil {
			oidOnly = false
		}
	}
	if !oidOnly { // Because all policy information is held in the same extension, when we use an extra extension to
		// add policy qualifier information, that overwrites any information in the PolicyIdentifiers field on the Cert
		// Template, so we need to reparse all the policy identifiers here
		extension, err := CreatePolicyInformationExtensionFromStorageStrings(data.Params.PolicyIdentifiers)
		if err == nil {
			// If this errors out, don't add it, rely on the OIDs parsed into PolicyIdentifiers above
			certTemplate.ExtraExtensions = append(certTemplate.ExtraExtensions, *extension)
		}
	}
}

// AddExtKeyUsageOids adds custom extended key usage OIDs to certificate
func AddExtKeyUsageOids(data *CreationBundle, certTemplate *x509.Certificate) {
	for _, oidstr := range data.Params.ExtKeyUsageOIDs {
		oid, err := StringToOid(oidstr)
		if err == nil {
			certTemplate.UnknownExtKeyUsage = append(certTemplate.UnknownExtKeyUsage, oid)
		}
	}
}

func HandleOtherCSRSANs(in *x509.CertificateRequest, sans map[string][]string) error {
	certTemplate := &x509.Certificate{
		DNSNames:       in.DNSNames,
		IPAddresses:    in.IPAddresses,
		EmailAddresses: in.EmailAddresses,
		URIs:           in.URIs,
	}
	if err := HandleOtherSANs(certTemplate, sans); err != nil {
		return err
	}
	if len(certTemplate.ExtraExtensions) > 0 {
		for _, v := range certTemplate.ExtraExtensions {
			in.ExtraExtensions = append(in.ExtraExtensions, v)
		}
	}
	return nil
}

func HandleOtherSANs(in *x509.Certificate, sans map[string][]string) error {
	// If other SANs is empty we return which causes normal Go stdlib parsing
	// of the other SAN types
	if len(sans) == 0 {
		return nil
	}

	var rawValues []asn1.RawValue

	// We need to generate an IMPLICIT sequence for compatibility with OpenSSL
	// -- it's an open question what the default for RFC 5280 actually is, see
	// https://github.com/openssl/openssl/issues/5091 -- so we have to use
	// cryptobyte because using the asn1 package's marshaling always produces
	// an EXPLICIT sequence. Note that asn1 is way too magical according to
	// agl, and cryptobyte is modeled after the CBB/CBS bits that agl put into
	// boringssl.
	for oid, vals := range sans {
		for _, val := range vals {
			var b cryptobyte.Builder
			oidStr, err := StringToOid(oid)
			if err != nil {
				return err
			}
			b.AddASN1ObjectIdentifier(oidStr)
			b.AddASN1(cbasn1.Tag(0).ContextSpecific().Constructed(), func(b *cryptobyte.Builder) {
				b.AddASN1(cbasn1.UTF8String, func(b *cryptobyte.Builder) {
					b.AddBytes([]byte(val))
				})
			})
			m, err := b.Bytes()
			if err != nil {
				return err
			}
			rawValues = append(rawValues, asn1.RawValue{Tag: 0, Class: 2, IsCompound: true, Bytes: m})
		}
	}

	// If other SANs is empty we return which causes normal Go stdlib parsing
	// of the other SAN types
	if len(rawValues) == 0 {
		return nil
	}

	// Append any existing SANs, sans marshalling
	rawValues = append(rawValues, marshalSANs(in.DNSNames, in.EmailAddresses, in.IPAddresses, in.URIs)...)

	// Marshal and add to ExtraExtensions
	ext := pkix.Extension{
		// This is the defined OID for subjectAltName
		Id: asn1.ObjectIdentifier{2, 5, 29, 17},
	}
	var err error
	ext.Value, err = asn1.Marshal(rawValues)
	if err != nil {
		return err
	}
	in.ExtraExtensions = append(in.ExtraExtensions, ext)

	return nil
}

// Note: Taken from the Go source code since it's not public, and used in the
// modified function below (which also uses these consts upstream)
const (
	nameTypeEmail = 1
	nameTypeDNS   = 2
	nameTypeURI   = 6
	nameTypeIP    = 7
)

// Note: Taken from the Go source code since it's not public, plus changed to not marshal
// marshalSANs marshals a list of addresses into a the contents of an X.509
// SubjectAlternativeName extension.
func marshalSANs(dnsNames, emailAddresses []string, ipAddresses []net.IP, uris []*url.URL) []asn1.RawValue {
	var rawValues []asn1.RawValue
	for _, name := range dnsNames {
		rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeDNS, Class: 2, Bytes: []byte(name)})
	}
	for _, email := range emailAddresses {
		rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeEmail, Class: 2, Bytes: []byte(email)})
	}
	for _, rawIP := range ipAddresses {
		// If possible, we always want to encode IPv4 addresses in 4 bytes.
		ip := rawIP.To4()
		if ip == nil {
			ip = rawIP
		}
		rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeIP, Class: 2, Bytes: ip})
	}
	for _, uri := range uris {
		rawValues = append(rawValues, asn1.RawValue{Tag: nameTypeURI, Class: 2, Bytes: []byte(uri.String())})
	}
	return rawValues
}

func StringToOid(in string) (asn1.ObjectIdentifier, error) {
	split := strings.Split(in, ".")
	ret := make(asn1.ObjectIdentifier, 0, len(split))
	for _, v := range split {
		i, err := strconv.Atoi(v)
		if err != nil {
			return nil, err
		}
		ret = append(ret, i)
	}
	return asn1.ObjectIdentifier(ret), nil
}

// Returns default key bits for the specified key type, or the present value
// if keyBits is non-zero.
func DefaultOrValueKeyBits(keyType string, keyBits int) (int, error) {
	if keyBits == 0 {
		newValue, present := defaultAlgorithmKeyBits[keyType]
		if present {
			keyBits = newValue
		} /* else {
		  // We cannot return an error here as ed25519 (and potentially ed448
		  // in the future) aren't in defaultAlgorithmKeyBits -- the value of
		  // the keyBits parameter is ignored under that algorithm.
		} */
	}

	return keyBits, nil
}

// Returns default signature hash bit length for the specified key type and
// bits, or the present value if hashBits is non-zero. Returns an error under
// certain internal circumstances.
func DefaultOrValueHashBits(keyType string, keyBits int, hashBits int) (int, error) {
	if keyType == "ec" {
		// Enforcement of curve moved to selectSignatureAlgorithmForECDSA. See
		// note there about why.
	} else if keyType == "rsa" && hashBits == 0 {
		// To match previous behavior (and ignoring NIST's recommendations for
		// hash size to align with RSA key sizes), default to SHA-2-256.
		hashBits = 256
	} else if keyType == "ed25519" || keyType == "ed448" || keyType == "any" {
		// No-op; ed25519 and ed448 internally specify their own hash and
		// we do not need to select one. Double hashing isn't supported in
		// certificate signing. Additionally, the any key type can't know
		// what hash algorithm to use yet, so default to zero.
		return 0, nil
	}

	return hashBits, nil
}

// Validates that the combination of keyType, keyBits, and hashBits are
// valid together; replaces individual calls to ValidateSignatureLength and
// ValidateKeyTypeLength. Also updates the value of keyBits and hashBits on
// return.
func ValidateDefaultOrValueKeyTypeSignatureLength(keyType string, keyBits int, hashBits int) (int, int, error) {
	var err error

	if keyBits, err = DefaultOrValueKeyBits(keyType, keyBits); err != nil {
		return keyBits, hashBits, err
	}

	if err = ValidateKeyTypeLength(keyType, keyBits); err != nil {
		return keyBits, hashBits, err
	}

	if hashBits, err = DefaultOrValueHashBits(keyType, keyBits, hashBits); err != nil {
		return keyBits, hashBits, err
	}

	// Note that this check must come after we've selected a value for
	// hashBits above, in the event it was left as the default, but we
	// were allowed to update it.
	if err = ValidateSignatureLength(keyType, hashBits); err != nil {
		return keyBits, hashBits, err
	}

	return keyBits, hashBits, nil
}

// Validates that the length of the hash (in bits) used in the signature
// calculation is a known, approved value.
func ValidateSignatureLength(keyType string, hashBits int) error {
	if keyType == "any" || keyType == "ec" || keyType == "ed25519" || keyType == "ed448" {
		// ed25519 and ed448 include built-in hashing and is not externally
		// configurable. There are three modes for each of these schemes:
		//
		// 1. Built-in hash (default, used in TLS, x509).
		// 2. Double hash (notably used in some block-chain implementations,
		//    but largely regarded as a specialized use case with security
		//    concerns).
		// 3. No hash (bring your own hash function, less commonly used).
		//
		// In all cases, we won't have a hash algorithm to validate here, so
		// return nil.
		//
		// Additionally, when KeyType is any, we can't yet validate the
		// signature algorithm size, so it takes the default zero value.
		//
		// When KeyType is ec, we also can't validate this value as we're
		// forcefully ignoring the users' choice and specifying a value based
		// on issuer type.
		return nil
	}

	switch hashBits {
	case 256:
	case 384:
	case 512:
	default:
		return fmt.Errorf("unsupported hash signature algorithm: %d", hashBits)
	}

	return nil
}

func ValidateKeyTypeLength(keyType string, keyBits int) error {
	switch keyType {
	case "rsa":
		if keyBits < rsaMinimumSecureKeySize {
			return fmt.Errorf("RSA keys < %d bits are unsafe and not supported: got %d", rsaMinimumSecureKeySize, keyBits)
		}

		switch keyBits {
		case 2048:
		case 3072:
		case 4096:
		case 8192:
		default:
			return fmt.Errorf("unsupported bit length for RSA key: %d", keyBits)
		}
	case "ec":
		_, present := expectedNISTPCurveHashBits[keyBits]
		if !present {
			return fmt.Errorf("unsupported bit length for EC key: %d", keyBits)
		}
	case "any", "ed25519":
	default:
		return fmt.Errorf("unknown key type %s", keyType)
	}

	return nil
}

// CreateCertificate uses CreationBundle and the default rand.Reader to
// generate a cert/keypair.
func CreateCertificate(data *CreationBundle) (*ParsedCertBundle, error) {
	return createCertificate(data, rand.Reader, generatePrivateKey)
}

// CreateCertificateWithRandomSource uses CreationBundle and a custom
// io.Reader for randomness to generate a cert/keypair.
func CreateCertificateWithRandomSource(data *CreationBundle, randReader io.Reader) (*ParsedCertBundle, error) {
	return createCertificate(data, randReader, generatePrivateKey)
}

// KeyGenerator Allow us to override how/what generates the private key
type KeyGenerator func(keyType string, keyBits int, container ParsedPrivateKeyContainer, entropyReader io.Reader) error

func CreateCertificateWithKeyGenerator(data *CreationBundle, randReader io.Reader, keyGenerator KeyGenerator) (*ParsedCertBundle, error) {
	return createCertificate(data, randReader, keyGenerator)
}

// Set correct RSA sig algo
func certTemplateSetSigAlgo(certTemplate *x509.Certificate, data *CreationBundle) {
	if data.Params.UsePSS {
		switch data.Params.SignatureBits {
		case 256:
			certTemplate.SignatureAlgorithm = x509.SHA256WithRSAPSS
		case 384:
			certTemplate.SignatureAlgorithm = x509.SHA384WithRSAPSS
		case 512:
			certTemplate.SignatureAlgorithm = x509.SHA512WithRSAPSS
		}
	} else {
		switch data.Params.SignatureBits {
		case 256:
			certTemplate.SignatureAlgorithm = x509.SHA256WithRSA
		case 384:
			certTemplate.SignatureAlgorithm = x509.SHA384WithRSA
		case 512:
			certTemplate.SignatureAlgorithm = x509.SHA512WithRSA
		}
	}
}

// selectSignatureAlgorithmForRSA returns the proper x509.SignatureAlgorithm based on various properties set in the
// Creation Bundle parameter. This method will default to a SHA256 signature algorithm if the requested signature
// bits is not set/unknown.
func selectSignatureAlgorithmForRSA(data *CreationBundle) x509.SignatureAlgorithm {
	if data.Params.UsePSS {
		switch data.Params.SignatureBits {
		case 256:
			return x509.SHA256WithRSAPSS
		case 384:
			return x509.SHA384WithRSAPSS
		case 512:
			return x509.SHA512WithRSAPSS
		default:
			return x509.SHA256WithRSAPSS
		}
	}

	switch data.Params.SignatureBits {
	case 256:
		return x509.SHA256WithRSA
	case 384:
		return x509.SHA384WithRSA
	case 512:
		return x509.SHA512WithRSA
	default:
		return x509.SHA256WithRSA
	}
}

func createCertificate(data *CreationBundle, randReader io.Reader, privateKeyGenerator KeyGenerator) (*ParsedCertBundle, error) {
	var err error
	result := &ParsedCertBundle{}

	serialNumber, err := GenerateSerialNumber()
	if err != nil {
		return nil, err
	}

	if err := privateKeyGenerator(data.Params.KeyType,
		data.Params.KeyBits,
		result, randReader); err != nil {
		return nil, err
	}

	subjKeyID, err := GetSubjKeyID(result.PrivateKey)
	if err != nil {
		return nil, errutil.InternalError{Err: fmt.Sprintf("error getting subject key ID: %s", err)}
	}

	certTemplate := &x509.Certificate{
		SerialNumber:   serialNumber,
		NotBefore:      time.Now().Add(-30 * time.Second),
		NotAfter:       data.Params.NotAfter,
		IsCA:           false,
		SubjectKeyId:   subjKeyID,
		Subject:        data.Params.Subject,
		DNSNames:       data.Params.DNSNames,
		EmailAddresses: data.Params.EmailAddresses,
		IPAddresses:    data.Params.IPAddresses,
		URIs:           data.Params.URIs,
	}
	if data.Params.NotBeforeDuration > 0 {
		certTemplate.NotBefore = time.Now().Add(-1 * data.Params.NotBeforeDuration)
	}

	if err := HandleOtherSANs(certTemplate, data.Params.OtherSANs); err != nil {
		return nil, errutil.InternalError{Err: errwrap.Wrapf("error marshaling other SANs: {{err}}", err).Error()}
	}

	// Add this before calling addKeyUsages
	if data.SigningBundle == nil {
		certTemplate.IsCA = true
	} else if data.Params.BasicConstraintsValidForNonCA {
		certTemplate.BasicConstraintsValid = true
		certTemplate.IsCA = false
	}

	// This will only be filled in from the generation paths
	if len(data.Params.PermittedDNSDomains) > 0 {
		certTemplate.PermittedDNSDomains = data.Params.PermittedDNSDomains
		certTemplate.PermittedDNSDomainsCritical = true
	}

	AddPolicyIdentifiers(data, certTemplate)

	AddKeyUsages(data, certTemplate)

	AddExtKeyUsageOids(data, certTemplate)

	certTemplate.IssuingCertificateURL = data.Params.URLs.IssuingCertificates
	certTemplate.CRLDistributionPoints = data.Params.URLs.CRLDistributionPoints
	certTemplate.OCSPServer = data.Params.URLs.OCSPServers

	var certBytes []byte
	if data.SigningBundle != nil {
		privateKeyType := data.SigningBundle.PrivateKeyType
		if privateKeyType == ManagedPrivateKey {
			privateKeyType = GetPrivateKeyTypeFromSigner(data.SigningBundle.PrivateKey)
		}
		switch privateKeyType {
		case RSAPrivateKey:
			certTemplateSetSigAlgo(certTemplate, data)
		case Ed25519PrivateKey:
			certTemplate.SignatureAlgorithm = x509.PureEd25519
		case ECPrivateKey:
			certTemplate.SignatureAlgorithm = selectSignatureAlgorithmForECDSA(data.SigningBundle.PrivateKey.Public(), data.Params.SignatureBits)
		}

		caCert := data.SigningBundle.Certificate
		certTemplate.AuthorityKeyId = caCert.SubjectKeyId

		certBytes, err = x509.CreateCertificate(randReader, certTemplate, caCert, result.PrivateKey.Public(), data.SigningBundle.PrivateKey)
	} else {
		// Creating a self-signed root
		if data.Params.MaxPathLength == 0 {
			certTemplate.MaxPathLen = 0
			certTemplate.MaxPathLenZero = true
		} else {
			certTemplate.MaxPathLen = data.Params.MaxPathLength
		}

		switch data.Params.KeyType {
		case "rsa":
			certTemplateSetSigAlgo(certTemplate, data)
		case "ed25519":
			certTemplate.SignatureAlgorithm = x509.PureEd25519
		case "ec":
			certTemplate.SignatureAlgorithm = selectSignatureAlgorithmForECDSA(result.PrivateKey.Public(), data.Params.SignatureBits)
		}

		certTemplate.AuthorityKeyId = subjKeyID
		certTemplate.BasicConstraintsValid = true
		certBytes, err = x509.CreateCertificate(randReader, certTemplate, certTemplate, result.PrivateKey.Public(), result.PrivateKey)
	}

	if err != nil {
		return nil, errutil.InternalError{Err: fmt.Sprintf("unable to create certificate: %s", err)}
	}

	result.CertificateBytes = certBytes
	result.Certificate, err = x509.ParseCertificate(certBytes)
	if err != nil {
		return nil, errutil.InternalError{Err: fmt.Sprintf("unable to parse created certificate: %s", err)}
	}

	if data.SigningBundle != nil {
		if (len(data.SigningBundle.Certificate.AuthorityKeyId) > 0 &&
			!bytes.Equal(data.SigningBundle.Certificate.AuthorityKeyId, data.SigningBundle.Certificate.SubjectKeyId)) ||
			data.Params.ForceAppendCaChain {
			var chain []*CertBlock

			signingChain := data.SigningBundle.CAChain
			// Some bundles already include the root included in the chain, so don't include it twice.
			if len(signingChain) == 0 || !bytes.Equal(signingChain[0].Bytes, data.SigningBundle.CertificateBytes) {
				chain = append(chain, &CertBlock{
					Certificate: data.SigningBundle.Certificate,
					Bytes:       data.SigningBundle.CertificateBytes,
				})
			}

			if len(signingChain) > 0 {
				chain = append(chain, signingChain...)
			}

			result.CAChain = chain
		}
	}

	return result, nil
}

func selectSignatureAlgorithmForECDSA(pub crypto.PublicKey, signatureBits int) x509.SignatureAlgorithm {
	// Previously we preferred the user-specified signature bits for ECDSA
	// keys. However, this could result in using a longer hash function than
	// the underlying NIST P-curve will encode (e.g., a SHA-512 hash with a
	// P-256 key). This isn't ideal: the hash is implicitly truncated
	// (effectively turning it into SHA-512/256) and we then need to rely
	// on the prefix security of the hash. Since both NIST and Mozilla guidance
	// suggest instead using the correct hash function, we should prefer that
	// over the operator-specified signatureBits.
	//
	// Lastly, note that pub above needs to be the _signer's_ public key;
	// the issue with DefaultOrValueHashBits is that it is called at role
	// configuration time, which might _precede_ issuer generation. Thus
	// it only has access to the desired key type and not the actual issuer.
	// The reference from that function is reproduced below:
	//
	// > To comply with BSI recommendations Section 4.2 and Mozilla root
	// > store policy section 5.1.2, enforce that NIST P-curves use a hash
	// > length corresponding to curve length. Note that ed25519 does not
	// > implement the "ec" key type.
	key, ok := pub.(*ecdsa.PublicKey)
	if !ok {
		return x509.ECDSAWithSHA256
	}
	switch key.Curve {
	case elliptic.P224(), elliptic.P256():
		return x509.ECDSAWithSHA256
	case elliptic.P384():
		return x509.ECDSAWithSHA384
	case elliptic.P521():
		return x509.ECDSAWithSHA512
	default:
		return x509.ECDSAWithSHA256
	}
}

var (
	ExtensionBasicConstraintsOID = []int{2, 5, 29, 19}
	ExtensionSubjectAltNameOID   = []int{2, 5, 29, 17}
)

// CreateCSR creates a CSR with the default rand.Reader to
// generate a cert/keypair. This is currently only meant
// for use when generating an intermediate certificate.
func CreateCSR(data *CreationBundle, addBasicConstraints bool) (*ParsedCSRBundle, error) {
	return createCSR(data, addBasicConstraints, rand.Reader, generatePrivateKey)
}

// CreateCSRWithRandomSource creates a CSR with a custom io.Reader
// for randomness to generate a cert/keypair.
func CreateCSRWithRandomSource(data *CreationBundle, addBasicConstraints bool, randReader io.Reader) (*ParsedCSRBundle, error) {
	return createCSR(data, addBasicConstraints, randReader, generatePrivateKey)
}

// CreateCSRWithKeyGenerator creates a CSR with a custom io.Reader
// for randomness to generate a cert/keypair with the provided private key generator.
func CreateCSRWithKeyGenerator(data *CreationBundle, addBasicConstraints bool, randReader io.Reader, keyGenerator KeyGenerator) (*ParsedCSRBundle, error) {
	return createCSR(data, addBasicConstraints, randReader, keyGenerator)
}

func createCSR(data *CreationBundle, addBasicConstraints bool, randReader io.Reader, keyGenerator KeyGenerator) (*ParsedCSRBundle, error) {
	var err error
	result := &ParsedCSRBundle{}

	if err := keyGenerator(data.Params.KeyType,
		data.Params.KeyBits,
		result, randReader); err != nil {
		return nil, err
	}

	// Like many root CAs, other information is ignored

	csrTemplate := &x509.CertificateRequest{
		Subject:        data.Params.Subject,
		DNSNames:       data.Params.DNSNames,
		EmailAddresses: data.Params.EmailAddresses,
		IPAddresses:    data.Params.IPAddresses,
		URIs:           data.Params.URIs,
	}

	if data.Params.KeyUsage != 0 {
		keyUsageExt, err := marshalKeyUsage(data.Params.KeyUsage)
		if err != nil {
			return nil, fmt.Errorf("failed marshalling existing key usage: %w", err)
		}
		csrTemplate.ExtraExtensions = []pkix.Extension{keyUsageExt}
	}

	if err := HandleOtherCSRSANs(csrTemplate, data.Params.OtherSANs); err != nil {
		return nil, errutil.InternalError{Err: errwrap.Wrapf("error marshaling other SANs: {{err}}", err).Error()}
	}

	if addBasicConstraints {
		type basicConstraints struct {
			IsCA       bool `asn1:"optional"`
			MaxPathLen int  `asn1:"optional,default:-1"`
		}
		val, err := asn1.Marshal(basicConstraints{IsCA: true, MaxPathLen: -1})
		if err != nil {
			return nil, errutil.InternalError{Err: errwrap.Wrapf("error marshaling basic constraints: {{err}}", err).Error()}
		}
		ext := pkix.Extension{
			Id:       ExtensionBasicConstraintsOID,
			Value:    val,
			Critical: true,
		}
		csrTemplate.ExtraExtensions = append(csrTemplate.ExtraExtensions, ext)
	}

	switch data.Params.KeyType {
	case "rsa":
		// use specified RSA algorithm defaulting to the appropriate SHA256 RSA signature type
		csrTemplate.SignatureAlgorithm = selectSignatureAlgorithmForRSA(data)
	case "ec":
		csrTemplate.SignatureAlgorithm = selectSignatureAlgorithmForECDSA(result.PrivateKey.Public(), data.Params.SignatureBits)
	case "ed25519":
		csrTemplate.SignatureAlgorithm = x509.PureEd25519
	}

	csr, err := x509.CreateCertificateRequest(randReader, csrTemplate, result.PrivateKey)
	if err != nil {
		return nil, errutil.InternalError{Err: fmt.Sprintf("unable to create certificate: %s", err)}
	}

	result.CSRBytes = csr
	result.CSR, err = x509.ParseCertificateRequest(csr)
	if err != nil {
		return nil, errutil.InternalError{Err: fmt.Sprintf("unable to parse created certificate: %v", err)}
	}

	if err = result.CSR.CheckSignature(); err != nil {
		return nil, errors.New("failed signature validation for CSR")
	}

	return result, nil
}

// Marshal Key Usage taken from: https://cs.opensource.google/go/go/+/master:src/crypto/x509/x509.go;drc=370a6959e3edd9d901446661ee9fef3f72d150d4;l=1339
// It requires the two following functions (reverseBitsInAByte and asn1BitLength) below, taken from the same code base
// It's used for that code only for certificates; but we need to marshal key usage on CSR (createCSR above) as well
func marshalKeyUsage(ku x509.KeyUsage) (pkix.Extension, error) {
	ext := pkix.Extension{Id: KeyUsageOID, Critical: true}

	var a [2]byte
	a[0] = reverseBitsInAByte(byte(ku))
	a[1] = reverseBitsInAByte(byte(ku >> 8))

	l := 1
	if a[1] != 0 {
		l = 2
	}

	bitString := a[:l]
	var err error
	ext.Value, err = asn1.Marshal(asn1.BitString{Bytes: bitString, BitLength: asn1BitLength(bitString)})
	return ext, err
}

// reverseBitsInAByte Taken from: https://cs.opensource.google/go/go/+/master:src/crypto/x509/x509.go;drc=370a6959e3edd9d901446661ee9fef3f72d150d4;l=1011
// needed for marshalKeyUsage called above
func reverseBitsInAByte(in byte) byte {
	b1 := in>>4 | in<<4
	b2 := b1>>2&0x33 | b1<<2&0xcc
	b3 := b2>>1&0x55 | b2<<1&0xaa
	return b3
}

// asn1BitLength returns the bit-length of bitString by considering the
// most-significant bit in a byte to be the "first" bit. This convention
// matches ASN.1, but differs from almost everything else.
func asn1BitLength(bitString []byte) int {
	bitLen := len(bitString) * 8

	for i := range bitString {
		b := bitString[len(bitString)-i-1]

		for bit := uint(0); bit < 8; bit++ {
			if (b>>bit)&1 == 1 {
				return bitLen
			}
			bitLen--
		}
	}

	return 0
}

// SignCertificate performs the heavy lifting
// of generating a certificate from a CSR.
// Returns a ParsedCertBundle sans private keys.
func SignCertificate(data *CreationBundle) (*ParsedCertBundle, error) {
	return signCertificate(data, rand.Reader)
}

// SignCertificateWithRandomSource generates a certificate
// from a CSR, using custom randomness from the randReader.
// Returns a ParsedCertBundle sans private keys.
func SignCertificateWithRandomSource(data *CreationBundle, randReader io.Reader) (*ParsedCertBundle, error) {
	return signCertificate(data, randReader)
}

func signCertificate(data *CreationBundle, randReader io.Reader) (*ParsedCertBundle, error) {
	switch {
	case data == nil:
		return nil, errutil.UserError{Err: "nil data bundle given to signCertificate"}
	case data.Params == nil:
		return nil, errutil.UserError{Err: "nil parameters given to signCertificate"}
	case data.SigningBundle == nil:
		return nil, errutil.UserError{Err: "nil signing bundle given to signCertificate"}
	case data.CSR == nil:
		return nil, errutil.UserError{Err: "nil csr given to signCertificate"}
	}

	if !data.Params.IgnoreCSRSignature {
		if err := data.CSR.CheckSignature(); err != nil {
			return nil, errutil.UserError{Err: "request signature invalid"}
		}
	}

	result := &ParsedCertBundle{}

	serialNumber, err := GenerateSerialNumber()
	if err != nil {
		return nil, err
	}

	subjKeyID, err := getSubjectKeyIDFromBundle(data)
	if err != nil {
		return nil, err
	}

	caCert := data.SigningBundle.Certificate

	certTemplate := &x509.Certificate{
		SerialNumber:   serialNumber,
		Subject:        data.Params.Subject,
		NotBefore:      time.Now().Add(-30 * time.Second),
		NotAfter:       data.Params.NotAfter,
		SubjectKeyId:   subjKeyID[:],
		AuthorityKeyId: caCert.SubjectKeyId,
	}
	if data.Params.NotBeforeDuration > 0 {
		certTemplate.NotBefore = time.Now().Add(-1 * data.Params.NotBeforeDuration)
	}

	privateKeyType := data.SigningBundle.PrivateKeyType
	if privateKeyType == ManagedPrivateKey {
		privateKeyType = GetPrivateKeyTypeFromSigner(data.SigningBundle.PrivateKey)
	}

	switch privateKeyType {
	case RSAPrivateKey:
		certTemplateSetSigAlgo(certTemplate, data)
	case ECPrivateKey:
		switch data.Params.SignatureBits {
		case 256:
			certTemplate.SignatureAlgorithm = x509.ECDSAWithSHA256
		case 384:
			certTemplate.SignatureAlgorithm = x509.ECDSAWithSHA384
		case 512:
			certTemplate.SignatureAlgorithm = x509.ECDSAWithSHA512
		}
	}

	if data.Params.UseCSRValues {
		certTemplate.Subject = data.CSR.Subject
		certTemplate.Subject.ExtraNames = certTemplate.Subject.Names

		certTemplate.DNSNames = data.CSR.DNSNames
		certTemplate.EmailAddresses = data.CSR.EmailAddresses
		certTemplate.IPAddresses = data.CSR.IPAddresses
		certTemplate.URIs = data.CSR.URIs

		for _, name := range data.CSR.Extensions {
			if !name.Id.Equal(ExtensionBasicConstraintsOID) && !(len(data.Params.OtherSANs) > 0 && name.Id.Equal(ExtensionSubjectAltNameOID)) {
				certTemplate.ExtraExtensions = append(certTemplate.ExtraExtensions, name)
			}
		}

	} else {
		certTemplate.DNSNames = data.Params.DNSNames
		certTemplate.EmailAddresses = data.Params.EmailAddresses
		certTemplate.IPAddresses = data.Params.IPAddresses
		certTemplate.URIs = data.Params.URIs
	}

	if err := HandleOtherSANs(certTemplate, data.Params.OtherSANs); err != nil {
		return nil, errutil.InternalError{Err: errwrap.Wrapf("error marshaling other SANs: {{err}}", err).Error()}
	}

	AddPolicyIdentifiers(data, certTemplate)

	AddKeyUsages(data, certTemplate)

	AddExtKeyUsageOids(data, certTemplate)

	var certBytes []byte

	certTemplate.IssuingCertificateURL = data.Params.URLs.IssuingCertificates
	certTemplate.CRLDistributionPoints = data.Params.URLs.CRLDistributionPoints
	certTemplate.OCSPServer = data.SigningBundle.URLs.OCSPServers

	if data.Params.IsCA {
		certTemplate.BasicConstraintsValid = true
		certTemplate.IsCA = true

		if data.SigningBundle.Certificate.MaxPathLen == 0 &&
			data.SigningBundle.Certificate.MaxPathLenZero {
			return nil, errutil.UserError{Err: "signing certificate has a max path length of zero, and cannot issue further CA certificates"}
		}

		certTemplate.MaxPathLen = data.Params.MaxPathLength
		if certTemplate.MaxPathLen == 0 {
			certTemplate.MaxPathLenZero = true
		}
	} else if data.Params.BasicConstraintsValidForNonCA {
		certTemplate.BasicConstraintsValid = true
		certTemplate.IsCA = false
	}

	if len(data.Params.PermittedDNSDomains) > 0 {
		certTemplate.PermittedDNSDomains = data.Params.PermittedDNSDomains
		certTemplate.PermittedDNSDomainsCritical = true
	}

	certBytes, err = x509.CreateCertificate(randReader, certTemplate, caCert, data.CSR.PublicKey, data.SigningBundle.PrivateKey)
	if err != nil {
		return nil, errutil.InternalError{Err: fmt.Sprintf("unable to create certificate: %s", err)}
	}

	result.CertificateBytes = certBytes
	result.Certificate, err = x509.ParseCertificate(certBytes)
	if err != nil {
		return nil, errutil.InternalError{Err: fmt.Sprintf("unable to parse created certificate: %s", err)}
	}

	result.CAChain = data.SigningBundle.GetFullChain()

	return result, nil
}

func NewCertPool(reader io.Reader) (*x509.CertPool, error) {
	pemBlock, err := io.ReadAll(reader)
	if err != nil {
		return nil, err
	}
	certs, err := ParseCertsPEM(pemBlock)
	if err != nil {
		return nil, fmt.Errorf("error reading certs: %s", err)
	}
	pool := x509.NewCertPool()
	for _, cert := range certs {
		pool.AddCert(cert)
	}
	return pool, nil
}

// ParseCertsPEM returns the x509.Certificates contained in the given PEM-encoded byte array
// Returns an error if a certificate could not be parsed, or if the data does not contain any certificates
func ParseCertsPEM(pemCerts []byte) ([]*x509.Certificate, error) {
	ok := false
	certs := []*x509.Certificate{}
	for len(pemCerts) > 0 {
		var block *pem.Block
		block, pemCerts = pem.Decode(pemCerts)
		if block == nil {
			break
		}
		// Only use PEM "CERTIFICATE" blocks without extra headers
		if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
			continue
		}

		cert, err := x509.ParseCertificate(block.Bytes)
		if err != nil {
			return certs, err
		}

		certs = append(certs, cert)
		ok = true
	}

	if !ok {
		return certs, errors.New("data does not contain any valid RSA or ECDSA certificates")
	}
	return certs, nil
}

// GetPublicKeySize returns the key size in bits for a given arbitrary crypto.PublicKey
// Returns -1 for an unsupported key type.
func GetPublicKeySize(key crypto.PublicKey) int {
	if key, ok := key.(*rsa.PublicKey); ok {
		return key.Size() * 8
	}
	if key, ok := key.(*ecdsa.PublicKey); ok {
		return key.Params().BitSize
	}
	if key, ok := key.(ed25519.PublicKey); ok {
		return len(key) * 8
	}
	if key, ok := key.(dsa.PublicKey); ok {
		return key.Y.BitLen()
	}

	return -1
}

// CreateKeyBundle create a KeyBundle struct object which includes a generated key
// of keyType with keyBits leveraging the randomness from randReader.
func CreateKeyBundle(keyType string, keyBits int, randReader io.Reader) (KeyBundle, error) {
	return CreateKeyBundleWithKeyGenerator(keyType, keyBits, randReader, generatePrivateKey)
}

// CreateKeyBundleWithKeyGenerator create a KeyBundle struct object which includes
// a generated key of keyType with keyBits leveraging the randomness from randReader and
// delegates the actual key generation to keyGenerator
func CreateKeyBundleWithKeyGenerator(keyType string, keyBits int, randReader io.Reader, keyGenerator KeyGenerator) (KeyBundle, error) {
	result := KeyBundle{}
	if err := keyGenerator(keyType, keyBits, &result, randReader); err != nil {
		return result, err
	}
	return result, nil
}

// CreateDeltaCRLIndicatorExt allows creating correctly formed delta CRLs
// that point back to the last complete CRL that they're based on.
func CreateDeltaCRLIndicatorExt(completeCRLNumber int64) (pkix.Extension, error) {
	bigNum := big.NewInt(completeCRLNumber)
	bigNumValue, err := asn1.Marshal(bigNum)
	if err != nil {
		return pkix.Extension{}, fmt.Errorf("unable to marshal complete CRL number (%v): %v", completeCRLNumber, err)
	}
	return pkix.Extension{
		Id: DeltaCRLIndicatorOID,
		// > When a conforming CRL issuer generates a delta CRL, the delta
		// > CRL MUST include a critical delta CRL indicator extension.
		Critical: true,
		// This extension only includes the complete CRL number:
		//
		// > BaseCRLNumber ::= CRLNumber
		//
		// But, this needs to be encoded as a big number for encoding/asn1
		// to work properly.
		Value: bigNumValue,
	}, nil
}

// ParseBasicConstraintExtension parses a basic constraint pkix.Extension, useful if attempting to validate
// CSRs are requesting CA privileges as Go does not expose its implementation. Values returned are
// IsCA, MaxPathLen or error. If MaxPathLen was not set, a value of -1 will be returned.
func ParseBasicConstraintExtension(ext pkix.Extension) (bool, int, error) {
	if !ext.Id.Equal(ExtensionBasicConstraintsOID) {
		return false, -1, fmt.Errorf("passed in extension was not a basic constraint extension")
	}

	// All elements are set to optional here, as it is possible that we receive a CSR with the extension
	// containing an empty sequence by spec.
	type basicConstraints struct {
		IsCA       bool `asn1:"optional"`
		MaxPathLen int  `asn1:"optional,default:-1"`
	}
	bc := &basicConstraints{}
	leftOver, err := asn1.Unmarshal(ext.Value, bc)
	if err != nil {
		return false, -1, fmt.Errorf("failed unmarshalling extension value: %w", err)
	}

	numLeftOver := len(bytes.TrimSpace(leftOver))
	if numLeftOver > 0 {
		return false, -1, fmt.Errorf("%d extra bytes within basic constraints value extension", numLeftOver)
	}

	return bc.IsCA, bc.MaxPathLen, nil
}

// CreateBasicConstraintExtension create a basic constraint extension based on inputs,
// if isCa is false, an empty value sequence will be returned with maxPath being
// ignored. If isCa is true maxPath can be set to -1 to not set a maxPath value.
func CreateBasicConstraintExtension(isCa bool, maxPath int) (pkix.Extension, error) {
	var asn1Bytes []byte
	var err error

	switch {
	case isCa && maxPath >= 0:
		CaAndMaxPathLen := struct {
			IsCa       bool `asn1:""`
			MaxPathLen int  `asn1:""`
		}{
			IsCa:       isCa,
			MaxPathLen: maxPath,
		}
		asn1Bytes, err = asn1.Marshal(CaAndMaxPathLen)
	case isCa && maxPath < 0:
		justCa := struct {
			IsCa bool `asn1:""`
		}{IsCa: isCa}
		asn1Bytes, err = asn1.Marshal(justCa)
	default:
		asn1Bytes, err = asn1.Marshal(struct{}{})
	}

	if err != nil {
		return pkix.Extension{}, err
	}

	return pkix.Extension{
		Id:       ExtensionBasicConstraintsOID,
		Critical: true,
		Value:    asn1Bytes,
	}, nil
}

// GetOtherSANsFromX509Extensions is used to find all the extensions which have the identifier (OID) of
// a SAN (Subject Alternative Name), and then look at each extension to find out if it is one of a set of
// well-known types (like IP SANs) or "other".  Currently, the only OtherSANs vault supports are of type UTF8.
func GetOtherSANsFromX509Extensions(exts []pkix.Extension) ([]OtherNameUtf8, error) {
	var ret []OtherNameUtf8
	for _, ext := range exts {
		if !ext.Id.Equal(OidExtensionSubjectAltName) {
			continue
		}
		err := forEachSAN(ext.Value, func(tag int, data []byte) error {
			if tag != 0 {
				return nil
			}

			var other OtherNameRaw
			_, err := asn1.UnmarshalWithParams(data, &other, "tag:0")
			if err != nil {
				return fmt.Errorf("could not parse requested other SAN: %w", err)
			}
			val, err := other.ExtractUTF8String()
			if err != nil {
				return err
			}
			ret = append(ret, *val)
			return nil
		})
		if err != nil {
			return nil, err
		}
	}

	return ret, nil
}

func forEachSAN(extension []byte, callback func(tag int, data []byte) error) error {
	// RFC 5280, 4.2.1.6

	// SubjectAltName ::= GeneralNames
	//
	// GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
	//
	// GeneralName ::= CHOICE {
	//      otherName                       [0]     OtherName,
	//      rfc822Name                      [1]     IA5String,
	//      dNSName                         [2]     IA5String,
	//      x400Address                     [3]     ORAddress,
	//      directoryName                   [4]     Name,
	//      ediPartyName                    [5]     EDIPartyName,
	//      uniformResourceIdentifier       [6]     IA5String,
	//      iPAddress                       [7]     OCTET STRING,
	//      registeredID                    [8]     OBJECT IDENTIFIER }
	var seq asn1.RawValue
	rest, err := asn1.Unmarshal(extension, &seq)
	if err != nil {
		return err
	} else if len(rest) != 0 {
		return fmt.Errorf("x509: trailing data after X.509 extension")
	}
	if !seq.IsCompound || seq.Tag != 16 || seq.Class != 0 {
		return asn1.StructuralError{Msg: "bad SAN sequence"}
	}

	rest = seq.Bytes
	for len(rest) > 0 {
		var v asn1.RawValue
		rest, err = asn1.Unmarshal(rest, &v)
		if err != nil {
			return err
		}

		if err := callback(v.Tag, v.FullBytes); err != nil {
			return err
		}
	}

	return nil
}

// otherNameRaw describes a name related to a certificate which is not in one
// of the standard name formats. RFC 5280, 4.2.1.6:
//
//	OtherName ::= SEQUENCE {
//	     type-id    OBJECT IDENTIFIER,
//	     Value      [0] EXPLICIT ANY DEFINED BY type-id }
type OtherNameRaw struct {
	TypeID asn1.ObjectIdentifier
	Value  asn1.RawValue
}

type OtherNameUtf8 struct {
	Oid   string
	Value string
}

// String() turns an OtherNameUtf8 object into the storage or field-value used to assign that name
// to a certificate in an API call
func (o OtherNameUtf8) String() string {
	return fmt.Sprintf("%s;%s:%s", o.Oid, "UTF-8", o.Value)
}

// ExtractUTF8String returns the UTF8 string contained in the Value, or an error
// if none is present.
func (oraw *OtherNameRaw) ExtractUTF8String() (*OtherNameUtf8, error) {
	svalue := cryptobyte.String(oraw.Value.Bytes)
	var outTag cbasn1.Tag
	var val cryptobyte.String
	read := svalue.ReadAnyASN1(&val, &outTag)

	if read && outTag == asn1.TagUTF8String {
		return &OtherNameUtf8{Oid: oraw.TypeID.String(), Value: string(val)}, nil
	}
	return nil, fmt.Errorf("no UTF-8 string found in OtherName")
}

func getOtherSANsStringFromExtensions(exts []pkix.Extension) (string, error) {
	otherNames, err := GetOtherSANsFromX509Extensions(exts)
	if err != nil {
		return "", err
	}

	otherSansList := make([]string, len(otherNames))
	for i, otherName := range otherNames {
		otherSansList[i] = otherName.String()
	}

	otherSans := strings.Join(otherSansList, ",")

	return otherSans, nil
}

func getOtherSANsMapFromExtensions(exts []pkix.Extension) (map[string][]string, error) {
	otherNames, err := GetOtherSANsFromX509Extensions(exts)
	if err != nil {
		return nil, err
	}

	otherSans := make(map[string][]string)
	for _, name := range otherNames {
		if otherSans[name.Oid] == nil {
			otherSans[name.Oid] = []string{name.Value}
		} else {
			otherSans[name.Oid] = append(otherSans[name.Oid], name.Value)
		}
	}

	return otherSans, nil
}

func getKeyUsage(exts []pkix.Extension) (x509.KeyUsage, error) {
	keyUsage := x509.KeyUsage(0)
	for _, ext := range exts {
		if ext.Id.Equal(KeyUsageOID) {
			return parseKeyUsageExtension(ext.Value)
		}
	}
	return keyUsage, nil
}

// Taken from: https://cs.opensource.google/go/go/+/master:src/crypto/x509/parser.go;drc=dd84bb682482390bb8465482cb7b13d2e3b17297;l=319
func parseKeyUsageExtension(der cryptobyte.String) (x509.KeyUsage, error) {
	var usageBits asn1.BitString
	if !der.ReadASN1BitString(&usageBits) {
		return 0, errors.New("x509: invalid key usage")
	}

	var usage int
	for i := 0; i < 9; i++ {
		if usageBits.At(i) != 0 {
			usage |= 1 << uint(i)
		}
	}
	return x509.KeyUsage(usage), nil
}

func getExtKeyUsageOids(exts []pkix.Extension) ([]string, error) {
	keyUsageOidStrings := make([]string, 0)
	keyUsageOids := make([]asn1.ObjectIdentifier, 0)
	for _, ext := range exts {
		if ext.Id.Equal(ExtendedKeyUsageOID) {
			_, err := asn1.Unmarshal(ext.Value, &keyUsageOids)
			if err != nil {
				return nil, fmt.Errorf("unable to unmarshal KeyUsageOid extension: %w", err)
			}
			for _, oid := range keyUsageOids {
				keyUsageOidStrings = append(keyUsageOidStrings, oid.String())
			}
			return keyUsageOidStrings, nil
		}
	}
	return nil, nil
}

func getPolicyIdentifiers(exts []pkix.Extension) ([]string, error) {
	policyIdentifiers := make([]string, 0)
	for _, ext := range exts {
		if ext.Id.Equal(policyInformationOid) {
			// PolicyInformation ::= SEQUENCE {
			//        policyIdentifier   CertPolicyId,
			//        policyQualifiers   SEQUENCE SIZE (1..MAX) OF
			//                                PolicyQualifierInfo OPTIONAL }
			type policyInformation struct {
				PolicyIdentifier asn1.ObjectIdentifier `asn1:"optional"`
				PolicyQualifier  any                   `asn1:"optional"`
			}
			policies := make([]policyInformation, 0)
			_, err := asn1.Unmarshal(ext.Value, &policies)
			if err != nil {
				return nil, err
			}
			for _, policy := range policies {
				policyIdentifiers = append(policyIdentifiers, policy.PolicyIdentifier.String())
			}
			return policyIdentifiers, nil
		}
	}
	return nil, nil
}

// Translate Certificates and CSRs into Certificate Template
// Four "Types" Here: Certificates; Certificate Signing Requests; Fields map[string]interface{}; Creation Parameters

func ParseCertificateToCreationParameters(certificate x509.Certificate) (creationParameters CreationParameters, err error) {
	otherSans, err := getOtherSANsMapFromExtensions(certificate.Extensions)
	if err != nil {
		return CreationParameters{}, err
	}

	creationParameters = CreationParameters{
		Subject:        removeNames(certificate.Subject),
		DNSNames:       certificate.DNSNames,
		EmailAddresses: certificate.EmailAddresses,
		IPAddresses:    certificate.IPAddresses,
		URIs:           certificate.URIs,
		OtherSANs:      otherSans,
		IsCA:           certificate.IsCA,
		KeyType:        GetKeyType(certificate.PublicKeyAlgorithm.String()),
		KeyBits:        FindBitLength(certificate.PublicKey),
		NotAfter:       certificate.NotAfter,
		KeyUsage:       certificate.KeyUsage,
		// ExtKeyUsage: We use ExtKeyUsageOIDs instead as the more general field
		// ExtKeyUsageOIDs: this is an extension that may not be set, so is handled below
		// PolicyIdentifiers: this is an extension that may not be set, so is handled below
		BasicConstraintsValidForNonCA: certificate.BasicConstraintsValid,
		SignatureBits:                 FindSignatureBits(certificate.SignatureAlgorithm),
		UsePSS:                        IsPSS(certificate.SignatureAlgorithm),
		// The following two values are on creation parameters, but are impossible to parse from the certificate
		// ForceAppendCaChain
		// UseCSRValues
		PermittedDNSDomains: certificate.PermittedDNSDomains,
		// URLs: punting on this for now
		MaxPathLength:     certificate.MaxPathLen,
		NotBeforeDuration: time.Now().Sub(certificate.NotBefore), // Assumes Certificate was created this moment
		SKID:              certificate.SubjectKeyId,
	}

	extKeyUsageOIDS, err := getExtKeyUsageOids(certificate.Extensions)
	if err != nil {
		return CreationParameters{}, err
	}
	creationParameters.ExtKeyUsageOIDs = extKeyUsageOIDS

	policyInformationOids, err := getPolicyIdentifiers(certificate.Extensions)
	if err != nil {
		return CreationParameters{}, err
	}
	creationParameters.PolicyIdentifiers = policyInformationOids

	return creationParameters, err
}

func removeNames(name pkix.Name) pkix.Name {
	name.Names = nil
	return name
}

func ParseCsrToCreationParameters(csr x509.CertificateRequest) (CreationParameters, error) {
	otherSANs, err := getOtherSANsMapFromExtensions(csr.Extensions)
	if err != nil {
		return CreationParameters{}, err
	}

	creationParameters := CreationParameters{
		Subject:        removeNames(csr.Subject),
		DNSNames:       csr.DNSNames,
		EmailAddresses: csr.EmailAddresses,
		IPAddresses:    csr.IPAddresses,
		URIs:           csr.URIs,
		OtherSANs:      otherSANs,
		// IsCA: is handled below since the basic constraint it comes from might not be set on the CSR
		KeyType: GetKeyType(csr.PublicKeyAlgorithm.String()),
		KeyBits: FindBitLength(csr.PublicKey),
		// NotAfter: this is not set on a CSR
		// KeyUsage: handled below since this may not be set
		// ExtKeyUsage: We use exclusively ExtKeyUsageOIDs here
		// ExtKeyUsageOIDs: handled below since this may not be set
		// PolicyIdentifiers: handled below since this may not be set
		// BasicConstraintsValidForNonCA is handled below, since it may or may not be set on the CSR
		SignatureBits: FindSignatureBits(csr.SignatureAlgorithm),
		UsePSS:        IsPSS(csr.SignatureAlgorithm),
		// The following two values are on creation parameters, but are impossible to parse from the csr
		// ForceAppendCaChain
		// UseCSRValues
		// PermittedDNSDomains : omitted, this generally isn't on a CSR
		// URLs : omitted, this generally isn't on a CSR
		// MaxPathLength is handled below since the basic constraint it comes from may not be set on the CSR
		// NotBeforeDuration : this is not set on a CSR
		// SKID: this is generally not set on a CSR, but calculated from the Key information itself
	}

	keyUsage, err := getKeyUsage(csr.Extensions)
	if err != nil {
		return CreationParameters{}, err
	}
	creationParameters.KeyUsage = keyUsage

	extKeyUsageOIDS, err := getExtKeyUsageOids(csr.Extensions)
	if err != nil {
		return CreationParameters{}, err
	}
	creationParameters.ExtKeyUsageOIDs = extKeyUsageOIDS

	policyInformationOids, err := getPolicyIdentifiers(csr.Extensions)
	if err != nil {
		return CreationParameters{}, err
	}
	creationParameters.PolicyIdentifiers = policyInformationOids

	found, isCA, maxPathLength, err := getBasicConstraintsFromExtension(csr.Extensions)
	if err != nil {
		return CreationParameters{}, err
	}
	if found {
		creationParameters.IsCA = isCA
		creationParameters.BasicConstraintsValidForNonCA = (isCA && maxPathLength != 0) || (!isCA && (maxPathLength == 0))
		if isCA { // MaxPathLength Only Has a Meaning on a Certificate Authority
			creationParameters.MaxPathLength = maxPathLength
		}
	}

	return creationParameters, err
}

func ParseCsrToFields(csr x509.CertificateRequest) (map[string]interface{}, error) {
	otherSans, err := getOtherSANsStringFromExtensions(csr.Extensions)
	if err != nil {
		return nil, err
	}

	templateData := map[string]interface{}{
		"common_name":          csr.Subject.CommonName,
		"alt_names":            MakeAltNamesCommaSeparatedString(csr.DNSNames, csr.EmailAddresses),
		"ip_sans":              MakeIpAddressCommaSeparatedString(csr.IPAddresses),
		"uri_sans":             MakeUriCommaSeparatedString(csr.URIs),
		"other_sans":           otherSans,
		"signature_bits":       FindSignatureBits(csr.SignatureAlgorithm),
		"exclude_cn_from_sans": DetermineExcludeCnFromCsrSans(csr),
		"ou":                   makeCommaSeparatedString(csr.Subject.OrganizationalUnit),
		"organization":         makeCommaSeparatedString(csr.Subject.Organization),
		"country":              makeCommaSeparatedString(csr.Subject.Country),
		"locality":             makeCommaSeparatedString(csr.Subject.Locality),
		"province":             makeCommaSeparatedString(csr.Subject.Province),
		"street_address":       makeCommaSeparatedString(csr.Subject.StreetAddress),
		"postal_code":          makeCommaSeparatedString(csr.Subject.PostalCode),
		"serial_number":        csr.Subject.SerialNumber,
		// There is no "TTL" on a CSR, that is always set by the signer
		// max_path_length is handled below
		// permitted_dns_domains is a CA thing, it generally does not appear on a CSR
		"use_pss": IsPSS(csr.SignatureAlgorithm),
		// skid could be calculated, but does not directly exist on a csr, so punting for now
		"key_type": GetKeyType(csr.PublicKeyAlgorithm.String()),
		"key_bits": FindBitLength(csr.PublicKey),
	}

	// isCA is not a field in our data call - that is represented inside vault by using a different endpoint
	found, _, _, err := getBasicConstraintsFromExtension(csr.Extensions)
	if err != nil {
		return nil, err
	}
	templateData["add_basic_constraints"] = found

	return templateData, nil
}

func ParseCertificateToFields(certificate x509.Certificate) (map[string]interface{}, error) {
	otherSans, err := getOtherSANsStringFromExtensions(certificate.Extensions)
	if err != nil {
		return nil, err
	}

	templateData := map[string]interface{}{
		"common_name":           certificate.Subject.CommonName,
		"alt_names":             MakeAltNamesCommaSeparatedString(certificate.DNSNames, certificate.EmailAddresses),
		"ip_sans":               MakeIpAddressCommaSeparatedString(certificate.IPAddresses),
		"uri_sans":              MakeUriCommaSeparatedString(certificate.URIs),
		"other_sans":            otherSans,
		"signature_bits":        FindSignatureBits(certificate.SignatureAlgorithm),
		"exclude_cn_from_sans":  DetermineExcludeCnFromCertSans(certificate),
		"ou":                    makeCommaSeparatedString(certificate.Subject.OrganizationalUnit),
		"organization":          makeCommaSeparatedString(certificate.Subject.Organization),
		"country":               makeCommaSeparatedString(certificate.Subject.Country),
		"locality":              makeCommaSeparatedString(certificate.Subject.Locality),
		"province":              makeCommaSeparatedString(certificate.Subject.Province),
		"street_address":        makeCommaSeparatedString(certificate.Subject.StreetAddress),
		"postal_code":           makeCommaSeparatedString(certificate.Subject.PostalCode),
		"serial_number":         certificate.Subject.SerialNumber,
		"ttl":                   (certificate.NotAfter.Sub(certificate.NotBefore)).String(),
		"max_path_length":       certificate.MaxPathLen,
		"permitted_dns_domains": strings.Join(certificate.PermittedDNSDomains, ","),
		"use_pss":               IsPSS(certificate.SignatureAlgorithm),
		"skid":                  hex.EncodeToString(certificate.SubjectKeyId),
		"key_type":              GetKeyType(certificate.PublicKeyAlgorithm.String()),
		"key_bits":              FindBitLength(certificate.PublicKey),
	}

	return templateData, nil
}

func getBasicConstraintsFromExtension(exts []pkix.Extension) (found bool, isCA bool, maxPathLength int, err error) {
	for _, ext := range exts {
		if ext.Id.Equal(ExtensionBasicConstraintsOID) {
			isCA, maxPathLength, err = ParseBasicConstraintExtension(ext)
			if err != nil {
				return false, false, -1, err
			}
			return true, isCA, maxPathLength, nil
		}
	}

	return false, false, -1, nil
}

func MakeAltNamesCommaSeparatedString(names []string, emails []string) string {
	return strings.Join(append(names, emails...), ",")
}

func MakeUriCommaSeparatedString(uris []*url.URL) string {
	stringAddresses := make([]string, len(uris))
	for i, uri := range uris {
		stringAddresses[i] = uri.String()
	}
	return strings.Join(stringAddresses, ",")
}

func MakeIpAddressCommaSeparatedString(addresses []net.IP) string {
	stringAddresses := make([]string, len(addresses))
	for i, address := range addresses {
		stringAddresses[i] = address.String()
	}
	return strings.Join(stringAddresses, ",")
}

func makeCommaSeparatedString(values []string) string {
	return strings.Join(values, ",")
}

func DetermineExcludeCnFromCertSans(certificate x509.Certificate) bool {
	cn := certificate.Subject.CommonName
	if cn == "" {
		return false
	}

	emails := certificate.EmailAddresses
	for _, email := range emails {
		if email == cn {
			return false
		}
	}

	dnses := certificate.DNSNames
	for _, dns := range dnses {
		if dns == cn {
			return false
		}
	}

	return true
}

func DetermineExcludeCnFromCsrSans(csr x509.CertificateRequest) bool {
	cn := csr.Subject.CommonName
	if cn == "" {
		return false
	}

	emails := csr.EmailAddresses
	for _, email := range emails {
		if email == cn {
			return false
		}
	}

	dnses := csr.DNSNames
	for _, dns := range dnses {
		if dns == cn {
			return false
		}
	}

	return true
}

func FindBitLength(publicKey any) int {
	if publicKey == nil {
		return 0
	}
	switch pub := publicKey.(type) {
	case *rsa.PublicKey:
		return pub.N.BitLen()
	case *ecdsa.PublicKey:
		switch pub.Curve {
		case elliptic.P224():
			return 224
		case elliptic.P256():
			return 256
		case elliptic.P384():
			return 384
		case elliptic.P521():
			return 521
		default:
			return 0
		}
	default:
		return 0
	}
}

func FindSignatureBits(algo x509.SignatureAlgorithm) int {
	switch algo {
	case x509.MD2WithRSA, x509.MD5WithRSA, x509.SHA1WithRSA, x509.DSAWithSHA1, x509.ECDSAWithSHA1:
		return -1
	case x509.SHA256WithRSA, x509.DSAWithSHA256, x509.ECDSAWithSHA256, x509.SHA256WithRSAPSS:
		return 256
	case x509.SHA384WithRSA, x509.ECDSAWithSHA384, x509.SHA384WithRSAPSS:
		return 384
	case x509.SHA512WithRSA, x509.SHA512WithRSAPSS, x509.ECDSAWithSHA512:
		return 512
	case x509.PureEd25519:
		return 0
	default:
		return -1
	}
}

func GetKeyType(goKeyType string) string {
	switch goKeyType {
	case "RSA":
		return "rsa"
	case "ECDSA":
		return "ec"
	case "Ed25519":
		return "ed25519"
	default:
		return ""
	}
}

func IsPSS(algorithm x509.SignatureAlgorithm) bool {
	switch algorithm {
	case x509.SHA384WithRSAPSS, x509.SHA512WithRSAPSS, x509.SHA256WithRSAPSS:
		return true
	default:
		return false
	}
}
