Skip to content

Commit 018f117

Browse files
committed
Ref #1641 - Detect absence of OCSP in OCSP response evaluation
1 parent 29c3d85 commit 018f117

File tree

7 files changed

+76
-24
lines changed

7 files changed

+76
-24
lines changed

checks/categories.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1467,6 +1467,11 @@ def result_not_trusted(self):
14671467
self.verdict = "detail web tls ocsp-stapling verdict bad"
14681468
self.tech_data = "detail tech data no"
14691469

1470+
def result_not_in_cert(self):
1471+
self._status(STATUS_SUCCESS)
1472+
self.verdict = "detail web tls ocsp-stapling verdict not-in-cert"
1473+
self.tech_data = "detail tech data not-in-cert"
1474+
14701475

14711476
class WebTlsKexHashFunc(Subtest):
14721477
def __init__(self):

checks/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ class OcspStatus(Enum):
8686
ok = 0
8787
good = 1
8888
not_trusted = 2
89+
not_in_cert = 3
8990

9091

9192
class ZeroRttStatus(Enum):

checks/tasks/tls/evaluation.py

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
from dataclasses import dataclass
2-
from typing import List, Optional, Any, Set
2+
from typing import List, Optional, Any, Set, cast
33

4+
from cryptography.hazmat._oid import AuthorityInformationAccessOID, ExtensionOID
5+
from cryptography.x509 import AuthorityInformationAccess, ExtensionNotFound
46
from nassl.ephemeral_key_info import EcDhEphemeralKeyInfo, DhEphemeralKeyInfo, OpenSslEvpPkeyEnum
5-
from sslyze import TlsVersionEnum, CipherSuiteAcceptedByServer, CipherSuite
7+
from sslyze import TlsVersionEnum, CipherSuiteAcceptedByServer, CipherSuite, CertificateDeploymentAnalysisResult
68
from sslyze.plugins.openssl_cipher_suites.cipher_suites import _TLS_1_3_CIPHER_SUITES
79

