npx skills add mukul975/Anthropic-Cybersecurity-SkillsScope and Authorization: This skill describes defensive cryptographic-migration engineering on systems you own or operate. Cryptographic discovery scanning can touch sensitive key material and production traffic — run inventory tooling only with authorization and in line with your organization's change-management and data-handling policies.
Overview
A cryptographically relevant quantum computer (CRQC) running Shor's algorithm will break the public-key cryptography that secures almost all of today's communications and signatures: RSA, finite-field and elliptic-curve Diffie-Hellman (DH/ECDH), and ECDSA. Symmetric primitives (AES) and hashes (SHA-2/3) are only weakened (Grover gives a quadratic speedup, mitigated by larger key/output sizes), but asymmetric algorithms are catastrophically broken. The most urgent threat is harvest-now, decrypt-later (HNDL): adversaries capturing encrypted traffic today to decrypt once a CRQC exists, which puts long-lived secrets (health records, state secrets, intellectual property, root-of-trust keys) at risk now.
On 13 August 2024 NIST finalized the first post-quantum standards: FIPS 203 (ML-KEM, Module-Lattice KEM, formerly CRYSTALS-Kyber) for key establishment, FIPS 204 (ML-DSA, Module-Lattice digital signatures, formerly CRYSTALS-Dilithium), and FIPS 205 (SLH-DSA, the stateless hash-based signature scheme SPHINCS+). The migration playbook (NIST SP 1800-38, Migration to Post-Quantum Cryptography) is: (1) build a cryptographic inventory / CBOM, (2) prioritize by HNDL exposure and crypto-agility, (3) deploy hybrid schemes (a classical algorithm AND a PQC algorithm combined, e.g. X25519MLKEM768) so a break in either leg does not compromise the session, and (4) re-key and rotate.
This skill maps to ATT&CK T1573 – Encrypted Channel: the same cryptographic channels adversaries abuse for stealthy C2 are the channels defenders must make quantum-resistant; understanding the algorithms in use is foundational to both attack detection and defensive migration. The NIST CSF outcome is PR.DS-02 (data-in-transit protection) — and by extension data-at-rest for HNDL-sensitive stores.
When to Use
- When building an enterprise cryptographic inventory / Cryptography Bill of Materials (CBOM) for quantum-readiness.
- When prioritizing which systems must migrate first based on data lifetime and HNDL exposure.
- When enabling hybrid post-quantum key exchange (
X25519MLKEM768) on TLS endpoints, VPNs, or SSH. - When issuing PQC or hybrid certificates and testing PQC signature verification.
- When evaluating crypto-agility — the ability to swap algorithms without re-architecting applications.
Prerequisites
- OpenSSL 3.5.0 or later, which ships native ML-KEM, ML-DSA, and SLH-DSA support:
openssl version # expect 3.5.0+ openssl list -kem-algorithms | grep -i mlkem openssl list -signature-algorithms | grep -i mldsa - For OpenSSL 3.0–3.4, the Open Quantum Safe oqs-provider plus liboqs:
git clone https://github.com/open-quantum-safe/liboqs && \ cmake -S liboqs -B liboqs/build && cmake --build liboqs/build && \ sudo cmake --install liboqs/build git clone https://github.com/open-quantum-safe/oqs-provider && \ cmake -S oqs-provider -B oqs-provider/_build && \ cmake --build oqs-provider/_build && \ sudo cmake --install oqs-provider/_build - Python 3.8+ for the inventory helper:
python3 -m pip install cryptography - (Optional) A CBOM generator: CycloneDX
cdxgen, orcbomkit-theiafor container/directory crypto discovery.
Objectives
- Produce a cryptographic inventory (CBOM) of algorithms, key sizes, certificates, and protocols in use.
- Classify assets by quantum vulnerability and HNDL exposure and prioritize migration.
- Stand up and verify hybrid
X25519MLKEM768key exchange on a TLS endpoint. - Generate ML-KEM and ML-DSA keys and a PQC/hybrid certificate, and verify signatures.
- Establish a crypto-agility baseline and a re-keying / rotation plan.
MITRE ATT&CK Mapping
| ID | Official Technique Name | Relevance |
|---|---|---|
| T1573 | Encrypted Channel | Migration secures the encrypted channels (TLS/VPN/SSH) that protect data in transit; cryptographic inventory of these channels also underpins detection of adversary-controlled encrypted C2. |
| T1573.002 | Encrypted Channel: Asymmetric Cryptography | RSA/ECDH key exchange — the exact asymmetric primitives broken by a CRQC and replaced by ML-KEM hybrids. |
| T1573.001 | Encrypted Channel: Symmetric Cryptography | AES and other symmetric ciphers; quantum-weakened by Grover, mitigated by 256-bit keys rather than replacement. |
Workflow
1. Confirm PQC algorithm availability
openssl version
# List quantum-safe KEMs and signatures available in this OpenSSL build
openssl list -kem-algorithms | grep -Ei 'mlkem|kyber'
openssl list -signature-algorithms | grep -Ei 'mldsa|dilithium|slhdsa|sphincs'
openssl list -tls-groups 2>/dev/null | grep -Ei 'mlkem'If using oqs-provider on OpenSSL 3.0–3.4, activate it in openssl.cnf:
[provider_sect]
default = default_sect
oqsprovider = oqsprovider_sect
[default_sect]
activate = 1
[oqsprovider_sect]
activate = 12. Build a cryptographic inventory (CBOM)
Generate a CycloneDX CBOM from a code repo or container with cbomkit-theia / cdxgen:
# Directory / container image crypto discovery
cbomkit-theia dir ./myapp --output cbom.json
# or with cdxgen (Java keystores, certs, source-level algorithms)
cdxgen -t java --include-crypto -o cbom.json ./myappEnumerate TLS algorithms and certificate signature schemes across live endpoints with the helper agent.py scan (below), and the public-key strength of any certificate:
openssl x509 -in server.crt -noout -text | grep -E 'Signature Algorithm|Public Key'3. Classify and prioritize by HNDL exposure
For each inventoried asset, record: algorithm, key size, where the key lives, data sensitivity, and data lifetime. Prioritize migration where data_lifetime_years + migration_time > years_until_CRQC (Mosca's inequality). Long-lived confidential data over public networks ranks highest; ephemeral internal traffic ranks lower. Hash-based signature roots-of-trust (firmware signing) are also high priority because they protect long-lived trust anchors.
4. Generate ML-KEM and ML-DSA key material
# ML-KEM-768 (key establishment) keypair
openssl genpkey -algorithm ML-KEM-768 -out mlkem768.key
# OpenSSL 3.0-3.4 + oqs-provider uses lowercase 'mlkem768'
# openssl genpkey -algorithm mlkem768 -out mlkem768.key
# ML-DSA-65 (signature) keypair
openssl genpkey -algorithm ML-DSA-65 -out mldsa65.key
openssl pkey -in mldsa65.key -pubout -out mldsa65.pub5. Issue a PQC (ML-DSA) certificate
# Self-signed ML-DSA-65 certificate for testing
openssl req -new -x509 -key mldsa65.key -out mldsa65.crt -days 365 \
-subj "/CN=pqc-test.example.com"
openssl x509 -in mldsa65.crt -noout -text | grep -A1 'Signature Algorithm'6. Sign and verify with ML-DSA
echo "firmware-image-v2.bin" > artifact.txt
openssl dgst -sign mldsa65.key -out artifact.sig artifact.txt
openssl dgst -verify mldsa65.pub -signature artifact.sig artifact.txt
# -> "Verified OK"7. Deploy and test hybrid TLS key exchange
Run a TLS 1.3 server and force the hybrid group X25519MLKEM768 (classical X25519 + ML-KEM-768):
# Server (use a classical or ML-DSA cert/key)
openssl s_server -accept 4433 -www -tls1_3 \
-cert mldsa65.crt -key mldsa65.key -groups X25519MLKEM768
# Client — negotiate the hybrid group and confirm it was used
openssl s_client -connect localhost:4433 -tls1_3 -groups X25519MLKEM768 \
</dev/null 2>/dev/null | grep -E 'Negotiated|Server Temp Key|Cipher'For external endpoints, confirm support against a public PQC test server:
openssl s_client -groups X25519MLKEM768 -tls1_3 -connect pq.cloudflareresearch.com:443 </dev/null8. Enable hybrid PQC on production TLS terminators
Configure the web server / load balancer to offer the hybrid group while keeping classical fallback for old clients. NGINX with OpenSSL 3.5+:
server {
listen 443 ssl;
ssl_protocols TLSv1.3;
ssl_ecdh_curve X25519MLKEM768:X25519:secp256r1; # hybrid first, classical fallback
ssl_certificate /etc/nginx/certs/server.crt;
ssl_certificate_key /etc/nginx/certs/server.key;
}Reload and verify with the s_client command from step 7 against the live host.
9. Establish crypto-agility and a rotation plan
Centralize algorithm selection (config, not code), record key/cert expiry, and schedule re-keying. Re-run the inventory (step 2) on a cadence to confirm no quantum-vulnerable-only algorithms remain on prioritized assets, and track residual RSA/ECDH usage to zero on high-HNDL paths.
Tools and Resources
| Tool / Resource | Purpose | Link |
|---|---|---|
| FIPS 203 (ML-KEM) | KEM standard | https://csrc.nist.gov/pubs/fips/203/final |
| FIPS 204 (ML-DSA) | Signature standard | https://csrc.nist.gov/pubs/fips/204/final |
| FIPS 205 (SLH-DSA) | Hash-based signature standard | https://csrc.nist.gov/pubs/fips/205/final |
| NIST SP 1800-38 | Migration practice guide / crypto discovery | https://www.nccoe.nist.gov/crypto-agility-considerations-migrating-post-quantum-cryptographic-algorithms |
| OpenSSL 3.5 | Native ML-KEM/ML-DSA/SLH-DSA + hybrid groups | https://www.openssl.org |
| oqs-provider / liboqs | PQC for OpenSSL 3.0–3.4 | https://github.com/open-quantum-safe/oqs-provider |
| CycloneDX CBOM | Cryptography Bill of Materials spec | https://cyclonedx.org/capabilities/cbom/ |
| CBOMkit / cbomkit-theia | Crypto discovery & CBOM generation | https://github.com/cbomkit/cbomkit-theia |
Algorithm Reference
| Classical (broken/weakened) | Quantum-safe replacement | Standard | Use |
|---|---|---|---|
| RSA / ECDH / DH key exchange | ML-KEM-512/768/1024 (hybrid: X25519MLKEM768) | FIPS 203 | Key establishment |
| RSA / ECDSA / EdDSA signatures | ML-DSA-44/65/87 | FIPS 204 | General signatures |
| (backup signature) | SLH-DSA (SPHINCS+) | FIPS 205 | Conservative/firmware signing |
| AES-128 | AES-256 | FIPS 197 | Symmetric (Grover-hardened) |
| SHA-256 | SHA-384/512, SHA-3 | FIPS 180-4/202 | Hashing |
Validation Criteria
- OpenSSL 3.5+ (or 3.x + oqs-provider) confirmed exposing ML-KEM and ML-DSA.
- Cryptographic inventory / CBOM produced covering algorithms, keys, certs, and protocols.
- Assets classified and prioritized by HNDL exposure (Mosca's inequality applied).
- ML-KEM-768 and ML-DSA-65 keypairs generated successfully.
- PQC (ML-DSA) certificate issued and its signature algorithm verified.
- Sign/verify round trip with ML-DSA returns "Verified OK".
- Hybrid
X25519MLKEM768key exchange negotiated and confirmed on a test endpoint. - Production TLS terminator offers the hybrid group with classical fallback.
- Crypto-agility/rotation plan documented and inventory re-run scheduled.
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 2
api-reference.md2.6 KB
OpenSSL PQC Command Reference
Discovery
| Task | Command |
|---|---|
| OpenSSL version (need 3.5+) | openssl version |
| List quantum-safe KEMs | openssl list -kem-algorithms | grep -i mlkem |
| List quantum-safe signatures | openssl list -signature-algorithms | grep -Ei 'mldsa|slhdsa' |
| List TLS groups | openssl list -tls-groups | grep -i mlkem |
| Inspect cert algorithm | openssl x509 -in server.crt -noout -text | grep -E 'Signature Algorithm|Public Key' |
Key generation
| Task | Command (OpenSSL 3.5+) | oqs-provider (3.0–3.4) |
|---|---|---|
| ML-KEM-768 keypair | openssl genpkey -algorithm ML-KEM-768 -out mlkem768.key |
-algorithm mlkem768 |
| ML-DSA-65 keypair | openssl genpkey -algorithm ML-DSA-65 -out mldsa65.key |
-algorithm mldsa65 |
| Extract public key | openssl pkey -in mldsa65.key -pubout -out mldsa65.pub |
same |
Certificates
| Task | Command |
|---|---|
| Self-signed ML-DSA cert | openssl req -new -x509 -key mldsa65.key -out mldsa65.crt -days 365 -subj "/CN=pqc.example.com" |
| Inspect signature alg | openssl x509 -in mldsa65.crt -noout -text | grep -A1 'Signature Algorithm' |
Sign / verify
| Task | Command |
|---|---|
| Sign | openssl dgst -sign mldsa65.key -out artifact.sig artifact.txt |
| Verify | openssl dgst -verify mldsa65.pub -signature artifact.sig artifact.txt |
Hybrid TLS key exchange
| Task | Command |
|---|---|
| TLS server (hybrid group) | openssl s_server -accept 4433 -www -tls1_3 -cert mldsa65.crt -key mldsa65.key -groups X25519MLKEM768 |
| TLS client (hybrid group) | openssl s_client -connect localhost:4433 -tls1_3 -groups X25519MLKEM768 |
| Test public PQC endpoint | openssl s_client -groups X25519MLKEM768 -tls1_3 -connect pq.cloudflareresearch.com:443 |
Standardized hybrid TLS groups
| Group | Classical leg | PQC leg |
|---|---|---|
| X25519MLKEM768 | X25519 | ML-KEM-768 |
| SecP256r1MLKEM768 | NIST P-256 | ML-KEM-768 |
| SecP384r1MLKEM1024 | NIST P-384 | ML-KEM-1024 |
NGINX hybrid config (OpenSSL 3.5+)
ssl_protocols TLSv1.3;
ssl_ecdh_curve X25519MLKEM768:X25519:secp256r1; # hybrid first, classical fallbackoqs-provider activation (openssl.cnf)
[provider_sect]
default = default_sect
oqsprovider = oqsprovider_sect
[default_sect]
activate = 1
[oqsprovider_sect]
activate = 1CBOM generation
| Tool | Command |
|---|---|
| cbomkit-theia (dir) | cbomkit-theia dir ./myapp --output cbom.json |
| cdxgen (Java + crypto) | cdxgen -t java --include-crypto -o cbom.json ./myapp |
standards.md1.8 KB
Standards and Framework Mapping
NIST Cybersecurity Framework 2.0
| ID | Name | Rationale |
|---|---|---|
| PR.DS-02 | The confidentiality, integrity, and availability of data-in-transit are protected | Hybrid PQC key exchange (X25519MLKEM768) protects data in transit against harvest-now-decrypt-later attacks by a future CRQC. |
MITRE ATT&CK
| ID | Name | Rationale |
|---|---|---|
| T1573 | Encrypted Channel | Migration hardens the encrypted channels protecting data in transit; cryptographic inventory of these channels also underpins detection of adversary encrypted C2. |
| T1573.001 | Encrypted Channel: Symmetric Cryptography | AES/symmetric ciphers — quantum-weakened by Grover and hardened via 256-bit keys. |
| T1573.002 | Encrypted Channel: Asymmetric Cryptography | RSA/ECDH — the asymmetric primitives broken by Shor's algorithm and replaced by ML-KEM. |
NIST Post-Quantum Standards (finalized 13 Aug 2024)
| Standard | Algorithm | Former name | Purpose |
|---|---|---|---|
| FIPS 203 | ML-KEM (Module-Lattice KEM) | CRYSTALS-Kyber | Key encapsulation / establishment |
| FIPS 204 | ML-DSA (Module-Lattice DSA) | CRYSTALS-Dilithium | Primary digital signatures |
| FIPS 205 | SLH-DSA (Stateless Hash-based DSA) | SPHINCS+ | Conservative backup signatures |
Migration Guidance
| Reference | Rationale |
|---|---|
| NIST SP 1800-38 (NCCoE, Migration to Post-Quantum Cryptography) | Crypto-discovery test plan, CBOM-driven inventory, and migration architecture across CI/CD, operational systems, and network services. |
| Mosca's inequality | Prioritization rule: migrate when data_lifetime + migration_time > time_to_CRQC. |
| CycloneDX 1.6 CBOM | Cryptography Bill of Materials object model for inventory and dependency tracking. |
Scripts 1
agent.py7.8 KB
#!/usr/bin/env python3
"""
pqc_agent.py — Post-quantum cryptography migration helper.
Three defensive functions for quantum-readiness work:
scan Inventory the public-key crypto of a remote TLS endpoint and a set
of local X.509 certificates, flagging quantum-vulnerable algorithms
(RSA/EC/DSA/DH) vs. quantum-safe (ML-KEM / ML-DSA / SLH-DSA).
prioritize Apply Mosca's inequality to a CSV of assets to rank migration order.
hybrid-test Use the local OpenSSL CLI to negotiate the hybrid X25519MLKEM768
TLS group against a host and report whether it succeeded.
Run only against systems you are authorized to assess. The scan opens TLS
sockets and reads certificates; it does not transmit or store key material.
Examples:
python3 pqc_agent.py scan --host example.com --port 443 --certs ./certs/*.pem
python3 pqc_agent.py prioritize --csv assets.csv --crqc-years 8
python3 pqc_agent.py hybrid-test --host pq.cloudflareresearch.com --port 443
"""
import argparse
import csv
import glob
import socket
import ssl
import subprocess
import sys
try:
from cryptography import x509
from cryptography.hazmat.primitives.asymmetric import rsa, ec, dsa, ed25519, ed448
HAVE_CRYPTO = True
except ImportError:
HAVE_CRYPTO = False
QUANTUM_VULNERABLE = ("rsa", "ec", "ecdsa", "dsa", "dh", "ecdh", "ed25519", "ed448")
QUANTUM_SAFE = ("ml-kem", "mlkem", "ml-dsa", "mldsa", "slh-dsa", "slhdsa")
def _classify_public_key(pubkey):
"""Return (algorithm_label, vulnerable_bool, key_size_or_curve)."""
if isinstance(pubkey, rsa.RSAPublicKey):
return "RSA", True, pubkey.key_size
if isinstance(pubkey, ec.EllipticCurvePublicKey):
return "EC", True, pubkey.curve.name
if isinstance(pubkey, dsa.DSAPublicKey):
return "DSA", True, pubkey.key_size
if isinstance(pubkey, (ed25519.Ed25519PublicKey, ed448.Ed448PublicKey)):
return type(pubkey).__name__, True, "edwards"
return pubkey.__class__.__name__, False, "?"
def _fetch_peer_cert_pem(host, port, timeout):
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
with socket.create_connection((host, port), timeout=timeout) as sock:
with ctx.wrap_socket(sock, server_hostname=host) as ssock:
der = ssock.getpeercert(binary_form=True)
version = ssock.version()
cipher = ssock.cipher()
return ssl.DER_cert_to_PEM_cert(der), version, cipher
def _inspect_cert_pem(pem_text, label):
if not HAVE_CRYPTO:
sys.stderr.write("ERROR: install dependency: python3 -m pip install cryptography\n")
sys.exit(2)
cert = x509.load_pem_x509_certificate(pem_text.encode())
alg, vuln, size = _classify_public_key(cert.public_key())
sig = cert.signature_algorithm_oid._name
sig_vuln = any(v in sig.lower() for v in QUANTUM_VULNERABLE) and \
not any(s in sig.lower() for s in QUANTUM_SAFE)
flag = "VULNERABLE" if (vuln or sig_vuln) else "quantum-safe"
print(f"[{flag:>12}] {label}")
print(f" subject : {cert.subject.rfc4514_string()}")
print(f" pubkey : {alg} ({size})")
print(f" sig alg : {sig}")
return vuln or sig_vuln
def cmd_scan(args):
findings = 0
if args.host:
try:
pem, version, cipher = _fetch_peer_cert_pem(args.host, args.port, args.timeout)
print(f"== Remote endpoint {args.host}:{args.port} ({version}, {cipher[0]}) ==")
if _inspect_cert_pem(pem, f"{args.host}:{args.port} leaf cert"):
findings += 1
except (socket.error, ssl.SSLError, OSError) as exc:
sys.stderr.write(f"ERROR: could not connect to {args.host}:{args.port}: {exc}\n")
cert_paths = []
for pattern in args.certs:
cert_paths.extend(glob.glob(pattern))
if cert_paths:
print("\n== Local certificates ==")
for path in cert_paths:
try:
with open(path, "r", encoding="utf-8") as fh:
if _inspect_cert_pem(fh.read(), path):
findings += 1
except (OSError, ValueError) as exc:
sys.stderr.write(f"WARN: skipping {path}: {exc}\n")
print(f"\nQuantum-vulnerable assets found: {findings}")
return 1 if findings else 0
def cmd_prioritize(args):
"""Mosca's inequality: migrate if data_lifetime + migration_time > crqc_years."""
try:
with open(args.csv, newline="", encoding="utf-8") as fh:
rows = list(csv.DictReader(fh))
except OSError as exc:
sys.stderr.write(f"ERROR: cannot read {args.csv}: {exc}\n")
sys.exit(1)
scored = []
for r in rows:
try:
life = float(r.get("data_lifetime_years", 0))
mig = float(r.get("migration_time_years", 1))
except ValueError:
life, mig = 0.0, 1.0
urgent = (life + mig) > args.crqc_years
margin = (life + mig) - args.crqc_years
scored.append((margin, urgent, r.get("asset", "?"), life, mig))
scored.sort(reverse=True)
print(f"{'PRIORITY':<10}{'MARGIN':>8} ASSET (data_life + migration vs CRQC={args.crqc_years}y)")
print("-" * 70)
for margin, urgent, asset, life, mig in scored:
tag = "MIGRATE" if urgent else "monitor"
print(f"{tag:<10}{margin:>+8.1f} {asset} (life={life}, mig={mig})")
return 0
def cmd_hybrid_test(args):
cmd = ["openssl", "s_client", "-tls1_3", "-groups", "X25519MLKEM768",
"-connect", f"{args.host}:{args.port}"]
try:
proc = subprocess.run(cmd, input=b"", capture_output=True, timeout=args.timeout)
except FileNotFoundError:
sys.stderr.write("ERROR: openssl not found on PATH (need 3.5+ or oqs-provider).\n")
sys.exit(2)
except subprocess.TimeoutExpired:
sys.stderr.write("ERROR: openssl s_client timed out.\n")
sys.exit(1)
out = (proc.stdout + proc.stderr).decode(errors="replace")
ok = "Server Temp Key" in out or "Negotiated TLS1.3 group: X25519MLKEM768" in out
if proc.returncode == 0 and ok:
for line in out.splitlines():
if "Temp Key" in line or "Negotiated" in line or "Cipher" in line:
print(line.strip())
print(f"\n[OK] {args.host}:{args.port} negotiated hybrid X25519MLKEM768")
return 0
sys.stderr.write(f"[FAIL] {args.host}:{args.port} did not negotiate X25519MLKEM768\n")
sys.stderr.write(out[-800:] + "\n")
return 1
def build_parser():
p = argparse.ArgumentParser(description="Post-quantum cryptography migration helper.")
sub = p.add_subparsers(dest="cmd", required=True)
s = sub.add_parser("scan", help="Inventory TLS endpoint + local certs for quantum-vulnerable crypto")
s.add_argument("--host", help="Remote host to inspect")
s.add_argument("--port", type=int, default=443)
s.add_argument("--certs", nargs="*", default=[], help="Glob(s) of local PEM certs")
s.add_argument("--timeout", type=float, default=10)
s.set_defaults(func=cmd_scan)
pr = sub.add_parser("prioritize", help="Rank assets via Mosca's inequality from a CSV")
pr.add_argument("--csv", required=True,
help="CSV with columns: asset,data_lifetime_years,migration_time_years")
pr.add_argument("--crqc-years", type=float, default=10,
help="Estimated years until a cryptographically relevant quantum computer")
pr.set_defaults(func=cmd_prioritize)
h = sub.add_parser("hybrid-test", help="Test hybrid X25519MLKEM768 negotiation via openssl")
h.add_argument("--host", required=True)
h.add_argument("--port", type=int, default=443)
h.add_argument("--timeout", type=float, default=15)
h.set_defaults(func=cmd_hybrid_test)
return p
def main():
args = build_parser().parse_args()
return args.func(args)
if __name__ == "__main__":
sys.exit(main())