Skip to content

Commit 14002c5

Browse files
committed
Fix certificate download freshness and rebuild QR verification flow
Add no-cache handling for certificate and PDF responses to prevent stale downloads. Rework certificate PDF generation to better match the certificate view. Rebuild the QR verification web app flow so scan, upload, and link verification paths redirect into result.html with a simplified UI.
1 parent 6d624b9 commit 14002c5

14 files changed

Lines changed: 759 additions & 2230 deletions

app/app.py

Lines changed: 217 additions & 221 deletions
Large diffs are not rendered by default.

brainstrom.txt

Lines changed: 0 additions & 197 deletions
This file was deleted.

core/crypto_utils.py

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -144,14 +144,27 @@ def verify_signature(self, data, signature):
144144
logging.debug(f"Signature verification failed: {str(e)}")
145145
return False
146146

147+
@staticmethod
148+
def _compact_json(data):
149+
"""Serialize JSON without extra whitespace for shorter QR payloads."""
150+
return json.dumps(data, sort_keys=True, separators=(',', ':'))
151+
152+
@staticmethod
153+
def _jws_hash_algorithm():
154+
return hashes.SHA256()
155+
156+
@classmethod
157+
def _jws_standard_salt_length(cls):
158+
return cls._jws_hash_algorithm().digest_size
159+
147160
def sign_jws(self, data):
148161
"""Create a JWS-compact style signature (Header.Payload.Signature)"""
149162
try:
150163
header = {"alg": "PS256", "typ": "JWS"}
151-
header_b64 = base64.urlsafe_b64encode(json.dumps(header, sort_keys=True).encode()).decode().rstrip('=')
164+
header_b64 = base64.urlsafe_b64encode(self._compact_json(header).encode()).decode().rstrip('=')
152165

153166
if isinstance(data, dict):
154-
payload_string = json.dumps(data, sort_keys=True)
167+
payload_string = self._compact_json(data)
155168
else:
156169
payload_string = str(data)
157170
payload_b64 = base64.urlsafe_b64encode(payload_string.encode()).decode().rstrip('=')
@@ -161,10 +174,10 @@ def sign_jws(self, data):
161174
signature = self.private_key.sign(
162175
signing_input.encode(),
163176
padding.PSS(
164-
mgf=padding.MGF1(hashes.SHA256()),
165-
salt_length=padding.PSS.MAX_LENGTH
177+
mgf=padding.MGF1(self._jws_hash_algorithm()),
178+
salt_length=self._jws_standard_salt_length()
166179
),
167-
hashes.SHA256()
180+
self._jws_hash_algorithm()
168181
)
169182
signature_b64 = base64.urlsafe_b64encode(signature).decode().rstrip('=')
170183

@@ -189,17 +202,24 @@ def pad_b64(s):
189202

190203
signature = base64.urlsafe_b64decode(pad_b64(signature_b64))
191204
payload_json = json.loads(base64.urlsafe_b64decode(pad_b64(payload_b64)).decode())
192-
193-
self.public_key.verify(
194-
signature,
195-
signing_input.encode(),
196-
padding.PSS(
197-
mgf=padding.MGF1(hashes.SHA256()),
198-
salt_length=padding.PSS.MAX_LENGTH
199-
),
200-
hashes.SHA256()
201-
)
202-
return True, payload_json
205+
206+
last_error = None
207+
for salt_length in (self._jws_standard_salt_length(), padding.PSS.MAX_LENGTH):
208+
try:
209+
self.public_key.verify(
210+
signature,
211+
signing_input.encode(),
212+
padding.PSS(
213+
mgf=padding.MGF1(self._jws_hash_algorithm()),
214+
salt_length=salt_length
215+
),
216+
self._jws_hash_algorithm()
217+
)
218+
return True, payload_json
219+
except Exception as verify_error:
220+
last_error = verify_error
221+
222+
raise last_error or ValueError("JWS verification failed")
203223
except Exception as e:
204224
logging.debug(f"JWS verification failed: {str(e)}")
205225
return False, None

0 commit comments

Comments
 (0)