Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- #
- # keychanger.py - a script to create PGP keys with fingerprints begining
- # with a custom hex string.
- # Requires GnuPG 2.2.x or later and Python 3.4 or later
- #
- #
- # Max number of iterations to try before giving up:
- limit = 1209600
- # If the script fails to find a match for your target, try increasing
- # this number. Note that this is equivalent to the maximum number of
- # seconds to subtract from the timestamp. The default of 1209600 is 2
- # weeks and should be sufficient to find any 4 character or less hex
- # string. One year is 31557600 seconds. Longer string lengths require a
- # greater number of iterations.
- ########################################################################
- # ! ! ! DON'T CHANGE ANYTHING FROM THIS POINT ON ! ! ! #
- # ! ! ! UNLESS YOU REALLY KNOW WHAT YOU'RE DOING. ! ! ! #
- ########################################################################
- # Python version check (3.4+ required for cleanup function)
- import sys
- pv = sys.version_info >= (3, 4)
- def cleanup(pv,public_key,secret_key):
- if pv == False:
- print("\nPlease manually delete these files:\n"+public_key+"\n"+secret_key)
- sys.exit()
- import pathlib
- pathlib.Path(public_key).unlink()
- pathlib.Path(secret_key).unlink()
- sys.exit()
- import hashlib, time, subprocess, uuid
- # Algorithm strings recognized by GnuPG
- algos = {'rsa', 'rsa1024', 'rsa2048', 'rsa3072', 'rsa4096', 'dsa', 'dsa1024', 'dsa2048', 'ed25519', 'nistp256', 'nistp384', 'nistp521', 'brainpoolP256r1', 'brainpoolP384r1', 'brainpoolP512r1', 'secp256k1'}
- # Message prompts for user input
- P = ["\nEnter the target hex string you want the key fingerprint to start with.\nIf you want to match a string longer than 4 characters, you'll need to increase to iteration 'limit' value in the script.\n\nEnter hex string target> ","\nChoose the algorithm for the signing key. Valid signing key algorithms are:\n rsa rsa1024 rsa2048 rsa3072 rsa4096\n dsa dsa1024 dsa2048 dsa3072\nGnuPG also allows elliptic curve key algorithms:\n ed25519\n nistp256 nistp384 nistp521\n brainpoolP256r1 brainpoolP384r1 brainpoolP512r1\n secp256k1\n(note: some elliptic curve algorithms might not be available in your version of GnuPG)\n\nEnter key algorithm> ", "\n * * * * * NOTICE * * * * *\n GnuPG *will* prompt you 3 times to confirm key deletion.\n Enter y at each of these prompts.\n If a Pinentry window pups up, the passphrase is '' (two single quotes).\n\n Press enter to continue..."]
- def hexcheck(target):
- digits = '0123456789abcdef'
- target = target.lower()
- return set(list(target)).issubset(list(digits))
- # Prompt user for target hex string
- target = input(P[0])
- while hexcheck(target) == False:
- print("String contains non-hex digits. Try again.")
- target = input(P[0])
- target = target.lower()
- # Prompt user for key algorithm
- algo = input(P[1])
- while algo not in algos:
- print("Algorithm not in list. Try again.\n")
- algo = input(P[1])
- # Set user ID and generated file names to random strings.
- # This is to ensure none of them already exist
- user_id = str(uuid.uuid4())
- public_key = str(uuid.uuid4())
- secret_key = str(uuid.uuid4())
- # Inform user of GnuPG behavior during script execution
- input(P[2])
- # Commands to generate, export, then delete the generated keys
- cmd1 = ["gpg --quiet --batch --pinentry-mode loopback --passphrase '' --quick-gen-key "+user_id+" "+algo, "gpg --quiet -o "+public_key+" --export "+user_id,"gpg --quiet -o "+secret_key+" --export-secret-keys "+user_id, "gpg --quiet --yes --delete-secret-and-public-keys "+user_id]
- for c in cmd1:
- subprocess.call(c, shell=True)
- # Functions to read gpg exported key data and list individual packets
- def header_info(x):
- if x[0]&64 == 64:
- tag = x[0]&63
- if x[1] < 192: [hlen,plen] = [2,x[1]]
- elif x[1] <= 223: [hlen,plen] = [3,((x[1] - 192) << 8) + x[2] + 192]
- elif x[1] == 255: [hlen,plen] = [5,int.from_bytes(x[2:6],byteorder='big',signed=False)]
- else:
- tag = (x[0]&60)>>2
- if x[0]&3 == 0: [hlen,plen] = [2,x[1]]
- elif x[0]&3 == 1: [hlen,plen] = [3,int.from_bytes(x[1:3],byteorder='big',signed=False)]
- elif x[0]&3 == 2: [hlen,plen] = [5,int.from_bytes(x[1:5],byteorder='big',signed=False)]
- return [tag, hlen, plen]
- def splitpackets(x):
- packets = []
- i = 0
- while i < (len(x)-1):
- h = header_info(x[i:i+5])
- tag = h[0]
- packet = x[i:i+h[1]+h[2]]
- i += (h[1]+h[2])
- packets.append([tag,packet])
- return packets
- # Rewrite secret_key file without signature packet
- with open(secret_key, 'rb') as f:
- key = bytes(f.read())
- key = splitpackets(key)
- key = b''.join([x[1] for x in key if x[0] in {5,13}])
- with open(secret_key, 'wb') as f:
- f.write(key)
- # Get public_key packet
- with open(public_key,'rb') as f:
- key = bytes(f.read())
- # Get public_key header and packet length.
- [hlen,plen] = header_info(key[0:5])[1:3]
- # Format header for fingerprinting.
- h = b'\x99'+plen.to_bytes(2,byteorder='big',signed=False)
- # Create initial input for SHA1
- key = bytearray(h+key[hlen:hlen+plen])
- # Extract current timestamp
- timestamp = int.from_bytes(key[4:8],byteorder='big',signed=True)
- # Set length of string to match
- L = len(target)
- # Set the initial hash value and iteration counter.
- fingerprint = hashlib.sha1()
- fingerprint.update(key)
- current = fingerprint.hexdigest()[0:L]
- i = 0
- # Timer to measure duration of search.
- start = time.time()
- # Begin search for fingerprint string match
- while current != target and i < limit:
- fingerprint = hashlib.sha1()
- timestamp -= 1
- i += 1
- key[4:8] = timestamp.to_bytes(4,byteorder='big',signed=True)
- fingerprint.update(key)
- current = fingerprint.hexdigest()[0:L]
- # Stop time for timer.
- end = time.time()
- if current == target:
- # Success :)
- with open(secret_key,'r+b') as f:
- # Go to start of timestamp data in exported secret key packet
- f.seek(hlen+1)
- # Overwrite timestamp with the one found in the while loop
- f.write(bytes(key[4:8]))
- print("\nMatch found in "+str(i)+" iterations. Total time: "+str(end-start)+" seconds.")
- input("Press enter to continue")
- print("\nKey fingerprint will be "+fingerprint.hexdigest().upper())
- answer = input("import this key to GnuPG (y/n)?")
- if answer.lower() == 'y':
- pass
- else:
- print("Exiting")
- cleanup(pv,public_key,secret_key)
- # List of commands to import key and display fingerprint
- cmd2 = ["gpg --quiet --allow-non-selfsigned-uid --import "+secret_key, "gpg -K "+user_id]
- for c in cmd2:
- subprocess.call(c, shell=True)
- print("\nYOU NEED TO EDIT THIS KEY.\nEnter 'gpg --edit-key "+user_id+"' then use the following commands after the 'gpg>' prompt in the order shown:\n gpg> adduid ...add a new user ID by following the prompts.\n gpg> 1 ...selects the '"+user_id+"' user id\n gpg> deluid ...delete the selected user ID then confirm.\n gpg> change-usage ...toggle usage options until it shows 'Current allowed actions: Sign Certify' then enter Q.\n gpg> passwd ...set a password\n gpg> addkey ...follow prompts to add encryption subkey\n gpg> save ...save the changes made in editing mode")
- cleanup(pv,public_key,secret_key)
- else:
- # Failure :(
- print("Failed to match "+str(target)+" in "+str(i)+" iterations. Total time: "+str(end-start)+" seconds")
- cleanup(pv,public_key,secret_key)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement