1// Copyright (C) 2023 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Utility methods for generating X.509 certificate chains.
16package certutil
17
18import (
19	"crypto/ecdsa"
20	"crypto/elliptic"
21	"crypto/rand"
22	"crypto/x509"
23	"crypto/x509/pkix"
24	"encoding/pem"
25	"log"
26	"math/big"
27	"os"
28	"time"
29)
30
31type Entity struct {
32	PrivateKey *ecdsa.PrivateKey
33	Template   *x509.Certificate
34	// CA entities only
35	LastSerial *big.Int
36}
37
38var two32 = big.NewInt(1 << 32)
39var one = big.NewInt(1)
40
41func newKey() *ecdsa.PrivateKey {
42	key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
43	if err != nil {
44		log.Fatal(err)
45	}
46	return key
47}
48
49func newTemplate(cn string) *x509.Certificate {
50	notBefore, err := time.Parse(time.RFC3339, "2020-01-01T00:00:00Z")
51	if err != nil {
52		log.Fatal(err)
53	}
54	notAfter, err := time.Parse(time.RFC3339, "2030-01-01T00:00:00Z")
55	if err != nil {
56		log.Fatal(err)
57	}
58	return &x509.Certificate{
59		NotBefore: notBefore,
60		NotAfter:  notAfter,
61		Subject:   pkix.Name{CommonName: cn},
62	}
63}
64
65func NewEntity(name string) *Entity {
66	return &Entity{
67		PrivateKey: newKey(),
68		Template:   newTemplate(name),
69	}
70}
71
72func NewCA(name string) *Entity {
73	ca := NewEntity(name)
74	ca.Template.BasicConstraintsValid = true
75	ca.Template.IsCA = true
76	ca.LastSerial = mustRandInt(two32)
77	return ca
78}
79
80func (e *Entity) publicKey() *ecdsa.PublicKey {
81	return &e.PrivateKey.PublicKey
82}
83
84func (e *Entity) name() string {
85	return e.Template.Subject.String()
86}
87
88func (e *Entity) nextSerial() *big.Int {
89	if e.LastSerial == nil {
90		log.Fatal("Not a CA: " + e.name())
91	}
92	e.LastSerial = e.LastSerial.Add(e.LastSerial, one)
93	return e.LastSerial
94}
95
96func (ca *Entity) doSign(childTemplate *x509.Certificate, pubKey *ecdsa.PublicKey) []byte {
97	copyTemplate := *childTemplate
98	copyTemplate.SerialNumber = ca.nextSerial()
99	cert, err := x509.CreateCertificate(
100		rand.Reader,
101		&copyTemplate,
102		ca.Template,
103		pubKey,
104		ca.PrivateKey)
105	if err != nil {
106		log.Fatal(err)
107	}
108	return cert
109}
110
111func (ca *Entity) Sign(child *Entity) []byte {
112	return ca.doSign(child.Template, child.publicKey())
113}
114
115func (ca *Entity) SignWithAlgorithm(child *Entity, algorithm x509.SignatureAlgorithm) []byte {
116	copyTemplate := *child.Template
117	copyTemplate.SignatureAlgorithm = algorithm
118	return ca.doSign(&copyTemplate, child.publicKey())
119}
120
121func (ca *Entity) SignToPEM(child *Entity, filename string) {
122	cert := ca.Sign(child)
123	mustWriteFile(filename+".pem", encodePEM(cert))
124}
125
126func (ca *Entity) SignWithAlgorithmToPEM(child *Entity, algorithm x509.SignatureAlgorithm, filename string) {
127	cert := ca.SignWithAlgorithm(child, algorithm)
128	mustWriteFile(filename+".pem", encodePEM(cert))
129}
130
131func encodePEM(b []byte) []byte {
132	return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: b})
133}
134
135func mustWriteFile(path string, bs []byte) {
136	if err := os.WriteFile(path, bs, 0666); err != nil {
137		log.Fatal(err)
138	}
139}
140
141func mustRandInt(max *big.Int) *big.Int {
142	r, err := rand.Int(rand.Reader, max)
143	if err != nil {
144		log.Fatal(err)
145	}
146	return r
147}
148