class CryptoTransportLayer(TransportLayer): def __init__(self, my_ip, my_port, market_id, db, bm_user=None, bm_pass=None, bm_port=None, seed_mode=0, dev_mode=False, disable_ip_update=False): self.log = logging.getLogger( '[%s] %s' % (market_id, self.__class__.__name__) ) requests_log = logging.getLogger("requests") requests_log.setLevel(logging.WARNING) # Connect to database self.db = db self.bitmessage_api = None if (bm_user, bm_pass, bm_port) != (None, None, None): if not self._connect_to_bitmessage(bm_user, bm_pass, bm_port): self.log.info('Bitmessage not installed or started') try: socket.inet_pton(socket.AF_INET6, my_ip) my_uri = "tcp://[%s]:%s" % (my_ip, my_port) except (socket.error, ValueError): my_uri = "tcp://%s:%s" % (my_ip, my_port) self.market_id = market_id self.nick_mapping = {} self.uri = my_uri self.ip = my_ip self.nickname = "" self._dev_mode = dev_mode # Set up self._setup_settings() self.dht = DHT(self, self.market_id, self.settings, self.db) # self._myself = ec.ECC(pubkey=self.pubkey.decode('hex'), # privkey=self.secret.decode('hex'), # curve='secp256k1') TransportLayer.__init__(self, market_id, my_ip, my_port, self.guid, self.nickname) self.setup_callbacks() self.listen(self.pubkey) if seed_mode == 0 and not dev_mode and not disable_ip_update: self.start_ip_address_checker() def setup_callbacks(self): self.add_callbacks([('hello', self._ping), ('findNode', self._find_node), ('findNodeResponse', self._find_node_response), ('store', self._store_value)]) def start_ip_address_checker(self): '''Checks for possible public IP change''' self.caller = PeriodicCallback(self._ip_updater_periodic_callback, 5000, ioloop.IOLoop.instance()) self.caller.start() def _ip_updater_periodic_callback(self): try: r = requests.get('http://ipv4.icanhazip.com') if r and hasattr(r, 'text'): ip = r.text ip = ip.strip(' \t\n\r') if ip != self.ip: self.ip = ip try: socket.inet_pton(socket.AF_INET6, self.ip) my_uri = 'tcp://[%s]:%s' % (self.ip, self.port) except (socket.error, ValueError): my_uri = 'tcp://%s:%s' % (self.ip, self.port) self.uri = my_uri self.stream.close() self.listen(self.pubkey) self.dht._iterativeFind(self.guid, [], 'findNode') else: self.log.error('Could not get IP') except Exception as e: self.log.error('[Requests] error: %s' % e) def save_peer_to_db(self, peer_tuple): uri = peer_tuple[0] pubkey = peer_tuple[1] guid = peer_tuple[2] nickname = peer_tuple[3] # Update query self.db.deleteEntries("peers", {"uri": uri, "guid": guid}, "OR") # if len(results) > 0: # self.db.updateEntries("peers", {"id": results[0]['id']}, {"market_id": self.market_id, "uri": uri, "pubkey": pubkey, "guid": guid, "nickname": nickname}) # else: if guid is not None: self.db.insertEntry("peers", { "uri": uri, "pubkey": pubkey, "guid": guid, "nickname": nickname, "market_id": self.market_id }) def _connect_to_bitmessage(self, bm_user, bm_pass, bm_port): # Get bitmessage going # First, try to find a local instance result = False try: self.log.info('[_connect_to_bitmessage] Connecting to Bitmessage on port %s' % bm_port) self.bitmessage_api = xmlrpclib.ServerProxy("http://{}:{}@localhost:{}/".format(bm_user, bm_pass, bm_port), verbose=0) result = self.bitmessage_api.add(2, 3) self.log.info("[_connect_to_bitmessage] Bitmessage API is live: %s", result) # If we failed, fall back to starting our own except Exception as e: self.log.info("Failed to connect to bitmessage instance: {}".format(e)) self.bitmessage_api = None # self._log.info("Spawning internal bitmessage instance") # # Add bitmessage submodule path # sys.path.insert(0, os.path.join( # os.path.dirname(__file__), '..', 'pybitmessage', 'src')) # import bitmessagemain as bitmessage # bitmessage.logger.setLevel(logging.WARNING) # bitmessage_instance = bitmessage.Main() # bitmessage_instance.start(daemon=True) # bminfo = bitmessage_instance.getApiAddress() # if bminfo is not None: # self._log.info("Started bitmessage daemon at %s:%s".format( # bminfo['address'], bminfo['port'])) # bitmessage_api = xmlrpclib.ServerProxy("http://{}:{}@{}:{}/".format( # bm_user, bm_pass, bminfo['address'], bminfo['port'])) # else: # self._log.info("Failed to start bitmessage dameon") # self._bitmessage_api = None return result def _checkok(self, msg): self.log.info('Check ok') def get_guid(self): return self.guid def get_dht(self): return self.dht def get_bitmessage_api(self): return self.bitmessage_api def get_market_id(self): return self.market_id # def get_myself(self): # return self._myself def _ping(self, msg): self.log.info('Pinged %s ' % json.dumps(msg, ensure_ascii=False)) # # pinger = CryptoPeerConnection(self, msg['uri'], msg['pubkey'], msg['senderGUID']) # pinger.send_raw(json.dumps( # {"type": "hello_response", # "senderGUID": self.guid, # "uri": self.uri, # "senderNick": self.nickname, # "pubkey": self.pubkey, # })) def _store_value(self, msg): self.dht._on_storeValue(msg) def _find_node(self, msg): self.dht.on_find_node(msg) def _find_node_response(self, msg): self.dht.on_findNodeResponse(self, msg) def _setup_settings(self): try: self.settings = self.db.selectEntries("settings", {"market_id": self.market_id}) except (OperationalError, DatabaseError) as e: print e raise SystemExit("database file %s corrupt or empty - cannot continue" % self.db.db_path) if len(self.settings) == 0: self.settings = {"market_id": self.market_id, "welcome": "enable"} self.db.insertEntry("settings", self.settings) else: self.settings = self.settings[0] # Generate PGP key during initial setup or if previous PGP gen failed if not ('PGPPubKey' in self.settings and self.settings["PGPPubKey"]): try: self.log.info('Generating PGP keypair. This may take several minutes...') print 'Generating PGP keypair. This may take several minutes...' gpg = gnupg.GPG() input_data = gpg.gen_key_input(key_type="RSA", key_length=2048, name_email='*****@*****.**', name_comment="Autogenerated by Open Bazaar", passphrase="P@ssw0rd") assert input_data is not None key = gpg.gen_key(input_data) assert key is not None pubkey_text = gpg.export_keys(key.fingerprint) newsettings = {"PGPPubKey": pubkey_text, "PGPPubkeyFingerprint": key.fingerprint} self.db.updateEntries("settings", {"market_id": self.market_id}, newsettings) self.settings.update(newsettings) self.log.info('PGP keypair generated.') except Exception as e: self.log.error("Encountered a problem with GPG: %s" % e) raise SystemExit("Encountered a problem with GPG: %s" % e) if not ('pubkey' in self.settings and self.settings['pubkey']): # Generate Bitcoin keypair self._generate_new_keypair() if not ('nickname' in self.settings and self.settings['nickname']): newsettings = {'nickname': 'Default'} self.db.updateEntries('settings', {"market_id": self.market_id}, newsettings) self.settings.update(newsettings) self.nickname = self.settings['nickname'] if 'nickname' in self.settings else "" self.secret = self.settings['secret'] if 'secret' in self.settings else "" self.pubkey = self.settings['pubkey'] if 'pubkey' in self.settings else "" self.privkey = self.settings.get('privkey') self.btc_pubkey = privkey_to_pubkey(self.privkey) self.guid = self.settings['guid'] if 'guid' in self.settings else "" self.sin = self.settings['sin'] if 'sin' in self.settings else "" self.bitmessage = self.settings['bitmessage'] if 'bitmessage' in self.settings else "" if not ('bitmessage' in self.settings and self.settings['bitmessage']): # Generate Bitmessage address if self.bitmessage_api is not None: self._generate_new_bitmessage_address() self._myself = ec.ECC( pubkey=pubkey_to_pyelliptic(self.pubkey).decode('hex'), raw_privkey=self.secret.decode('hex'), curve='secp256k1' ) self.log.debug('Retrieved Settings: \n%s', pformat(self.settings)) def _generate_new_keypair(self): secret = str(random.randrange(2 ** 256)) self.secret = hashlib.sha256(secret).hexdigest() self.pubkey = privtopub(self.secret) self.privkey = random_key() print 'PRIVATE KEY: ', self.privkey self.btc_pubkey = privtopub(self.privkey) print 'PUBLIC KEY: ', self.btc_pubkey # Generate SIN sha_hash = hashlib.sha256() sha_hash.update(self.pubkey) ripe_hash = hashlib.new('ripemd160') ripe_hash.update(sha_hash.digest()) self.guid = ripe_hash.digest().encode('hex') self.sin = obelisk.EncodeBase58Check('\x0F\x02%s' + ripe_hash.digest()) newsettings = { "secret": self.secret, "pubkey": self.pubkey, "privkey": self.privkey, "guid": self.guid, "sin": self.sin } self.db.updateEntries("settings", {"market_id": self.market_id}, newsettings) self.settings.update(newsettings) def _generate_new_bitmessage_address(self): # Use the guid generated previously as the key self.bitmessage = self.bitmessage_api.createRandomAddress( self.guid.encode('base64'), False, 1.05, 1.1111 ) newsettings = {"bitmessage": self.bitmessage} self.db.updateEntries("settings", {"market_id": self.market_id}, newsettings) self.settings.update(newsettings) def join_network(self, seed_peers=None, callback=lambda msg: None): if seed_peers is None: seed_peers = [] self.log.info('Joining network') known_peers = [] # Connect up through seed servers for idx, seed in enumerate(seed_peers): try: socket.inet_pton(socket.AF_INET6, seed) seed_peers[idx] = "tcp://[%s]:12345" % seed except (socket.error, ValueError): seed_peers[idx] = "tcp://%s:12345" % seed # Connect to persisted peers db_peers = self.get_past_peers() known_peers = list(set(seed_peers)) + list(set(db_peers)) self.connect_to_peers(known_peers) # TODO: This needs rethinking. Normally we can search for ourselves # but because we are not connected to them quick enough this # will always fail. Need @gubatron to review # Populate routing table by searching for self # if len(known_peers) > 0: # self.search_for_my_node() if callback is not None: callback('Joined') def get_past_peers(self): peers = [] result = self.db.selectEntries("peers", {"market_id": self.market_id}) for peer in result: peers.append(peer['uri']) return peers def search_for_my_node(self): print 'Searching for myself' self.dht._iterativeFind(self.guid, self.dht.knownNodes, 'findNode') def connect_to_peers(self, known_peers): for known_peer in known_peers: t = Thread(target=self.dht.add_peer, args=(self, known_peer,)) t.start() def get_crypto_peer(self, guid=None, uri=None, pubkey=None, nickname=None, callback=None): if guid == self.guid: self.log.error('Cannot get CryptoPeerConnection for your own node') return self.log.debug('Getting CryptoPeerConnection' + '\nGUID:%s\nURI:%s\nPubkey:%s\nNickname:%s' % (guid, uri, pubkey, nickname)) return connection.CryptoPeerConnection(self, uri, pubkey, guid=guid, nickname=nickname) def addCryptoPeer(self, peer_to_add): foundOutdatedPeer = False for idx, peer in enumerate(self.dht.activePeers): if (peer.address, peer.guid, peer.pub) == \ (peer_to_add.address, peer_to_add.guid, peer_to_add.pub): self.log.info('Found existing peer, not adding.') return if peer.guid == peer_to_add.guid or \ peer.pub == peer_to_add.pub or \ peer.address == peer_to_add.address: foundOutdatedPeer = True self.log.info('Found an outdated peer') # Update existing peer self.activePeers[idx] = peer_to_add self.dht.add_peer(self, peer_to_add.address, peer_to_add.pub, peer_to_add.guid, peer_to_add.nickname) if not foundOutdatedPeer and peer_to_add.guid != self.guid: self.log.info('Adding crypto peer at %s' % peer_to_add.nickname) self.dht.add_peer(self, peer_to_add.address, peer_to_add.pub, peer_to_add.guid, peer_to_add.nickname) def get_profile(self): peers = {} self.settings = self.db.selectEntries("settings", {"market_id": self.market_id})[0] for uri, peer in self.peers.iteritems(): if peer.pub: peers[uri] = peer.pub.encode('hex') return {'uri': self.uri, 'pub': self._myself.get_pubkey().encode('hex'), 'nickname': self.nickname, 'peers': peers} def respond_pubkey_if_mine(self, nickname, ident_pubkey): if ident_pubkey != self.pubkey: self.log.info("Public key does not match your identity") return # Return signed pubkey pubkey = self._myself.pubkey ec_key = obelisk.EllipticCurveKey() ec_key.set_secret(self.secret) digest = obelisk.Hash(pubkey) signature = ec_key.sign(digest) # Send array of nickname, pubkey, signature to transport layer self.send(proto_response_pubkey(nickname, pubkey, signature)) def pubkey_exists(self, pub): for peer in self.peers.itervalues(): self.log.info( 'PEER: %s Pub: %s' % ( peer.pub.encode('hex'), pub.encode('hex') ) ) if peer.pub.encode('hex') == pub.encode('hex'): return True return False def create_peer(self, uri, pub, node_guid): if pub: pub = pub.decode('hex') # Create the peer if public key is not already in the peer list # if not self.pubkey_exists(pub): self.peers[uri] = connection.CryptoPeerConnection(self, uri, pub, node_guid) # Call 'peer' callbacks on listeners self.trigger_callbacks('peer', self.peers[uri]) # else: # print 'Pub Key is already in peer list' def send(self, data, send_to=None, callback=lambda msg: None): self.log.debug("Outgoing Data: %s %s" % (data, send_to)) # Directed message if send_to is not None: peer = self.dht.routingTable.getContact(send_to) if not peer: for activePeer in self.dht.activePeers: if activePeer.guid == send_to: peer = activePeer break # peer = CryptoPeerConnection(msg['uri']) if peer: self.log.debug('Directed Data (%s): %s' % (send_to, data)) try: peer.send(data, callback=callback) except Exception as e: self.log.error('Not sending message directly to peer %s' % e) else: self.log.error('No peer found') else: # FindKey and then send for peer in self.dht.activePeers: try: peer = self.dht.routingTable.getContact(peer.guid) data['senderGUID'] = self.guid data['pubkey'] = self.pubkey def cb(msg): self.log.debug('Message Back: \n%s' % pformat(msg)) peer.send(data, cb) except: self.log.info("Error sending over peer!") traceback.print_exc() def send_enc(self, uri, msg): peer = self.peers[uri] pub = peer.pub # Now send a hello message to the peer if pub: self.log.info( "Sending encrypted [%s] message to %s" % ( msg['type'], uri ) ) peer.send(msg) else: # Will send clear profile on initial if no pub self.log.info( "Sending unencrypted [%s] message to %s" % ( msg['type'], uri ) ) self.peers[uri].send_raw(json.dumps(msg)) def _init_peer(self, msg): uri = msg['uri'] pub = msg.get('pub') nickname = msg.get('nickname') msg_type = msg.get('type') guid = msg['guid'] if not self.valid_peer_uri(uri): self.log.error("Invalid Peer: %s " % uri) return if uri not in self.peers: # Unknown peer self.log.info('Add New Peer: %s' % uri) self.create_peer(uri, pub, guid) if not msg_type: self.send_enc(uri, hello_request(self.get_profile())) elif msg_type == 'hello_request': self.send_enc(uri, hello_response(self.get_profile())) else: # Known peer if pub: # test if we have to update the pubkey if not self.peers[uri].pub: self.log.info("Setting public key for seed node") self.peers[uri].pub = pub.decode('hex') self.trigger_callbacks('peer', self.peers[uri]) if self.peers[uri].pub != pub.decode('hex'): self.log.info("Updating public key for node") self.peers[uri].nickname = nickname self.peers[uri].pub = pub.decode('hex') self.trigger_callbacks('peer', self.peers[uri]) if msg_type == 'hello_request': # reply only if necessary self.send_enc(uri, hello_response(self.get_profile())) def _on_message(self, msg): # here goes the application callbacks # we get a "clean" msg which is a dict holding whatever # self.log.info("[On Message] Data received: %s" % msg) pubkey = msg.get('pubkey') uri = msg.get('uri') ip = urlparse(uri).hostname port = urlparse(uri).port guid = msg.get('senderGUID') nickname = msg.get('senderNick')[:120] self.dht.add_known_node((ip, port, guid, nickname)) self.log.info('On Message: %s' % json.dumps(msg, ensure_ascii=False)) self.dht.add_peer(self, uri, pubkey, guid, nickname) t = Thread(target=self.trigger_callbacks, args=(msg['type'], msg,)) t.start() def _on_raw_message(self, serialized): try: msg = json.loads(serialized) self.log.info("Message Received [%s]" % msg.get('type', 'unknown')) if msg.get('type') is None: data = msg.get('data').decode('hex') sig = msg.get('sig').decode('hex') try: cryptor = makePrivCryptor(self.secret) try: data = cryptor.decrypt(data) except Exception as e: self.log.info('Exception: %s' % e) self.log.debug('Signature: %s' % sig.encode('hex')) self.log.debug('Signed Data: %s' % data) # Check signature data_json = json.loads(data) sigCryptor = makePubCryptor(data_json['pubkey']) if sigCryptor.verify(sig, data): self.log.info('Verified') else: self.log.error('Message signature could not be verified %s' % msg) # return msg = json.loads(data) self.log.debug('Message Data %s ' % msg) except Exception as e: self.log.error('Could not decrypt message properly %s' % e) except ValueError: try: # Encrypted? try: msg = self._myself.decrypt(serialized) msg = json.loads(msg) self.log.info( "Decrypted Message [%s]" % msg.get('type', 'unknown') ) except: self.log.error("Could not decrypt message: %s" % msg) return except: self.log.error('Message probably sent using incorrect pubkey') return if msg.get('type') is not None: self._on_message(msg) else: self.log.error('Received a message with no type') def shutdown(self): print "CryptoTransportLayer.shutdown()!" try: TransportLayer.shutdown(self) print "CryptoTransportLayer.shutdown(): ZMQ sockets destroyed." except Exception as e: self.log.error("Transport shutdown error: " + e.message) print "Notice: explicit DHT Shutdown not implemented." try: self.bitmessage_api.close() except Exception as e: # might not even be open, not much more we can do on our way out if exception thrown here. self.log.error("Could not shutdown bitmessage_api's ServerProxy. " + e.message)
class CryptoTransportLayer(TransportLayer): def __init__(self, my_ip, my_port, market_id, bm_user=None, bm_pass=None, bm_port=None, seed_mode=0, dev_mode=False): self._log = logging.getLogger('[%s] %s' % (market_id, self.__class__.__name__)) requests_log = logging.getLogger("requests") requests_log.setLevel(logging.WARNING) # Connect to database MONGODB_URI = 'mongodb://*****:*****@localhost:{}/".format(bm_user, bm_pass, bm_port), verbose=0) result = self._bitmessage_api.add(2,3) self._log.info("Bitmessage test result: {}, API is live".format(result)) # If we failed, fall back to starting our own except Exception as e: self._log.info("Failed to connect to bitmessage instance: {}".format(e)) self._bitmessage_api = None # self._log.info("Spawning internal bitmessage instance") # # Add bitmessage submodule path # sys.path.insert(0, os.path.join( # os.path.dirname(__file__), '..', 'pybitmessage', 'src')) # import bitmessagemain as bitmessage # bitmessage.logger.setLevel(logging.WARNING) # bitmessage_instance = bitmessage.Main() # bitmessage_instance.start(daemon=True) # bminfo = bitmessage_instance.getApiAddress() # if bminfo is not None: # self._log.info("Started bitmessage daemon at %s:%s".format( # bminfo['address'], bminfo['port'])) # bitmessage_api = xmlrpclib.ServerProxy("http://{}:{}@{}:{}/".format( # bm_user, bm_pass, bminfo['address'], bminfo['port'])) # else: # self._log.info("Failed to start bitmessage dameon") # self._bitmessage_api = None return result def _checkok(self, msg): self._log.info('Check ok') def get_guid(self): return self._guid def getDHT(self): return self._dht def getBitmessageAPI(self): return self._bitmessage_api def getMarketID(self): return self._market_id def getMyself(self): return self._myself def _ping(self, msg): self._log.info('Pinged %s ' % msg) pinger = CryptoPeerConnection(self,msg['uri'], msg['pubkey'], msg['senderGUID']) pinger.send_raw(json.dumps( {"type": "hello_response", "senderGUID": self.guid, "uri": self._uri, "senderNick": self._nickname, "pubkey": self.pubkey, })) def _storeValue(self, msg): self._dht._on_storeValue(msg) def _findNode(self, msg): self._dht.on_find_node(msg) def _findNodeResponse(self, msg): self._dht.on_findNodeResponse(self, msg) def _setup_settings(self): self.settings = self._db.settings.find_one({'id':"%s" % self._market_id}) if self.settings: self._nickname = self.settings['nickname'] if self.settings.has_key("nickname") else "" self.secret = self.settings['secret'] if self.settings.has_key("secret") else "" self.pubkey = self.settings['pubkey'] if self.settings.has_key("pubkey") else "" self.guid = self.settings['guid'] if self.settings.has_key("guid") else "" self.sin = self.settings['sin'] if self.settings.has_key("sin") else "" self.bitmessage = self.settings['bitmessage'] if self.settings.has_key('bitmessage') else "" else: self._nickname = 'Default' # Generate Bitcoin keypair self._generate_new_keypair() # Generate Bitmessage address if self._bitmessage_api is not None: self._generate_new_bitmessage_address() # Generate PGP key gpg = gnupg.GPG() input_data = gpg.gen_key_input(key_type="RSA", key_length=2048, name_comment="Autogenerated by Open Bazaar", passphrase="P@ssw0rd") key = gpg.gen_key(input_data) pubkey_text = gpg.export_keys(key.fingerprint) self._db.settings.update({"id":'%s' % self._market_id}, {"$set": {"PGPPubKey":pubkey_text, "PGPPubkeyFingerprint":key.fingerprint}}, True) self.settings = self._db.settings.find_one({'id':"%s" % self._market_id}) self._log.debug('Retrieved Settings: %s', self.settings) def _generate_new_keypair(self): # Generate new keypair key = ec.ECC(curve='secp256k1') self.secret = key.get_privkey().encode('hex') pubkey = key.get_pubkey() signedPubkey = key.sign(pubkey) self.pubkey = pubkey.encode('hex') self._myself = key # Generate SIN sha_hash = hashlib.sha256() sha_hash.update(pubkey) ripe_hash = hashlib.new('ripemd160') ripe_hash.update(sha_hash.digest()) self.guid = ripe_hash.digest().encode('hex') self.sin = obelisk.EncodeBase58Check('\x0F\x02%s' + ripe_hash.digest()) self._db.settings.update({"id":'%s' % self._market_id}, {"$set": {"secret":self.secret, "pubkey":self.pubkey, "guid":self.guid, "sin":self.sin}}, True) def _generate_new_bitmessage_address(self): # Use the guid generated previously as the key self.bitmessage = self._bitmessage_api.createRandomAddress(self.guid.encode('base64'), False, 1.05, 1.1111) self._db.settings.update({"id":'%s' % self._market_id}, {"$set": {"bitmessage":self.bitmessage}}, True) def join_network(self, dev_mode=0, callback=lambda msg: None): if dev_mode: self._log.info('DEV MODE') seed_peers = {'127.0.0.1'} else: seed_peers = ('seed.openbazaar.org', 'seed2.openbazaar.org') for seed in seed_peers: self._log.info('Initializing Seed Peer(s): [%s]' % seed) def cb(msg): self._dht._iterativeFind(self._guid, self._dht._knownNodes, 'findNode') callback(msg) self.connect('tcp://%s:12345' % seed, callback=cb) # Try to connect to known peers known_peers = self._db.peers.find() for known_peer in known_peers: def cb(msg): #self._dht._iterativeFind(self._guid, self._dht._knownNodes, 'findNode') callback(msg) self.connect(known_peer['uri'], callback=cb) # self.listen(self.pubkey) # Turn on zmq socket # # if seed_uri: # self._log.info('Initializing Seed Peer(s): [%s]' % seed_uri) # seed_peer = CryptoPeerConnection(self, seed_uri) # self._dht.start(seed_peer) def connect(self, uri, callback): def cb(msg): ip = urlparse(uri).hostname port = urlparse(uri).port self._dht.add_known_node((ip, port, peer._guid)) # Turning off peers #self._init_peer({'uri': seed_uri, 'guid':seed_guid}) # Add to routing table #self.addCryptoPeer(peer) callback(msg) peer = CryptoPeerConnection(self, uri, callback=cb) return peer def get_crypto_peer(self, guid, uri, pubkey=None, nickname=None): if guid == self.guid: self._log.info('Trying to get crypto peer for yourself') return self._log.info('%s %s %s %s' % (guid, uri, pubkey, nickname)) peer = CryptoPeerConnection(self, uri, pubkey, guid=guid, nickname=nickname) return peer def addCryptoPeer(self, peer_to_add): foundOutdatedPeer = False for idx, peer in enumerate(self._dht._activePeers): if (peer._address, peer._guid, peer._pub) == (peer_to_add._address, peer_to_add._guid, peer_to_add._pub): self._log.info('Found existing peer, not adding.') return if peer._guid == peer_to_add._guid or peer._pub == peer_to_add._pub or peer._address == peer_to_add._address: foundOutdatedPeer = True self._log.info('Found an outdated peer') # Update existing peer self._activePeers[idx] = peer_to_add if not foundOutdatedPeer and peer_to_add._guid != self._guid: self._log.info('Adding crypto peer at %s' % peer_to_add._nickname) self._dht.add_active_peer(self, (peer_to_add._pub, peer_to_add._address, peer_to_add._guid, peer_to_add._nickname)) # Return data array with details from the crypto file # TODO: This needs to be protected better; potentially encrypted file or DB @staticmethod def load_crypto_details(store_file): with open(store_file) as f: data = json.loads(f.read()) assert "nickname" in data assert "secret" in data assert "pubkey" in data assert len(data["secret"]) == 2 * 32 assert len(data["pubkey"]) == 2 * 33 return data["nickname"], data["secret"].decode("hex"), \ data["pubkey"].decode("hex") def get_profile(self): peers = {} self.settings = self._db.settings.find_one({'id':"%s" % self._market_id}) for uri, peer in self._peers.iteritems(): if peer._pub: peers[uri] = peer._pub.encode('hex') return {'uri': self._uri, 'pub': self._myself.get_pubkey().encode('hex'),'nickname': self._nickname, 'peers': peers} def respond_pubkey_if_mine(self, nickname, ident_pubkey): if ident_pubkey != self.pubkey: self._log.info("Public key does not match your identity") return # Return signed pubkey pubkey = self._myself.pubkey ec_key = obelisk.EllipticCurveKey() ec_key.set_secret(self.secret) digest = obelisk.Hash(pubkey) signature = ec_key.sign(digest) # Send array of nickname, pubkey, signature to transport layer self.send(proto_response_pubkey(nickname, pubkey, signature)) def pubkey_exists(self, pub): for uri, peer in self._peers.iteritems(): self._log.info('PEER: %s Pub: %s' % (peer._pub.encode('hex'), pub.encode('hex'))) if peer._pub.encode('hex') == pub.encode('hex'): return True return False def create_peer(self, uri, pub, node_guid): if pub: pub = pub.decode('hex') # Create the peer if public key is not already in the peer list # if not self.pubkey_exists(pub): self._peers[uri] = CryptoPeerConnection(self, uri, pub, node_guid) # Call 'peer' callbacks on listeners self.trigger_callbacks('peer', self._peers[uri]) # else: # print 'Pub Key is already in peer list' def send(self, data, send_to=None, callback=lambda msg: None): self._log.debug("Outgoing Data: %s %s" % (data, send_to)) # Directed message if send_to is not None: peer = self._dht._routingTable.getContact(send_to) #peer = CryptoPeerConnection(msg['uri']) if peer: self._log.debug('Directed Data (%s): %s' % (send_to, data)) try: peer.send(data, callback=callback) except: self._log.error('Not sending messing directly to peer') else: self._log.error('No peer found') else: # FindKey and then send for peer in self._dht._activePeers: try: peer = self._dht._routingTable.getContact(peer._guid) data['senderGUID'] = self._guid data['pubkey'] = self.pubkey print data #if peer._pub: # peer.send(data, callback) #else: def cb(msg): print 'msg %s' % msg peer.send(data, cb) except: self._log.info("Error sending over peer!") traceback.print_exc() def send_enc(self, uri, msg): peer = self._peers[uri] pub = peer._pub # Now send a hello message to the peer if pub: self._log.info("Sending encrypted [%s] message to %s" % (msg['type'], uri)) peer.send(msg) else: # Will send clear profile on initial if no pub self._log.info("Sending unencrypted [%s] message to %s" % (msg['type'], uri)) self._peers[uri].send_raw(json.dumps(msg)) def _init_peer(self, msg): uri = msg['uri'] pub = msg.get('pub') nickname = msg.get('nickname') msg_type = msg.get('type') guid = msg['guid'] if not self.valid_peer_uri(uri): self._log.error("Invalid Peer: %s " % uri) return if uri not in self._peers: # Unknown peer self._log.info('Add New Peer: %s' % uri) self.create_peer(uri, pub, guid) if not msg_type: self.send_enc(uri, hello_request(self.get_profile())) elif msg_type == 'hello_request': self.send_enc(uri, hello_response(self.get_profile())) else: # Known peer if pub: # test if we have to update the pubkey if not self._peers[uri]._pub: self._log.info("Setting public key for seed node") self._peers[uri]._pub = pub.decode('hex') self.trigger_callbacks('peer', self._peers[uri]) if self._peers[uri]._pub != pub.decode('hex'): self._log.info("Updating public key for node") self._peers[uri]._nickname = nickname self._peers[uri]._pub = pub.decode('hex') self.trigger_callbacks('peer', self._peers[uri]) if msg_type == 'hello_request': # reply only if necessary self.send_enc(uri, hello_response(self.get_profile())) def _on_message(self, msg): # here goes the application callbacks # we get a "clean" msg which is a dict holding whatever #self._log.info("[On Message] Data received: %s" % msg) pubkey = msg.get('pubkey') uri = msg.get('uri') ip = urlparse(uri).hostname port = urlparse(uri).port guid = msg.get('senderGUID') nickname = msg.get('senderNick') self._log.info('on message %s' % nickname) self._dht.add_known_node((ip, port, guid, nickname)) self._dht.add_active_peer(self, (pubkey, uri, guid, nickname)) self.trigger_callbacks(msg['type'], msg) def _on_raw_message(self, serialized): try: # Try to de-serialize clear text message msg = json.loads(serialized) self._log.info("Message Received [%s]" % msg.get('type', 'unknown')) if msg.get('type') is None: data = msg.get('data').decode('hex') sig = msg.get('sig').decode('hex') try: data = self._myself.decrypt(data) self._log.debug('Signature: %s' % sig.encode('hex')) self._log.debug('Signed Data: %s' % data) guid = json.loads(data).get('guid') peer = self._dht._routingTable.getContact(guid) ecc = ec.ECC(curve='secp256k1',pubkey=json.loads(data).get('pubkey').decode('hex')) # Check signature if ecc.verify(sig, data): self._log.info('Verified') else: self._log.error('Message signature could not be verified %s' % msg) return msg = json.loads(data) self._log.debug('Message Data %s ' % msg) except: self._log.error('Could not decrypt message properly') except ValueError: try: # Encrypted? try: msg = self._myself.decrypt(serialized) msg = json.loads(msg) self._log.info("Decrypted Message [%s]" % msg.get('type', 'unknown')) except: self._log.error("Could not decrypt message: %s" % msg) return except: self._log.error('Message probably sent using incorrect pubkey') return if msg.get('type') is not None: msg_type = msg.get('type') msg_uri = msg.get('uri') msg_guid = msg.get('guid') self._on_message(msg) else: self._log.error('Received a message with no type')
class CryptoTransportLayer(TransportLayer): def __init__(self, my_ip, my_port, market_id, db, bm_user=None, bm_pass=None, bm_port=None, seed_mode=0, dev_mode=False): self.log = logging.getLogger('[%s] %s' % (market_id, self.__class__.__name__)) requests_log = logging.getLogger("requests") requests_log.setLevel(logging.WARNING) # Connect to database self.db = db self.bitmessage_api = None if (bm_user, bm_pass, bm_port) != (None, None, None): if not self._connect_to_bitmessage(bm_user, bm_pass, bm_port): self.log.info('Bitmessage not installed or started') try: socket.inet_pton(socket.AF_INET6, my_ip) my_uri = "tcp://[%s]:%s" % (my_ip, my_port) except socket.error: my_uri = "tcp://%s:%s" % (my_ip, my_port) self.market_id = market_id self.nick_mapping = {} self.uri = my_uri self.ip = my_ip self.nickname = "" self._dev_mode = dev_mode # Set up self._setup_settings() self.dht = DHT(self, self.market_id, self.settings, self.db) # self._myself = ec.ECC(pubkey=self.pubkey.decode('hex'), # privkey=self.secret.decode('hex'), # curve='secp256k1') TransportLayer.__init__(self, market_id, my_ip, my_port, self.guid, self.nickname) self.setup_callbacks() self.listen(self.pubkey) if seed_mode == 0 and not dev_mode: self.start_ip_address_checker() def setup_callbacks(self): self.add_callbacks([('hello', self._ping), ('findNode', self._find_node), ('findNodeResponse', self._find_node_response), ('store', self._store_value)]) def start_ip_address_checker(self): '''Checks for possible public IP change''' self.caller = PeriodicCallback(self._ip_updater_periodic_callback, 5000, ioloop.IOLoop.instance()) self.caller.start() def _ip_updater_periodic_callback(self): try: r = requests.get('https://icanhazip.com') if r and hasattr(r, 'text'): ip = r.text ip = ip.strip(' \t\n\r') if ip != self.ip: self.ip = ip try: socket.inet_pton(socket.AF_INET6, self.ip) my_uri = 'tcp://[%s]:%s' % (self.ip, self.port) except socket.error: my_uri = 'tcp://%s:%s' % (self.ip, self.port) self.uri = my_uri self.stream.close() self.listen(self.pubkey) self.dht._iterativeFind(self.guid, [], 'findNode') else: self.log.error('Could not get IP') except Exception as e: self.log.error('[Requests] error: %s' % e) def save_peer_to_db(self, peer_tuple): uri = peer_tuple[0] pubkey = peer_tuple[1] guid = peer_tuple[2] nickname = peer_tuple[3] # Update query self.db.deleteEntries("peers", {"uri": uri, "guid": guid}, "OR") # if len(results) > 0: # self.db.updateEntries("peers", {"id": results[0]['id']}, {"market_id": self.market_id, "uri": uri, "pubkey": pubkey, "guid": guid, "nickname": nickname}) # else: if guid is not None: self.db.insertEntry( "peers", { "uri": uri, "pubkey": pubkey, "guid": guid, "nickname": nickname, "market_id": self.market_id }) def _connect_to_bitmessage(self, bm_user, bm_pass, bm_port): # Get bitmessage going # First, try to find a local instance result = False try: self.log.info( '[_connect_to_bitmessage] Connecting to Bitmessage on port %s' % bm_port) self.bitmessage_api = xmlrpclib.ServerProxy( "http://{}:{}@localhost:{}/".format(bm_user, bm_pass, bm_port), verbose=0) result = self.bitmessage_api.add(2, 3) self.log.info( "[_connect_to_bitmessage] Bitmessage API is live".format( result)) # If we failed, fall back to starting our own except Exception as e: self.log.info( "Failed to connect to bitmessage instance: {}".format(e)) self.bitmessage_api = None # self._log.info("Spawning internal bitmessage instance") # # Add bitmessage submodule path # sys.path.insert(0, os.path.join( # os.path.dirname(__file__), '..', 'pybitmessage', 'src')) # import bitmessagemain as bitmessage # bitmessage.logger.setLevel(logging.WARNING) # bitmessage_instance = bitmessage.Main() # bitmessage_instance.start(daemon=True) # bminfo = bitmessage_instance.getApiAddress() # if bminfo is not None: # self._log.info("Started bitmessage daemon at %s:%s".format( # bminfo['address'], bminfo['port'])) # bitmessage_api = xmlrpclib.ServerProxy("http://{}:{}@{}:{}/".format( # bm_user, bm_pass, bminfo['address'], bminfo['port'])) # else: # self._log.info("Failed to start bitmessage dameon") # self._bitmessage_api = None return result def _checkok(self, msg): self.log.info('Check ok') def get_guid(self): return self.guid def get_dht(self): return self.dht def get_bitmessage_api(self): return self.bitmessage_api def get_market_id(self): return self.market_id # def get_myself(self): # return self._myself def _ping(self, msg): self.log.info('Pinged %s ' % json.dumps(msg, ensure_ascii=False)) # # pinger = CryptoPeerConnection(self, msg['uri'], msg['pubkey'], msg['senderGUID']) # pinger.send_raw(json.dumps( # {"type": "hello_response", # "senderGUID": self.guid, # "uri": self.uri, # "senderNick": self.nickname, # "pubkey": self.pubkey, # })) def _store_value(self, msg): self.dht._on_storeValue(msg) def _find_node(self, msg): self.dht.on_find_node(msg) def _find_node_response(self, msg): self.dht.on_findNodeResponse(self, msg) def _setup_settings(self): try: self.settings = self.db.selectEntries( "settings", {"market_id": self.market_id}) except (OperationalError, DatabaseError) as e: print e raise SystemExit( "database file %s corrupt or empty - cannot continue" % self.db.db_path) if len(self.settings) == 0: self.settings = {"market_id": self.market_id, "welcome": "enable"} self.db.insertEntry("settings", self.settings) else: self.settings = self.settings[0] # Generate PGP key during initial setup or if previous PGP gen failed if not ('PGPPubKey' in self.settings and self.settings["PGPPubKey"]): try: self.log.info( 'Generating PGP keypair. This may take several minutes...') print 'Generating PGP keypair. This may take several minutes...' gpg = gnupg.GPG() input_data = gpg.gen_key_input( key_type="RSA", key_length=2048, name_email='*****@*****.**', name_comment="Autogenerated by Open Bazaar", passphrase="P@ssw0rd") assert input_data is not None key = gpg.gen_key(input_data) assert key is not None pubkey_text = gpg.export_keys(key.fingerprint) newsettings = { "PGPPubKey": pubkey_text, "PGPPubkeyFingerprint": key.fingerprint } self.db.updateEntries("settings", {"market_id": self.market_id}, newsettings) self.settings.update(newsettings) self.log.info('PGP keypair generated.') except Exception as e: self.log.error("Encountered a problem with GPG: %s" % e) raise SystemExit("Encountered a problem with GPG: %s" % e) if not ('pubkey' in self.settings and self.settings['pubkey']): # Generate Bitcoin keypair self._generate_new_keypair() if not ('bitmessage' in self.settings and self.settings['bitmessage']): # Generate Bitmessage address if self.bitmessage_api is not None: self._generate_new_bitmessage_address() if not ('nickname' in self.settings and self.settings['nickname']): newsettings = {'nickname': 'Default'} self.db.updateEntries('settings', {"market_id": self.market_id}, newsettings) self.settings.update(newsettings) self.nickname = self.settings[ 'nickname'] if 'nickname' in self.settings else "" self.secret = self.settings[ 'secret'] if 'secret' in self.settings else "" self.pubkey = self.settings[ 'pubkey'] if 'pubkey' in self.settings else "" self.privkey = self.settings.get('privkey') self.btc_pubkey = privkey_to_pubkey(self.privkey) self.guid = self.settings['guid'] if 'guid' in self.settings else "" self.sin = self.settings['sin'] if 'sin' in self.settings else "" self.bitmessage = self.settings[ 'bitmessage'] if 'bitmessage' in self.settings else "" self._myself = ec.ECC(pubkey=pubkey_to_pyelliptic( self.pubkey).decode('hex'), raw_privkey=self.secret.decode('hex'), curve='secp256k1') self.log.debug('Retrieved Settings: \n%s', pformat(self.settings)) def _generate_new_keypair(self): secret = str(random.randrange(2**256)) self.secret = hashlib.sha256(secret).hexdigest() self.pubkey = privtopub(self.secret) self.privkey = random_key() print 'PRIVATE KEY: ', self.privkey self.btc_pubkey = privtopub(self.privkey) print 'PUBLIC KEY: ', self.btc_pubkey # Generate SIN sha_hash = hashlib.sha256() sha_hash.update(self.pubkey) ripe_hash = hashlib.new('ripemd160') ripe_hash.update(sha_hash.digest()) self.guid = ripe_hash.digest().encode('hex') self.sin = obelisk.EncodeBase58Check('\x0F\x02%s' + ripe_hash.digest()) newsettings = { "secret": self.secret, "pubkey": self.pubkey, "privkey": self.privkey, "guid": self.guid, "sin": self.sin } self.db.updateEntries("settings", {"market_id": self.market_id}, newsettings) self.settings.update(newsettings) def _generate_new_bitmessage_address(self): # Use the guid generated previously as the key self.bitmessage = self.bitmessage_api.createRandomAddress( self.guid.encode('base64'), False, 1.05, 1.1111) newsettings = {"bitmessage": self.bitmessage} self.db.updateEntries("settings", {"market_id": self.market_id}, newsettings) self.settings.update(newsettings) def join_network(self, seed_peers=[], callback=lambda msg: None): self.log.info('Joining network') known_peers = [] # Connect up through seed servers for idx, seed in enumerate(seed_peers): try: socket.inet_pton(socket.AF_INET6, seed) seed_peers[idx] = "tcp://[%s]:12345" % seed except socket.error: seed_peers[idx] = "tcp://%s:12345" % seed # Connect to persisted peers db_peers = self.get_past_peers() known_peers = list(set(seed_peers)) + list(set(db_peers)) print 'known_peers', known_peers self.connect_to_peers(known_peers) # Populate routing table by searching for self if len(known_peers) > 0: self.search_for_my_node() if callback is not None: callback('Joined') def get_past_peers(self): peers = [] result = self.db.selectEntries("peers", {"market_id": self.market_id}) for peer in result: peers.append(peer['uri']) return peers def search_for_my_node(self): print 'Searching for myself' self.dht._iterativeFind(self.guid, self.dht.knownNodes, 'findNode') def connect_to_peers(self, known_peers): for known_peer in known_peers: t = Thread(target=self.dht.add_peer, args=( self, known_peer, )) t.start() def get_crypto_peer(self, guid=None, uri=None, pubkey=None, nickname=None, callback=None): if guid == self.guid: self.log.error('Cannot get CryptoPeerConnection for your own node') return self.log.debug('Getting CryptoPeerConnection' + '\nGUID:%s\nURI:%s\nPubkey:%s\nNickname:%s' % (guid, uri, pubkey, nickname)) return connection.CryptoPeerConnection(self, uri, pubkey, guid=guid, nickname=nickname, callback=callback) def addCryptoPeer(self, peer_to_add): foundOutdatedPeer = False for idx, peer in enumerate(self.dht.activePeers): if (peer.address, peer.guid, peer.pub) == \ (peer_to_add.address, peer_to_add.guid, peer_to_add.pub): self.log.info('Found existing peer, not adding.') return if peer.guid == peer_to_add.guid or \ peer.pub == peer_to_add.pub or \ peer.address == peer_to_add.address: foundOutdatedPeer = True self.log.info('Found an outdated peer') # Update existing peer self.activePeers[idx] = peer_to_add self.dht.add_peer(self, peer_to_add.address, peer_to_add.pub, peer_to_add.guid, peer_to_add.nickname) if not foundOutdatedPeer and peer_to_add.guid != self.guid: self.log.info('Adding crypto peer at %s' % peer_to_add.nickname) self.dht.add_peer(self, peer_to_add.address, peer_to_add.pub, peer_to_add.guid, peer_to_add.nickname) def get_profile(self): peers = {} self.settings = self.db.selectEntries("settings", {"market_id": self.market_id})[0] for uri, peer in self.peers.iteritems(): if peer.pub: peers[uri] = peer.pub.encode('hex') return { 'uri': self.uri, 'pub': self._myself.get_pubkey().encode('hex'), 'nickname': self.nickname, 'peers': peers } def respond_pubkey_if_mine(self, nickname, ident_pubkey): if ident_pubkey != self.pubkey: self.log.info("Public key does not match your identity") return # Return signed pubkey pubkey = self._myself.pubkey ec_key = obelisk.EllipticCurveKey() ec_key.set_secret(self.secret) digest = obelisk.Hash(pubkey) signature = ec_key.sign(digest) # Send array of nickname, pubkey, signature to transport layer self.send(proto_response_pubkey(nickname, pubkey, signature)) def pubkey_exists(self, pub): for uri, peer in self.peers.iteritems(): self.log.info('PEER: %s Pub: %s' % (peer.pub.encode('hex'), pub.encode('hex'))) if peer.pub.encode('hex') == pub.encode('hex'): return True return False def create_peer(self, uri, pub, node_guid): if pub: pub = pub.decode('hex') # Create the peer if public key is not already in the peer list # if not self.pubkey_exists(pub): self.peers[uri] = connection.CryptoPeerConnection( self, uri, pub, node_guid) # Call 'peer' callbacks on listeners self.trigger_callbacks('peer', self.peers[uri]) # else: # print 'Pub Key is already in peer list' def send(self, data, send_to=None, callback=lambda msg: None): self.log.debug("Outgoing Data: %s %s" % (data, send_to)) # Directed message if send_to is not None: peer = self.dht.routingTable.getContact(send_to) if not peer: for activePeer in self.dht.activePeers: if activePeer.guid == send_to: peer = activePeer break # peer = CryptoPeerConnection(msg['uri']) if peer: self.log.debug('Directed Data (%s): %s' % (send_to, data)) try: peer.send(data, callback=callback) except Exception as e: self.log.error('Not sending message directly to peer %s' % e) else: self.log.error('No peer found') else: # FindKey and then send for peer in self.dht.activePeers: try: peer = self.dht.routingTable.getContact(peer.guid) data['senderGUID'] = self.guid data['pubkey'] = self.pubkey def cb(msg): self.log.debug('Message Back: \n%s' % pformat(msg)) peer.send(data, cb) except: self.log.info("Error sending over peer!") traceback.print_exc() def send_enc(self, uri, msg): peer = self.peers[uri] pub = peer.pub # Now send a hello message to the peer if pub: self.log.info("Sending encrypted [%s] message to %s" % (msg['type'], uri)) peer.send(msg) else: # Will send clear profile on initial if no pub self.log.info("Sending unencrypted [%s] message to %s" % (msg['type'], uri)) self.peers[uri].send_raw(json.dumps(msg)) def _init_peer(self, msg): uri = msg['uri'] pub = msg.get('pub') nickname = msg.get('nickname') msg_type = msg.get('type') guid = msg['guid'] if not self.valid_peer_uri(uri): self.log.error("Invalid Peer: %s " % uri) return if uri not in self.peers: # Unknown peer self.log.info('Add New Peer: %s' % uri) self.create_peer(uri, pub, guid) if not msg_type: self.send_enc(uri, hello_request(self.get_profile())) elif msg_type == 'hello_request': self.send_enc(uri, hello_response(self.get_profile())) else: # Known peer if pub: # test if we have to update the pubkey if not self.peers[uri].pub: self.log.info("Setting public key for seed node") self.peers[uri].pub = pub.decode('hex') self.trigger_callbacks('peer', self.peers[uri]) if self.peers[uri].pub != pub.decode('hex'): self.log.info("Updating public key for node") self.peers[uri].nickname = nickname self.peers[uri].pub = pub.decode('hex') self.trigger_callbacks('peer', self.peers[uri]) if msg_type == 'hello_request': # reply only if necessary self.send_enc(uri, hello_response(self.get_profile())) def _on_message(self, msg): # here goes the application callbacks # we get a "clean" msg which is a dict holding whatever # self.log.info("[On Message] Data received: %s" % msg) pubkey = msg.get('pubkey') uri = msg.get('uri') ip = urlparse(uri).hostname port = urlparse(uri).port guid = msg.get('senderGUID') nickname = msg.get('senderNick')[:120] self.dht.add_known_node((ip, port, guid, nickname)) self.log.info('ON MESSAGE %s' % json.dumps(msg, ensure_ascii=False)) self.dht.add_peer(self, uri, pubkey, guid, nickname) self.log.debug('Callbacks %s' % self.callbacks) t = Thread(target=self.trigger_callbacks, args=( msg['type'], msg, )) t.start() def _on_raw_message(self, serialized): try: # Decompress message serialized = zlib.decompress(serialized) msg = json.loads(serialized) self.log.info("Message Received [%s]" % msg.get('type', 'unknown')) if msg.get('type') is None: data = msg.get('data').decode('hex') sig = msg.get('sig').decode('hex') try: cryptor = makePrivCryptor(self.secret) try: data = cryptor.decrypt(data) except Exception as e: self.log.info('Exception: %s' % e) self.log.debug('Signature: %s' % sig.encode('hex')) self.log.debug('Signed Data: %s' % data) # Check signature data_json = json.loads(data) sigCryptor = makePubCryptor(data_json['pubkey']) if sigCryptor.verify(sig, data): self.log.info('Verified') else: self.log.error( 'Message signature could not be verified %s' % msg) # return msg = json.loads(data) self.log.debug('Message Data %s ' % msg) except Exception as e: self.log.error('Could not decrypt message properly %s' % e) except ValueError: try: # Encrypted? try: msg = self._myself.decrypt(serialized) msg = json.loads(msg) self.log.info("Decrypted Message [%s]" % msg.get('type', 'unknown')) except: self.log.error("Could not decrypt message: %s" % msg) return except: self.log.error('Message probably sent using incorrect pubkey') return if msg.get('type') is not None: self._on_message(msg) else: self.log.error('Received a message with no type') def shutdown(self): print "CryptoTransportLayer.shutdown()!" try: TransportLayer.shutdown(self) print "CryptoTransportLayer.shutdown(): ZMQ sockets destroyed." except Exception as e: self.log.error("Transport shutdown error: " + e.message) print "Notice: explicit DHT Shutdown not implemented." try: self.bitmessage_api.close() except Exception as e: # might not even be open, not much more we can do on our way out if exception thrown here. self.log.error( "Could not shutdown bitmessage_api's ServerProxy. " + e.message)
class CryptoTransportLayer(TransportLayer): def __init__(self, my_ip, my_port, market_id): self._log = logging.getLogger('[%s] %s' % (market_id, self.__class__.__name__)) # Connect to database MONGODB_URI = 'mongodb://localhost:27017' _dbclient = MongoClient() self._db = _dbclient.openbazaar self._market_id = market_id self.nick_mapping = {} self._uri = "tcp://%s:%s" % (my_ip, my_port) # Set up self._setup_settings() self._dht = DHT(self, market_id, self.settings) self._myself = ec.ECC(pubkey=self.pubkey.decode('hex'), privkey=self.secret.decode('hex'), curve='secp256k1') TransportLayer.__init__(self, market_id, my_ip, my_port, self.guid) # Set up callbacks self.add_callback('hello', self._ping) self.add_callback('findNode', self._findNode) self.add_callback('findNodeResponse', self._findNodeResponse) self.add_callback('store', self._storeValue) def get_guid(self): return self._guid def getDHT(self): return self._dht def getMarketID(self): return self._market_id def getMyself(self): return self._myself def _ping(self, msg): self._log.info('Pinged %s ' % msg) pinger = CryptoPeerConnection(self,msg['uri'], msg['pubkey'], msg['senderGUID']) msg = pinger.send_raw(json.dumps( {"type": "hello_response", "senderGUID": self.guid, "uri": self._uri, "pubkey": self.pubkey, })) print msg def _storeValue(self, msg): self._dht._on_storeValue(msg) def _findNode(self, msg): self._dht.on_find_node(msg) def _findNodeResponse(self, msg): self._dht.on_findNodeResponse(self, msg) def _setup_settings(self): self.settings = self._db.settings.find_one({'id':"%s" % self._market_id}) if self.settings: self.nickname = self.settings['nickname'] if self.settings.has_key("nickname") else "" self.secret = self.settings['secret'] self.pubkey = self.settings['pubkey'] self.guid = self.settings['guid'] else: self.nickname = 'Default' self._generate_new_keypair() self.settings = self._db.settings.find_one({'id':"%s" % self._market_id}) self._log.debug('Retrieved Settings: %s', self.settings) def _generate_new_keypair(self): # Generate new keypair key = ec.ECC(curve='secp256k1') self.secret = key.get_privkey().encode('hex') pubkey = key.get_pubkey() signedPubkey = key.sign(pubkey) self.pubkey = pubkey.encode('hex') self._myself = key # Generate a node ID by ripemd160 hashing the signed pubkey guid = hashlib.new('ripemd160') guid.update(signedPubkey) self.guid = guid.digest().encode('hex') self._db.settings.update({"id":'%s' % self._market_id}, {"$set": {"secret":self.secret, "pubkey":self.pubkey, "guid":self.guid}}, True) def join_network(self, seed_uri): self.listen(self.pubkey) # Turn on zmq socket if seed_uri: self._log.info('Initializing Seed Peer(s): [%s]' % seed_uri) seed_peer = CryptoPeerConnection(self, seed_uri) self._dht.start(seed_peer) def get_crypto_peer(self, guid, uri, pubkey=None): if guid == self.guid: self._log.info('Trying to get cryptopeer for yourself') return peer = CryptoPeerConnection(self, uri, pubkey, guid=guid) return peer def addCryptoPeer(self, peer): peerExists = False for idx, aPeer in enumerate(self._activePeers): if aPeer._guid == peer._guid or aPeer._pub == peer._pub or aPeer._address == peer._address: self._log.info('guids or pubkey match') peerExists = True if peer._pub and aPeer._pub == '': self._log.info('no pubkey') aPeer._pub = peer._pub self._activePeers[idx] = aPeer if not peerExists and peer._guid != self._guid: self._log.info('Adding crypto peer %s' % peer._pub) self._routingTable.addContact(peer) print peer self._dht.add_active_peer(self, (peer._pub, peer._address, peer._guid)) # Return data array with details from the crypto file # TODO: This needs to be protected better; potentially encrypted file or DB @staticmethod def load_crypto_details(store_file): with open(store_file) as f: data = json.loads(f.read()) assert "nickname" in data assert "secret" in data assert "pubkey" in data assert len(data["secret"]) == 2 * 32 assert len(data["pubkey"]) == 2 * 33 return data["nickname"], data["secret"].decode("hex"), \ data["pubkey"].decode("hex") def get_profile(self): peers = {} self.settings = self._db.settings.find_one({'id':"%s" % self._market_id}) for uri, peer in self._peers.iteritems(): if peer._pub: peers[uri] = peer._pub.encode('hex') return {'uri': self._uri, 'pub': self._myself.get_pubkey().encode('hex'),'nickname': self.nickname, 'peers': peers} def respond_pubkey_if_mine(self, nickname, ident_pubkey): if ident_pubkey != self.pubkey: self._log.info("Public key does not match your identity") return # Return signed pubkey pubkey = self._myself.pubkey ec_key = obelisk.EllipticCurveKey() ec_key.set_secret(self.secret) digest = obelisk.Hash(pubkey) signature = ec_key.sign(digest) # Send array of nickname, pubkey, signature to transport layer self.send(proto_response_pubkey(nickname, pubkey, signature)) def pubkey_exists(self, pub): for uri, peer in self._peers.iteritems(): self._log.info('PEER: %s Pub: %s' % (peer._pub.encode('hex'), pub.encode('hex'))) if peer._pub.encode('hex') == pub.encode('hex'): return True return False def create_peer(self, uri, pub, node_guid): if pub: pub = pub.decode('hex') # Create the peer if public key is not already in the peer list # if not self.pubkey_exists(pub): self._peers[uri] = CryptoPeerConnection(self, uri, pub, node_guid) # Call 'peer' callbacks on listeners self.trigger_callbacks('peer', self._peers[uri]) # else: # print 'Pub Key is already in peer list' def send_enc(self, uri, msg): peer = self._peers[uri] pub = peer._pub # Now send a hello message to the peer if pub: self._log.info("Sending encrypted [%s] message to %s" % (msg['type'], uri)) peer.send(msg) else: # Will send clear profile on initial if no pub self._log.info("Sending unencrypted [%s] message to %s" % (msg['type'], uri)) self._peers[uri].send_raw(json.dumps(msg)) def init_peer(self, msg): uri = msg['uri'] pub = msg.get('pub') nickname = msg.get('nickname') msg_type = msg.get('type') guid = msg['guid'] if not self.valid_peer_uri(uri): self._log.error("Invalid Peer: %s " % uri) return if uri not in self._peers: # Unknown peer self._log.info('Add New Peer: %s' % uri) self.create_peer(uri, pub, guid) if not msg_type: self.send_enc(uri, hello_request(self.get_profile())) elif msg_type == 'hello_request': self.send_enc(uri, hello_response(self.get_profile())) else: # Known peer if pub: # test if we have to update the pubkey if not self._peers[uri]._pub: self._log.info("Setting public key for seed node") self._peers[uri]._pub = pub.decode('hex') self.trigger_callbacks('peer', self._peers[uri]) if self._peers[uri]._pub != pub.decode('hex'): self._log.info("Updating public key for node") self._peers[uri]._nickname = nickname self._peers[uri]._pub = pub.decode('hex') self.trigger_callbacks('peer', self._peers[uri]) if msg_type == 'hello_request': # reply only if necessary self.send_enc(uri, hello_response(self.get_profile())) def on_message(self, msg): # here goes the application callbacks # we get a "clean" msg which is a dict holding whatever self._log.info("[On Message] Data received: %s" % msg) pubkey = msg.get('pubkey') uri = msg.get('uri') ip = urlparse(uri).hostname port = urlparse(uri).port guid = msg.get('senderGUID') self._dht.add_active_peer(self, (pubkey, uri, guid)) self._dht.add_known_node((ip, port, guid)) self.trigger_callbacks(msg['type'], msg) def on_raw_message(self, serialized): try: # Try to deserialize cleartext message msg = json.loads(serialized) self._log.info("Message Received [%s]" % msg.get('type', 'unknown')) except ValueError: try: # Encrypted? try: msg = self._myself.decrypt(serialized) msg = json.loads(msg) self._log.info("Decrypted Message [%s]" % msg.get('type', 'unknown')) except: self._log.error("Could not decrypt message: %s" % msg) return except: self._log.info("Bad Message: %s..." % self._myself.decrypt(serialized)) traceback.print_exc() return if msg.get('type') != '': msg_type = msg.get('type') msg_uri = msg.get('uri') msg_guid = msg.get('guid') self._log.info(msg.get('type')) # # if msg_type.startswith('hello') and msg_uri: # self.init_peer(msg) # for uri, pub in msg.get('peers', {}).iteritems(): # # Do not add yourself as a peer # if uri != self._uri: # self.init_peer({'uri': uri, 'pub': pub}) # self._log.info("Update peer table [%s peers]" % len(self._peers)) # # elif msg_type == 'goodbye' and msg_uri: # self._log.info("Received goodbye from %s" % msg_uri) # self.remove_peer(msg_uri) # # else: self.on_message(msg) else: self._log.error('Received a message with no type')
class CryptoTransportLayer(TransportLayer): def __init__(self, my_ip, my_port, market_id): self._log = logging.getLogger('[%s] %s' % (market_id, self.__class__.__name__)) # Connect to database MONGODB_URI = 'mongodb://localhost:27017' _dbclient = MongoClient() self._db = _dbclient.openbazaar self._market_id = market_id self.nick_mapping = {} self._uri = "tcp://%s:%s" % (my_ip, my_port) # Set up self._setup_settings() self._dht = DHT(market_id, self.settings) self._myself = ec.ECC(pubkey=self.pubkey.decode('hex'), privkey=self.secret.decode('hex'), curve='secp256k1') TransportLayer.__init__(self, market_id, my_ip, my_port, self.guid) # Set up callbacks self.add_callback('ping', self._dht._on_ping) self.add_callback('findNode', self._dht._on_findNode) self.add_callback('findNodeResponse', self._dht._on_findNodeResponse) def _setup_settings(self): self.settings = self._db.settings.find_one( {'id': "%s" % self._market_id}) if self.settings: self.nickname = self.settings['nickname'] if self.settings.has_key( "nickname") else "" self.secret = self.settings['secret'] self.pubkey = self.settings['pubkey'] self.guid = self.settings['guid'] else: self.nickname = 'Default' self._generate_new_keypair() self.settings = self._db.settings.find_one( {'id': "%s" % self._market_id}) self._log.debug('Retrieved Settings: %s', self.settings) def _generate_new_keypair(self): # Generate new keypair key = ec.ECC(curve='secp256k1') self.secret = key.get_privkey().encode('hex') pubkey = key.get_pubkey() signedPubkey = key.sign(pubkey) self.pubkey = pubkey.encode('hex') self._myself = key # Generate a node ID by ripemd160 hashing the signed pubkey guid = hashlib.new('ripemd160') guid.update(signedPubkey) self.guid = guid.digest().encode('hex') self._db.settings.update({"id": '%s' % self._market_id}, { "$set": { "secret": self.secret, "pubkey": self.pubkey, "guid": self.guid } }, True) def join_network(self, seed_uri): self.listen(self.pubkey) # Turn on zmq socket if seed_uri: self._log.info('Initializing Seed Peer(s): [%s]' % (seed_uri)) seed_peer = CryptoPeerConnection(self, seed_uri) self._dht.start(seed_peer) def _addCryptoPeer(self, uri, guid, pubkey): peer = CryptoPeerConnection(self, uri, pubkey) self.addCryptoPeer(peer) def addCryptoPeer(self, peer): peerExists = False for idx, aPeer in enumerate(self._activePeers): if aPeer._guid == peer._guid or aPeer._pub == peer._pub or aPeer._address == peer._address: self._log.info('guids or pubkey match') peerExists = True if peer._pub and aPeer._pub == '': self._log.info('no pubkey') aPeer._pub = peer._pub self._activePeers[idx] = aPeer if not peerExists: self._log.info('Adding crypto peer %s' % peer._pub) self._routingTable.addContact(peer) self._activePeers.append(peer) # Return data array with details from the crypto file # TODO: This needs to be protected better; potentially encrypted file or DB def load_crypto_details(self, store_file): with open(store_file) as f: data = json.loads(f.read()) assert "nickname" in data assert "secret" in data assert "pubkey" in data assert len(data["secret"]) == 2 * 32 assert len(data["pubkey"]) == 2 * 33 return data["nickname"], data["secret"].decode("hex"), \ data["pubkey"].decode("hex") def get_profile(self): peers = {} self.settings = self._db.settings.find_one( {'id': "%s" % self._market_id}) for uri, peer in self._peers.iteritems(): if peer._pub: peers[uri] = peer._pub.encode('hex') return { 'uri': self._uri, 'pub': self._myself.get_pubkey().encode('hex'), 'nickname': self.nickname, 'peers': peers } def respond_pubkey_if_mine(self, nickname, ident_pubkey): if ident_pubkey != self.pubkey: self._log.info("Public key does not match your identity") return # Return signed pubkey pubkey = self._myself.pubkey ec_key = obelisk.EllipticCurveKey() ec_key.set_secret(self.secret) digest = obelisk.Hash(pubkey) signature = ec_key.sign(digest) # Send array of nickname, pubkey, signature to transport layer self.send(proto_response_pubkey(nickname, pubkey, signature)) def pubkey_exists(self, pub): for uri, peer in self._peers.iteritems(): self._log.info('PEER: %s Pub: %s' % (peer._pub.encode('hex'), pub.encode('hex'))) if peer._pub.encode('hex') == pub.encode('hex'): return True return False def create_peer(self, uri, pub, node_guid): if pub: pub = pub.decode('hex') # Create the peer if public key is not already in the peer list # if not self.pubkey_exists(pub): self._peers[uri] = CryptoPeerConnection(self, uri, pub, node_guid) # Call 'peer' callbacks on listeners self.trigger_callbacks('peer', self._peers[uri]) # else: # print 'Pub Key is already in peer list' def send_enc(self, uri, msg): peer = self._peers[uri] pub = peer._pub # Now send a hello message to the peer if pub: self._log.info("Sending encrypted [%s] message to %s" % (msg['type'], uri)) peer.send(msg) else: # Will send clear profile on initial if no pub self._log.info("Sending unencrypted [%s] message to %s" % (msg['type'], uri)) self._peers[uri].send_raw(json.dumps(msg)) def init_peer(self, msg): uri = msg['uri'] pub = msg.get('pub') nickname = msg.get('nickname') msg_type = msg.get('type') guid = msg['guid'] if not self.valid_peer_uri(uri): self._log.error("Invalid Peer: %s " % uri) return if uri not in self._peers: # Unknown peer self._log.info('Add New Peer: %s' % uri) self.create_peer(uri, pub, guid) if not msg_type: self.send_enc(uri, hello_request(self.get_profile())) elif msg_type == 'hello_request': self.send_enc(uri, hello_response(self.get_profile())) else: # Known peer if pub: # test if we have to update the pubkey if not self._peers[uri]._pub: self._log.info("Setting public key for seed node") self._peers[uri]._pub = pub.decode('hex') self.trigger_callbacks('peer', self._peers[uri]) if (self._peers[uri]._pub != pub.decode('hex')): self._log.info("Updating public key for node") self._peers[uri]._nickname = nickname self._peers[uri]._pub = pub.decode('hex') self.trigger_callbacks('peer', self._peers[uri]) if msg_type == 'hello_request': # reply only if necessary self.send_enc(uri, hello_response(self.get_profile())) def on_message(self, msg): # here goes the application callbacks # we get a "clean" msg which is a dict holding whatever self._log.info("Data received: %s" % msg) pubkey = msg.get('pubkey') uri = msg.get('uri') ip = urlparse(uri).hostname port = urlparse(uri).port guid = msg.get('senderGUID') new_peer = CryptoPeerConnection(self, uri, pubkey, guid) self._dht.add_active_peer(new_peer) self._dht.add_known_node((ip, port, guid)) self.trigger_callbacks(msg['type'], msg) def on_raw_message(self, serialized): try: # Try to deserialize cleartext message msg = json.loads(serialized) self._log.info("Message Received [%s]" % msg.get('type', 'unknown')) except ValueError: try: # Encrypted? try: msg = self._myself.decrypt(serialized) msg = json.loads(msg) self._log.info("Decrypted Message [%s]" % msg.get('type', 'unknown')) except: self._log.error("Could not decrypt message: %s" % msg) return except: self._log.info("Bad Message: %s..." % self._myself.decrypt(serialized)) traceback.print_exc() return if msg.get('type') != '': msg_type = msg.get('type') msg_uri = msg.get('uri') msg_guid = msg.get('guid') # # if msg_type.startswith('hello') and msg_uri: # self.init_peer(msg) # for uri, pub in msg.get('peers', {}).iteritems(): # # Do not add yourself as a peer # if uri != self._uri: # self.init_peer({'uri': uri, 'pub': pub}) # self._log.info("Update peer table [%s peers]" % len(self._peers)) # # elif msg_type == 'goodbye' and msg_uri: # self._log.info("Received goodbye from %s" % msg_uri) # self.remove_peer(msg_uri) # # else: self.on_message(msg)
class CryptoTransportLayer(TransportLayer): def __init__(self, my_ip, my_port, market_id, bm_user=None, bm_pass=None, bm_port=None): self._log = logging.getLogger('[%s] %s' % (market_id, self.__class__.__name__)) # Connect to database MONGODB_URI = 'mongodb://*****:*****@localhost:{}/".format(bm_user, bm_pass, bm_port), verbose=1) result = self._bitmessage_api.add(2,3) self._log.info("Bitmessage test result: {}, API is live".format(result)) # If we failed, fall back to starting our own except Exception as e: self._log.info("Failed to connect to bitmessage instance: {}".format(e)) # self._log.info("Spawning internal bitmessage instance") # # Add bitmessage submodule path # sys.path.insert(0, os.path.join( # os.path.dirname(__file__), '..', 'pybitmessage', 'src')) # import bitmessagemain as bitmessage # bitmessage.logger.setLevel(logging.WARNING) # bitmessage_instance = bitmessage.Main() # bitmessage_instance.start(daemon=True) # bminfo = bitmessage_instance.getApiAddress() # if bminfo is not None: # self._log.info("Started bitmessage daemon at %s:%s".format( # bminfo['address'], bminfo['port'])) # bitmessage_api = xmlrpclib.ServerProxy("http://{}:{}@{}:{}/".format( # bm_user, bm_pass, bminfo['address'], bminfo['port'])) # else: # self._log.info("Failed to start bitmessage dameon") # self._bitmessage_api = None return result def _checkok(self, msg): self._log.info('Check ok') def get_guid(self): return self._guid def getDHT(self): return self._dht def getBitmessageAPI(self): return self._bitmessage_api def getMarketID(self): return self._market_id def getMyself(self): return self._myself def _ping(self, msg): self._log.info('Pinged %s ' % msg) pinger = CryptoPeerConnection(self,msg['uri'], msg['pubkey'], msg['senderGUID']) pinger.send_raw(json.dumps( {"type": "hello_response", "senderGUID": self.guid, "uri": self._uri, "pubkey": self.pubkey, })) def _storeValue(self, msg): self._dht._on_storeValue(msg) def _findNode(self, msg): self._dht.on_find_node(msg) def _findNodeResponse(self, msg): self._dht.on_findNodeResponse(self, msg) def _setup_settings(self): self.settings = self._db.settings.find_one({'id':"%s" % self._market_id}) if self.settings: self.nickname = self.settings['nickname'] if self.settings.has_key("nickname") else "" self.secret = self.settings['secret'] if self.settings.has_key("secret") else "" self.pubkey = self.settings['pubkey'] if self.settings.has_key("pubkey") else "" self.guid = self.settings['guid'] if self.settings.has_key("guid") else "" self.bitmessage = self.settings['bitmessage'] if self.settings.has_key('bitmessage') else "" else: self.nickname = 'Default' self._generate_new_keypair() if self._bitmessage_api is not None: self._generate_new_bitmessage_address() self.settings = self._db.settings.find_one({'id':"%s" % self._market_id}) self._log.debug('Retrieved Settings: %s', self.settings) def _generate_new_keypair(self): # Generate new keypair key = ec.ECC(curve='secp256k1') self.secret = key.get_privkey().encode('hex') pubkey = key.get_pubkey() signedPubkey = key.sign(pubkey) self.pubkey = pubkey.encode('hex') self._myself = key # Generate a node ID by ripemd160 hashing the signed pubkey guid = hashlib.new('ripemd160') guid.update(signedPubkey) self.guid = guid.digest().encode('hex') self._db.settings.update({"id":'%s' % self._market_id}, {"$set": {"secret":self.secret, "pubkey":self.pubkey, "guid":self.guid}}, True) def _generate_new_bitmessage_address(self): # Use the guid generated previously as the key self.bitmessage = self._bitmessage_api.createRandomAddress(self.guid.encode('base64'), False, 1.05, 1.1111) self._db.settings.update({"id":'%s' % self._market_id}, {"$set": {"bitmessage":self.bitmessage}}, True) def join_network(self, seed_uri, callback=lambda msg: None): if seed_uri: self._log.info('Initializing Seed Peer(s): [%s]' % (seed_uri)) def cb(msg): self._dht._iterativeFind(self._guid, self._dht._knownNodes, 'findNode') callback(msg) self.connect(seed_uri, callback=cb) # self.listen(self.pubkey) # Turn on zmq socket # # if seed_uri: # self._log.info('Initializing Seed Peer(s): [%s]' % seed_uri) # seed_peer = CryptoPeerConnection(self, seed_uri) # self._dht.start(seed_peer) def connect(self, uri, callback): def cb(msg): ip = urlparse(uri).hostname port = urlparse(uri).port self._dht.add_known_node((ip, port, peer._guid)) # Turning off peers #self.init_peer({'uri': seed_uri, 'guid':seed_guid}) # Add to routing table self.addCryptoPeer(peer) callback(msg) peer = CryptoPeerConnection(self, uri, callback=cb) return peer def get_crypto_peer(self, guid, uri, pubkey=None): if guid == self.guid: self._log.info('Trying to get cryptopeer for yourself') return peer = CryptoPeerConnection(self, uri, pubkey, guid=guid) return peer def addCryptoPeer(self, peer_to_add): foundOutdatedPeer = False for idx, peer in enumerate(self._dht._activePeers): if (peer._address, peer._guid, peer._pub) == (peer_to_add._address, peer_to_add._guid, peer_to_add._pub): self._log.info('Found existing peer, not adding.') return if peer._guid == peer_to_add._guid or peer._pub == peer_to_add._pub or peer._address == peer_to_add._address: foundOutdatedPeer = True self._log.info('Found an outdated peer') # Update existing peer self._activePeers[idx] = peer_to_add if not foundOutdatedPeer and peer_to_add._guid != self._guid: self._log.info('Adding crypto peer at %s' % peer_to_add._address) self._dht.add_active_peer(self, (peer_to_add._pub, peer_to_add._address, peer_to_add._guid)) # Return data array with details from the crypto file # TODO: This needs to be protected better; potentially encrypted file or DB @staticmethod def load_crypto_details(store_file): with open(store_file) as f: data = json.loads(f.read()) assert "nickname" in data assert "secret" in data assert "pubkey" in data assert len(data["secret"]) == 2 * 32 assert len(data["pubkey"]) == 2 * 33 return data["nickname"], data["secret"].decode("hex"), \ data["pubkey"].decode("hex") def get_profile(self): peers = {} self.settings = self._db.settings.find_one({'id':"%s" % self._market_id}) for uri, peer in self._peers.iteritems(): if peer._pub: peers[uri] = peer._pub.encode('hex') return {'uri': self._uri, 'pub': self._myself.get_pubkey().encode('hex'),'nickname': self.nickname, 'peers': peers} def respond_pubkey_if_mine(self, nickname, ident_pubkey): if ident_pubkey != self.pubkey: self._log.info("Public key does not match your identity") return # Return signed pubkey pubkey = self._myself.pubkey ec_key = obelisk.EllipticCurveKey() ec_key.set_secret(self.secret) digest = obelisk.Hash(pubkey) signature = ec_key.sign(digest) # Send array of nickname, pubkey, signature to transport layer self.send(proto_response_pubkey(nickname, pubkey, signature)) def pubkey_exists(self, pub): for uri, peer in self._peers.iteritems(): self._log.info('PEER: %s Pub: %s' % (peer._pub.encode('hex'), pub.encode('hex'))) if peer._pub.encode('hex') == pub.encode('hex'): return True return False def create_peer(self, uri, pub, node_guid): if pub: pub = pub.decode('hex') # Create the peer if public key is not already in the peer list # if not self.pubkey_exists(pub): self._peers[uri] = CryptoPeerConnection(self, uri, pub, node_guid) # Call 'peer' callbacks on listeners self.trigger_callbacks('peer', self._peers[uri]) # else: # print 'Pub Key is already in peer list' def send_enc(self, uri, msg): peer = self._peers[uri] pub = peer._pub # Now send a hello message to the peer if pub: self._log.info("Sending encrypted [%s] message to %s" % (msg['type'], uri)) peer.send(msg) else: # Will send clear profile on initial if no pub self._log.info("Sending unencrypted [%s] message to %s" % (msg['type'], uri)) self._peers[uri].send_raw(json.dumps(msg)) def init_peer(self, msg): uri = msg['uri'] pub = msg.get('pub') nickname = msg.get('nickname') msg_type = msg.get('type') guid = msg['guid'] if not self.valid_peer_uri(uri): self._log.error("Invalid Peer: %s " % uri) return if uri not in self._peers: # Unknown peer self._log.info('Add New Peer: %s' % uri) self.create_peer(uri, pub, guid) if not msg_type: self.send_enc(uri, hello_request(self.get_profile())) elif msg_type == 'hello_request': self.send_enc(uri, hello_response(self.get_profile())) else: # Known peer if pub: # test if we have to update the pubkey if not self._peers[uri]._pub: self._log.info("Setting public key for seed node") self._peers[uri]._pub = pub.decode('hex') self.trigger_callbacks('peer', self._peers[uri]) if self._peers[uri]._pub != pub.decode('hex'): self._log.info("Updating public key for node") self._peers[uri]._nickname = nickname self._peers[uri]._pub = pub.decode('hex') self.trigger_callbacks('peer', self._peers[uri]) if msg_type == 'hello_request': # reply only if necessary self.send_enc(uri, hello_response(self.get_profile())) def on_message(self, msg): # here goes the application callbacks # we get a "clean" msg which is a dict holding whatever #self._log.info("[On Message] Data received: %s" % msg) pubkey = msg.get('pubkey') uri = msg.get('uri') ip = urlparse(uri).hostname port = urlparse(uri).port guid = msg.get('senderGUID') self._dht.add_known_node((ip, port, guid)) self._dht.add_active_peer(self, (pubkey, uri, guid)) self.trigger_callbacks(msg['type'], msg) def on_raw_message(self, serialized): try: # Try to deserialize cleartext message msg = json.loads(serialized) self._log.info("Message Received [%s]" % msg.get('type', 'unknown')) except ValueError: try: # Encrypted? try: msg = self._myself.decrypt(serialized) msg = json.loads(msg) self._log.info("Decrypted Message [%s]" % msg.get('type', 'unknown')) except: self._log.error("Could not decrypt message: %s" % msg) return except: self._log.error('Message probably sent using incorrect pubkey') return if msg.get('type') is not None: msg_type = msg.get('type') msg_uri = msg.get('uri') msg_guid = msg.get('guid') self._log.info('Type: %s' % msg.get('type')) self.on_message(msg) else: self._log.error('Received a message with no type')