blob: 5ab1089bd739705cdc84ec577d3dd10769a789a4 [file] [log] [blame]
Serge Bazanski9411f7c2021-03-10 13:12:53 +01001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17package pki
18
19import (
20 "context"
21 "crypto/ed25519"
22 "crypto/rand"
23 "crypto/x509"
24 "fmt"
25 "math/big"
26 "time"
27
28 "go.etcd.io/etcd/clientv3"
29)
30
31// Issuer is an entity that can issue certificates. This interface is
32// implemented by SelfSigned, which is an issuer that emits self-signed
33// certificates, and any other Certificate that has been created with CA(),
34// which makes this Certificate act as a CA and issue (sign) ceritficates.
35type Issuer interface {
Serge Bazanski216fe7b2021-05-21 18:36:16 +020036 // CACertificate returns the DER-encoded x509 certificate of the CA that
37 // will sign certificates when Issue is called, or nil if this is
38 // self-signing issuer.
Serge Bazanski9411f7c2021-03-10 13:12:53 +010039 CACertificate(ctx context.Context, kv clientv3.KV) ([]byte, error)
Serge Bazanski216fe7b2021-05-21 18:36:16 +020040 // Issue will generate a key and certificate signed by the Issuer. The
41 // returned certificate is x509 DER-encoded, while the key is a bare
42 // ed25519 key.
Serge Bazanski9411f7c2021-03-10 13:12:53 +010043 Issue(ctx context.Context, req *Certificate, kv clientv3.KV) (cert, key []byte, err error)
44}
45
Serge Bazanski216fe7b2021-05-21 18:36:16 +020046// issueCertificate is a generic low level certificate-and-key issuance
47// function. If ca or cakey is null, the certificate will be self-signed. The
48// returned certificate is DER-encoded, while the returned key is internal.
Serge Bazanski9411f7c2021-03-10 13:12:53 +010049func issueCertificate(req *Certificate, ca *x509.Certificate, caKey interface{}) (cert, key []byte, err error) {
50 var privKey ed25519.PrivateKey
51 var pubKey ed25519.PublicKey
52 if req.key != nil {
53 privKey = req.key
54 pubKey = privKey.Public().(ed25519.PublicKey)
55 } else {
56 var err error
57 pubKey, privKey, err = ed25519.GenerateKey(rand.Reader)
58 if err != nil {
59 panic(err)
60 }
61 }
62
63 serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 127)
64 serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
65 if err != nil {
66 err = fmt.Errorf("failed to generate serial number: %w", err)
67 return
68 }
69
70 skid, err := calculateSKID(pubKey)
71 if err != nil {
72 return []byte{}, privKey, err
73 }
74
75 req.template.SerialNumber = serialNumber
76 req.template.NotBefore = time.Now()
77 req.template.NotAfter = unknownNotAfter
78 req.template.BasicConstraintsValid = true
79 req.template.SubjectKeyId = skid
80
Serge Bazanski216fe7b2021-05-21 18:36:16 +020081 // Set the AuthorityKeyID to the SKID of the signing certificate (or self,
82 // if self-signing).
Serge Bazanski9411f7c2021-03-10 13:12:53 +010083 if ca != nil && caKey != nil {
84 req.template.AuthorityKeyId = ca.AuthorityKeyId
85 } else {
86 req.template.AuthorityKeyId = req.template.SubjectKeyId
87 }
88
89 if ca == nil || caKey == nil {
90 ca = &req.template
91 caKey = privKey
92 }
93
94 caCertRaw, err := x509.CreateCertificate(rand.Reader, &req.template, ca, pubKey, caKey)
95 return caCertRaw, privKey, err
96}
97
98type selfSigned struct{}
99
100var (
101 // SelfSigned is an Issuer that generates self-signed certificates.
102 SelfSigned = &selfSigned{}
103)
104
105// Issue will generate a key and certificate that is self-signed.
106func (s *selfSigned) Issue(ctx context.Context, req *Certificate, kv clientv3.KV) (cert, key []byte, err error) {
107 return issueCertificate(req, nil, nil)
108}
109
110// CACertificate returns nil for self-signed issuers.
111func (s *selfSigned) CACertificate(ctx context.Context, kv clientv3.KV) ([]byte, error) {
112 return nil, nil
113}
114
115// Issue will generate a key and certificate that is signed by this
116// Certificate, if the Certificate is a CA.
117func (c *Certificate) Issue(ctx context.Context, req *Certificate, kv clientv3.KV) (cert, key []byte, err error) {
118 caCert, caKey, err := c.ensure(ctx, kv)
119 if err != nil {
120 return nil, nil, fmt.Errorf("could not ensure CA certificate %q exists: %w", c.name, err)
121 }
122
123 ca, err := x509.ParseCertificate(caCert)
124 if err != nil {
125 return nil, nil, fmt.Errorf("could not parse CA certificate: %w", err)
126 }
127 // Ensure only one level of CAs exist, and that they are created explicitly.
128 req.template.IsCA = false
129 return issueCertificate(req, ca, ed25519.PrivateKey(caKey))
130}
131
132// CACertificate returns the DER encoded x509 form of this Certificate that
133// will be the used to issue child certificates.
134func (c *Certificate) CACertificate(ctx context.Context, kv clientv3.KV) ([]byte, error) {
135 cert, _, err := c.ensure(ctx, kv)
136 return cert, err
137}