blob: 64453cd899efe4e4368ecfbbf43cf3aea02372a3 [file] [log] [blame]
Serge Bazanskidbfc6382020-06-19 20:35:43 +02001// 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"
22 "crypto/ed25519"
23 "crypto/rand"
24 "crypto/sha1"
25 "crypto/x509"
26 "crypto/x509/pkix"
27 "encoding/asn1"
28 "fmt"
29 "math/big"
30 "time"
31
32 "go.etcd.io/etcd/clientv3"
33)
34
35// Issuer is a CA that can issue certificates. Two issuers are currently implemented:
36// - SelfSigned, which will generated a certificate signed by its corresponding private key.
37// - Certificate, which will use another existing Certificate as a CA.
38type Issuer interface {
39 // CACertificate returns the DER-encoded x509 certificate of the CA that will sign certificates when Issue is
40 // called, or nil if this is self-signing issuer.
41 CACertificate(ctx context.Context, kv clientv3.KV) ([]byte, error)
42 // Issue will generate a key and certificate signed by the Issuer. The returned certificate is x509 DER-encoded,
43 // while the key is a bare ed25519 key.
44 Issue(ctx context.Context, template x509.Certificate, kv clientv3.KV) (cert, key []byte, err error)
45}
46
47var (
48 // From RFC 5280 Section 4.1.2.5
49 unknownNotAfter = time.Unix(253402300799, 0)
50)
51
52// Workaround for https://github.com/golang/go/issues/26676 in Go's crypto/x509. Specifically Go
53// violates Section 4.2.1.2 of RFC 5280 without this.
54// Fixed for 1.15 in https://go-review.googlesource.com/c/go/+/227098/.
55//
56// Taken from https://github.com/FiloSottile/mkcert/blob/master/cert.go#L295 written by one of Go's
57// crypto engineers
58func calculateSKID(pubKey crypto.PublicKey) ([]byte, error) {
59 spkiASN1, err := x509.MarshalPKIXPublicKey(pubKey)
60 if err != nil {
61 return nil, err
62 }
63
64 var spki struct {
65 Algorithm pkix.AlgorithmIdentifier
66 SubjectPublicKey asn1.BitString
67 }
68 _, err = asn1.Unmarshal(spkiASN1, &spki)
69 if err != nil {
70 return nil, err
71 }
72 skid := sha1.Sum(spki.SubjectPublicKey.Bytes)
73 return skid[:], nil
74}
75
76// issueCertificate is a generic low level certificate-and-key issuance function. If ca or cakey is null, the
77// certificate will be self-signed. The returned certificate is DER-encoded, while the returned key is internal.
78func issueCertificate(template x509.Certificate, ca *x509.Certificate, caKey interface{}) (cert, key []byte, err error) {
79 pubKey, privKey, err := ed25519.GenerateKey(rand.Reader)
80 if err != nil {
81 panic(err)
82 }
83
84 serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 127)
85 serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
86 if err != nil {
87 err = fmt.Errorf("failed to generate serial number: %w", err)
88 return
89 }
90
91 skid, err := calculateSKID(pubKey)
92 if err != nil {
93 return []byte{}, privKey, err
94 }
95
96 template.SerialNumber = serialNumber
97 template.NotBefore = time.Now()
98 template.NotAfter = unknownNotAfter
99 template.BasicConstraintsValid = true
100 template.SubjectKeyId = skid
101
102 // Set the AuthorityKeyID to the SKID of the signing certificate (or self, if self-signing).
103 if ca != nil && caKey != nil {
104 template.AuthorityKeyId = ca.AuthorityKeyId
105 } else {
106 template.AuthorityKeyId = template.SubjectKeyId
107 }
108
109 if ca == nil || caKey == nil {
110 ca = &template
111 caKey = privKey
112 }
113
114 caCertRaw, err := x509.CreateCertificate(rand.Reader, &template, ca, pubKey, caKey)
115 return caCertRaw, privKey, err
116}
117
118type selfSigned struct{}
119
120func (s *selfSigned) Issue(ctx context.Context, template x509.Certificate, kv clientv3.KV) (cert, key []byte, err error) {
121 return issueCertificate(template, nil, nil)
122}
123
124func (s *selfSigned) CACertificate(ctx context.Context, kv clientv3.KV) ([]byte, error) {
125 return nil, nil
126}
127
128var (
129 // SelfSigned is an Issuer that generates self-signed certificates.
130 SelfSigned = &selfSigned{}
131)
132
133func (c *Certificate) Issue(ctx context.Context, template x509.Certificate, kv clientv3.KV) (cert, key []byte, err error) {
134 caCert, caKey, err := c.ensure(ctx, kv)
135 if err != nil {
136 return nil, nil, fmt.Errorf("could not ensure CA certificate %q exists: %w", c.name, err)
137 }
138
139 ca, err := x509.ParseCertificate(caCert)
140 if err != nil {
141 return nil, nil, fmt.Errorf("could not parse CA certificate: %w", err)
142 }
143 // Ensure only one level of CAs exist, and that they are created explicitly.
144 template.IsCA = false
145 return issueCertificate(template, ca, ed25519.PrivateKey(caKey))
146}
147
148func (c *Certificate) CACertificate(ctx context.Context, kv clientv3.KV) ([]byte, error) {
149 cert, _, err := c.ensure(ctx, kv)
150 return cert, err
151}