Created
December 3, 2016 14:13
-
-
Save anonymous/1ecf48d15be29479ba4789a39e009e07 to your computer and use it in GitHub Desktop.
BitMessage V4 Vanity Addresses Generator Multi-threaded
This file contains 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
import hashlib | |
from struct import * | |
from pyelliptic import arithmetic | |
from binascii import hexlify, unhexlify | |
#There is another copy of this function in Bitmessagemain.py | |
def convertIntToString(n): | |
a = __builtins__.hex(n) | |
if a[-1:] == 'L': | |
a = a[:-1] | |
if (len(a) % 2) == 0: | |
return unhexlify(a[2:]) | |
else: | |
return unhexlify('0'+a[2:]) | |
ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" | |
def encodeBase58(num, alphabet=ALPHABET): | |
"""Encode a number in Base X | |
`num`: The number to encode | |
`alphabet`: The alphabet to use for encoding | |
""" | |
if (num == 0): | |
return alphabet[0] | |
arr = [] | |
base = len(alphabet) | |
while num: | |
rem = num % base | |
#print 'num is:', num | |
num = num // base | |
arr.append(alphabet[rem]) | |
arr.reverse() | |
return ''.join(arr) | |
def decodeBase58(string, alphabet=ALPHABET): | |
"""Decode a Base X encoded string into the number | |
Arguments: | |
- `string`: The encoded string | |
- `alphabet`: The alphabet to use for encoding | |
""" | |
base = len(alphabet) | |
num = 0 | |
try: | |
for char in string: | |
num *= base | |
num += alphabet.index(char) | |
except: | |
#character not found (like a space character or a 0) | |
return 0 | |
return num | |
def encodeVarint(integer): | |
if integer < 0: | |
logger.error('varint cannot be < 0') | |
raise SystemExit | |
if integer < 253: | |
return pack('>B',integer) | |
if integer >= 253 and integer < 65536: | |
return pack('>B',253) + pack('>H',integer) | |
if integer >= 65536 and integer < 4294967296: | |
return pack('>B',254) + pack('>I',integer) | |
if integer >= 4294967296 and integer < 18446744073709551616: | |
return pack('>B',255) + pack('>Q',integer) | |
if integer >= 18446744073709551616: | |
logger.error('varint cannot be >= 18446744073709551616') | |
raise SystemExit | |
class varintDecodeError(Exception): | |
pass | |
def decodeVarint(data): | |
""" | |
Decodes an encoded varint to an integer and returns it. | |
Per protocol v3, the encoded value must be encoded with | |
the minimum amount of data possible or else it is malformed. | |
Returns a tuple: (theEncodedValue, theSizeOfTheVarintInBytes) | |
""" | |
if len(data) == 0: | |
return (0,0) | |
firstByte, = unpack('>B',data[0:1]) | |
if firstByte < 253: | |
# encodes 0 to 252 | |
return (firstByte,1) #the 1 is the length of the varint | |
if firstByte == 253: | |
# encodes 253 to 65535 | |
if len(data) < 3: | |
raise varintDecodeError('The first byte of this varint as an integer is %s but the total length is only %s. It needs to be at least 3.' % (firstByte, len(data))) | |
encodedValue, = unpack('>H',data[1:3]) | |
if encodedValue < 253: | |
raise varintDecodeError('This varint does not encode the value with the lowest possible number of bytes.') | |
return (encodedValue,3) | |
if firstByte == 254: | |
# encodes 65536 to 4294967295 | |
if len(data) < 5: | |
raise varintDecodeError('The first byte of this varint as an integer is %s but the total length is only %s. It needs to be at least 5.' % (firstByte, len(data))) | |
encodedValue, = unpack('>I',data[1:5]) | |
if encodedValue < 65536: | |
raise varintDecodeError('This varint does not encode the value with the lowest possible number of bytes.') | |
return (encodedValue,5) | |
if firstByte == 255: | |
# encodes 4294967296 to 18446744073709551615 | |
if len(data) < 9: | |
raise varintDecodeError('The first byte of this varint as an integer is %s but the total length is only %s. It needs to be at least 9.' % (firstByte, len(data))) | |
encodedValue, = unpack('>Q',data[1:9]) | |
if encodedValue < 4294967296: | |
raise varintDecodeError('This varint does not encode the value with the lowest possible number of bytes.') | |
return (encodedValue,9) | |
def calculateInventoryHash(data): | |
sha = hashlib.new('sha512') | |
sha2 = hashlib.new('sha512') | |
sha.update(data) | |
sha2.update(sha.digest()) | |
return sha2.digest()[0:32] | |
def encodeAddress(version,stream,ripe): | |
if version >= 2 and version < 4: | |
if len(ripe) != 20: | |
raise Exception("Programming error in encodeAddress: The length of a given ripe hash was not 20.") | |
if ripe[:2] == '\x00\x00': | |
ripe = ripe[2:] | |
elif ripe[:1] == '\x00': | |
ripe = ripe[1:] | |
elif version == 4: | |
if len(ripe) != 20: | |
raise Exception("Programming error in encodeAddress: The length of a given ripe hash was not 20.") | |
ripe = ripe.lstrip('\x00') | |
storedBinaryData = encodeVarint(version) + encodeVarint(stream) + ripe | |
# Generate the checksum | |
sha = hashlib.new('sha512') | |
sha.update(storedBinaryData) | |
currentHash = sha.digest() | |
sha = hashlib.new('sha512') | |
sha.update(currentHash) | |
checksum = sha.digest()[0:4] | |
asInt = int(hexlify(storedBinaryData) + hexlify(checksum),16) | |
return 'BM-'+ encodeBase58(asInt) | |
def decodeAddress(address): | |
#returns (status, address version number, stream number, data (almost certainly a ripe hash)) | |
address = str(address).strip() | |
if address[:3] == 'BM-': | |
integer = decodeBase58(address[3:]) | |
else: | |
integer = decodeBase58(address) | |
if integer == 0: | |
status = 'invalidcharacters' | |
return status,0,0,"" | |
#after converting to hex, the string will be prepended with a 0x and appended with a L | |
hexdata = hex(integer)[2:-1] | |
if len(hexdata) % 2 != 0: | |
hexdata = '0' + hexdata | |
#print 'hexdata', hexdata | |
data = unhexlify(hexdata) | |
checksum = data[-4:] | |
sha = hashlib.new('sha512') | |
sha.update(data[:-4]) | |
currentHash = sha.digest() | |
#print 'sha after first hashing: ', sha.hexdigest() | |
sha = hashlib.new('sha512') | |
sha.update(currentHash) | |
#print 'sha after second hashing: ', sha.hexdigest() | |
if checksum != sha.digest()[0:4]: | |
status = 'checksumfailed' | |
return status,0,0,"" | |
#else: | |
# print 'checksum PASSED' | |
try: | |
addressVersionNumber, bytesUsedByVersionNumber = decodeVarint(data[:9]) | |
except varintDecodeError as e: | |
logger.error(str(e)) | |
status = 'varintmalformed' | |
return status,0,0,"" | |
#print 'addressVersionNumber', addressVersionNumber | |
#print 'bytesUsedByVersionNumber', bytesUsedByVersionNumber | |
if addressVersionNumber > 4: | |
logger.error('cannot decode address version numbers this high') | |
status = 'versiontoohigh' | |
return status,0,0,"" | |
elif addressVersionNumber == 0: | |
logger.error('cannot decode address version numbers of zero.') | |
status = 'versiontoohigh' | |
return status,0,0,"" | |
try: | |
streamNumber, bytesUsedByStreamNumber = decodeVarint(data[bytesUsedByVersionNumber:]) | |
except varintDecodeError as e: | |
logger.error(str(e)) | |
status = 'varintmalformed' | |
return status,0,0,"" | |
#print streamNumber | |
status = 'success' | |
if addressVersionNumber == 1: | |
return status,addressVersionNumber,streamNumber,data[-24:-4] | |
elif addressVersionNumber == 2 or addressVersionNumber == 3: | |
embeddedRipeData = data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4] | |
if len(embeddedRipeData) == 19: | |
return status,addressVersionNumber,streamNumber,'\x00'+embeddedRipeData | |
elif len(embeddedRipeData) == 20: | |
return status,addressVersionNumber,streamNumber,embeddedRipeData | |
elif len(embeddedRipeData) == 18: | |
return status,addressVersionNumber,streamNumber,'\x00\x00'+embeddedRipeData | |
elif len(embeddedRipeData) < 18: | |
return 'ripetooshort',0,0,"" | |
elif len(embeddedRipeData) > 20: | |
return 'ripetoolong',0,0,"" | |
else: | |
return 'otherproblem',0,0,"" | |
elif addressVersionNumber == 4: | |
embeddedRipeData = data[bytesUsedByVersionNumber+bytesUsedByStreamNumber:-4] | |
if embeddedRipeData[0:1] == '\x00': | |
# In order to enforce address non-malleability, encoded RIPE data must have NULL bytes removed from the front | |
return 'encodingproblem',0,0,"" | |
elif len(embeddedRipeData) > 20: | |
return 'ripetoolong',0,0,"" | |
elif len(embeddedRipeData) < 4: | |
return 'ripetooshort',0,0,"" | |
else: | |
x00string = '\x00' * (20 - len(embeddedRipeData)) | |
return status,addressVersionNumber,streamNumber,x00string+embeddedRipeData | |
def addBMIfNotPresent(address): | |
address = str(address).strip() | |
if address[:3] != 'BM-': | |
return 'BM-'+address | |
else: | |
return address | |
if __name__ == "__main__": | |
print 'Let us make an address from scratch. Suppose we generate two random 32 byte values and call the first one the signing key and the second one the encryption key:' | |
privateSigningKey = '93d0b61371a54b53df143b954035d612f8efa8a3ed1cf842c2186bfd8f876665' | |
privateEncryptionKey = '4b0b73a54e19b059dc274ab69df095fe699f43b17397bca26fdf40f4d7400a3a' | |
print 'privateSigningKey =', privateSigningKey | |
print 'privateEncryptionKey =', privateEncryptionKey | |
print 'Now let us convert them to public keys by doing an elliptic curve point multiplication.' | |
publicSigningKey = arithmetic.privtopub(privateSigningKey) | |
publicEncryptionKey = arithmetic.privtopub(privateEncryptionKey) | |
print 'publicSigningKey =', publicSigningKey | |
print 'publicEncryptionKey =', publicEncryptionKey | |
print 'Notice that they both begin with the \\x04 which specifies the encoding type. This prefix is not send over the wire. You must strip if off before you send your public key across the wire, and you must add it back when you receive a public key.' | |
publicSigningKeyBinary = arithmetic.changebase(publicSigningKey,16,256,minlen=64) | |
publicEncryptionKeyBinary = arithmetic.changebase(publicEncryptionKey,16,256,minlen=64) | |
ripe = hashlib.new('ripemd160') | |
sha = hashlib.new('sha512') | |
sha.update(publicSigningKeyBinary+publicEncryptionKeyBinary) | |
ripe.update(sha.digest()) | |
addressVersionNumber = 2 | |
streamNumber = 1 | |
print 'Ripe digest that we will encode in the address:', hexlify(ripe.digest()) | |
returnedAddress = encodeAddress(addressVersionNumber,streamNumber,ripe.digest()) | |
print 'Encoded address:', returnedAddress | |
status,addressVersionNumber,streamNumber,data = decodeAddress(returnedAddress) | |
print '\nAfter decoding address:' | |
print 'Status:', status | |
print 'addressVersionNumber', addressVersionNumber | |
print 'streamNumber', streamNumber | |
print 'length of data(the ripe hash):', len(data) | |
print 'ripe data:', hexlify(data) | |
This file contains 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
import sys, os, base64, hashlib, time | |
from struct import * | |
from pyelliptic.openssl import OpenSSL | |
import ctypes | |
from pyelliptic import arithmetic | |
from addresses import * | |
import threading | |
#Does an EC point multiplication; turns a private key into a public key. | |
def pointMult(secret): | |
#ctx = OpenSSL.BN_CTX_new() #This value proved to cause Seg Faults on Linux. It turns out that it really didn't speed up EC_POINT_mul anyway. | |
k = OpenSSL.EC_KEY_new_by_curve_name(OpenSSL.get_curve('secp256k1')) | |
priv_key = OpenSSL.BN_bin2bn(secret, 32, 0) | |
group = OpenSSL.EC_KEY_get0_group(k) | |
pub_key = OpenSSL.EC_POINT_new(group) | |
OpenSSL.EC_POINT_mul(group, pub_key, priv_key, None, None, None) | |
OpenSSL.EC_KEY_set_private_key(k, priv_key) | |
OpenSSL.EC_KEY_set_public_key(k, pub_key) | |
#print 'priv_key',priv_key | |
#print 'pub_key',pub_key | |
size = OpenSSL.i2o_ECPublicKey(k, 0) | |
mb = ctypes.create_string_buffer(size) | |
OpenSSL.i2o_ECPublicKey(k, ctypes.byref(ctypes.pointer(mb))) | |
#print 'mb.raw', mb.raw.encode('hex'), 'length:', len(mb.raw) | |
#print 'mb.raw', mb.raw, 'length:', len(mb.raw) | |
OpenSSL.EC_POINT_free(pub_key) | |
#OpenSSL.BN_CTX_free(ctx) | |
OpenSSL.BN_free(priv_key) | |
OpenSSL.EC_KEY_free(k) | |
return mb.raw | |
found_one = False | |
def thread_main(): | |
global found_one | |
prefix = args[0] | |
deterministicNonce = 0 | |
startTime = time.time() | |
while found_one != True: | |
#We generate addresses based off of a secure random string plus an integer nonce. | |
deterministicNall = base64.b64encode(os.urandom(options.bytes)) | |
address="" | |
while found_one != True: | |
#This next section is a little bit strange. We're going to generate keys over and over until we | |
#find one that has a RIPEMD hash that starts with either \x00 or \x00\x00. Then when we pack them | |
#into a Bitmessage address, we won't store the \x00 or \x00\x00 bytes thus making the address shorter. | |
signingKeyNonce = 0 | |
encryptionKeyNonce = 1 | |
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix = 0 | |
deterministicPassphrase = deterministicNall + str(deterministicNonce) | |
while found_one != True: #find a keypair pair whose hash starts with \x00 | |
numberOfAddressesWeHadToMakeBeforeWeFoundOneWithTheCorrectRipePrefix += 1 | |
potentialPrivSigningKey = hashlib.sha512(deterministicPassphrase + encodeVarint(signingKeyNonce)).digest()[:32] | |
potentialPrivEncryptionKey = hashlib.sha512(deterministicPassphrase + encodeVarint(encryptionKeyNonce)).digest()[:32] | |
potentialPubSigningKey = pointMult(potentialPrivSigningKey) | |
potentialPubEncryptionKey = pointMult(potentialPrivEncryptionKey) | |
signingKeyNonce += 2 | |
encryptionKeyNonce += 2 | |
ripe = hashlib.new('ripemd160') | |
sha = hashlib.new('sha512') | |
sha.update(potentialPubSigningKey+potentialPubEncryptionKey) | |
ripe.update(sha.digest()) | |
#print 'potential ripe.digest', ripe.digest().encode('hex') | |
if options.eighteenByteRipe: | |
if ripe.digest()[:2] == '\x00\x00': | |
break | |
else: | |
if ripe.digest()[:1] == '\x00': | |
break | |
address = encodeAddress(4,options.streamNumber,ripe.digest()) | |
deterministicNonce += 1 | |
if options.insensitive: | |
if (address[:len(prefix)].lower() == prefix.lower()): | |
print address +" "+ str(deterministicPassphrase) | |
found_one = True | |
if not options.quiet: | |
print "Generated " + str(deterministicNonce) + " addresses in " + str(time.time() - startTime) + " seconds." | |
break | |
else: | |
if (address[:len(prefix)] == prefix): | |
print address +" "+ str(deterministicPassphrase) | |
found_one = True | |
if not options.quiet: | |
print "Generated " + str(deterministicNonce) + " addresses in " + str(time.time() - startTime) + " seconds." | |
break | |
if not options.keep: | |
break | |
from optparse import OptionParser | |
usage = "usage: %prog [options] prefix" | |
parser = OptionParser(usage=usage) | |
parser.add_option("-i", "--insensitive", | |
action="store_true", dest="insensitive", default=False, | |
help="Case-insensitive prefix search") | |
parser.add_option("-e", "--short", | |
action="store_true", dest="eighteenByteRipe", default=False, | |
help="Require 'generate shorter address' to be checked") | |
parser.add_option("-s", "--stream", | |
dest="streamNumber", default=1, | |
help="Use this stream number (default 1)") | |
parser.add_option("-k", "--continue", | |
action="store_true", dest="keep", default=False, | |
help="Keep address and continue search after finding a match") | |
parser.add_option("-b", "--bytes", | |
dest="bytes", default=63, type="int", | |
help="Number of secure random bytes to base the passphrase on (default 63)") | |
parser.add_option("-t", "--threads", | |
dest="threadCount", default=1, type="int", | |
help="Number of threads to use (default 1)") | |
parser.add_option("-q", "--quiet", | |
action="store_true", dest="quiet", default=False, | |
help="Only print addresses and privkeys") | |
(options, args) = parser.parse_args() | |
if len(args) == 0: | |
parser.print_help() | |
sys.exit() | |
threads = [] | |
for i in range(0,options.threadCount): | |
t = threading.Thread(target=thread_main,) | |
threads.append(t) | |
t.start() | |
# wait for all the threads to complete | |
for thread in threads: | |
thread.join() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment