|  | // Copyright 2020 The Monogon Project Authors. | 
|  | // | 
|  | // SPDX-License-Identifier: Apache-2.0 | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | package pki | 
|  |  | 
|  | import ( | 
|  | "context" | 
|  | "crypto/ed25519" | 
|  | "crypto/rand" | 
|  | "crypto/x509" | 
|  | "fmt" | 
|  | "math/big" | 
|  | "time" | 
|  |  | 
|  | clientv3 "go.etcd.io/etcd/client/v3" | 
|  | ) | 
|  |  | 
|  | // Issuer is an entity that can issue certificates. This interface is | 
|  | // implemented by SelfSigned, which is an issuer that emits self-signed | 
|  | // certificates, and any other Certificate that has been created with CA(), | 
|  | // which makes this Certificate act as a CA and issue (sign) ceritficates. | 
|  | type Issuer interface { | 
|  | // CACertificate returns the DER-encoded x509 certificate of the CA that | 
|  | // will sign certificates when Issue is called, or nil if this is | 
|  | // self-signing issuer. | 
|  | CACertificate(ctx context.Context, kv clientv3.KV) ([]byte, error) | 
|  | // Issue will generate a certificate signed by the Issuer. The returned | 
|  | // certificate is x509 DER-encoded. | 
|  | Issue(ctx context.Context, req *Certificate, kv clientv3.KV) (cert []byte, err error) | 
|  | } | 
|  |  | 
|  | // issueCertificate is a generic low level certificate-and-key issuance | 
|  | // function. If ca is null, the certificate will be self-signed. The returned | 
|  | // certificate is DER-encoded | 
|  | func issueCertificate(req *Certificate, ca *x509.Certificate, caKey ed25519.PrivateKey) (cert []byte, err error) { | 
|  | serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 127) | 
|  | serialNumber, err := rand.Int(rand.Reader, serialNumberLimit) | 
|  | if err != nil { | 
|  | err = fmt.Errorf("failed to generate serial number: %w", err) | 
|  | return | 
|  | } | 
|  |  | 
|  | skid, err := calculateSKID(req.PublicKey) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  |  | 
|  | req.Template.SerialNumber = serialNumber | 
|  | req.Template.NotBefore = time.Now() | 
|  | req.Template.NotAfter = UnknownNotAfter | 
|  | req.Template.BasicConstraintsValid = true | 
|  | req.Template.SubjectKeyId = skid | 
|  |  | 
|  | // Set the AuthorityKeyID to the SKID of the signing certificate (or self, | 
|  | // if self-signing). | 
|  | if ca != nil { | 
|  | req.Template.AuthorityKeyId = ca.SubjectKeyId | 
|  | } else { | 
|  | req.Template.AuthorityKeyId = req.Template.SubjectKeyId | 
|  | ca = &req.Template | 
|  | } | 
|  |  | 
|  | return x509.CreateCertificate(rand.Reader, &req.Template, ca, req.PublicKey, caKey) | 
|  | } | 
|  |  | 
|  | type selfSigned struct{} | 
|  |  | 
|  | var ( | 
|  | // SelfSigned is an Issuer that generates self-signed certificates. | 
|  | SelfSigned = &selfSigned{} | 
|  | ) | 
|  |  | 
|  | // Issue will generate a key and certificate that is self-signed. | 
|  | func (s *selfSigned) Issue(ctx context.Context, req *Certificate, kv clientv3.KV) (cert []byte, err error) { | 
|  | if err := req.ensureKey(ctx, kv); err != nil { | 
|  | return nil, err | 
|  | } | 
|  | if req.PrivateKey == nil { | 
|  | return nil, fmt.Errorf("cannot issue self-signed certificate without a private key") | 
|  | } | 
|  | return issueCertificate(req, nil, req.PrivateKey) | 
|  | } | 
|  |  | 
|  | // CACertificate returns nil for self-signed issuers. | 
|  | func (s *selfSigned) CACertificate(ctx context.Context, kv clientv3.KV) ([]byte, error) { | 
|  | return nil, nil | 
|  | } | 
|  |  | 
|  | // Issue will generate a key and certificate that is signed by this | 
|  | // Certificate, if the Certificate is a CA. | 
|  | func (c *Certificate) Issue(ctx context.Context, req *Certificate, kv clientv3.KV) (cert []byte, err error) { | 
|  | if err := c.ensureKey(ctx, kv); err != nil { | 
|  | return nil, fmt.Errorf("could not ensure CA %q key exists: %w", c.Name, err) | 
|  | } | 
|  | if err := req.ensureKey(ctx, kv); err != nil { | 
|  | return nil, fmt.Errorf("could not subject %q key exists: %w", req.Name, err) | 
|  | } | 
|  | if c.PrivateKey == nil { | 
|  | return nil, fmt.Errorf("cannot use certificate without private key as CA") | 
|  | } | 
|  |  | 
|  | caCert, err := c.ensure(ctx, kv) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("could not ensure CA %q certificate exists: %w", c.Name, err) | 
|  | } | 
|  |  | 
|  | ca, err := x509.ParseCertificate(caCert) | 
|  | if err != nil { | 
|  | return nil, fmt.Errorf("could not parse CA certificate: %w", err) | 
|  | } | 
|  | // Ensure only one level of CAs exist, and that they are created explicitly. | 
|  | req.Template.IsCA = false | 
|  | return issueCertificate(req, ca, c.PrivateKey) | 
|  | } | 
|  |  | 
|  | // CACertificate returns the DER encoded x509 form of this Certificate that | 
|  | // will be the used to issue child certificates. | 
|  | func (c *Certificate) CACertificate(ctx context.Context, kv clientv3.KV) ([]byte, error) { | 
|  | return c.ensure(ctx, kv) | 
|  | } |