From ec4a43631ca2ac9b2d477781c8c7b9e4456bffe4 Mon Sep 17 00:00:00 2001 From: Chris Butler Date: Mon, 24 Aug 2026 00:13:49 +0000 Subject: [PATCH] fix: check-pck-expiry checks X.509 cert expiry, not just TCB nextUpdate The PCK cache blob contains URL-encoded PEM X.509 certificates (PCK certs + Intel SGX CA chain) alongside embedded JSON TCB info. The merged version (PR #107) only checked TCB nextUpdate (collateral freshness). This adds extraction and checking of the actual X.509 certificate notAfter dates. PCK certs have ~7-year validity; TCB nextUpdate is ~30 days. Both matter: stale TCB causes attestation failures, expired certs break the trust chain. Co-Authored-By: Claude Opus 4.8 (1M context) --- scripts/check-pck-expiry.sh | 64 +++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/scripts/check-pck-expiry.sh b/scripts/check-pck-expiry.sh index 60df6845..bb32ef86 100755 --- a/scripts/check-pck-expiry.sh +++ b/scripts/check-pck-expiry.sh @@ -51,26 +51,58 @@ else qe_id="${secret_name%-pck}" echo " PCK secret: ${secret_name} (QE ID: ${qe_id})" - DATES=$(oc get secret "$secret_name" -n "$NS" -o jsonpath='{.data.certificate}' | \ + # The PCK cache blob contains URL-encoded PEM X.509 certs + embedded JSON TCB info + oc get secret "$secret_name" -n "$NS" -o jsonpath='{.data.certificate}' | \ base64 -d | python3 -c " -import sys, re +import sys, re, subprocess, urllib.parse data = sys.stdin.buffer.read() text = data.decode('ascii', errors='ignore') -matches = re.findall(r'\"nextUpdate\":\"([^\"]+)\"', text) -if matches: - for m in matches: - print(m) -else: + +# X.509 certificate expiry +decoded = urllib.parse.unquote(text) +certs = re.findall(r'-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----', decoded, re.DOTALL) +pck_expiries = [] +for cert in certs: + r = subprocess.run(['openssl', 'x509', '-noout', '-subject', '-enddate'], + input=cert, capture_output=True, text=True) + if r.returncode == 0: + lines = r.stdout.strip().split('\n') + subj = lines[0].replace('subject=', '').strip() + end = lines[1].replace('notAfter=', '').strip() if len(lines) > 1 else '' + if 'PCK Certificate' in subj: + pck_expiries.append(end) + elif end: + print(f'CERT|{subj}|{end}') +if pck_expiries: + print(f'CERT|PCK Certificate (x{len(pck_expiries)})|{pck_expiries[0]}') + +# TCB info nextUpdate +for m in re.findall(r'\"nextUpdate\":\"([^\"]+)\"', text): + print(f'TCB|{m}') + +if not certs and not re.search(r'nextUpdate', text): print('NONE') -") - - if [ "$DATES" = "NONE" ]; then - echo -e " ${YELLOW}UNKNOWN${NC} No expiry date found in PCK cache blob" - else - echo "$DATES" | while read -r next_update; do - check_date " embedded TCB" "$next_update" - done - fi +" | while IFS='|' read -r rtype val1 val2; do + case "$rtype" in + NONE) + echo -e " ${YELLOW}UNKNOWN${NC} No expiry data found in PCK cache blob" + ;; + CERT) + iso=$(python3 -c " +from datetime import datetime +try: + dt = datetime.strptime('$val2', '%b %d %H:%M:%S %Y %Z') +except ValueError: + dt = datetime.strptime('$val2', '%b %d %H:%M:%S %Y %Z') +print(dt.strftime('%Y-%m-%dT%H:%M:%SZ')) +" 2>/dev/null) + [ -n "$iso" ] && check_date " cert: $val1" "$iso" + ;; + TCB) + check_date " TCB nextUpdate" "$val1" + ;; + esac + done done fi