David Benjamin | 54b04fd | 2023-01-29 11:56:25 -0500 | [diff] [blame] | 1 | // Copyright (c) 2020, Google Inc. |
| 2 | // |
| 3 | // Permission to use, copy, modify, and/or distribute this software for any |
| 4 | // purpose with or without fee is hereby granted, provided that the above |
| 5 | // copyright notice and this permission notice appear in all copies. |
| 6 | // |
| 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
| 10 | // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| 12 | // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
| 13 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 14 | |
David Benjamin | ece1f86 | 2023-04-24 16:14:08 -0400 | [diff] [blame^] | 15 | //go:build ignore |
| 16 | |
David Benjamin | fd86eaa | 2020-06-17 15:13:16 -0400 | [diff] [blame] | 17 | // make_invalid_extensions.go generates a number of certificate chains with |
| 18 | // invalid extension encodings. |
| 19 | package main |
| 20 | |
| 21 | import ( |
| 22 | "crypto/ecdsa" |
| 23 | "crypto/rand" |
| 24 | "crypto/x509" |
| 25 | "crypto/x509/pkix" |
| 26 | "encoding/pem" |
| 27 | "fmt" |
| 28 | "math/big" |
| 29 | "os" |
| 30 | "time" |
| 31 | ) |
| 32 | |
| 33 | type extension struct { |
| 34 | // The name of the extension, in a form suitable for including in a |
| 35 | // filename. |
| 36 | name string |
| 37 | // The extension's OID. |
| 38 | oid []int |
| 39 | } |
| 40 | |
| 41 | var extensions = []extension{ |
| 42 | {name: "authority_key_identifier", oid: []int{2, 5, 29, 35}}, |
| 43 | {name: "basic_constraints", oid: []int{2, 5, 29, 19}}, |
| 44 | {name: "ext_key_usage", oid: []int{2, 5, 29, 37}}, |
| 45 | {name: "key_usage", oid: []int{2, 5, 29, 15}}, |
| 46 | {name: "name_constraints", oid: []int{2, 5, 29, 30}}, |
| 47 | {name: "subject_alt_name", oid: []int{2, 5, 29, 17}}, |
| 48 | {name: "subject_key_identifier", oid: []int{2, 5, 29, 14}}, |
| 49 | } |
| 50 | |
| 51 | var leafKey, intermediateKey, rootKey *ecdsa.PrivateKey |
| 52 | |
| 53 | func init() { |
David Benjamin | d1b20a9 | 2022-12-13 17:10:09 -0500 | [diff] [blame] | 54 | leafKey = mustParseECDSAKey(leafKeyPEM) |
| 55 | intermediateKey = mustParseECDSAKey(intermediateKeyPEM) |
| 56 | rootKey = mustParseECDSAKey(rootKeyPEM) |
David Benjamin | fd86eaa | 2020-06-17 15:13:16 -0400 | [diff] [blame] | 57 | } |
| 58 | |
| 59 | type templateAndKey struct { |
| 60 | template x509.Certificate |
| 61 | key *ecdsa.PrivateKey |
| 62 | } |
| 63 | |
David Benjamin | d1b20a9 | 2022-12-13 17:10:09 -0500 | [diff] [blame] | 64 | func mustGenerateCertificate(path string, subject, issuer *templateAndKey) []byte { |
David Benjamin | fd86eaa | 2020-06-17 15:13:16 -0400 | [diff] [blame] | 65 | cert, err := x509.CreateCertificate(rand.Reader, &subject.template, &issuer.template, &subject.key.PublicKey, issuer.key) |
| 66 | if err != nil { |
| 67 | panic(err) |
| 68 | } |
| 69 | file, err := os.Create(path) |
| 70 | if err != nil { |
| 71 | panic(err) |
| 72 | } |
| 73 | defer file.Close() |
| 74 | err = pem.Encode(file, &pem.Block{Type: "CERTIFICATE", Bytes: cert}) |
| 75 | if err != nil { |
| 76 | panic(err) |
| 77 | } |
David Benjamin | 491af10 | 2021-10-29 09:05:29 -0400 | [diff] [blame] | 78 | return cert |
David Benjamin | fd86eaa | 2020-06-17 15:13:16 -0400 | [diff] [blame] | 79 | } |
| 80 | |
| 81 | func main() { |
| 82 | notBefore, err := time.Parse(time.RFC3339, "2000-01-01T00:00:00Z") |
| 83 | if err != nil { |
| 84 | panic(err) |
| 85 | } |
| 86 | notAfter, err := time.Parse(time.RFC3339, "2100-01-01T00:00:00Z") |
| 87 | if err != nil { |
| 88 | panic(err) |
| 89 | } |
| 90 | |
| 91 | root := templateAndKey{ |
| 92 | template: x509.Certificate{ |
| 93 | SerialNumber: new(big.Int).SetInt64(1), |
| 94 | Subject: pkix.Name{CommonName: "Invalid Extensions Root"}, |
| 95 | NotBefore: notBefore, |
| 96 | NotAfter: notAfter, |
| 97 | BasicConstraintsValid: true, |
| 98 | IsCA: true, |
| 99 | ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, |
| 100 | KeyUsage: x509.KeyUsageCertSign, |
| 101 | SignatureAlgorithm: x509.ECDSAWithSHA256, |
David Benjamin | 491af10 | 2021-10-29 09:05:29 -0400 | [diff] [blame] | 102 | SubjectKeyId: []byte("root"), |
David Benjamin | fd86eaa | 2020-06-17 15:13:16 -0400 | [diff] [blame] | 103 | }, |
| 104 | key: rootKey, |
| 105 | } |
| 106 | intermediate := templateAndKey{ |
| 107 | template: x509.Certificate{ |
| 108 | SerialNumber: new(big.Int).SetInt64(2), |
| 109 | Subject: pkix.Name{CommonName: "Invalid Extensions Intermediate"}, |
| 110 | NotBefore: notBefore, |
| 111 | NotAfter: notAfter, |
| 112 | BasicConstraintsValid: true, |
| 113 | IsCA: true, |
| 114 | ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, |
| 115 | KeyUsage: x509.KeyUsageCertSign, |
| 116 | SignatureAlgorithm: x509.ECDSAWithSHA256, |
David Benjamin | 491af10 | 2021-10-29 09:05:29 -0400 | [diff] [blame] | 117 | SubjectKeyId: []byte("intermediate"), |
David Benjamin | fd86eaa | 2020-06-17 15:13:16 -0400 | [diff] [blame] | 118 | }, |
| 119 | key: intermediateKey, |
| 120 | } |
| 121 | leaf := templateAndKey{ |
| 122 | template: x509.Certificate{ |
| 123 | SerialNumber: new(big.Int).SetInt64(3), |
| 124 | Subject: pkix.Name{CommonName: "www.example.com"}, |
| 125 | NotBefore: notBefore, |
| 126 | NotAfter: notAfter, |
| 127 | BasicConstraintsValid: true, |
| 128 | IsCA: false, |
| 129 | ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, |
| 130 | KeyUsage: x509.KeyUsageCertSign, |
| 131 | SignatureAlgorithm: x509.ECDSAWithSHA256, |
| 132 | DNSNames: []string{"www.example.com"}, |
David Benjamin | 491af10 | 2021-10-29 09:05:29 -0400 | [diff] [blame] | 133 | SubjectKeyId: []byte("leaf"), |
| 134 | PermittedDNSDomains: []string{"www.example.com"}, |
David Benjamin | fd86eaa | 2020-06-17 15:13:16 -0400 | [diff] [blame] | 135 | }, |
| 136 | key: leafKey, |
| 137 | } |
| 138 | |
| 139 | // Generate a valid certificate chain from the templates. |
David Benjamin | d1b20a9 | 2022-12-13 17:10:09 -0500 | [diff] [blame] | 140 | mustGenerateCertificate("invalid_extension_root.pem", &root, &root) |
| 141 | mustGenerateCertificate("invalid_extension_intermediate.pem", &intermediate, &root) |
| 142 | leafDER := mustGenerateCertificate("invalid_extension_leaf.pem", &leaf, &intermediate) |
David Benjamin | fd86eaa | 2020-06-17 15:13:16 -0400 | [diff] [blame] | 143 | |
David Benjamin | 491af10 | 2021-10-29 09:05:29 -0400 | [diff] [blame] | 144 | leafCert, err := x509.ParseCertificate(leafDER) |
| 145 | if err != nil { |
| 146 | panic(err) |
| 147 | } |
| 148 | |
| 149 | // Make copies of the certificates with invalid extensions. These copies may |
| 150 | // be substituted into the valid chain. |
David Benjamin | fd86eaa | 2020-06-17 15:13:16 -0400 | [diff] [blame] | 151 | for _, ext := range extensions { |
| 152 | invalidExtension := []pkix.Extension{{Id: ext.oid, Value: []byte("INVALID")}} |
| 153 | |
| 154 | rootInvalid := root |
| 155 | rootInvalid.template.ExtraExtensions = invalidExtension |
David Benjamin | d1b20a9 | 2022-12-13 17:10:09 -0500 | [diff] [blame] | 156 | mustGenerateCertificate(fmt.Sprintf("invalid_extension_root_%s.pem", ext.name), &rootInvalid, &rootInvalid) |
David Benjamin | fd86eaa | 2020-06-17 15:13:16 -0400 | [diff] [blame] | 157 | |
| 158 | intermediateInvalid := intermediate |
| 159 | intermediateInvalid.template.ExtraExtensions = invalidExtension |
David Benjamin | d1b20a9 | 2022-12-13 17:10:09 -0500 | [diff] [blame] | 160 | mustGenerateCertificate(fmt.Sprintf("invalid_extension_intermediate_%s.pem", ext.name), &intermediateInvalid, &root) |
David Benjamin | fd86eaa | 2020-06-17 15:13:16 -0400 | [diff] [blame] | 161 | |
| 162 | leafInvalid := leaf |
| 163 | leafInvalid.template.ExtraExtensions = invalidExtension |
David Benjamin | d1b20a9 | 2022-12-13 17:10:09 -0500 | [diff] [blame] | 164 | mustGenerateCertificate(fmt.Sprintf("invalid_extension_leaf_%s.pem", ext.name), &leafInvalid, &intermediate) |
David Benjamin | 491af10 | 2021-10-29 09:05:29 -0400 | [diff] [blame] | 165 | |
| 166 | // Additionally generate a copy of the leaf certificate with extra data in |
| 167 | // the extension. |
| 168 | var trailingDataExtension []pkix.Extension |
| 169 | for _, leafExt := range leafCert.Extensions { |
| 170 | if leafExt.Id.Equal(ext.oid) { |
| 171 | newValue := make([]byte, len(leafExt.Value)+1) |
| 172 | copy(newValue, leafExt.Value) |
| 173 | trailingDataExtension = append(trailingDataExtension, pkix.Extension{Id: ext.oid, Critical: leafExt.Critical, Value: newValue}) |
| 174 | } |
| 175 | } |
| 176 | if len(trailingDataExtension) != 1 { |
| 177 | panic(fmt.Sprintf("could not find sample extension %s", ext.name)) |
| 178 | } |
| 179 | |
| 180 | leafTrailingData := leaf |
| 181 | leafTrailingData.template.ExtraExtensions = trailingDataExtension |
David Benjamin | d1b20a9 | 2022-12-13 17:10:09 -0500 | [diff] [blame] | 182 | mustGenerateCertificate(fmt.Sprintf("trailing_data_leaf_%s.pem", ext.name), &leafTrailingData, &intermediate) |
David Benjamin | fd86eaa | 2020-06-17 15:13:16 -0400 | [diff] [blame] | 183 | } |
| 184 | } |
| 185 | |
| 186 | const leafKeyPEM = `-----BEGIN PRIVATE KEY----- |
| 187 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgoPUXNXuH9mgiS/nk |
| 188 | 024SYxryxMa3CyGJldiHymLxSquhRANCAASRKti8VW2Rkma+Kt9jQkMNitlCs0l5 |
| 189 | w8u3SSwm7HZREvmcBCJBjVIREacRqI0umhzR2V5NLzBBP9yPD/A+Ch5X |
| 190 | -----END PRIVATE KEY-----` |
| 191 | |
| 192 | const intermediateKeyPEM = `-----BEGIN PRIVATE KEY----- |
| 193 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgWHKCKgY058ahE3t6 |
| 194 | vpxVQgzlycgCVMogwjK0y3XMNfWhRANCAATiOnyojN4xS5C8gJ/PHL5cOEsMbsoE |
| 195 | Y6KT9xRQSh8lEL4d1Vb36kqUgkpqedEImo0Og4Owk6VWVVR/m4Lk+yUw |
| 196 | -----END PRIVATE KEY-----` |
| 197 | |
| 198 | const rootKeyPEM = `-----BEGIN PRIVATE KEY----- |
| 199 | MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgBwND/eHytW0I417J |
| 200 | Hr+qcPlp5N1jM3ACXys57bPujg+hRANCAAQmdqXYl1GvY7y3jcTTK6MVXIQr44Tq |
| 201 | ChRYI6IeV9tIB6jIsOY+Qol1bk8x/7A5FGOnUWFVLEAPEPSJwPndjolt |
| 202 | -----END PRIVATE KEY-----` |
| 203 | |
David Benjamin | d1b20a9 | 2022-12-13 17:10:09 -0500 | [diff] [blame] | 204 | func mustParseECDSAKey(in string) *ecdsa.PrivateKey { |
David Benjamin | fd86eaa | 2020-06-17 15:13:16 -0400 | [diff] [blame] | 205 | keyBlock, _ := pem.Decode([]byte(in)) |
| 206 | if keyBlock == nil || keyBlock.Type != "PRIVATE KEY" { |
| 207 | panic("could not decode private key") |
| 208 | } |
| 209 | key, err := x509.ParsePKCS8PrivateKey(keyBlock.Bytes) |
| 210 | if err != nil { |
| 211 | panic(err) |
| 212 | } |
| 213 | return key.(*ecdsa.PrivateKey) |
| 214 | } |