Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a1468b-63fa-7710-85dc-8f2e16b3ebfa
55 lines
2.9 KiB
Python
55 lines
2.9 KiB
Python
#!/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 0<path.stat().st_size<=LIMIT:raise ValueError()
|
|
fd=os.open(path,os.O_RDONLY|os.O_NOFOLLOW|os.O_NONBLOCK)
|
|
with os.fdopen(fd,'rb') as source:
|
|
if not stat.S_ISREG(os.fstat(source.fileno()).st_mode) or not 0<os.fstat(source.fileno()).st_size<=LIMIT:raise ValueError()
|
|
raw=source.read(LIMIT+1)
|
|
if len(raw)>LIMIT: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())
|