alitheg opened a new issue, #1417:
URL: https://github.com/apache/tooling-trusted-releases/issues/1417

   <div class="markdown-heading" dir="auto"><h4 tabindex="-1" 
class="heading-element" dir="auto">Expired signing keys accepted during 
signature verification; OpenPGP revocation handling not explicit or 
tested</h4></div>
   <markdown-accessiblity-table data-catalyst="">
   Attribute | Value
   -- | --
   Severity | 🔴 Critical
   ASVS Level(s) | L2, L3
   CWE | —
   ASVS Sections | 11.6.1, 4.1.5
   Files | atr/tasks/checks/signature.py, atr/storage/writers/keys.py, 
atr/get/keys.py
   Source Reports | 11.6.1.md, 4.1.5.md
   Related | —
   
   </markdown-accessiblity-table>
   <p dir="auto"><strong>Description:</strong></p>
   <p dir="auto">ATR computes and persists key expiration as a first-class 
security attribute (<code>_key_expires_at</code> via 
<code>_effective_key_expiration_self_signature</code> in 
<code>atr/storage/writers/keys.py</code>, stored in 
<code>PublicSigningKey.expires</code>; <code>atr/get/keys.py::details</code> 
renders an 'Expired' danger badge from this field). However, the 
signature-verification path 
(<code>atr/tasks/checks/signature.py::_check_core_logic</code>) loads full 
<code>PublicSigningKey</code> rows — including <code>expires</code> — and never 
consults it. The <code>where</code> clause filters only on committee membership 
and <code>deleted IS NULL</code>; there is no <code>expires</code> filter, and 
<code>_check_core_logic_verify_signature</code> calls 
<code>signature.verify_file(candidate_key, artifact_path)</code>
    with no expiry check on the key or on the signature creation time 
   relative to key validity. An expired key — precisely the class of keys 
   most likely to have lapsed operational protection or been deliberately 
   expired after suspected compromise — verifies identically to a valid 
   key, yielding 'Signature verified successfully'. Data flow: 
   attacker-held expired key (committee-linked, not soft-deleted) → 
   detached <code>.asc</code> uploaded with tampered artifact → 
<code>check()</code> → <code>_check_core_logic</code> (no expiry filter) → 
<code>verify_file</code>
    succeeds. Impact: forged signatures on release artifacts pass the 
   committee signature check, so the primary supply-chain assurance ('this 
   artifact was signed by a currently valid committee key') is false. 
   Additionally, it is unverified whether the <code>openpgp</code> library 
evaluates OpenPGP revocation self-signatures during <code>verify_file</code>; 
the embedded test key in <code>tests/unit/test_keys_writer.py</code>
    is titled 'Demonstrating Revoker Trouble (DO NOT USE)' yet no test 
   asserts rejection of a revoked or expired key at verification time.</p>
   <p dir="auto"><strong>Remediation:</strong></p>
   <p dir="auto">In <code>_check_core_logic</code>, after loading keys, filter 
out expired keys: <code>db_public_keys = [key for key in db_public_keys if 
(key.expires is None) or (key.expires &gt; 
datetime.datetime.now(datetime.UTC))]</code>. Better: also check the signature 
creation time against key validity — reject if <code>key.expires is not None 
and signature_info.creation_time &gt; key.expires</code>. Additionally, verify 
(and document with a test) whether the <code>openpgp</code> library evaluates 
OpenPGP revocation self-signatures during <code>verify_file</code>;
    if it does not, revocation certificates present in imported key blocks 
   must be evaluated explicitly. Add regression tests using the 
   already-embedded expiring test key.</p>


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to