blob: 95d0287dc13f30247c11bd8d5b6c3f628eeced9b [file] [log] [blame]
agl@chromium.org77a9ad92012-03-20 15:14:27 +00001# Copyright (c) 2012 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import asn1
dadrian4ccf51c2016-07-20 15:36:58 -07006import datetime
agl@chromium.org77a9ad92012-03-20 15:14:27 +00007import hashlib
dadrian4ccf51c2016-07-20 15:36:58 -07008import itertools
agl@chromium.org77a9ad92012-03-20 15:14:27 +00009import os
dadrian4ccf51c2016-07-20 15:36:58 -070010import time
agl@chromium.org77a9ad92012-03-20 15:14:27 +000011
dadrian4ccf51c2016-07-20 15:36:58 -070012GENERALIZED_TIME_FORMAT = "%Y%m%d%H%M%SZ"
13
14OCSP_STATE_GOOD = 1
15OCSP_STATE_REVOKED = 2
16OCSP_STATE_INVALID_RESPONSE = 3
17OCSP_STATE_UNAUTHORIZED = 4
18OCSP_STATE_UNKNOWN = 5
19OCSP_STATE_TRY_LATER = 6
20OCSP_STATE_INVALID_RESPONSE_DATA = 7
21OCSP_STATE_MISMATCHED_SERIAL = 8
22
23OCSP_DATE_VALID = 1
24OCSP_DATE_OLD = 2
25OCSP_DATE_EARLY = 3
26OCSP_DATE_LONG = 4
Matt Mueller55aef642018-05-02 18:53:57 +000027OCSP_DATE_LONGER = 5
dadrian4ccf51c2016-07-20 15:36:58 -070028
29OCSP_PRODUCED_VALID = 1
30OCSP_PRODUCED_BEFORE_CERT = 2
31OCSP_PRODUCED_AFTER_CERT = 3
agl@chromium.org77a9ad92012-03-20 15:14:27 +000032
33# This file implements very minimal certificate and OCSP generation. It's
34# designed to test revocation checking.
35
36def RandomNumber(length_in_bytes):
37 '''RandomNumber returns a random number of length 8*|length_in_bytes| bits'''
38 rand = os.urandom(length_in_bytes)
39 n = 0
40 for x in rand:
41 n <<= 8
42 n |= ord(x)
43 return n
44
45
46def ModExp(n, e, p):
47 '''ModExp returns n^e mod p'''
48 r = 1
49 while e != 0:
50 if e & 1:
51 r = (r*n) % p
52 e >>= 1
53 n = (n*n) % p
54 return r
55
rsleevi85d21772014-08-28 15:46:51 -070056# PKCS1v15_SHA256_PREFIX is the ASN.1 prefix for a SHA256 signature.
57PKCS1v15_SHA256_PREFIX = '3031300d060960864801650304020105000420'.decode('hex')
agl@chromium.org77a9ad92012-03-20 15:14:27 +000058
59class RSA(object):
60 def __init__(self, modulus, e, d):
61 self.m = modulus
62 self.e = e
63 self.d = d
64
65 self.modlen = 0
66 m = modulus
67 while m != 0:
68 self.modlen += 1
69 m >>= 8
70
71 def Sign(self, message):
rsleevi85d21772014-08-28 15:46:51 -070072 digest = hashlib.sha256(message).digest()
73 prefix = PKCS1v15_SHA256_PREFIX
agl@chromium.org77a9ad92012-03-20 15:14:27 +000074
75 em = ['\xff'] * (self.modlen - 1 - len(prefix) - len(digest))
76 em[0] = '\x00'
77 em[1] = '\x01'
78 em += "\x00" + prefix + digest
79
80 n = 0
81 for x in em:
82 n <<= 8
83 n |= ord(x)
84
85 s = ModExp(n, self.d, self.m)
86 out = []
87 while s != 0:
88 out.append(s & 0xff)
89 s >>= 8
90 out.reverse()
91 return '\x00' * (self.modlen - len(out)) + asn1.ToBytes(out)
92
93 def ToDER(self):
94 return asn1.ToDER(asn1.SEQUENCE([self.m, self.e]))
95
96
Adam Langley1b622652017-12-05 23:57:33 +000097def Name(cn):
98 return asn1.SEQUENCE([
99 asn1.SET([
100 asn1.SEQUENCE([
101 COMMON_NAME, cn,
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000102 ])
Adam Langley1b622652017-12-05 23:57:33 +0000103 ])
104 ])
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000105
106
107# The private key and root certificate name are hard coded here:
108
mattm10ede842016-11-29 11:57:16 -0800109# This is the root private key
110ROOT_KEY = RSA(0x00c1541fac63d3b969aa231a02cb2e0d9ee7b26724f136c121b2c28bdae5caa87733cc407ad83842ef20ec67d941b448a1ce3557cf5ddebf3c9bde8f36f253ee73e670d1c4c6631d1ddc0e39cbde09b833f66347ea379c3fa891d61a0ca005b38b0b2cad1058e3589c9f30600be81e4ff4ac220972c17b74f92f03d72b496f643543d0b27a5227f1efee13c138888b23cb101877b3b4dc091f0b3bb6fc3c792187b05ab38e97862f8af6156bcbfbb824385132c6741e6c65cfcd5f13142421a210b95185884c4866f3ea644dfb8006133d14e72a4704f3e700cf827ca5ffd2ef74c2ab6a5259ffff40f0f7f607891388f917fc9fc9e65742df1bfa0b322140bb65,
111 65537,
112 0x00980f2db66ef249e4954074a5fbdf663135363a3071554ac4d19079661bd5b179c890ffaa5fc4a8c8e3116e81104fd7cd049f2a48dd2165332bb9fad511f6f817cb09b3c45cf1fa25d13e9331099c8578c173c74dae9dc3e83784ba0a7216e9e8144af8786221b741c167d033ad47a245e4da04aa710a44aff5cdc480b48adbba3575d1315555690f081f9f69691e801e34c21240bcd3df9573ec5f9aa290c5ed19404fb911ab28b7680e0be086487273db72da6621f24d8c66197a5f1b7687efe1d9e3b6655af2891d4540482e1246ff5f62ce61b8b5dcb2c66ade6bb41e0bf071445fb8544aa0a489780f770a6f1031ee19347641794f4ad17354d579a9d061)
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000113
114# Root certificate CN
mattm10ede842016-11-29 11:57:16 -0800115ROOT_CN = "Testing CA"
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000116
117# All certificates are issued under this policy OID, in the Google arc:
118CERT_POLICY_OID = asn1.OID([1, 3, 6, 1, 4, 1, 11129, 2, 4, 1])
119
120# These result in the following root certificate:
121# -----BEGIN CERTIFICATE-----
mattm10ede842016-11-29 11:57:16 -0800122# MIIC1DCCAbygAwIBAgIBATANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwpUZXN0
123# aW5nIENBMB4XDTEwMDEwMTA2MDAwMFoXDTMyMTIwMTA2MDAwMFowFTETMBEGA1UE
124# AxMKVGVzdGluZyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMFU
125# H6xj07lpqiMaAssuDZ7nsmck8TbBIbLCi9rlyqh3M8xAetg4Qu8g7GfZQbRIoc41
126# V89d3r88m96PNvJT7nPmcNHExmMdHdwOOcveCbgz9mNH6jecP6iR1hoMoAWziwss
127# rRBY41icnzBgC+geT/SsIglywXt0+S8D1ytJb2Q1Q9CyelIn8e/uE8E4iIsjyxAY
128# d7O03AkfCzu2/Dx5IYewWrOOl4YvivYVa8v7uCQ4UTLGdB5sZc/NXxMUJCGiELlR
129# hYhMSGbz6mRN+4AGEz0U5ypHBPPnAM+CfKX/0u90wqtqUln//0Dw9/YHiROI+Rf8
130# n8nmV0LfG/oLMiFAu2UCAwEAAaMvMC0wEgYDVR0TAQH/BAgwBgEB/wIBATAXBgNV
131# HSAEEDAOMAwGCisGAQQB1nkCBAEwDQYJKoZIhvcNAQELBQADggEBADNrvoAyqAVm
132# bydPBBfLRqyH4DXt2vuMVmnSdnWnOxYiEezGmNSNiO1k1ZFBwVSsd+JHrT24lax9
133# kvU1yQDW//PBu3ijfZOCaIUleQiGXHMGfV4MjzgYbxpvHOvEUC6IXmYCsIEwcZgK
134# lrwnfJQ3MVU4hOgGTlOTWYPtCwvTsBObNRLdIs+ifMQiWmzPBlM8XeX4e5acDjTb
135# emcN4szU3EcgmCA0LvBIRI4F6NWpaIJl2WnLyMUDyKq4vjpRJOZkNwAC+525duDr
136# JFE4PKR2Lh53nJQIJv6mcTZQkX1mmw0yzqWxcGCoHACma3TgSwOHryvSopL+t26+
137# ZlQvP2ygwqY=
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000138# -----END CERTIFICATE-----
139
mattmadace8e2016-10-03 14:07:15 -0700140# If you update any of the above, you can generate a new root by running this
141# file as a script.
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000142
mattm10ede842016-11-29 11:57:16 -0800143INTERMEDIATE_KEY = RSA(0x00c661afcc659f88855a83ade8fb792dc13d0cf388b17bece9149cf0b8556d27b19101d081fb2a842d13a2ac95d8308ddd66783843ecc5806513959eb6b30dd69b2845d97e10d0bbbf653d686dc8828935022cc96f9e030b567157257d3d6526734080bb9727cee0d30f4209d5820e1d662f358fc789c0e9366d84f89adf1beb8d843f74e6f325876ac35d5c11691fcb296967c06edf69450c16bb2314c14599fe90725d5ec90f2db6698afae72bba0cfbf77967c7e8b49f2172f9381827c27ab7f9471c62bd8da4a6c657966ec1385cf41d739449835888f30d64971619dcd380408cd74f25c3be19833a92620c9cf710da67e15ac8cef69bc7e4e5e7f813c1ed,
144 65537,
145 0x77c5e2edf52d2cafd6c649e9b06aa9455226cfa26805fa337f4e81c7c94bedfb3721715208e2d28aa4a042b2f5a3db03212ad44dae564ffeb6a44efedf7c2b65e21aca056301a3591b36c82600394fbdc16268fc0adaabadb5207871f4ef6d17888a30b84240955cd889768681cf23d0de0fe88f008c8841643e341acd397e2d1104a23242e566088b7617c26ae8b48a85b6c9b7dc64ef1fa5e9b124ff8c1659a82d8225f28a820cc6ca07beff0354364c631a9142309fea1d8b054f6e00e23c54b493a21fcbe89a646b39d1acba5bc2ace9bba0252671d42a15202f3afccc912114d6c20eb3131e74289f2c744c5b39e7d3780fe21402ab1c3ae65854fee401)
146
Matt Mueller75863342018-05-01 22:58:47 +0000147# Intermediate certificate CN prefix (random serial number is added to the CN
148# in order to avoid caching issues.)
149INTERMEDIATE_CN_PREFIX = "Testing Intermediate CA"
mattm10ede842016-11-29 11:57:16 -0800150
151LEAF_KEY = RSA(0x00cd12d317b39cfbb160fb1dc9c9f0dc8fef3604dda4d8c557392ce1d616483713f78216cadbefd1c76ea0f3bbbe410e24b233b1b73583922b09314e249b2cfde1be0995e13f160fb630c10d447750da20ffaa4880006717feaa3e4db602e4f511b5cc312f770f44b037784effec62640f948aa189c3769f03bdd0e22a36ecfa5951f5577de195a4fba33c879b657968b79138fd7ab389a9968522f7389c6052be1ff78bc168d3ea961e132a044eba33ac07ead95367c7b815e91eca924d914fd0d811349b8bf500707ba71a43a2901a545f34e1792e72654f6649fab9716f4ba17379ee8042186bbba9b9bac416a60474cc60686f0e6e4b01259cc3cb5873edf9,
152 65537,
153 0x009c23e81bd4c30314743dded9646b82d408937db2f0afa7d9988be6cba59d886a287aa13605ad9c7117776efc94885de76cd3554da46e301d9a5b331f4613449edb9ddac36cd0345848d8c46c4bd880acbd5cfee48ee9efe813e16a33da124fd213348c8292494ac84d03ca4aabc5e25fc67ea32e0c6845fc884b01d8988768b8b931c41de49708dbcd5fcb61823f9a1f7507c6f364be4cb5a8cf24af4925997030dd4f67a0c9c6813401cc8b2f5d1971ee0022770239b7042fde8228c33942e9c0a0b18854cb1b5542be928338ab33ac936bbba174e55457007b16f36011dbb8f4258abe64e42b1cfa79803d30170b7ecf3e7c595d42003fff72591e07acd9cd)
154
155LEAF_KEY_PEM = '''-----BEGIN RSA PRIVATE KEY-----
156MIIEpQIBAAKCAQEAzRLTF7Oc+7Fg+x3JyfDcj+82BN2k2MVXOSzh1hZINxP3ghbK
1572+/Rx26g87u+QQ4ksjOxtzWDkisJMU4kmyz94b4JleE/Fg+2MMENRHdQ2iD/qkiA
158AGcX/qo+TbYC5PURtcwxL3cPRLA3eE7/7GJkD5SKoYnDdp8DvdDiKjbs+llR9Vd9
1594ZWk+6M8h5tleWi3kTj9erOJqZaFIvc4nGBSvh/3i8Fo0+qWHhMqBE66M6wH6tlT
160Z8e4FekeypJNkU/Q2BE0m4v1AHB7pxpDopAaVF804XkucmVPZkn6uXFvS6Fzee6A
161Qhhru6m5usQWpgR0zGBobw5uSwElnMPLWHPt+QIDAQABAoIBAQCcI+gb1MMDFHQ9
1623tlka4LUCJN9svCvp9mYi+bLpZ2Iaih6oTYFrZxxF3du/JSIXeds01VNpG4wHZpb
163Mx9GE0Se253aw2zQNFhI2MRsS9iArL1c/uSO6e/oE+FqM9oST9ITNIyCkklKyE0D
164ykqrxeJfxn6jLgxoRfyISwHYmIdouLkxxB3klwjbzV/LYYI/mh91B8bzZL5MtajP
165JK9JJZlwMN1PZ6DJxoE0AcyLL10Zce4AIncCObcEL96CKMM5QunAoLGIVMsbVUK+
166koM4qzOsk2u7oXTlVFcAexbzYBHbuPQlir5k5Csc+nmAPTAXC37PPnxZXUIAP/9y
167WR4HrNnNAoGBAPmOqTe7ntto6rDEsU1cKOJFKIZ7UVcSByyz8aLrvj1Rb2mkrNJU
168SdTqJvtqrvDXgO0HuGtFOzsZrRV9+XRPd2P0mP0uhfRiYGWT8hnILGyI2+7zlC/w
169HDtLEefelhtdOVKgUaLQXptSn7aGalUHghZKWjRNT5ah+U85MoI2ZkDbAoGBANJe
170KvrBBPSFLj+x2rsMhG+ksK0I6tivapVvSTtDV3ME1DvA/4BIMV/nIZyoH4AYI72c
171m/vD66+eCqh75cq5BzbVD63tR+ZRi/VdT1HJcl2IFXynk6eaBw8v7gpQyx6t3iSK
172lx/dIdpLt1BQuR4qI6x1wYp7Utn98soEkiFXzgq7AoGBAJTLBYPQXvgNBxlcPSaV
173016Nw4rjTe0vN43kwCbWjjf7LQV9BPnm/Zpv/cwboLDCnQE2gDOdNKKZPYS59pjt
174pI65UNpr+bxrR3RpEIlku2/+7br8ChfG/t4vdT6djTxFih8ErYf42t+bFNT8Mbv+
1753QYzULMsgU6bxo0A2meezbrPAoGBAK/IxmtQXP6iRxosWRUSCZxs5sFAgVVdh1el
176bXEa/Xj8IQhpZlbgfHmh3oFULzZPdZYcxm7jsQ7HpipRlZwHbtLPyNFSRFFd9PCr
1777vrttSYY77OBKC3V1G5JY8S07HYPXV/1ewDCPGZ3/I8dVQKyvap/n6FDGeFUhctv
178dFhuUZq/AoGAWLXlbcIl1cvOhfFJ5owohJhzh9oW9tlCtjV5/dlix2RaE5CtDZWS
179oMm4sQu9HiA8jLDP1MEEMRFPrPXdrZnxnSqVd1DgabSegD1/ZCb1QlWwQWkk5QU+
180wotPOMI33L50kZqUaDP+1XSL0Dyfo/pYpm4tYy/5QmP6WKXCtFUXybI=
181-----END RSA PRIVATE KEY-----
182'''
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000183
184# Various OIDs
185
186AIA_OCSP = asn1.OID([1, 3, 6, 1, 5, 5, 7, 48, 1])
mattm10ede842016-11-29 11:57:16 -0800187AIA_CA_ISSUERS = asn1.OID([1, 3, 6, 1, 5, 5, 7, 48, 2])
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000188AUTHORITY_INFORMATION_ACCESS = asn1.OID([1, 3, 6, 1, 5, 5, 7, 1, 1])
189BASIC_CONSTRAINTS = asn1.OID([2, 5, 29, 19])
190CERT_POLICIES = asn1.OID([2, 5, 29, 32])
191COMMON_NAME = asn1.OID([2, 5, 4, 3])
192COUNTRY = asn1.OID([2, 5, 4, 6])
193HASH_SHA1 = asn1.OID([1, 3, 14, 3, 2, 26])
194OCSP_TYPE_BASIC = asn1.OID([1, 3, 6, 1, 5, 5, 7, 48, 1, 1])
195ORGANIZATION = asn1.OID([2, 5, 4, 10])
196PUBLIC_KEY_RSA = asn1.OID([1, 2, 840, 113549, 1, 1, 1])
rsleevi85d21772014-08-28 15:46:51 -0700197SHA256_WITH_RSA_ENCRYPTION = asn1.OID([1, 2, 840, 113549, 1, 1, 11])
rsleevia58a92d2017-03-03 19:07:20 -0800198SUBJECT_ALTERNATIVE_NAME = asn1.OID([2, 5, 29, 17])
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000199
200def MakeCertificate(
mattm10ede842016-11-29 11:57:16 -0800201 issuer_cn, subject_cn, serial, pubkey, privkey, ocsp_url = None,
rsleevia58a92d2017-03-03 19:07:20 -0800202 ca_issuers_url = None, is_ca=False, path_len=None, ip_sans=None,
203 dns_sans=None):
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000204 '''MakeCertificate returns a DER encoded certificate, signed by privkey.'''
205 extensions = asn1.SEQUENCE([])
206
mattm10ede842016-11-29 11:57:16 -0800207 if is_ca:
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000208 # Root certificate.
209 c = None
210 o = None
211 extensions.children.append(
212 asn1.SEQUENCE([
mattmadace8e2016-10-03 14:07:15 -0700213 BASIC_CONSTRAINTS,
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000214 True,
215 asn1.OCTETSTRING(asn1.ToDER(asn1.SEQUENCE([
mattm10ede842016-11-29 11:57:16 -0800216 True, # IsCA
217 ] + ([path_len] if path_len is not None else []) # Path len
218 ))),
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000219 ]))
rsleevia58a92d2017-03-03 19:07:20 -0800220 if ip_sans is not None or dns_sans is not None:
221 sans = []
222 if dns_sans is not None:
223 for dns_name in dns_sans:
224 sans.append(
225 asn1.Raw(asn1.TagAndLength(0x82, len(dns_name)) + dns_name))
226 if ip_sans is not None:
227 for ip_addr in ip_sans:
228 sans.append(
229 asn1.Raw(asn1.TagAndLength(0x87, len(ip_addr)) + ip_addr))
230 extensions.children.append(
231 asn1.SEQUENCE([
232 SUBJECT_ALTERNATIVE_NAME,
233 # There is implicitly a critical=False here. Since false is the
234 # default, encoding the value would be invalid DER.
235 asn1.OCTETSTRING(asn1.ToDER(asn1.SEQUENCE(sans)))
236 ]))
237
mattm10ede842016-11-29 11:57:16 -0800238 if ocsp_url is not None or ca_issuers_url is not None:
239 aia_entries = []
240 if ocsp_url is not None:
241 aia_entries.append(
242 asn1.SEQUENCE([
243 AIA_OCSP,
244 asn1.Raw(asn1.TagAndLength(0x86, len(ocsp_url)) + ocsp_url),
245 ]))
246 if ca_issuers_url is not None:
247 aia_entries.append(
248 asn1.SEQUENCE([
249 AIA_CA_ISSUERS,
250 asn1.Raw(asn1.TagAndLength(0x86,
251 len(ca_issuers_url)) + ca_issuers_url),
252 ]))
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000253 extensions.children.append(
254 asn1.SEQUENCE([
255 AUTHORITY_INFORMATION_ACCESS,
mattmadace8e2016-10-03 14:07:15 -0700256 # There is implicitly a critical=False here. Since false is the default,
257 # encoding the value would be invalid DER.
mattm10ede842016-11-29 11:57:16 -0800258 asn1.OCTETSTRING(asn1.ToDER(asn1.SEQUENCE(aia_entries))),
259 ]))
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000260
261 extensions.children.append(
262 asn1.SEQUENCE([
263 CERT_POLICIES,
mattmadace8e2016-10-03 14:07:15 -0700264 # There is implicitly a critical=False here. Since false is the default,
265 # encoding the value would be invalid DER.
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000266 asn1.OCTETSTRING(asn1.ToDER(asn1.SEQUENCE([
267 asn1.SEQUENCE([ # PolicyInformation
268 CERT_POLICY_OID,
269 ]),
270 ]))),
271 ])
272 )
273
274 tbsCert = asn1.ToDER(asn1.SEQUENCE([
275 asn1.Explicit(0, 2), # Version
276 serial,
rsleevi85d21772014-08-28 15:46:51 -0700277 asn1.SEQUENCE([SHA256_WITH_RSA_ENCRYPTION, None]), # SignatureAlgorithm
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000278 Name(cn = issuer_cn), # Issuer
279 asn1.SEQUENCE([ # Validity
280 asn1.UTCTime("100101060000Z"), # NotBefore
281 asn1.UTCTime("321201060000Z"), # NotAfter
282 ]),
Adam Langley1b622652017-12-05 23:57:33 +0000283 Name(cn = subject_cn), # Subject
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000284 asn1.SEQUENCE([ # SubjectPublicKeyInfo
285 asn1.SEQUENCE([ # Algorithm
286 PUBLIC_KEY_RSA,
287 None,
288 ]),
289 asn1.BitString(asn1.ToDER(pubkey)),
290 ]),
291 asn1.Explicit(3, extensions),
292 ]))
293
294 return asn1.ToDER(asn1.SEQUENCE([
295 asn1.Raw(tbsCert),
296 asn1.SEQUENCE([
rsleevi85d21772014-08-28 15:46:51 -0700297 SHA256_WITH_RSA_ENCRYPTION,
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000298 None,
299 ]),
300 asn1.BitString(privkey.Sign(tbsCert)),
301 ]))
302
dadrian4ccf51c2016-07-20 15:36:58 -0700303def MakeOCSPSingleResponse(
304 issuer_name_hash, issuer_key_hash, serial, ocsp_state, ocsp_date):
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000305 cert_status = None
agl@chromium.orgb5f388a2012-06-29 17:54:06 +0000306 if ocsp_state == OCSP_STATE_REVOKED:
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000307 cert_status = asn1.Explicit(1, asn1.GeneralizedTime("20100101060000Z"))
agl@chromium.orgb5f388a2012-06-29 17:54:06 +0000308 elif ocsp_state == OCSP_STATE_UNKNOWN:
309 cert_status = asn1.Raw(asn1.TagAndLength(0x80 | 2, 0))
310 elif ocsp_state == OCSP_STATE_GOOD:
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000311 cert_status = asn1.Raw(asn1.TagAndLength(0x80 | 0, 0))
dadrian4ccf51c2016-07-20 15:36:58 -0700312 elif ocsp_state == OCSP_STATE_MISMATCHED_SERIAL:
313 cert_status = asn1.Raw(asn1.TagAndLength(0x80 | 0, 0))
314 serial -= 1
agl@chromium.orgb5f388a2012-06-29 17:54:06 +0000315 else:
316 raise ValueError('Bad OCSP state: ' + str(ocsp_state))
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000317
dadrian4ccf51c2016-07-20 15:36:58 -0700318 now = datetime.datetime.fromtimestamp(time.mktime(time.gmtime()))
319 if ocsp_date == OCSP_DATE_VALID:
320 thisUpdate = now - datetime.timedelta(days=1)
321 nextUpdate = thisUpdate + datetime.timedelta(weeks=1)
322 elif ocsp_date == OCSP_DATE_OLD:
dadrian9a07cc92016-07-22 10:44:37 -0700323 thisUpdate = now - datetime.timedelta(days=1, weeks=1)
dadrian4ccf51c2016-07-20 15:36:58 -0700324 nextUpdate = thisUpdate + datetime.timedelta(weeks=1)
325 elif ocsp_date == OCSP_DATE_EARLY:
dadrian9a07cc92016-07-22 10:44:37 -0700326 thisUpdate = now + datetime.timedelta(days=1)
dadrian4ccf51c2016-07-20 15:36:58 -0700327 nextUpdate = thisUpdate + datetime.timedelta(weeks=1)
328 elif ocsp_date == OCSP_DATE_LONG:
329 thisUpdate = now - datetime.timedelta(days=365)
dadrian9a07cc92016-07-22 10:44:37 -0700330 nextUpdate = thisUpdate + datetime.timedelta(days=366)
Matt Mueller55aef642018-05-02 18:53:57 +0000331 elif ocsp_date == OCSP_DATE_LONGER:
332 thisUpdate = now - datetime.timedelta(days=367)
333 nextUpdate = thisUpdate + datetime.timedelta(days=368)
dadrian4ccf51c2016-07-20 15:36:58 -0700334 else:
335 raise ValueError('Bad OCSP date: ' + str(ocsp_date))
336
337 return asn1.SEQUENCE([ # SingleResponse
338 asn1.SEQUENCE([ # CertID
339 asn1.SEQUENCE([ # hashAlgorithm
340 HASH_SHA1,
341 None,
342 ]),
343 issuer_name_hash,
344 issuer_key_hash,
345 serial,
346 ]),
347 cert_status,
348 asn1.GeneralizedTime( # thisUpdate
349 thisUpdate.strftime(GENERALIZED_TIME_FORMAT)
350 ),
351 asn1.Explicit( # nextUpdate
352 0,
353 asn1.GeneralizedTime(nextUpdate.strftime(GENERALIZED_TIME_FORMAT))
354 ),
355 ])
356
357def MakeOCSPResponse(
358 issuer_cn, issuer_key, serial, ocsp_states, ocsp_dates, ocsp_produced):
Matt Mueller55aef642018-05-02 18:53:57 +0000359 if ocsp_states[0] == OCSP_STATE_UNAUTHORIZED:
360 return unauthorizedDER
361 elif ocsp_states[0] == OCSP_STATE_INVALID_RESPONSE:
362 return '3'
363 elif ocsp_states[0] == OCSP_STATE_TRY_LATER:
364 resp = asn1.SEQUENCE([
365 asn1.ENUMERATED(3),
366 ])
367 return asn1.ToDER(resp)
368 elif ocsp_states[0] == OCSP_STATE_INVALID_RESPONSE_DATA:
369 invalid_data = asn1.ToDER(asn1.OCTETSTRING('not ocsp data'))
370 basic_resp = asn1.SEQUENCE([
371 asn1.Raw(invalid_data),
372 asn1.SEQUENCE([
373 SHA256_WITH_RSA_ENCRYPTION,
374 None,
375 ]),
376 asn1.BitString(ROOT_KEY.Sign(invalid_data)),
377 ])
378 resp = asn1.SEQUENCE([
379 asn1.ENUMERATED(0),
380 asn1.Explicit(0, asn1.SEQUENCE([
381 OCSP_TYPE_BASIC,
382 asn1.OCTETSTRING(asn1.ToDER(basic_resp)),
383 ])),
384 ])
385 return asn1.ToDER(resp)
386
dadrian4ccf51c2016-07-20 15:36:58 -0700387 # https://tools.ietf.org/html/rfc2560
388 issuer_name_hash = asn1.OCTETSTRING(
389 hashlib.sha1(asn1.ToDER(Name(cn = issuer_cn))).digest())
390
391 issuer_key_hash = asn1.OCTETSTRING(
392 hashlib.sha1(asn1.ToDER(issuer_key)).digest())
393
394 now = datetime.datetime.fromtimestamp(time.mktime(time.gmtime()))
395 if ocsp_produced == OCSP_PRODUCED_VALID:
396 producedAt = now - datetime.timedelta(days=1)
397 elif ocsp_produced == OCSP_PRODUCED_BEFORE_CERT:
398 producedAt = datetime.datetime.strptime(
399 "19100101050000Z", GENERALIZED_TIME_FORMAT)
400 elif ocsp_produced == OCSP_PRODUCED_AFTER_CERT:
401 producedAt = datetime.datetime.strptime(
402 "20321201070000Z", GENERALIZED_TIME_FORMAT)
403 else:
404 raise ValueError('Bad OCSP produced: ' + str(ocsp_produced))
405
406 single_responses = [
407 MakeOCSPSingleResponse(issuer_name_hash, issuer_key_hash, serial,
408 ocsp_state, ocsp_date)
409 for ocsp_state, ocsp_date in itertools.izip(ocsp_states, ocsp_dates)
410 ]
411
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000412 basic_resp_data_der = asn1.ToDER(asn1.SEQUENCE([
413 asn1.Explicit(2, issuer_key_hash),
dadrian4ccf51c2016-07-20 15:36:58 -0700414 asn1.GeneralizedTime(producedAt.strftime(GENERALIZED_TIME_FORMAT)),
415 asn1.SEQUENCE(single_responses),
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000416 ]))
417
418 basic_resp = asn1.SEQUENCE([
419 asn1.Raw(basic_resp_data_der),
420 asn1.SEQUENCE([
rsleevi85d21772014-08-28 15:46:51 -0700421 SHA256_WITH_RSA_ENCRYPTION,
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000422 None,
423 ]),
424 asn1.BitString(issuer_key.Sign(basic_resp_data_der)),
425 ])
426
427 resp = asn1.SEQUENCE([
428 asn1.ENUMERATED(0),
429 asn1.Explicit(0, asn1.SEQUENCE([
430 OCSP_TYPE_BASIC,
431 asn1.OCTETSTRING(asn1.ToDER(basic_resp)),
432 ]))
433 ])
434
435 return asn1.ToDER(resp)
436
437
438def DERToPEM(der):
439 pem = '-----BEGIN CERTIFICATE-----\n'
440 pem += der.encode('base64')
441 pem += '-----END CERTIFICATE-----\n'
442 return pem
443
agl@chromium.orgb5f388a2012-06-29 17:54:06 +0000444# unauthorizedDER is an OCSPResponse with a status of 6:
445# SEQUENCE { ENUM(6) }
446unauthorizedDER = '30030a0106'.decode('hex')
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000447
448def GenerateCertKeyAndOCSP(subject = "127.0.0.1",
449 ocsp_url = "http://127.0.0.1",
dadrian4ccf51c2016-07-20 15:36:58 -0700450 ocsp_states = None,
451 ocsp_dates = None,
452 ocsp_produced = OCSP_PRODUCED_VALID,
Matt Mueller55aef642018-05-02 18:53:57 +0000453 ocsp_intermediate_url = None,
454 ocsp_intermediate_states = None,
455 ocsp_intermediate_dates = None,
456 ocsp_intermediate_produced = OCSP_PRODUCED_VALID,
rsleevia58a92d2017-03-03 19:07:20 -0800457 ip_sans = ["\x7F\x00\x00\x01"],
458 dns_sans = None,
agl@chromium.orgdf778142013-07-31 21:57:28 +0000459 serial = 0):
Matt Mueller55aef642018-05-02 18:53:57 +0000460 '''GenerateCertKeyAndOCSP returns a (cert_and_key_pem,
461 (ocsp_der, ocsp_intermediate_der)) where:
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000462 * cert_and_key_pem contains a certificate and private key in PEM format
463 with the given subject common name and OCSP URL.
Matt Mueller55aef642018-05-02 18:53:57 +0000464 It also contains the intermediate certificate PEM if
465 ocsp_intermediate_url is not None.
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000466 * ocsp_der contains a DER encoded OCSP response or None if ocsp_url is
Matt Mueller55aef642018-05-02 18:53:57 +0000467 None
468 * ocsp_intermediate_der contains a DER encoded OCSP response for the
469 intermediate or None if ocsp_intermediate_url is None'''
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000470
dadrian4ccf51c2016-07-20 15:36:58 -0700471 if ocsp_states is None:
472 ocsp_states = [OCSP_STATE_GOOD]
473 if ocsp_dates is None:
474 ocsp_dates = [OCSP_DATE_VALID]
475
Matt Mueller55aef642018-05-02 18:53:57 +0000476 issuer_cn = ROOT_CN
477 issuer_key = ROOT_KEY
478 intermediate_pem = ''
479 intermediate_ocsp_der = None
480
481 if ocsp_intermediate_url is not None:
482 ocsp_intermediate_url = bytes(ocsp_intermediate_url)
483 if ocsp_intermediate_states is None:
484 ocsp_intermediate_states = [OCSP_STATE_GOOD]
485 if ocsp_intermediate_dates is None:
486 ocsp_intermediate_dates = [OCSP_DATE_VALID]
487 intermediate_serial = RandomNumber(16)
488 intermediate_cn = "%s %X" % (INTERMEDIATE_CN_PREFIX, intermediate_serial)
489 intermediate_cert_der = MakeCertificate(ROOT_CN, intermediate_cn,
490 intermediate_serial,
491 INTERMEDIATE_KEY, ROOT_KEY,
492 ocsp_intermediate_url,
493 is_ca=True)
494 intermediate_pem = DERToPEM(intermediate_cert_der)
495 issuer_cn = intermediate_cn
496 issuer_key = INTERMEDIATE_KEY
497 intermediate_ocsp_der = MakeOCSPResponse(
498 ROOT_CN, ROOT_KEY, intermediate_serial, ocsp_intermediate_states,
499 ocsp_intermediate_dates, ocsp_intermediate_produced)
500
agl@chromium.orgdf778142013-07-31 21:57:28 +0000501 if serial == 0:
502 serial = RandomNumber(16)
Matt Mueller55aef642018-05-02 18:53:57 +0000503 if ocsp_url is not None:
504 ocsp_url = bytes(ocsp_url)
505 cert_der = MakeCertificate(issuer_cn, bytes(subject), serial, LEAF_KEY,
506 issuer_key, ocsp_url, ip_sans=ip_sans,
rsleevia58a92d2017-03-03 19:07:20 -0800507 dns_sans=dns_sans)
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000508 cert_pem = DERToPEM(cert_der)
509
510 ocsp_der = None
511 if ocsp_url is not None:
Matt Mueller55aef642018-05-02 18:53:57 +0000512 ocsp_der = MakeOCSPResponse(
513 issuer_cn, issuer_key, serial, ocsp_states, ocsp_dates, ocsp_produced)
agl@chromium.org77a9ad92012-03-20 15:14:27 +0000514
Matt Mueller55aef642018-05-02 18:53:57 +0000515 return cert_pem + LEAF_KEY_PEM + intermediate_pem, (ocsp_der,
516 intermediate_ocsp_der)
mattm10ede842016-11-29 11:57:16 -0800517
518
Ryan Sleevi3bfd15d2018-01-23 21:12:24 +0000519def GenerateCertKeyAndIntermediate(subject,
520 ca_issuers_url,
521 ip_sans=None,
522 dns_sans=None,
523 serial=0):
mattm10ede842016-11-29 11:57:16 -0800524 '''Returns a (cert_and_key_pem, intermediate_cert_pem) where:
525 * cert_and_key_pem contains a certificate and private key in PEM format
526 with the given subject common name and caIssuers URL.
527 * intermediate_cert_pem contains a PEM encoded certificate that signed
528 cert_and_key_pem and was signed by ocsp-test-root.pem.'''
529 if serial == 0:
530 serial = RandomNumber(16)
Ryan Sleevi3bfd15d2018-01-23 21:12:24 +0000531
Matt Mueller75863342018-05-01 22:58:47 +0000532 intermediate_serial = RandomNumber(16)
533 intermediate_cn = "%s %X" % (INTERMEDIATE_CN_PREFIX, intermediate_serial)
534
535 target_cert_der = MakeCertificate(intermediate_cn, bytes(subject), serial,
mattm10ede842016-11-29 11:57:16 -0800536 LEAF_KEY, INTERMEDIATE_KEY,
Ryan Sleevi3bfd15d2018-01-23 21:12:24 +0000537 ip_sans=ip_sans, dns_sans=dns_sans,
mattm10ede842016-11-29 11:57:16 -0800538 ca_issuers_url=bytes(ca_issuers_url))
539 target_cert_pem = DERToPEM(target_cert_der)
540
Matt Mueller75863342018-05-01 22:58:47 +0000541 intermediate_cert_der = MakeCertificate(ROOT_CN, intermediate_cn,
mattm10ede842016-11-29 11:57:16 -0800542 intermediate_serial,
543 INTERMEDIATE_KEY, ROOT_KEY,
544 is_ca=True)
545
546 return target_cert_pem + LEAF_KEY_PEM, intermediate_cert_der
mattmadace8e2016-10-03 14:07:15 -0700547
548
549if __name__ == '__main__':
550 def bin_to_array(s):
551 return ' '.join(['0x%02x,'%ord(c) for c in s])
552
553 import sys
554 sys.path.append(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..',
555 '..', 'data', 'ssl', 'scripts'))
556 import crlsetutil
557
mattm10ede842016-11-29 11:57:16 -0800558 der_root = MakeCertificate(ROOT_CN, ROOT_CN, 1, ROOT_KEY, ROOT_KEY,
559 is_ca=True, path_len=1)
mattmadace8e2016-10-03 14:07:15 -0700560 print 'ocsp-test-root.pem:'
561 print DERToPEM(der_root)
562
563 print
564 print 'kOCSPTestCertFingerprint:'
565 print bin_to_array(hashlib.sha1(der_root).digest())
566
567 print
568 print 'kOCSPTestCertSPKI:'
569 print bin_to_array(crlsetutil.der_cert_to_spki_hash(der_root))