810
from checks import scoring
9-
from checks.models import KexHashFuncStatus, CipherOrderStatus
11+
from checks.models import KexHashFuncStatus, CipherOrderStatus, OcspStatus
1012
from checks.tasks.tls.tls_constants import (
1113
PROTOCOLS_GOOD,
1214
PROTOCOLS_SUFFICIENT,
@@ -197,6 +199,58 @@ def score(self) -> scoring.Score:
197199
return scoring.WEB_TLS_SUITES_BAD if self.ciphers_bad else scoring.WEB_TLS_SUITES_GOOD
198200

199201

202+
@dataclass(frozen=True)
203+
class TLSOCSPEvaluation:
204+
"""
205+
Evaluate the OCSP setup, based on certificate info.
206+
"""
207+
208+
ocsp_in_cert: bool
209+
has_ocsp_response: bool
210+
ocsp_response_trusted: bool
211+
212+
GOOD_STATUSES = {OcspStatus.good, OcspStatus.not_in_cert}
213+
214+
@classmethod
215+
def from_certificate_deployments(cls, certificate_deployment: CertificateDeploymentAnalysisResult):
216+
leaf_cert = certificate_deployment.received_certificate_chain[0]
217+
try:
218+
aia_extension = leaf_cert.extensions.get_extension_for_oid(ExtensionOID.AUTHORITY_INFORMATION_ACCESS)
219+
aia_value = cast(AuthorityInformationAccess, aia_extension.value)
220+
ocsp_access = [ad for ad in aia_value if ad.access_method == AuthorityInformationAccessOID.OCSP]
221+
ocsp_in_cert = len(ocsp_access) > 0
222+
except ExtensionNotFound:
223+
ocsp_in_cert = False
224+
225+
has_ocsp_response = certificate_deployment.ocsp_response is not None
226+
ocsp_response_trusted = certificate_deployment.ocsp_response is True
227+
228+
return cls(
229+
ocsp_in_cert=ocsp_in_cert,
230+
has_ocsp_response=has_ocsp_response,
231+
ocsp_response_trusted=ocsp_response_trusted,
232+
)
233+
234+
@property
235+
def status(self) -> OcspStatus:
236+
if not self.ocsp_in_cert:
237+
return OcspStatus.not_in_cert
238+
if self.has_ocsp_response:
239+
if self.ocsp_response_trusted:
240+
return OcspStatus.good
241+
else:
242+
return OcspStatus.not_trusted
243+
return OcspStatus.ok
244+
245+
@property
246+
def score(self) -> scoring.Score:
247+
return (
248+
scoring.WEB_TLS_OCSP_STAPLING_GOOD
249+
if self.status in self.GOOD_STATUSES
250+
else scoring.WEB_TLS_OCSP_STAPLING_BAD
251+
)
252+
253+
200254
@dataclass(frozen=True)
201255
class KeyExchangeHashFunctionEvaluation:
202256
"""

checks/tasks/tls/scans.py

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from enum import Enum
44
from pathlib import Path
55
from ssl import match_hostname, CertificateError
6-
from typing import List, Tuple, Generator, Dict, Any, Optional
6+
from typing import Any, Dict, Generator, List, Optional, Tuple
77

88
import subprocess
99
from cryptography.hazmat._oid import NameOID
@@ -54,7 +54,6 @@
5454
DaneStatus,
5555
ZeroRttStatus,
5656
KexHashFuncStatus,
57-
OcspStatus,
5857
CipherOrderStatus,
5958
)
6059
from checks.tasks.shared import resolve_dane
@@ -65,6 +64,7 @@
6564
TLSCipherEvaluation,
6665
KeyExchangeHashFunctionEvaluation,
6766
TLSCipherOrderEvaluation,
67+
TLSOCSPEvaluation,
6868
)
6969
from checks.tasks.tls.tls_constants import (
7070
CERT_SIGALG_GOOD,
@@ -728,18 +728,9 @@ def check_web_tls(url, af_ip_pair=None, *args, **kwargs):
728728
),
729729
)
730730

731-
ocsp_status = OcspStatus.ok
732-
if any(
733-
[d.ocsp_response_is_trusted is True for d in result.scan_result.certificate_info.result.certificate_deployments]
734-
):
735-
ocsp_status = OcspStatus.good
736-
elif any(
737-
[
738-
d.ocsp_response_is_trusted is False
739-
for d in result.scan_result.certificate_info.result.certificate_deployments
740-
]
741-
):
742-
ocsp_status = OcspStatus.not_trusted
731+
ocsp_evaluation = TLSOCSPEvaluation.from_certificate_deployments(
732+
result.scan_result.certificate_info.result.certificate_deployments[0]
733+
)
743734

744735
probe_result = dict(
745736
tls_enabled=True,
@@ -787,10 +778,8 @@ def check_web_tls(url, af_ip_pair=None, *args, **kwargs):
787778
if result.scan_result.tls_1_3_early_data.result.supports_early_data
788779
else scoring.WEB_TLS_ZERO_RTT_GOOD
789780
),
790-
ocsp_stapling=ocsp_status,
791-
ocsp_stapling_score=(
792-
scoring.WEB_TLS_OCSP_STAPLING_GOOD if ocsp_status == OcspStatus.good else scoring.WEB_TLS_OCSP_STAPLING_BAD
793-
),
781+
ocsp_stapling=ocsp_evaluation.status,
782+
ocsp_stapling_score=ocsp_evaluation.score,
794783
kex_hash_func=key_exchange_hash_evaluation.status,
795784
kex_hash_func_score=key_exchange_hash_evaluation.score,
796785
)

checks/tasks/tls/tasks_reports.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,8 @@ def annotate_and_combine_all(good_items, sufficient_items, bad_items, phaseout_i
549549
category.subtests["ocsp_stapling"].result_not_trusted()
550550
elif dttls.ocsp_stapling == OcspStatus.ok:
551551
category.subtests["ocsp_stapling"].result_ok()
552+
elif dttls.ocsp_stapling == OcspStatus.not_in_cert:
553+
category.subtests["ocsp_stapling"].result_not_in_cert()
552554

553555
if dttls.kex_hash_func == KexHashFuncStatus.good:
554556
category.subtests["kex_hash_func"].result_good()

integration_tests/batch/results.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"web_https_dane_valid": {"status": "not_tested", "verdict": "not-tested"},
4040
"web_https_tls_0rtt": {"status": "passed", "verdict": "good"},
4141
"web_https_tls_caa": {"status": "warning", "verdict": "bad"},
42-
"web_https_tls_ocsp": {"status": "info", "verdict": "ok"},
42+
"web_https_tls_ocsp": {"status": "passed", "verdict": "not-in-cert"},
4343
"web_https_tls_keyexchangehash": {"status": "passed", "verdict": "good"},
4444
"web_appsecpriv_x_frame_options": {"status": "passed", "verdict": "good"},
4545
"web_appsecpriv_referrer_policy": {"status": "passed", "verdict": "good"},
@@ -80,7 +80,7 @@
8080
"secure_reneg": True,
8181
"client_reneg": False,
8282
"zero_rtt": "good",
83-
"ocsp_stapling": "ok",
83+
"ocsp_stapling": "not_in_cert",
8484
"kex_hash_func": "good",
8585
"https_redirect": "good",
8686
"http_compression": False,
@@ -144,7 +144,7 @@
144144
"secure_reneg": True,
145145
"client_reneg": False,
146146
"zero_rtt": "good",
147-
"ocsp_stapling": "ok",
147+
"ocsp_stapling": "not_in_cert",
148148
"kex_hash_func": "good",
149149
"https_redirect": "good",
150150
"http_compression": False,

interface/batch/openapi.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,7 @@ components:
821821
* `good` - OCSP is supported.
822822
* `ok` - OCSP is not supported,
823823
* `not_trusted` - OCSP is supported but the returned data is invalid.
824+
* `not_in_cert` - OCSP not enabled in the certificate, therefore OCSP stapling is not required or possible.
824825
825826
x_frame_options_enabled:
826827
type: boolean

0 commit comments

Comments
 (0)