/* Copyright (c) 2020, Google Inc.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */

// make_basic_constraints.go generates self-signed certificates with the basic
// constraints extension.
package main

import (
	"crypto/ecdsa"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"math/big"
	"time"
)

func main() {
	key := ecdsaKeyFromPEMOrPanic(keyPEM)

	notBefore, err := time.Parse(time.RFC3339, "2000-01-01T00:00:00Z")
	if err != nil {
		panic(err)
	}
	notAfter, err := time.Parse(time.RFC3339, "2100-01-01T00:00:00Z")
	if err != nil {
		panic(err)
	}

	baseTemplate := x509.Certificate{
		SerialNumber:       new(big.Int).SetInt64(1),
		Subject:            pkix.Name{CommonName: "Basic Constraints"},
		NotBefore:          notBefore,
		NotAfter:           notAfter,
		SignatureAlgorithm: x509.ECDSAWithSHA256,
	}

	certs := []struct {
		name                  string
		basicConstraintsValid bool
		isCA                  bool
		maxPathLen            int
		maxPathLenZero        bool
	}{
		{name: "none"},
		{name: "leaf", basicConstraintsValid: true},
		{name: "ca", basicConstraintsValid: true, isCA: true},
		{name: "ca_pathlen_0", basicConstraintsValid: true, isCA: true, maxPathLenZero: true},
		{name: "ca_pathlen_1", basicConstraintsValid: true, isCA: true, maxPathLen: 1},
		{name: "ca_pathlen_10", basicConstraintsValid: true, isCA: true, maxPathLen: 10},
	}
	for _, cert := range certs {
		template := baseTemplate
		template.BasicConstraintsValid = cert.basicConstraintsValid
		template.IsCA = cert.isCA
		template.MaxPathLen = cert.maxPathLen
		template.MaxPathLenZero = cert.maxPathLenZero

		certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
		if err != nil {
			panic(err)
		}

		certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certBytes})
		if err := ioutil.WriteFile(fmt.Sprintf("basic_constraints_%s.pem", cert.name), certPEM, 0666); err != nil {
			panic(err)
		}
	}
}

const keyPEM = `-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgoPUXNXuH9mgiS/nk
024SYxryxMa3CyGJldiHymLxSquhRANCAASRKti8VW2Rkma+Kt9jQkMNitlCs0l5
w8u3SSwm7HZREvmcBCJBjVIREacRqI0umhzR2V5NLzBBP9yPD/A+Ch5X
-----END PRIVATE KEY-----`

func ecdsaKeyFromPEMOrPanic(in string) *ecdsa.PrivateKey {
	keyBlock, _ := pem.Decode([]byte(in))
	if keyBlock == nil || keyBlock.Type != "PRIVATE KEY" {
		panic("could not decode private key")
	}
	key, err := x509.ParsePKCS8PrivateKey(keyBlock.Bytes)
	if err != nil {
		panic(err)
	}
	return key.(*ecdsa.PrivateKey)
}
