| Index: net/tools/testserver/minica.py
|
| diff --git a/net/tools/testserver/minica.py b/net/tools/testserver/minica.py
|
| index acf68fcbb935059182661b275a106bb3c74472ad..7d7b06b634d5e3873dacd26200f05416532ccdde 100644
|
| --- a/net/tools/testserver/minica.py
|
| +++ b/net/tools/testserver/minica.py
|
| @@ -3,9 +3,31 @@
|
| # found in the LICENSE file.
|
|
|
| import asn1
|
| +import datetime
|
| import hashlib
|
| +import itertools
|
| import os
|
| +import time
|
|
|
| +GENERALIZED_TIME_FORMAT = "%Y%m%d%H%M%SZ"
|
| +
|
| +OCSP_STATE_GOOD = 1
|
| +OCSP_STATE_REVOKED = 2
|
| +OCSP_STATE_INVALID_RESPONSE = 3
|
| +OCSP_STATE_UNAUTHORIZED = 4
|
| +OCSP_STATE_UNKNOWN = 5
|
| +OCSP_STATE_TRY_LATER = 6
|
| +OCSP_STATE_INVALID_RESPONSE_DATA = 7
|
| +OCSP_STATE_MISMATCHED_SERIAL = 8
|
| +
|
| +OCSP_DATE_VALID = 1
|
| +OCSP_DATE_OLD = 2
|
| +OCSP_DATE_EARLY = 3
|
| +OCSP_DATE_LONG = 4
|
| +
|
| +OCSP_PRODUCED_VALID = 1
|
| +OCSP_PRODUCED_BEFORE_CERT = 2
|
| +OCSP_PRODUCED_AFTER_CERT = 3
|
|
|
| # This file implements very minimal certificate and OCSP generation. It's
|
| # designed to test revocation checking.
|
| @@ -245,15 +267,8 @@ def MakeCertificate(
|
| asn1.BitString(privkey.Sign(tbsCert)),
|
| ]))
|
|
|
| -
|
| -def MakeOCSPResponse(issuer_cn, issuer_key, serial, ocsp_state):
|
| - # https://tools.ietf.org/html/rfc2560
|
| - issuer_name_hash = asn1.OCTETSTRING(
|
| - hashlib.sha1(asn1.ToDER(Name(cn = issuer_cn))).digest())
|
| -
|
| - issuer_key_hash = asn1.OCTETSTRING(
|
| - hashlib.sha1(asn1.ToDER(issuer_key)).digest())
|
| -
|
| +def MakeOCSPSingleResponse(
|
| + issuer_name_hash, issuer_key_hash, serial, ocsp_state, ocsp_date):
|
| cert_status = None
|
| if ocsp_state == OCSP_STATE_REVOKED:
|
| cert_status = asn1.Explicit(1, asn1.GeneralizedTime("20100101060000Z"))
|
| @@ -261,28 +276,85 @@ def MakeOCSPResponse(issuer_cn, issuer_key, serial, ocsp_state):
|
| cert_status = asn1.Raw(asn1.TagAndLength(0x80 | 2, 0))
|
| elif ocsp_state == OCSP_STATE_GOOD:
|
| cert_status = asn1.Raw(asn1.TagAndLength(0x80 | 0, 0))
|
| + elif ocsp_state == OCSP_STATE_MISMATCHED_SERIAL:
|
| + cert_status = asn1.Raw(asn1.TagAndLength(0x80 | 0, 0))
|
| + serial -= 1
|
| else:
|
| raise ValueError('Bad OCSP state: ' + str(ocsp_state))
|
|
|
| - basic_resp_data_der = asn1.ToDER(asn1.SEQUENCE([
|
| - asn1.Explicit(2, issuer_key_hash),
|
| - asn1.GeneralizedTime("20100101060000Z"), # producedAt
|
| - asn1.SEQUENCE([
|
| - asn1.SEQUENCE([ # SingleResponse
|
| - asn1.SEQUENCE([ # CertID
|
| - asn1.SEQUENCE([ # hashAlgorithm
|
| - HASH_SHA1,
|
| - None,
|
| - ]),
|
| - issuer_name_hash,
|
| - issuer_key_hash,
|
| - serial,
|
| - ]),
|
| - cert_status,
|
| - asn1.GeneralizedTime("20100101060000Z"), # thisUpdate
|
| - asn1.Explicit(0, asn1.GeneralizedTime("20300101060000Z")), # nextUpdate
|
| + now = datetime.datetime.fromtimestamp(time.mktime(time.gmtime()))
|
| + if ocsp_date == OCSP_DATE_VALID:
|
| + thisUpdate = now - datetime.timedelta(days=1)
|
| + nextUpdate = thisUpdate + datetime.timedelta(weeks=1)
|
| + elif ocsp_date == OCSP_DATE_OLD:
|
| + thisUpdate = now - datetime.timedelta(hours=1, weeks=1)
|
| + nextUpdate = thisUpdate + datetime.timedelta(weeks=1)
|
| + elif ocsp_date == OCSP_DATE_EARLY:
|
| + thisUpdate = now + datetime.timedelta(hours=1)
|
| + nextUpdate = thisUpdate + datetime.timedelta(weeks=1)
|
| + elif ocsp_date == OCSP_DATE_LONG:
|
| + thisUpdate = now - datetime.timedelta(days=365)
|
| + nextUpdate = thisUpdate + datetime.timedelta(hours=1, days=365)
|
| + elif ocsp_date == OCSP_DATE_BEFORE_CERT:
|
| + thisUpdate = now - datetime.timedelta(days=1)
|
| + nextUpdate = thisUpdate + datetime.timedelta(weeks=1)
|
| + elif ocsp_date == OCSP_DATE_AFTER_CERT:
|
| + thisUpdate = now - datetime.timedelta(days=1)
|
| + nextUpdate = thisUpdate + datetime.timedelta(weeks=1)
|
| + else:
|
| + raise ValueError('Bad OCSP date: ' + str(ocsp_date))
|
| +
|
| + return asn1.SEQUENCE([ # SingleResponse
|
| + asn1.SEQUENCE([ # CertID
|
| + asn1.SEQUENCE([ # hashAlgorithm
|
| + HASH_SHA1,
|
| + None,
|
| ]),
|
| + issuer_name_hash,
|
| + issuer_key_hash,
|
| + serial,
|
| ]),
|
| + cert_status,
|
| + asn1.GeneralizedTime( # thisUpdate
|
| + thisUpdate.strftime(GENERALIZED_TIME_FORMAT)
|
| + ),
|
| + asn1.Explicit( # nextUpdate
|
| + 0,
|
| + asn1.GeneralizedTime(nextUpdate.strftime(GENERALIZED_TIME_FORMAT))
|
| + ),
|
| + ])
|
| +
|
| +def MakeOCSPResponse(
|
| + issuer_cn, issuer_key, serial, ocsp_states, ocsp_dates, ocsp_produced):
|
| + # https://tools.ietf.org/html/rfc2560
|
| + issuer_name_hash = asn1.OCTETSTRING(
|
| + hashlib.sha1(asn1.ToDER(Name(cn = issuer_cn))).digest())
|
| +
|
| + issuer_key_hash = asn1.OCTETSTRING(
|
| + hashlib.sha1(asn1.ToDER(issuer_key)).digest())
|
| +
|
| + now = datetime.datetime.fromtimestamp(time.mktime(time.gmtime()))
|
| + if ocsp_produced == OCSP_PRODUCED_VALID:
|
| + producedAt = now - datetime.timedelta(days=1)
|
| + elif ocsp_produced == OCSP_PRODUCED_BEFORE_CERT:
|
| + producedAt = datetime.datetime.strptime(
|
| + "19100101050000Z", GENERALIZED_TIME_FORMAT)
|
| + elif ocsp_produced == OCSP_PRODUCED_AFTER_CERT:
|
| + producedAt = datetime.datetime.strptime(
|
| + "20321201070000Z", GENERALIZED_TIME_FORMAT)
|
| + else:
|
| + raise ValueError('Bad OCSP produced: ' + str(ocsp_produced))
|
| +
|
| + single_responses = [
|
| + MakeOCSPSingleResponse(issuer_name_hash, issuer_key_hash, serial,
|
| + ocsp_state, ocsp_date)
|
| + for ocsp_state, ocsp_date in itertools.izip(ocsp_states, ocsp_dates)
|
| + ]
|
| +
|
| + basic_resp_data_der = asn1.ToDER(asn1.SEQUENCE([
|
| + asn1.Explicit(2, issuer_key_hash),
|
| + asn1.GeneralizedTime(producedAt.strftime(GENERALIZED_TIME_FORMAT)),
|
| + asn1.SEQUENCE(single_responses),
|
| ]))
|
|
|
| basic_resp = asn1.SEQUENCE([
|
| @@ -311,19 +383,15 @@ def DERToPEM(der):
|
| pem += '-----END CERTIFICATE-----\n'
|
| return pem
|
|
|
| -OCSP_STATE_GOOD = 1
|
| -OCSP_STATE_REVOKED = 2
|
| -OCSP_STATE_INVALID = 3
|
| -OCSP_STATE_UNAUTHORIZED = 4
|
| -OCSP_STATE_UNKNOWN = 5
|
| -
|
| # unauthorizedDER is an OCSPResponse with a status of 6:
|
| # SEQUENCE { ENUM(6) }
|
| unauthorizedDER = '30030a0106'.decode('hex')
|
|
|
| def GenerateCertKeyAndOCSP(subject = "127.0.0.1",
|
| ocsp_url = "http://127.0.0.1",
|
| - ocsp_state = OCSP_STATE_GOOD,
|
| + ocsp_states = None,
|
| + ocsp_dates = None,
|
| + ocsp_produced = OCSP_PRODUCED_VALID,
|
| serial = 0):
|
| '''GenerateCertKeyAndOCSP returns a (cert_and_key_pem, ocsp_der) where:
|
| * cert_and_key_pem contains a certificate and private key in PEM format
|
| @@ -331,6 +399,11 @@ def GenerateCertKeyAndOCSP(subject = "127.0.0.1",
|
| * ocsp_der contains a DER encoded OCSP response or None if ocsp_url is
|
| None'''
|
|
|
| + if ocsp_states is None:
|
| + ocsp_states = [OCSP_STATE_GOOD]
|
| + if ocsp_dates is None:
|
| + ocsp_dates = [OCSP_DATE_VALID]
|
| +
|
| if serial == 0:
|
| serial = RandomNumber(16)
|
| cert_der = MakeCertificate(ISSUER_CN, bytes(subject), serial, KEY, KEY,
|
| @@ -339,11 +412,35 @@ def GenerateCertKeyAndOCSP(subject = "127.0.0.1",
|
|
|
| ocsp_der = None
|
| if ocsp_url is not None:
|
| - if ocsp_state == OCSP_STATE_UNAUTHORIZED:
|
| + if ocsp_states[0] == OCSP_STATE_UNAUTHORIZED:
|
| ocsp_der = unauthorizedDER
|
| - elif ocsp_state == OCSP_STATE_INVALID:
|
| + elif ocsp_states[0] == OCSP_STATE_INVALID_RESPONSE:
|
| ocsp_der = '3'
|
| + elif ocsp_states[0] == OCSP_STATE_TRY_LATER:
|
| + resp = asn1.SEQUENCE([
|
| + asn1.ENUMERATED(3),
|
| + ])
|
| + ocsp_der = asn1.ToDER(resp)
|
| + elif ocsp_states[0] == OCSP_STATE_INVALID_RESPONSE_DATA:
|
| + invalid_data = asn1.ToDER(asn1.OCTETSTRING('not ocsp data'))
|
| + basic_resp = asn1.SEQUENCE([
|
| + asn1.Raw(invalid_data),
|
| + asn1.SEQUENCE([
|
| + SHA256_WITH_RSA_ENCRYPTION,
|
| + None,
|
| + ]),
|
| + asn1.BitString(KEY.Sign(invalid_data)),
|
| + ])
|
| + resp = asn1.SEQUENCE([
|
| + asn1.ENUMERATED(0),
|
| + asn1.Explicit(0, asn1.SEQUENCE([
|
| + OCSP_TYPE_BASIC,
|
| + asn1.OCTETSTRING(asn1.ToDER(basic_resp)),
|
| + ])),
|
| + ])
|
| + ocsp_der = asn1.ToDER(resp)
|
| else:
|
| - ocsp_der = MakeOCSPResponse(ISSUER_CN, KEY, serial, ocsp_state)
|
| + ocsp_der = MakeOCSPResponse(
|
| + ISSUER_CN, KEY, serial, ocsp_states, ocsp_dates, ocsp_produced)
|
|
|
| return (cert_pem + KEY_PEM, ocsp_der)
|
|
|