1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
|
# coding: utf-8
from datetime import datetime
from typing import List, Optional, Type, TypeVar
from asn1crypto.crl import CRLReason
from cryptography.exceptions import InvalidSignature
from pyhanko_certvalidator._state import ValProcState
from pyhanko_certvalidator.path import ValidationPath
class PathError(Exception):
pass
class PathBuildingError(PathError):
pass
class CertificateFetchError(PathBuildingError):
pass
class CRLValidationError(Exception):
pass
class CRLNoMatchesError(CRLValidationError):
pass
class CRLFetchError(CRLValidationError):
pass
class CRLValidationIndeterminateError(CRLValidationError):
def __init__(
self,
msg: str,
failures: List[str],
suspect_stale: Optional[datetime] = None,
):
self.msg = msg
self.failures = failures
self.suspect_stale = suspect_stale
super().__init__(msg, failures)
class OCSPValidationError(Exception):
pass
class OCSPNoMatchesError(OCSPValidationError):
pass
class OCSPValidationIndeterminateError(OCSPValidationError):
def __init__(
self,
msg: str,
failures: List[str],
suspect_stale: Optional[datetime] = None,
):
self.msg = msg
self.failures = failures
self.suspect_stale = suspect_stale
super().__init__(msg, failures)
class OCSPFetchError(OCSPValidationError):
pass
class ValidationError(Exception):
def __init__(self, message: str):
self.failure_msg = message
super().__init__(message)
TPathErr = TypeVar('TPathErr', bound='PathValidationError')
class PathValidationError(ValidationError):
@classmethod
def from_state(
cls: Type[TPathErr], msg: str, proc_state: ValProcState
) -> TPathErr:
return cls(msg, proc_state=proc_state)
def __init__(self, msg: str, *, proc_state: ValProcState):
self.is_ee_cert = proc_state.is_ee_cert
self.is_side_validation = proc_state.is_side_validation
current = proc_state.cert_path_stack.head
orig = proc_state.cert_path_stack.last
assert current is not None and orig is not None
self.current_path: ValidationPath = current
self.original_path: ValidationPath = orig
super().__init__(msg)
class RevokedError(PathValidationError):
@classmethod
def format(
cls,
reason: CRLReason,
revocation_dt: datetime,
revinfo_type: str,
proc_state: ValProcState,
):
reason_str = reason.human_friendly
date = revocation_dt.strftime('%Y-%m-%d')
time = revocation_dt.strftime('%H:%M:%S')
msg = (
f'{revinfo_type} indicates {proc_state.describe_cert()} '
f'was revoked at {time} on {date}, due to {reason_str}.'
)
return RevokedError(msg, reason, revocation_dt, proc_state)
def __init__(
self,
msg,
reason: CRLReason,
revocation_dt: datetime,
proc_state: ValProcState,
):
self.reason = reason
self.revocation_dt = revocation_dt
super().__init__(msg, proc_state=proc_state)
class InsufficientRevinfoError(PathValidationError):
pass
class StaleRevinfoError(InsufficientRevinfoError):
@classmethod
def format(
cls,
msg: str,
time_cutoff: datetime,
proc_state: ValProcState,
):
return StaleRevinfoError(msg, time_cutoff, proc_state)
def __init__(
self, msg: str, time_cutoff: datetime, proc_state: ValProcState
):
self.time_cutoff = time_cutoff
super().__init__(msg, proc_state=proc_state)
class InsufficientPOEError(PathValidationError):
pass
class ExpiredError(PathValidationError):
@classmethod
def format(
cls,
*,
expired_dt: datetime,
proc_state: ValProcState,
):
msg = (
f"The path could not be validated because "
f"{proc_state.describe_cert()} expired "
f"{expired_dt.strftime('%Y-%m-%d %H:%M:%SZ')}"
)
return ExpiredError(msg, expired_dt, proc_state)
def __init__(self, msg, expired_dt: datetime, proc_state: ValProcState):
self.expired_dt = expired_dt
super().__init__(msg, proc_state=proc_state)
class NotYetValidError(PathValidationError):
@classmethod
def format(
cls,
*,
valid_from: datetime,
proc_state: ValProcState,
):
msg = (
f"The path could not be validated because "
f"{proc_state.describe_cert()} is not valid until "
f"{valid_from.strftime('%Y-%m-%d %H:%M:%SZ')}"
)
return NotYetValidError(msg, valid_from, proc_state)
def __init__(self, msg, valid_from: datetime, proc_state: ValProcState):
self.valid_from = valid_from
super().__init__(msg, proc_state=proc_state)
class InvalidCertificateError(ValidationError):
pass
class DisallowedAlgorithmError(PathValidationError):
def __init__(
self, *args, banned_since: Optional[datetime] = None, **kwargs
):
self.banned_since = banned_since
super().__init__(*args, **kwargs)
@classmethod
def from_state(
cls,
msg: str,
proc_state: ValProcState,
banned_since: Optional[datetime] = None,
) -> 'DisallowedAlgorithmError':
return cls(msg, banned_since=banned_since, proc_state=proc_state)
class InvalidAttrCertificateError(InvalidCertificateError):
pass
class PSSParameterMismatch(InvalidSignature):
pass
class DSAParametersUnavailable(InvalidSignature):
# TODO Technically, such a signature isn't _really_ invalid
# (we merely couldn't validate it).
# However, this is only an issue for CRLs and OCSP responses that
# make use of DSA parameter inheritance, which is pretty much a
# completely irrelevant problem in this day and age, so treating those
# signatures as invalid as a matter of course seems pretty much OK.
pass
|