Created
March 28, 2023 11:56
-
-
Save m-rey/2452cad1a015f67e8bed128cf307f94f to your computer and use it in GitHub Desktop.
use this to generate a hashed URI of your pgp fingerprint for keyoxide
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import sys | |
import secrets | |
import re | |
import argon2 | |
import os | |
import base64 | |
import logging | |
import argparse | |
from concurrent.futures import ProcessPoolExecutor, wait, FIRST_COMPLETED | |
from typing import Optional | |
def create_argon2id_hash(secret: str, time_cost: int = 512, memory_cost: int = 64, | |
parallelism: int = 2, hash_len: int = 16, version: int = 19) -> str: | |
salt = secrets.token_bytes(16) | |
hash = argon2.low_level.hash_secret_raw( | |
secret=secret.encode(), | |
salt=salt, | |
time_cost=time_cost, | |
memory_cost=memory_cost, | |
parallelism=parallelism, | |
hash_len=hash_len, | |
type=argon2.low_level.Type.ID, | |
version=version | |
) | |
encoded_salt = base64.b64encode(salt).decode().rstrip('=') | |
encoded_hash = base64.b64encode(hash).decode().rstrip('=') | |
return f"$argon2id$v={version}$m={memory_cost},t={time_cost},p={parallelism}${encoded_salt}${encoded_hash}" | |
def worker(secret: str, vanity_pattern: re.Pattern, verbose: bool, continuous: bool, progress: bool) -> Optional[str]: | |
while True: | |
argon2id_hash = create_argon2id_hash(secret) | |
if vanity_pattern and vanity_pattern.search(argon2id_hash): | |
logging.info(f"Vanity hash found: {argon2id_hash}") | |
if not continuous: | |
return argon2id_hash | |
elif verbose: | |
logging.debug(f"Hash generated: {argon2id_hash}") | |
if progress: | |
print(".", end="", flush=True) | |
def main(args: argparse.Namespace) -> None: | |
log_level = logging.DEBUG if args.verbose else logging.INFO | |
logging.basicConfig(level=log_level, format='%(levelname)s: %(message)s') | |
flags = re.IGNORECASE if args.ignore_case else 0 | |
vanity_pattern = re.compile(args.regex, flags=flags) if args.regex else None | |
try: | |
with ProcessPoolExecutor(max_workers=args.num_workers) as executor: | |
futures = {executor.submit(worker, args.secret, vanity_pattern, args.verbose, args.continuous, args.progress)} | |
if not args.continuous: | |
done, _ = wait(futures, return_when=FIRST_COMPLETED) | |
vanity_hash = done.pop().result() | |
print(f"\nVanity hash found: {vanity_hash}") | |
for future in futures: | |
future.cancel() | |
except KeyboardInterrupt: | |
logging.debug("\nStopping script and workers.") | |
for future in futures: | |
future.cancel() | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser(description='Generate vanity Argon2id hashes using regex matching.') | |
parser.add_argument('secret', type=str, help='The secret string to be hashed.') | |
parser.add_argument('--regex', type=str, default=None, help='The regex pattern to match in the vanity hash.') | |
parser.add_argument('--num-workers', type=int, default=os.cpu_count(), | |
help='The number of worker processes to use. Default is the number of CPU cores.') | |
parser.add_argument('--ignore-case', action='store_true', help='Ignore case when matching the regex pattern.') | |
parser.add_argument('--verbose', action='store_true', help='Display DEBUG log messages in the terminal.') | |
parser.add_argument('--progress', action='store_true', help='Show progress indicator.') | |
parser.add_argument('--continuous', action='store_true', help='Continuously generate hashes even after finding a vanity hash.') | |
args = parser.parse_args() | |
main(args) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment