From 2ab1729a18a8ddaa62c16f07e2b320665bcec2f3 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Mon, 11 Nov 2019 13:17:46 -0800 Subject: [PATCH] CA: use mockable clock in CA's OCSP signer. (#4539) This brings OCSP signing into alignment with the other components of the CA in that they use ca.clk, which can be mocked out in unittests. This tweaks test_ocsp_exp_unauth to be compatible with the change. Fixes #4441. --- ca/ca.go | 11 +---------- test/v2_integration.py | 25 ++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/ca/ca.go b/ca/ca.go index 6f8d48f82..3f54f8cab 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -412,16 +412,7 @@ func (ca *CertificateAuthorityImpl) GenerateOCSP(ctx context.Context, xferObj co core.SerialToString(cert.SerialNumber), cn, err) } - // We use time.Now() rather than ca.clk.Now() here as we use openssl - // to verify the validity of the OCSP responses we generate during testing - // and openssl doesn't understand our concept of fake time so it thinks - // our responses are expired when we are doing things with our fake clock - // time set in the past. Since we don't rely on fake clock time for any - // OCSP unit tests always using the real time doesn't cause any problems. - // Currently the only integration test case that triggers this is - // ocsp_exp_unauth_setup, but any other integration test introduced that - // sets the fake clock into the past and verifies OCSP will also do so. - now := time.Now().Truncate(time.Hour) + now := ca.clk.Now().Truncate(time.Hour) tbsResponse := ocsp.Response{ Status: ocspStatusToCode[xferObj.Status], SerialNumber: cert.SerialNumber, diff --git a/test/v2_integration.py b/test/v2_integration.py index 0bf3a7f2a..b477086ae 100644 --- a/test/v2_integration.py +++ b/test/v2_integration.py @@ -1173,6 +1173,25 @@ def test_auth_deactivation_v2(): raise Exception("unexpected authorization status") +def check_ocsp_basic_oid(cert_file, issuer_file, url): + """ + This function checks if an OCSP response was successful, but doesn't verify + the signature or timestamp. This is useful when simulating the past, so we + don't incorrectly reject a response for being in the past. + """ + ocsp_request = make_ocsp_req(cert_file, issuer_file) + responses = fetch_ocsp(ocsp_request, url) + # An unauthorized response (for instance, if the OCSP responder doesn't know + # about this cert) will just be 30 03 0A 01 06. A "good" or "revoked" + # response will contain, among other things, the id-pkix-ocsp-basic OID + # identifying the response type. We look for that OID to confirm we got a + # succesful response. + expected = bytearray.fromhex("06 09 2B 06 01 05 05 07 30 01 01") + for resp in responses: + if not expected in bytearray(resp): + raise Exception("Did not receive successful OCSP response: %s doesn't contain %s" % + (base64.b64encode(resp), base64.b64encode(expected))) + expired_cert_name = "" @register_six_months_ago def ocsp_exp_unauth_setup(): @@ -1184,7 +1203,11 @@ def ocsp_exp_unauth_setup(): with open(cert_file_pem, "w") as f: f.write(OpenSSL.crypto.dump_certificate( OpenSSL.crypto.FILETYPE_PEM, cert).decode()) - verify_ocsp(cert_file_pem, "test/test-ca2.pem", "http://localhost:4002", "good") + + # Since we're pretending to be in the past, we'll get an expired OCSP + # response. Just check that it exists; don't do the full verification (which + # would fail). + check_ocsp_basic_oid(cert_file_pem, "test/test-ca2.pem", "http://localhost:4002") global expired_cert_name expired_cert_name = cert_file_pem