def validate_sig_hash(item): # The reason this is a combined check on sig+hash (instead of split methods) # Is that check must be atomic, as sig+hash mutate the tick in certain order # Deepcopy used to not modify instance we received item_copy = copy.deepcopy(item) signature = item_copy.pop('signature', None) if signature is None: logger.debug("Could not find signature in validate sighash..") return False # Check hash if not validate_difficulty(hasher(item_copy)): logger.debug("Invalid hash for item: " + str(item_copy) + " " + hasher(item_copy)) return False # Validate signature try: encoded_message = standard_encode(item_copy) if not verify(encoded_message, signature, item_copy['pubkey']): return False except ecdsa.BadSignatureError: # TODO : When new joiner joins, make sure peers relay latest hash logger.debug("Bad signature!" + str(item_copy) + " " + str(signature)) return False return True
def send_mutual_add_requests(self, peerslist): successful_adds = 0 # Mutual add peers for peer in peerslist: if peer not in self.peers and len( self.peers) <= config['max_peers']: content = {"port": self.port, 'pubkey': credentials.pubkey} signature = sign(standard_encode(content), credentials.privkey) content['signature'] = signature status_code = None response = None result, success = attempt(requests.post, False, url=peer + '/mutual_add', json=content, timeout=config['timeout']) if success: status_code = result.status_code response = result.text else: logger.debug("Couldn't connect to " + peer) if status_code in [201, 503]: if status_code == 201: logger.info("Adding peer " + str(peer)) peer_addr = response self.register_peer(peer, peer_addr) successful_adds += 1 if status_code == 503: logger.info("Peer was at peer-maximum") return successful_adds
def generate_and_process_tick(self): height = self.clockchain.current_height() + 1 tick = { 'list': list(self.clockchain.ping_pool.values()), 'pubkey': credentials.pubkey, 'prev_tick': self.clockchain.prev_tick_ref(), 'height': height } this_tick, nonce = mine(tick) tick['nonce'] = nonce signature = sign(standard_encode(tick), credentials.privkey) tick['signature'] = signature # This is to keep track of the "name" of the tick as debug info # this_tick is not actually necessary according to tick schema tick['this_tick'] = this_tick prev_tick = self.clockchain.latest_selected_tick() possible_previous = self.clockchain.possible_previous_ticks() # Validate own tick retries = 0 while retries < config['tick_retries']: if not validate_tick( tick, prev_tick, possible_previous, verbose=False): retries = retries + 1 time.sleep(config['tick_retries_sleep']) else: self.clockchain.add_to_tick_pool(tick) # Forward to peers (this must be after all validation) self.networker.forward(data_dict=tick, route='tick', origin=credentials.addr, redistribute=0) logger.debug("Forwarded own tick: " + str(tick)) return True logger.debug( "Failed own tick validation too many times. not forwarded") return False
def generate_and_process_ping(self, reference, vote=False): # TODO: Code duplication between here and api.. where to put?? # TODO: Can't be in helpers, and cant be in clockchain/networker.. # Always construct ping in the following order: # 1) Init 2) Mine+nonce 3) Add signature # This is because the order of nonce and sig creation matters ping = { 'pubkey': credentials.pubkey, 'timestamp': utcnow(), 'reference': reference } stage = 'vote' if vote else 'ping' _, nonce = mine(ping) ping['nonce'] = nonce signature = sign(standard_encode(ping), credentials.privkey) ping['signature'] = signature # Validate own ping if not validate_ping(ping, self.clockchain.ping_pool, vote): logger.debug("Failed own " + stage + " validation") return False if vote: self.clockchain.add_to_vote_pool(ping) else: self.clockchain.add_to_ping_pool(ping) route = 'vote' if vote else 'ping' # Forward to peers (this must be after all validation) self.networker.forward(data_dict=ping, route=route, origin=credentials.addr, redistribute=0) logger.debug("Forwarded own " + route + ": " + str(ping)) return True
def mutual_add(): values = request.get_json() if self.check_duplicate(values): return "duplicate request please wait 10s", 400 # Verify json schema if not validate_schema(values, 'mutual_add_schema.json'): return "Invalid request", 400 # Verify that pubkey and signature match signature = values.pop("signature") if not verify(standard_encode(values), signature, values['pubkey']): return "Invalid signature", 400 # Return a 503: service unavailable here # so that they can try adding my friends instead if len(self.networker.peers) > config['max_peers']: return "dont need more peers", 503 # TODO: What if rogue peer send fake port? Possible ddos reflection? # TODO: Do schema validation for integer sizes / string lengths.. remote_port = int(values.get('port')) remote_url = resolve(request.remote_addr) remote_url = "http://" + remote_url + ":" + str(remote_port) remote_cleaned_url = self.networker.get_full_location(remote_url) own_cleaned_url = self.networker.get_full_location( request.url_root) # Avoid inf loop by not adding self.. if remote_cleaned_url != own_cleaned_url: result, success = attempt(requests.get, False, url=remote_url + '/info/addr', timeout=config['timeout']) if success: addr = result.text else: return "couldn't get addr", 400 # Verify that the host's address matches the key pair used # to sign the mutual_add request if not pubkey_to_addr(values['pubkey']) == addr: return "Received request signed with key != host", 400 if not self.networker.register_peer(remote_url, addr): return "Could not register peer", 400 else: # Make sure the new joiner gets my pings (if I have any) if credentials.addr in self.clockchain.ping_pool: ping = self.clockchain.ping_pool[credentials.addr] # Forward but do not redistribute _, success = attempt( requests.post, False, url=remote_url + '/forward/ping?addr=' + credentials.addr + "&redistribute=-1", json=ping, timeout=config['timeout']) if not success: return "couldnt forward my ping", 400 else: return "cannot add self", 400 return credentials.addr, 201
from utils.pki import get_kp, sign from main import config from utils.helpers import standard_encode import requests # Make a mutual_add request pretending to be another host target = config['seeds'][0] spoof = config['seeds'][1] spoof_port = spoof.split(':')[2] pub, priv = get_kp() content = {"port": int(spoof_port), 'pubkey': pub} signature = sign(standard_encode(content), priv) content['signature'] = signature response = requests.post(target + '/mutual_add', json=content, timeout=1000) print(response.text)