From 88b4f5ed11054ae3feca487d83e7c87b192c5146 Mon Sep 17 00:00:00 2001 From: Daniel Dolezal Date: Fri, 17 Apr 2026 21:06:52 +0200 Subject: [PATCH] add key generation script --- scripts/gen_extension_key.py | 74 ++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 scripts/gen_extension_key.py diff --git a/scripts/gen_extension_key.py b/scripts/gen_extension_key.py new file mode 100644 index 0000000..064a228 --- /dev/null +++ b/scripts/gen_extension_key.py @@ -0,0 +1,74 @@ +#!/usr/bin/env -S uv run +"""Generate or derive Chrome extension key and ID. + +Usage: + python scripts/gen_extension_key.py # generate new key pair + python scripts/gen_extension_key.py --from-manifest # derive ID from extension/manifest.json + python scripts/gen_extension_key.py --key # derive ID from given public key +""" +import argparse, hashlib +import base64, json, sys +from pathlib import Path + +def public_key_to_extension_id(pub_key_der:bytes) -> str: + digest = hashlib.sha256(pub_key_der).hexdigest() + return "".join(chr(ord("a") + int(c, 16)) for c in digest[:32]) + +def derive_from_key_b64(key_b64:str) -> tuple[str, str]: + der = base64.b64decode(key_b64) + ext_id = public_key_to_extension_id(der) + return key_b64, ext_id + +def generate_new_key() -> tuple[str, str, str]: + try: + from cryptography.hazmat.primitives.asymmetric import rsa + from cryptography.hazmat.primitives.serialization import ( + Encoding, + PublicFormat, + PrivateFormat, + NoEncryption, + ) + except ImportError: + print("Install 'cryptography' to generate new keys: pip install cryptography", file=sys.stderr) + sys.exit(1) + + private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048) + pub_der = private_key.public_key().public_bytes(Encoding.DER, PublicFormat.SubjectPublicKeyInfo) + priv_pem = private_key.private_bytes(Encoding.PEM, PrivateFormat.TraditionalOpenSSL, NoEncryption()).decode() + + key_b64 = base64.b64encode(pub_der).decode() + ext_id = public_key_to_extension_id(pub_der) + return key_b64, ext_id, priv_pem + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Chrome extension key/ID tool") + group = parser.add_mutually_exclusive_group() + group.add_argument("--from-manifest", action="store_true", help="Derive ID from extension/manifest.json") + group.add_argument("--key", metavar="BASE64", help="Derive ID from given base64 public key") + args = parser.parse_args() + + if args.from_manifest: + manifest_path = Path(__file__).parent.parent / "extension" / "manifest.json" + manifest = json.loads(manifest_path.read_text()) + key_b64 = manifest.get("key") + if not key_b64: + print("No 'key' field in manifest.json", file=sys.stderr) + sys.exit(1) + key_b64, ext_id = derive_from_key_b64(key_b64) + print(f"Extension ID: {ext_id}") + print(f"Key (b64): {key_b64}") + + elif args.key: + key_b64, ext_id = derive_from_key_b64(args.key) + print(f"Extension ID: {ext_id}") + + else: + key_b64, ext_id, priv_pem = generate_new_key() + print(f"Extension ID: {ext_id}") + print(f"Key (b64): {key_b64}") + print() + print("Add this to extension/manifest.json:") + print(f' "key": "{key_b64}"') + print() + print("Private key (keep secret, needed to re-derive same ID):") + print(priv_pem)