From 1a824f06e67215c5ff91328d6ff9711263de8b5e Mon Sep 17 00:00:00 2001 From: Muneeb Ali Date: Sat, 28 Jun 2014 17:56:21 -0700 Subject: [PATCH] Added bip38 decryption files, tested out register and activate on new DB, and added profile conversion code (v1 to v2) --- .../blockdata/activate.py | 5 +- .../blockdata/register.py | 78 ++++++----------- blockstack_cli/blockstack_registrar/config.py | 9 +- .../blockstack_registrar/encrypt/__init__.py | 0 .../blockstack_registrar/encrypt/bip38.py | 83 +++++++++++++++++++ .../tools/profile_conversion.py | 61 ++++++++++++++ 6 files changed, 176 insertions(+), 60 deletions(-) create mode 100644 blockstack_cli/blockstack_registrar/encrypt/__init__.py create mode 100755 blockstack_cli/blockstack_registrar/encrypt/bip38.py create mode 100755 blockstack_cli/blockstack_registrar/tools/profile_conversion.py diff --git a/blockstack_cli/blockstack_registrar/blockdata/activate.py b/blockstack_cli/blockstack_registrar/blockdata/activate.py index 16076ba98..8102c698e 100755 --- a/blockstack_cli/blockstack_registrar/blockdata/activate.py +++ b/blockstack_cli/blockstack_registrar/blockdata/activate.py @@ -5,7 +5,7 @@ import json import requests from time import sleep -from coinrpc.coinrpc import namecoind_blocks, namecoind_firstupdate +from coinrpc.namecoin.namecoind_wrapper import namecoind_blocks, namecoind_firstupdate blocks = namecoind_blocks() @@ -74,7 +74,6 @@ def do_name_firstupdate(): output = namecoind_firstupdate(entry['key'],entry['rand'],update_value,entry['longhex']) - #output = namecoind_firstupdate(entry['key'], entry['rand'], update_value, entry['longhex']) print "Transaction ID ", output if 'message' in output and output['message'] == "this name is already active": @@ -90,7 +89,7 @@ def do_name_firstupdate(): #sleep(1) else: - pass + print "wait: " + str(entry['wait_till_block'] - current_blocks) + " blocks" #----------------------------------- if __name__ == '__main__': diff --git a/blockstack_cli/blockstack_registrar/blockdata/register.py b/blockstack_cli/blockstack_registrar/blockdata/register.py index eb8a5e6c4..75435392d 100755 --- a/blockstack_cli/blockstack_registrar/blockdata/register.py +++ b/blockstack_cli/blockstack_registrar/blockdata/register.py @@ -1,28 +1,34 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- - -SLEEP_INTERVAL = 1 +#----------------------- +# Copyright 2014 Halfmoon Labs, Inc. +# All Rights Reserved +#----------------------- #520 is the real limit VALUE_MAX_LIMIT = 512 -from time import sleep - import requests import json -from coinrpc.coinrpc import namecoind_blocks, namecoind_name_new, check_registration -from coinrpc.coinrpc import namecoind_name_update, namecoind_name_show +from coinrpc.namecoin.namecoind_wrapper import namecoind_blocks, namecoind_name_new, check_registration +from coinrpc.namecoin.namecoind_wrapper import namecoind_name_update, namecoind_name_show + +from config import LOAD_BALANCER #----------------------------------- from pymongo import MongoClient -db = MongoClient['namecoin'] -queue = db.queue -codes = db.codes +client = MongoClient() -from config import MONGODB_URI, HEROKU_APP +local_db = client['namecoin'] +queue = local_db.queue +codes = local_db.codes + +from config import MONGODB_URI remote_client = MongoClient(MONGODB_URI) -users = remote_client[HEROKU_APP].user +remote_db = remote_client.get_default_database() +users = remote_db.user +registrations = remote_db.user_registration #----------------------------------- def utf8len(s): @@ -132,7 +138,6 @@ def register_name(key,value): print reply print '---' - sleep(SLEEP_INTERVAL) #----------------------------------- def update_name(key,value): @@ -157,7 +162,6 @@ def update_name(key,value): print reply print info print '---' - sleep(SLEEP_INTERVAL) #---------------------------------- def get_old_keys(username): @@ -191,7 +195,7 @@ def get_old_keys(username): return old_keys #----------------------------------- -def process_user(username,profile,accesscode=None): +def process_user(username,profile): old_keys = get_old_keys(username) @@ -206,28 +210,8 @@ def process_user(username,profile,accesscode=None): if check_registration(key1): #if name is registered - check_profile = namecoind_name_show(key1) - - try: - check_profile = check_profile['value'] - - #if name is reserved, check code - if 'status' in check_profile and check_profile['status'] == 'reserved': - - print "name reserved: " + key1 - code = codes.find_one({'username':key1}) - if code['accesscode'] == accesscode: - print "code match" - update_name(key1,value1) - else: - #if registered but not reserved - print "name update: " + key1 - update_name(key1,value1) - - except Exception as e: - #if registered but not reserved - print "name update: " + key1 - update_name(key1,value1) + print "name update: " + key1 + update_name(key1,value1) else: #if not registered @@ -290,31 +274,23 @@ def set_backend_server(DISTRIBUTE=True): print "sending " + i['username'] + " to backend_server " + str(selected_server) #----------------------------------- -def check_new_registrations(IS_LIVE=False): +def check_new_registrations(LIVE=True): registered_counter = 0 unregistered_counter = 0 print '-' * 5 print "Checking for new users" - for user in users.find(): + for user in registrations.find(): if 'dispatched' in user and user['dispatched'] is False: - if not IS_LIVE: - print user['username'] - print user['email'] - print '-' * 5 unregistered_counter += 1 - accesscode = None - if 'accesscode' in user: - accesscode = user['accesscode'] - if ('backend_server' in user) and (user['backend_server'] == int(LOAD_BALANCER)): - if IS_LIVE: + if LIVE: try: - process_user(user['username'],json.loads(user['profile']),accesscode) + process_user(user['username'],json.loads(user['profile'])) print user['backend_server'] except Exception as e: print e @@ -326,7 +302,7 @@ def check_new_registrations(IS_LIVE=False): local = queue.find_one({'key':username}) if local is not None: print "in local DB" - if IS_LIVE: + if LIVE: user['dispatched'] = True user['accepted'] = True users.save(user) @@ -342,7 +318,7 @@ def check_new_registrations(IS_LIVE=False): #----------------------------------- if __name__ == '__main__': - IS_LIVE = True + LIVE = True DISTRIBUTE = False set_backend_server(DISTRIBUTE) - check_new_registrations(IS_LIVE) \ No newline at end of file + check_new_registrations(LIVE) \ No newline at end of file diff --git a/blockstack_cli/blockstack_registrar/config.py b/blockstack_cli/blockstack_registrar/config.py index 87c13a09f..4bd004ab3 100644 --- a/blockstack_cli/blockstack_registrar/config.py +++ b/blockstack_cli/blockstack_registrar/config.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# -*- coding: utf-8 -*- #----------------------- # Copyright 2014 Halfmoon Labs, Inc. # All Rights Reserved @@ -17,7 +18,7 @@ except: DEBUG = True #-------------------------------------------------- - NAMECOIND_READONLY = True + NAMECOIND_READONLY = False NAMECOIND_USE_HTTPS = True @@ -29,8 +30,4 @@ except: #-------------------------------------------------- MONGODB_URI = os.environ['MONGODB_URI'] - HEROKU_APP = os.environ['HEROKU_APP'] - - LOAD_BALANCER = os.environ['LOAD_BALANCER'] - - + LOAD_BALANCER = os.environ['LOAD_BALANCER'] \ No newline at end of file diff --git a/blockstack_cli/blockstack_registrar/encrypt/__init__.py b/blockstack_cli/blockstack_registrar/encrypt/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/blockstack_cli/blockstack_registrar/encrypt/bip38.py b/blockstack_cli/blockstack_registrar/encrypt/bip38.py new file mode 100755 index 000000000..c35e8e64e --- /dev/null +++ b/blockstack_cli/blockstack_registrar/encrypt/bip38.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +#----------------------- +# Copyright 2014 Halfmoon Labs, Inc. +# All Rights Reserved +#----------------------- + +""" + BIP 0038 + ~~~~~ + + :copyright: (c) 2014 by Halfmoon Labs + :license: MIT, see LICENSE for more details. +""" + +import re, os, random, struct, hashlib, binascii, scrypt +from hashlib import sha256 +from coinkit import BitcoinKeypair, b58check_encode, b58check_decode +from Crypto.Cipher import AES + +def bip38_encrypt(private_key, passphrase, n=16384, r=8, p=8, compressed=False): + # determine the flagbyte + if compressed: + flagbyte = '\xe0' + else: + flagbyte = '\xc0' + # get the private key's address and equivalent in hex format and wif format + k = BitcoinKeypair(private_key) + address = k.address() + hex_private_key = k.private_key() + wif_private_key = k.wif_pk() + # calculate the address's checksum + address_checksum = sha256(sha256(address).digest()).digest()[0:4] + # calculate the scrypt hash and split it in half + scrypt_derived_key = scrypt.hash(passphrase, address_checksum, n, r, p) + derived_half_1 = scrypt_derived_key[0:32] + derived_half_2 = scrypt_derived_key[32:64] + # combine parts of the private key and scrypt hash and AES encrypt them + aes = AES.new(derived_half_2) + encrypted_half_1 = aes.encrypt( + binascii.unhexlify( + '%0.32x' % (long(hex_private_key[0:32], 16) ^ long(binascii.hexlify(derived_half_1[0:16]), 16)) + ) + ) + encrypted_half_2 = aes.encrypt( + binascii.unhexlify( + '%0.32x' % (long(hex_private_key[32:64], 16) ^ long(binascii.hexlify(derived_half_1[16:32]), 16)) + ) + ) + # build the encrypted private key from the checksum and encrypted parts + encrypted_private_key = ('\x42' + flagbyte + address_checksum + encrypted_half_1 + encrypted_half_2) + # base 58 encode the encrypted private key + b58check_encrypted_private_key = b58check_encode(encrypted_private_key, version_byte=1) + # return the encrypted private key + return b58check_encrypted_private_key + +def bip38_decrypt(b58check_encrypted_private_key, passphrase, n=16384, r=8, p=8): + # decode private key from base 58 check to binary + encrypted_private_key = b58check_decode(b58check_encrypted_private_key) + # parse the encrypted key different byte sections + bip38_key_identification_byte = encrypted_private_key[0:1] + flagbyte = encrypted_private_key[1:2] + address_checksum = encrypted_private_key[2:6] + encrypted_half_1 = encrypted_private_key[6:6+16] + encrypted_half_2 = encrypted_private_key[6+16:6+32] + # derive a unique key from the passphrase and the address checksum + scrypt_derived_key = scrypt.hash(passphrase, address_checksum, n, r, p) + derived_half_1 = scrypt_derived_key[0:32] + derived_half_2 = scrypt_derived_key[32:64] + # decrypt the encrypted halves + aes = AES.new(derived_half_2) + decrypted_half_1 = aes.decrypt(encrypted_half_1) + decrypted_half_2 = aes.decrypt(encrypted_half_2) + # get the original private key from the encrypted halves + the derived half + decrypted_private_key = '%064x' % (long(binascii.hexlify(decrypted_half_1 + decrypted_half_2), 16) ^ long(binascii.hexlify(derived_half_1), 16)) + # get the address corresponding to the private key + k = BitcoinKeypair(decrypted_private_key) + address = k.address() + # make sure the address matches the checksum in the original encrypted key + if address_checksum != sha256(sha256(address).digest()).digest()[0:4]: + raise ValueError('Invalid private key and password combo.') + # return the decrypted private key + return k.private_key() diff --git a/blockstack_cli/blockstack_registrar/tools/profile_conversion.py b/blockstack_cli/blockstack_registrar/tools/profile_conversion.py new file mode 100755 index 000000000..d0beafe03 --- /dev/null +++ b/blockstack_cli/blockstack_registrar/tools/profile_conversion.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +#----------------------- +# Copyright 2014 Halfmoon Labs, Inc. +# All Rights Reserved +#----------------------- + +#----------------------------------- +def convert_v1_to_v2(profile): + + new_profile = {} + + if 'v' in profile: + new_profile['v'] = '0.2' + + if 'website' in profile: + new_profile['website'] = profile['website'] + + if 'bio' in profile: + new_profile['bio'] = profile['bio'] + + if 'github' in profile: + new_profile['github'] = profile['github'] + + if 'instagram' in profile: + new_profile['instagram'] = {"username":profile['instagram']} + + if 'twitter' in profile: + new_profile['twitter'] = {"username":profile['twitter']} + + if 'cover' in profile: + new_profile['cover'] = {"url":profile['cover']} + + if 'avatar' in profile: + new_profile['avatar'] = {"url":profile['avatar']} + + if 'bitcoin' in profile: + new_profile['bitcoin'] = {"address":profile['bitcoin']} + + if 'linkedin' in profile: + new_profile['linkedin'] = {"url":profile['linkedin']} + + if 'name' in profile: + new_profile['name'] = {"formatted":profile['name']} + + if 'facebook' in profile: + new_profile['facebook'] = {"username":profile['facebook']} + + if 'location' in profile: + new_profile['location'] = {"formatted":profile['location']} + + if 'angellist' in profile: + new_profile['angellist'] = {"username":profile['angellist']} + + if 'bitmessage' in profile: + new_profile['bitmessage'] = {"address":profile['bitmessage']} + + if 'pgp' in profile: + new_profile['pgp'] = profile['pgp'] + + return new_profile