Skip to content

Commit 32cc642

Browse files
committed
Validate experimental hybrid certificate data
1 parent 8dc230c commit 32cc642

1 file changed

Lines changed: 46 additions & 7 deletions

File tree

src/hybrid_pki/hybrid/hybrid_certificate.py

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,33 @@ def to_json(self) -> str:
7373

7474
@classmethod
7575
def from_dict(cls, data: dict) -> HybridCertificate:
76-
"""
77-
Create certificate from dictionary.
78-
"""
79-
return cls(**data)
76+
"""Create and validate a certificate from an untrusted dictionary."""
77+
expected_fields = set(cls.__dataclass_fields__)
78+
provided_fields = set(data)
79+
if provided_fields != expected_fields:
80+
missing = sorted(expected_fields - provided_fields)
81+
unknown = sorted(provided_fields - expected_fields)
82+
raise ValueError(f"Invalid hybrid certificate fields: missing={missing}, unknown={unknown}")
83+
84+
certificate = cls(**data)
85+
if certificate.version != 1:
86+
raise ValueError("Unsupported hybrid certificate version")
87+
required_text = (
88+
certificate.serial_number,
89+
certificate.subject,
90+
certificate.issuer,
91+
certificate.classical_algorithm,
92+
certificate.classical_public_key_pem,
93+
certificate.pqc_signature_algorithm,
94+
certificate.pqc_public_key_b64,
95+
)
96+
if not all(isinstance(value, str) and value.strip() for value in required_text):
97+
raise ValueError("Hybrid certificate contains an empty or invalid text field")
98+
99+
certificate.pqc_public_key
100+
_parse_aware_datetime(certificate.not_before)
101+
_parse_aware_datetime(certificate.not_after)
102+
return certificate
80103

81104
@classmethod
82105
def from_json(cls, data: str) -> HybridCertificate:
@@ -93,6 +116,16 @@ def pqc_public_key(self) -> bytes:
93116
return base64_to_bytes(self.pqc_public_key_b64)
94117

95118

119+
def _parse_aware_datetime(value: str) -> datetime:
120+
try:
121+
parsed = datetime.fromisoformat(value)
122+
except (TypeError, ValueError) as exc:
123+
raise ValueError("Invalid ISO-8601 certificate timestamp") from exc
124+
if parsed.tzinfo is None or parsed.utcoffset() is None:
125+
raise ValueError("Certificate timestamps must include a timezone")
126+
return parsed.astimezone(UTC)
127+
128+
96129
def generate_hybrid_serial_number(prefix: str = "HYB") -> str:
97130
"""
98131
Generate a random hybrid certificate serial number.
@@ -113,6 +146,9 @@ def create_unsigned_hybrid_certificate(
113146
"""
114147
Create an unsigned hybrid certificate.
115148
"""
149+
if not 1 <= days_valid <= 397:
150+
raise ValueError("days_valid must be between 1 and 397")
151+
116152
now = datetime.now(UTC)
117153

118154
return HybridCertificate(
@@ -156,7 +192,10 @@ def is_hybrid_certificate_time_valid(certificate: HybridCertificate) -> bool:
156192
Verify hybrid certificate validity period.
157193
"""
158194
now = datetime.now(UTC)
159-
not_before = datetime.fromisoformat(certificate.not_before)
160-
not_after = datetime.fromisoformat(certificate.not_after)
195+
try:
196+
not_before = _parse_aware_datetime(certificate.not_before)
197+
not_after = _parse_aware_datetime(certificate.not_after)
198+
except ValueError:
199+
return False
161200

162-
return not_before <= now <= not_after
201+
return not_before <= not_after and not_before <= now <= not_after

0 commit comments

Comments
 (0)