blob: 9c1f4a6ef9fbb9060ef66bb242f9437c71a0f0bd [file] [log] [blame]
Lorenz Bruna4ea9d02019-10-31 11:40:30 +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
17// I've fixed this upstream, compat is going away once
18// https://go-review.googlesource.com/c/go/+/204046 hits stable
19package ca
20
21import (
22 "crypto"
23 "crypto/x509"
24 "crypto/x509/pkix"
25 "encoding/asn1"
26 "errors"
27 "io"
28 "time"
29)
30
31// Workaround for Go not supporting Ed25519 CRLs
32type CompatCertificate x509.Certificate
33
34var oidExtensionAuthorityKeyId = []int{2, 5, 29, 35}
35var oidSignatureEd25519 = asn1.ObjectIdentifier{1, 3, 101, 112}
36
37func signingParamsForPublicKey(pub interface{}, requestedSigAlgo x509.SignatureAlgorithm) (hashFunc crypto.Hash, sigAlgo pkix.AlgorithmIdentifier, err error) {
38 sigAlgo.Algorithm = oidSignatureEd25519
39 return
40}
41
42// RFC 5280, 4.2.1.1
43type authKeyId struct {
44 Id []byte `asn1:"optional,tag:0"`
45}
46
47// CreateCRL returns a DER encoded CRL, signed by this Certificate, that
48// contains the given list of revoked certificates.
49func (c *CompatCertificate) CreateCRL(rand io.Reader, priv interface{}, revokedCerts []pkix.RevokedCertificate, now, expiry time.Time) (crlBytes []byte, err error) {
50 key, ok := priv.(crypto.Signer)
51 if !ok {
52 return nil, errors.New("x509: certificate private key does not implement crypto.Signer")
53 }
54
55 hashFunc, signatureAlgorithm, err := signingParamsForPublicKey(key.Public(), 0)
56 if err != nil {
57 return nil, err
58 }
59
60 // Force revocation times to UTC per RFC 5280.
61 revokedCertsUTC := make([]pkix.RevokedCertificate, len(revokedCerts))
62 for i, rc := range revokedCerts {
63 rc.RevocationTime = rc.RevocationTime.UTC()
64 revokedCertsUTC[i] = rc
65 }
66
67 tbsCertList := pkix.TBSCertificateList{
68 Version: 1,
69 Signature: signatureAlgorithm,
70 Issuer: c.Subject.ToRDNSequence(),
71 ThisUpdate: now.UTC(),
72 NextUpdate: expiry.UTC(),
73 RevokedCertificates: revokedCertsUTC,
74 }
75
76 // Authority Key Id
77 if len(c.SubjectKeyId) > 0 {
78 var aki pkix.Extension
79 aki.Id = oidExtensionAuthorityKeyId
80 aki.Value, err = asn1.Marshal(authKeyId{Id: c.SubjectKeyId})
81 if err != nil {
82 return
83 }
84 tbsCertList.Extensions = append(tbsCertList.Extensions, aki)
85 }
86
87 tbsCertListContents, err := asn1.Marshal(tbsCertList)
88 if err != nil {
89 return
90 }
91
92 signed := tbsCertListContents
93 if hashFunc != 0 {
94 h := hashFunc.New()
95 h.Write(signed)
96 signed = h.Sum(nil)
97 }
98
99 var signature []byte
100 signature, err = key.Sign(rand, signed, hashFunc)
101 if err != nil {
102 return
103 }
104
105 return asn1.Marshal(pkix.CertificateList{
106 TBSCertList: tbsCertList,
107 SignatureAlgorithm: signatureAlgorithm,
108 SignatureValue: asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},
109 })
110}