def sign(self, key): """ Signs this block with the given key :param key: the key to sign this block with """ crypto = ECCrypto() self.signature = crypto.create_signature(key, self.pack(signature=False))
def get_public_key(private_key): """ Get the public key pertaining to a private_key :param private_key: The private key, encoded in HEX :return: Returns the public key encoded in HEX """ eccrypto = ECCrypto() try: if eccrypto.is_valid_private_bin(private_key.decode("HEX")): priv = eccrypto.key_from_private_bin(private_key.decode("HEX")) return priv.pub().key_to_bin().encode("HEX") return None except TypeError: return None
def sign(self, api): """ Sign a model by hashing its contents and signing this hash. """ if not self.id: raise RuntimeError("Can't sign an unsaved model") ec = ECCrypto() hash = self.generate_sha1_hash() private_key = api.db.backend.get_option('user_key_priv') public_key = api.db.backend.get_option('user_key_pub') signing_key = ec.key_from_private_bin(private_key.decode('HEX')) self._signature = ec.create_signature(signing_key, hash) self._signer = public_key self._time_signed = int(time.time()) self.post_or_put(api.db)
def main(): eccrypto = ECCrypto() parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument( "curves", metavar="CURVE", nargs="+", choices=sorted([str(curve) for curve in eccrypto.security_levels]), help="EC curves to create") args = parser.parse_args() create_key(eccrypto, (unicode(curve) for curve in args.curves))
def run(): crypto = ECCrypto() dispersy = Dispersy( StandaloneEndpoint(options["port"], options["ip"]), options["statedir"], u'dispersy.db', crypto) if not dispersy.start(): raise RuntimeError("Unable to start Dispersy") master_member = MarketCommunity.get_master_members(dispersy)[0] my_member = dispersy.get_member(private_key=crypto.key_to_bin( crypto.generate_key(u"curve25519"))) MarketCommunity.init_community(dispersy, master_member, my_member) self._stopping = False def signal_handler(sig, frame): logger.info("Received signal '%s' in %s (shutting down)" % (sig, frame)) if not self._stopping: self._stopping = True dispersy.stop() reactor.stop() signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler)
def __init__(self, endpoint, working_directory, database_filename=u"dispersy.db", crypto=ECCrypto()): """Provide hooks into the endpoint, otherwise equal to the normal Dispersy() constructor. Hooks into: - Endpoint socket data loop: to establish local port (/peer id) - Endpoint packet handler: to log data between peer ids """ self.myid = endpoint._port # Eavesdrop on the listen server connection accepting loop pt_ep_loop = endpoint._loop funcType = type(StandaloneEndpoint._loop) def epLoopMim(eself): self.myid = eself._port # This can change at this point, update it accordingly pt_ep_loop() endpoint._loop = funcType(epLoopMim, endpoint, StandaloneEndpoint) # Eavesdrop on the packet delegator pt_ep_data_came_in = endpoint.dispersythread_data_came_in funcType = type(StandaloneEndpoint.dispersythread_data_came_in) def epPacketRcvMim(eself, packets, timestamp, cache=True): fakepackets = [] for sock_addr, data in packets: if data.startswith("dpvizidrq"): # On an id request, log our id sending to the id of the # requester community_name = data[9:data.index(',')] oid = int(data[data.index(',') + 1:]) report_event( VD_EVT_COMMUNICATION(self.myid, oid, community_name)) else: # On normal data, request the other's id so we can log # communication try: if data[22] != chr( 248): # dispersy-identity has no community community_name = type( self.get_community(data[2:22], False, False)).__name__ request = "dpvizidrq" + \ community_name + "," + str(self.myid) try: eself._socket.sendto(request, sock_addr) except socket.error: with eself._sendqueue_lock: did_have_senqueue = bool(eself._sendqueue) eself._sendqueue.append( (time(), candidate.sock_addr, request)) if not did_have_senqueue: eself._process_sendqueue() except CommunityNotFoundException: pass # We have discovered external communities, ignore these fakepackets.append((sock_addr, data)) # If the incoming packets are more than VisualDispersy id requests # forward them to the actual Dispersy object. if len(fakepackets) > 0: pt_ep_data_came_in(fakepackets, timestamp, cache) endpoint.dispersythread_data_came_in = funcType( epPacketRcvMim, endpoint, StandaloneEndpoint) # Actually init Dispersy with our modified endpoint super(VisualDispersy, self).__init__(endpoint, working_directory, database_filename, crypto)
def signature_valid(cls, obj): ec = ECCrypto() signing_key = ec.key_from_public_bin(obj._signer.decode("HEX")) signature_valid = ec.is_valid_signature(signing_key, obj.generate_sha1_hash(), obj.signature) return signature_valid
def validate(self, database): """ Validates this block against what is known in the database :param database: the database to check against :return: A tuple consisting of a ValidationResult and a list of user string errors """ # we start off thinking everything is hunky dory result = [ValidationResult.valid] errors = [] crypto = ECCrypto() # short cut for invalidating so we don't have repeating similar code for every error. # this is also the reason result is a list, we need a mutable container. Assignments in err are limited to its # scope. So setting result directly is not possible. def err(reason): result[0] = ValidationResult.invalid errors.append(reason) # Step 1: get all related blocks from the database. # The validity of blocks is immutable. Once they are accepted they cannot change validation result. In such # cases subsequent blocks can get validation errors and will not get inserted into the database. Thus we can # assume that all retrieved blocks are not invalid themselves. Blocks can get inserted into the database in any # order, so we need to find successors, predecessors as well as the block itself and its linked block. blk = database.get(self.public_key, self.sequence_number) link = database.get_linked(self) prev_blk = database.get_block_before(self) next_blk = database.get_block_after(self) # Step 2: determine the maximum validation level # Depending on the blocks we get from the database, we can decide to reduce the validation level. We must do # this prior to flagging any errors. This way we are only ever reducing the validation level without having to # resort to min()/max() every time we set it. We first determine some booleans to make everything readable. is_genesis = self.sequence_number == GENESIS_SEQ or self.previous_hash == GENESIS_HASH is_prev_gap = prev_blk.sequence_number != self.sequence_number - 1 if prev_blk else True is_next_gap = next_blk.sequence_number != self.sequence_number + 1 if next_blk else True if not prev_blk and not next_blk: # Is this block a non genesis block? If so, we know nothing about this public key, else pretend the # prev_blk exists if not is_genesis: result[0] = ValidationResult.no_info else: # We pretend prev_blk exists. This leaves us with next missing, which means partial-next at best. result[0] = ValidationResult.partial_next elif not prev_blk and next_blk: # Is this block a non genesis block? if not is_genesis: # We are really missing prev_blk. So now partial-prev at best. result[0] = ValidationResult.partial_previous if is_next_gap: # Both sides are unknown or non-contiguous return a full partial result. result[0] = ValidationResult.partial elif is_next_gap: # This is a genesis block, so the missing previous is expected. If there is a gap to the next block # this reduces the validation result to partial-next result[0] = ValidationResult.partial_next elif prev_blk and not next_blk: # We are missing next_blk, so now partial-next at best. result[0] = ValidationResult.partial_next if is_prev_gap: # Both sides are unknown or non-contiguous return a full partial result. result[0] = ValidationResult.partial else: # both sides have known blocks, see if there are gaps if is_prev_gap and is_next_gap: result[0] = ValidationResult.partial elif is_prev_gap: result[0] = ValidationResult.partial_previous elif is_next_gap: result[0] = ValidationResult.partial_next # Step 3: validate that the block is sane, including the validity of the transaction # Some basic self tests. It is possible to violate these when constructing a block in code or getting a block # from the database. The wire format is such that it impossible to hit many of these for blocks that went over # the network. tx_validate_res, tx_errors = self.validate_transaction(database) if tx_validate_res != ValidationResult.valid: result[0] = tx_validate_res errors += tx_errors if self.sequence_number < GENESIS_SEQ: err("Sequence number is prior to genesis") if self.link_sequence_number < GENESIS_SEQ and self.link_sequence_number != UNKNOWN_SEQ: err("Link sequence number not empty and is prior to genesis") if not crypto.is_valid_public_bin(self.public_key): err("Public key is not valid") else: # If the public key is valid, we can use it to check the signature. We want just a yes/no answer here, and # we want to keep checking for more errors, so just catch all packing exceptions and err() if any happen. try: pck = self.pack(signature=False) except: pck = None if pck is None or not crypto.is_valid_signature( crypto.key_from_public_bin(self.public_key), pck, self.signature): err("Invalid signature") if not crypto.is_valid_public_bin(self.link_public_key): err("Linked public key is not valid") if self.public_key == self.link_public_key: # Blocks to self serve no purpose and are thus invalid. err("Self signed block") if is_genesis: if self.sequence_number == GENESIS_SEQ and self.previous_hash != GENESIS_HASH: err("Sequence number implies previous hash should be Genesis ID") if self.sequence_number != GENESIS_SEQ and self.previous_hash == GENESIS_HASH: err("Sequence number implies previous hash should not be Genesis ID") # Step 4: does the database already know about this block? If so it should be equal or else we caught a # branch in someones trustchain. if blk: # Sanity check to see if the database returned the expected block, we want to cover all our bases before # crying wolf and making a fraud claim. assert blk.public_key == self.public_key and blk.sequence_number == self.sequence_number, \ "Database returned unexpected block" if blk.link_public_key != self.link_public_key: err("Link public key does not match known block") if blk.link_sequence_number != self.link_sequence_number: err("Link sequence number does not match known block") if blk.previous_hash != self.previous_hash: err("Previous hash does not match known block") if blk.signature != self.signature: err("Signature does not match known block") # if the known block is not equal, and the signatures are valid, we have a double signed PK/seq. Fraud! if self.hash != blk.hash and "Invalid signature" not in errors and "Public key is not valid" not in errors: err("Double sign fraud") # Step 5: does the database have the linked block? If so do the values match up? If the values do not match up # someone comitted fraud, but it is impossible to decide who. So we just invalidate the block that is the latter # to get validated. We can also detect double counter sign fraud at this point. if link: # Sanity check to see if the database returned the expected block, we want to cover all our bases before # crying wolf and making a fraud claim. assert link.public_key == self.link_public_key and \ (link.link_sequence_number == self.sequence_number or link.sequence_number == self.link_sequence_number), \ "Database returned unexpected block" if self.public_key != link.link_public_key: err("Public key mismatch on linked block") elif self.link_sequence_number != UNKNOWN_SEQ: # self counter signs another block (link). If link has a linked block that is not equal to self, # then self is fraudulent, since it tries to countersign a block that is already countersigned linklinked = database.get_linked(link) if linklinked is not None and linklinked.hash != self.hash: err("Double countersign fraud") # Step 6: Did we get blocks from the database before or after self? They should be checked for violations too. if prev_blk: # Sanity check of the block the database gave us. assert prev_blk.public_key == self.public_key and prev_blk.sequence_number < self.sequence_number,\ "Database returned unexpected block" if not is_prev_gap and prev_blk.hash != self.previous_hash: err("Previous hash is not equal to the hash id of the previous block") # Is this fraud? It is certainly an error, but fixing it would require a different signature on the same # sequence number which is fraud. if next_blk: # Sanity check of the block the database gave us. assert next_blk.public_key == self.public_key and next_blk.sequence_number > self.sequence_number,\ "Database returned unexpected block" if not is_next_gap and next_blk.previous_hash != self.hash: err("Next hash is not equal to the hash id of the block") # Again, this might not be fraud, but fixing it can only result in fraud. return result[0], errors