{pkgs ? import {}}: pkgs.python3Packages.buildPythonApplication rec { pname = "tor-hostname"; version = "1.0.0"; format = "other"; propagatedBuildInputs = with pkgs.python3Packages; [ pynacl cryptography ]; dontUnpack = true; installPhase = '' mkdir -p $out/bin cat > $out/bin/tor-hostname <<'EOF' #!/usr/bin/env python3 """Convert Tor v3 secret key to .onion hostname""" import hashlib import base64 import sys def onion_address_from_public_key(public_key): """Generate .onion address from ed25519 public key""" version = b'\x03' checksum = hashlib.sha3_256(b'.onion checksum' + public_key + version).digest()[:2] onion_address = base64.b32encode(public_key + checksum + version).decode().lower() return f"{onion_address}.onion" def read_tor_secret_key(filepath): """Read Tor hs_ed25519_secret_key file""" with open(filepath, 'rb') as f: content = f.read() header = b'== ed25519v1-secret: type0 ==\x00\x00\x00' if not content.startswith(header): raise ValueError("Invalid Tor secret key file format") expanded_key = content[32:96] return expanded_key def derive_public_from_expanded_secret(expanded_key): """Derive ed25519 public key from 64-byte expanded secret key""" from nacl.bindings import crypto_scalarmult_ed25519_base_noclamp secret_scalar = expanded_key[:32] public_key = crypto_scalarmult_ed25519_base_noclamp(secret_scalar) return public_key def main(): if len(sys.argv) != 2: print(f"Usage: {sys.argv[0]} ") sys.exit(1) secret_key_path = sys.argv[1] try: expanded_key = read_tor_secret_key(secret_key_path) public_key = derive_public_from_expanded_secret(expanded_key) hostname = onion_address_from_public_key(public_key) print(hostname) except FileNotFoundError: print(f"Error: File not found: {secret_key_path}") sys.exit(1) except Exception as e: print(f"Error: {e}") sys.exit(1) if __name__ == '__main__': main() EOF chmod +x $out/bin/tor-hostname ''; meta = with pkgs.lib; { description = "Convert Tor v3 secret key to .onion hostname"; license = licenses.mit; platforms = platforms.unix; }; }