#!/usr/bin/env python3 """Owner-run public certificate/trust metadata; no private-key argument or network. The owner supplies public-only PEM files from the independently established original recovery source. This does not identify that source or accept reissue. """ import argparse from datetime import timezone import hashlib import json import os from pathlib import Path import re import stat from cryptography import x509 from cryptography.hazmat.primitives import serialization LIMIT=1024*1024 CERT=re.compile(br'-----BEGIN CERTIFICATE-----\r?\n[A-Za-z0-9+/=\r\n]+-----END CERTIFICATE-----') def public_bundle(path): # Bound the read before allocation; refuse every non-certificate PEM block. path=Path(os.path.abspath(path)) if any(p.is_symlink() for p in (path,*path.parents)) or not stat.S_ISREG(path.stat().st_mode) or not 0LIMIT:raise ValueError() blocks=CERT.findall(raw) if not blocks or len(blocks)>512 or CERT.sub(b'',raw).strip():raise ValueError() return raw,[x509.load_pem_x509_certificate(block) for block in blocks] def describe(cert_path,trust_path): certificate,chain=public_bundle(cert_path);trust,roots=public_bundle(trust_path);leaf=chain[0] try:names=leaf.extensions.get_extension_for_class(x509.SubjectAlternativeName).value.get_values_for_type(x509.DNSName) except x509.ExtensionNotFound:names=[] spki=leaf.public_key().public_bytes(serialization.Encoding.DER,serialization.PublicFormat.SubjectPublicKeyInfo) def utc_date(name): value=getattr(leaf,name+'_utc',None) if value is None:value=getattr(leaf,name).replace(tzinfo=timezone.utc) return value.isoformat() return {'schema':'nk49.public-tls-metadata.v1','status':'public_metadata_only_not_originality_or_owner_assent', 'tls_cert_sha256':hashlib.sha256(certificate).hexdigest(),'trust_sha256':hashlib.sha256(trust).hexdigest(), 'leaf_public_spki_sha256':'sha256:'+hashlib.sha256(spki).hexdigest(),'leaf_dns_sans':names, 'leaf_not_before':utc_date('not_valid_before'),'leaf_not_after':utc_date('not_valid_after'), 'server_chain_certificates':len(chain),'trust_certificates':len(roots),'network_contacted':False,'private_key_argument_supported':False} def main(): p=argparse.ArgumentParser(description=__doc__);p.add_argument('--public-cert',required=True);p.add_argument('--public-trust',required=True);a=p.parse_args() try:result=describe(a.public_cert,a.public_trust) except Exception:print('{"status":"public_metadata_refused","private_diagnostics_suppressed":true}');return 1 print(json.dumps(result,indent=2));return 0 if __name__=='__main__':raise SystemExit(main())