Skip to content

Commit

Permalink
Allow generating OCSP responses without nextUpdate
Browse files Browse the repository at this point in the history
  • Loading branch information
MatthiasValvekens committed May 14, 2023
1 parent e286c25 commit 3700f24
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 13 deletions.
1 change: 1 addition & 0 deletions certomancer/registry/pki_arch.py
Original file line number Diff line number Diff line change
Expand Up @@ -1156,6 +1156,7 @@ def summon_responder(
issuer_cert_label=issuer_cert_label,
is_aa_responder=info.is_aa_responder,
),
validity=info.validity_period,
response_extensions=extra_extensions,
)

Expand Down
17 changes: 16 additions & 1 deletion certomancer/registry/svc_config/ocsp.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from dataclasses import dataclass, field
from datetime import datetime
from datetime import datetime, timedelta
from typing import TYPE_CHECKING, List, Optional, Tuple

from asn1crypto import ocsp, x509
Expand Down Expand Up @@ -76,13 +76,28 @@ class OCSPResponderServiceInfo(ServiceInfo):
or regular certificates.
"""

validity_period: Optional[timedelta] = timedelta(minutes=10)
"""
Validity period for the OCSP response (i.e. time until ``nextUpdate``).
Defaults to ten minutes. ``None`` implies no ``nextUpdate``.
"""

@classmethod
def process_entries(cls, config_dict):
try:
config_dict.setdefault('signing_key', config_dict['responder_cert'])
except KeyError:
pass

try:
period_kwargs = config_dict['validity_period']
if not period_kwargs:
config_dict['validity_period'] = None
else:
config_dict['validity_period'] = timedelta(**period_kwargs)
except KeyError:
pass

parse_extension_settings(config_dict, 'ocsp_extensions')

def resolve_issuer_cert(self, arch: 'PKIArchitecture') -> CertLabel:
Expand Down
21 changes: 10 additions & 11 deletions certomancer/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ def __init__(
signature_algo: algos.SignedDigestAlgorithm,
at_time: datetime,
revinfo_interface: RevocationInfoInterface,
validity: timedelta = timedelta(minutes=10),
validity: Optional[timedelta] = timedelta(minutes=10),
response_extensions: Optional[List[ocsp.ResponseDataExtension]] = None,
):
self.responder_cert = responder_cert
Expand All @@ -380,16 +380,15 @@ def format_single_ocsp_response(
cid, self.at_time
)

single_resp = ocsp.SingleResponse(
{
'cert_id': cid,
'cert_status': cert_status,
'this_update': self.at_time,
'next_update': self.at_time + self.validity,
'single_extensions': exts or None,
}
)
return single_resp
response_data = {
'cert_id': cid,
'cert_status': cert_status,
'this_update': self.at_time,
'single_extensions': exts or None,
}
if self.validity is not None:
response_data['next_update'] = self.at_time + self.validity
return ocsp.SingleResponse(response_data)

def build_ocsp_response(self, req: ocsp.OCSPRequest) -> ocsp.OCSPResponse:
nonce_asn1 = req.nonce_value
Expand Down
5 changes: 5 additions & 0 deletions tests/data/full-service-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ services:
for-issuer: interm
responder-cert: interm-ocsp
signing-key: interm-ocsp
interm2:
for-issuer: interm
responder-cert: interm-ocsp
signing-key: interm-ocsp
validity-period: {}
crl-repo:
root:
for-issuer: root
Expand Down
1 change: 1 addition & 0 deletions tests/data/with-services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,4 @@ pki-architectures:
responder-cert: role-aa
signing-key: aa
is-aa-responder: true
validity-period: {minutes: 2}
26 changes: 25 additions & 1 deletion tests/test_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import importlib
import itertools
from collections import namedtuple
from datetime import datetime
from datetime import datetime, timedelta, timezone

import pytest
import pytz
Expand Down Expand Up @@ -210,6 +210,27 @@ def test_ocsp(requests_mock, time, expected):
assert status == expected


@pytest.mark.parametrize(
"time, expected", [('2020-11-05', 'good'), ('2020-12-05', 'revoked')]
)
def test_ocsp_without_nextupdate(requests_mock, time, expected):
setup = RSA_SETUP
setup.illusionist.register(requests_mock)
with open('tests/data/signer2-ocsp-req.der', 'rb') as req_in:
req_data = req_in.read()
with freeze_time(time):
response = requests.post(
"http://test.test/testing-ca/ocsp/interm2", data=req_data
)
resp: ocsp.OCSPResponse = ocsp.OCSPResponse.load(response.content)
assert resp['response_status'].native == 'successful'

rdata = resp['response_bytes']['response'].parsed['tbs_response_data']
status = rdata['responses'][0]['cert_status'].name
assert rdata['responses'][0]['next_update'].native is None
assert status == expected


@pytest.mark.parametrize(
"time, expected", [('2020-11-05', 'good'), ('2020-12-05', 'revoked')]
)
Expand All @@ -236,6 +257,9 @@ def test_aa_ocsp(requests_mock, time, expected):
rdata = resp['response_bytes']['response'].parsed['tbs_response_data']
status = rdata['responses'][0]['cert_status'].name
assert status == expected
this_update = rdata['responses'][0]['this_update'].native
next_update = rdata['responses'][0]['next_update'].native
assert next_update == this_update + timedelta(minutes=2)


@freeze_time('2020-11-01')
Expand Down

0 comments on commit 3700f24

Please sign in to comment.