smdpp: proper checks, proper check order, test mode

Change-Id: Ie4db65594b2eaf6c95ffc5e73ff9ae61c4d9d3a3
This commit is contained in:
Eric Wild
2025-06-25 10:22:42 +02:00
committed by Your Name
parent 0d24f35776
commit 4e27b5107b
3 changed files with 839 additions and 107 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -25,6 +25,7 @@ from cryptography.hazmat.primitives.serialization import load_pem_private_key, E
from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature
from pySim.utils import b2h from pySim.utils import b2h
from . import x509_err
def check_signed(signed: x509.Certificate, signer: x509.Certificate) -> bool: def check_signed(signed: x509.Certificate, signer: x509.Certificate) -> bool:
"""Verify if 'signed' certificate was signed using 'signer'.""" """Verify if 'signed' certificate was signed using 'signer'."""
@@ -64,9 +65,6 @@ class oid:
id_rspRole_ds_tls_v2 = x509.ObjectIdentifier(ID_RSP_ROLE + '.6') id_rspRole_ds_tls_v2 = x509.ObjectIdentifier(ID_RSP_ROLE + '.6')
id_rspRole_ds_auth_v2 = x509.ObjectIdentifier(ID_RSP_ROLE + '.7') id_rspRole_ds_auth_v2 = x509.ObjectIdentifier(ID_RSP_ROLE + '.7')
class VerifyError(Exception):
"""An error during certificate verification,"""
class CertificateSet: class CertificateSet:
"""A set of certificates consisting of a trusted [self-signed] CA root certificate, """A set of certificates consisting of a trusted [self-signed] CA root certificate,
and an optional number of intermediate certificates. Can be used to verify the certificate chain and an optional number of intermediate certificates. Can be used to verify the certificate chain
@@ -135,7 +133,7 @@ class CertificateSet:
# we cannot check if there's no CRL # we cannot check if there's no CRL
return return
if self.crl.get_revoked_certificate_by_serial_number(cert.serial_nr): if self.crl.get_revoked_certificate_by_serial_number(cert.serial_nr):
raise VerifyError('Certificate is present in CRL, verification failed') raise x509_err.CertificateRevoked()
def verify_cert_chain(self, cert: x509.Certificate, max_depth: int = 100): def verify_cert_chain(self, cert: x509.Certificate, max_depth: int = 100):
"""Verify if a given certificate's signature chain can be traced back to the root CA of this """Verify if a given certificate's signature chain can be traced back to the root CA of this
@@ -150,13 +148,13 @@ class CertificateSet:
return return
parent_cert = self.intermediate_certs.get(aki, None) parent_cert = self.intermediate_certs.get(aki, None)
if not parent_cert: if not parent_cert:
raise VerifyError('Could not find intermediate certificate for AuthKeyId %s' % b2h(aki)) raise x509_err.MissingIntermediateCert(b2h(aki))
check_signed(c, parent_cert) check_signed(c, parent_cert)
# if we reach here, we passed (no exception raised) # if we reach here, we passed (no exception raised)
c = parent_cert c = parent_cert
depth += 1 depth += 1
if depth > max_depth: if depth > max_depth:
raise VerifyError('Maximum depth %u exceeded while verifying certificate chain' % max_depth) raise x509_err.MaxDepthExceeded(max_depth, depth)
def ecdsa_dss_to_tr03111(sig: bytes) -> bytes: def ecdsa_dss_to_tr03111(sig: bytes) -> bytes:

58
pySim/esim/x509_err.py Normal file
View File

@@ -0,0 +1,58 @@
"""X.509 certificate verification exceptions for GSMA eSIM."""
class VerifyError(Exception):
"""Base class for certificate verification errors."""
pass
class MissingIntermediateCert(VerifyError):
"""Raised when an intermediate certificate in the chain cannot be found."""
def __init__(self, auth_key_id: str):
self.auth_key_id = auth_key_id
super().__init__(f'Could not find intermediate certificate for AuthKeyId {auth_key_id}')
class CertificateRevoked(VerifyError):
"""Raised when a certificate is found in the CRL."""
def __init__(self, cert_serial: str = None):
self.cert_serial = cert_serial
msg = 'Certificate is present in CRL, verification failed'
if cert_serial:
msg += f' (serial: {cert_serial})'
super().__init__(msg)
class MaxDepthExceeded(VerifyError):
"""Raised when certificate chain depth exceeds the maximum allowed."""
def __init__(self, max_depth: int, actual_depth: int):
self.max_depth = max_depth
self.actual_depth = actual_depth
super().__init__(f'Maximum depth {max_depth} exceeded while verifying certificate chain (actual: {actual_depth})')
class SignatureVerification(VerifyError):
"""Raised when certificate signature verification fails."""
def __init__(self, cert_subject: str = None, signer_subject: str = None):
self.cert_subject = cert_subject
self.signer_subject = signer_subject
msg = 'Certificate signature verification failed'
if cert_subject and signer_subject:
msg += f': {cert_subject} not signed by {signer_subject}'
super().__init__(msg)
class InvalidCertificate(VerifyError):
"""Raised when a certificate is invalid (missing required fields, wrong type, etc)."""
def __init__(self, reason: str):
self.reason = reason
super().__init__(f'Invalid certificate: {reason}')
class CertificateExpired(VerifyError):
"""Raised when a certificate has expired."""
def __init__(self, cert_subject: str = None):
self.cert_subject = cert_subject
msg = 'Certificate has expired'
if cert_subject:
msg += f': {cert_subject}'
super().__init__(msg)