def verify(channel, data, signature, channel_hash=None): pieces = [ signature['signing_ts'].encode(), channel_hash or channel.claim_hash, data ] return Output.is_signature_valid( get_encoded_signature(signature['signature']), sha256(b''.join(pieces)), channel.claim.channel.public_key_bytes)
def is_comment_signed_by_channel(comment: dict, channel: Output): try: pieces = [ comment['signing_ts'].encode(), channel.claim_hash, comment['comment'].encode() ] return Output.is_signature_valid( get_encoded_signature(comment['signature']), sha256(b''.join(pieces)), channel.claim.channel.public_key_bytes) except KeyError: pass return False
def is_comment_signed_by_channel(comment: dict, channel: Output, sign_comment_id=False): if isinstance(channel, Output): try: signing_field = comment['comment_id'] if sign_comment_id else comment['comment'] pieces = [ comment['signing_ts'].encode(), cid2hash(comment['channel_id']), signing_field.encode() ] return Output.is_signature_valid( get_encoded_signature(comment['signature']), sha256(b''.join(pieces)), channel.claim.channel.public_key_bytes ) except KeyError: pass return False
def check(db_path, claim_id): db = sqlite3.connect(db_path) db.row_factory = sqlite3.Row claim = db.execute('select * from claim where claim_id=?', (claim_id,)).fetchone() if not claim: print('Could not find claim.') return channel = db.execute('select * from claim where claim_hash=?', (claim['channel_hash'],)).fetchone() if not channel: print('Could not find channel for this claim.') print(f"Claim: {claim['claim_name']}") print(f"Channel: {channel['claim_name']}") print(f"Signature: {hexlify(claim['signature']).decode()}") print(f"Digest: {hexlify(claim['signature_digest']).decode()}") print(f"Pubkey: {hexlify(channel['public_key_bytes']).decode()}") print("Valid: {}".format(Output.is_signature_valid( claim['signature'], claim['signature_digest'], channel['public_key_bytes'] )))
def validate_channel_signatures(self, height, new_claims, updated_claims, spent_claims, affected_channels, timer): if not new_claims and not updated_claims and not spent_claims: return sub_timer = timer.add_timer('segregate channels and signables') sub_timer.start() channels, new_channel_keys, signables = {}, {}, {} for txo in chain(new_claims, updated_claims): try: claim = txo.claim except: continue if claim.is_channel: channels[txo.claim_hash] = txo new_channel_keys[txo.claim_hash] = claim.channel.public_key_bytes else: signables[txo.claim_hash] = txo sub_timer.stop() sub_timer = timer.add_timer('make list of channels we need to lookup') sub_timer.start() missing_channel_keys = set() for txo in signables.values(): claim = txo.claim if claim.is_signed and claim.signing_channel_hash not in new_channel_keys: missing_channel_keys.add(claim.signing_channel_hash) sub_timer.stop() sub_timer = timer.add_timer('lookup missing channels') sub_timer.start() all_channel_keys = {} if new_channel_keys or missing_channel_keys or affected_channels: all_channel_keys = dict(self.execute(*query( "SELECT claim_hash, public_key_bytes FROM claim", claim_hash__in=set(new_channel_keys) | missing_channel_keys | affected_channels ))) sub_timer.stop() sub_timer = timer.add_timer('prepare for updating claims') sub_timer.start() changed_channel_keys = {} for claim_hash, new_key in new_channel_keys.items(): if claim_hash not in all_channel_keys or all_channel_keys[claim_hash] != new_key: all_channel_keys[claim_hash] = new_key changed_channel_keys[claim_hash] = new_key claim_updates = [] for claim_hash, txo in signables.items(): claim = txo.claim update = { 'claim_hash': claim_hash, 'channel_hash': None, 'signature': None, 'signature_digest': None, 'signature_valid': None } if claim.is_signed: update.update({ 'channel_hash': claim.signing_channel_hash, 'signature': txo.get_encoded_signature(), 'signature_digest': txo.get_signature_digest(self.ledger), 'signature_valid': 0 }) claim_updates.append(update) sub_timer.stop() sub_timer = timer.add_timer('find claims affected by a change in channel key') sub_timer.start() if changed_channel_keys: sql = f""" SELECT * FROM claim WHERE channel_hash IN ({','.join('?' for _ in changed_channel_keys)}) AND signature IS NOT NULL """ for affected_claim in self.execute(sql, changed_channel_keys.keys()): if affected_claim.claim_hash not in signables: claim_updates.append({ 'claim_hash': affected_claim.claim_hash, 'channel_hash': affected_claim.channel_hash, 'signature': affected_claim.signature, 'signature_digest': affected_claim.signature_digest, 'signature_valid': 0 }) sub_timer.stop() sub_timer = timer.add_timer('verify signatures') sub_timer.start() for update in claim_updates: channel_pub_key = all_channel_keys.get(update['channel_hash']) if channel_pub_key and update['signature']: update['signature_valid'] = Output.is_signature_valid( bytes(update['signature']), bytes(update['signature_digest']), channel_pub_key ) sub_timer.stop() sub_timer = timer.add_timer('update claims') sub_timer.start() if claim_updates: self.executemany(f""" UPDATE claim SET channel_hash=:channel_hash, signature=:signature, signature_digest=:signature_digest, signature_valid=:signature_valid, channel_join=CASE WHEN signature_valid=1 AND :signature_valid=1 AND channel_hash=:channel_hash THEN channel_join WHEN :signature_valid=1 THEN {height} END, canonical_url=CASE WHEN signature_valid=1 AND :signature_valid=1 AND channel_hash=:channel_hash THEN canonical_url WHEN :signature_valid=1 THEN (SELECT short_url FROM claim WHERE claim_hash=:channel_hash)||'/'|| claim_name||COALESCE( (SELECT shortest_id(other_claim.claim_id, claim.claim_id) FROM claim AS other_claim WHERE other_claim.signature_valid = 1 AND other_claim.channel_hash = :channel_hash AND other_claim.normalized = claim.normalized), '#'||substr(claim_id, 1, 1) ) END WHERE claim_hash=:claim_hash; """, claim_updates) sub_timer.stop() sub_timer = timer.add_timer('update claims affected by spent channels') sub_timer.start() if spent_claims: self.execute( f""" UPDATE claim SET signature_valid=CASE WHEN signature IS NOT NULL THEN 0 END, channel_join=NULL, canonical_url=NULL WHERE channel_hash IN ({','.join('?' for _ in spent_claims)}) """, spent_claims ) sub_timer.stop() sub_timer = timer.add_timer('update channels') sub_timer.start() if channels: self.executemany( """ UPDATE claim SET public_key_bytes=:public_key_bytes, public_key_hash=:public_key_hash WHERE claim_hash=:claim_hash""", [{ 'claim_hash': claim_hash, 'public_key_bytes': txo.claim.channel.public_key_bytes, 'public_key_hash': self.ledger.address_to_hash160( self.ledger.public_key_to_address(txo.claim.channel.public_key_bytes) ) } for claim_hash, txo in channels.items()] ) sub_timer.stop() sub_timer = timer.add_timer('update claims_in_channel counts') sub_timer.start() if all_channel_keys: self.executemany(f""" UPDATE claim SET claims_in_channel=( SELECT COUNT(*) FROM claim AS claim_in_channel WHERE claim_in_channel.signature_valid=1 AND claim_in_channel.channel_hash=claim.claim_hash ) WHERE claim_hash = ? """, [(channel_hash,) for channel_hash in all_channel_keys.keys()]) sub_timer.stop() sub_timer = timer.add_timer('update blocked claims list') sub_timer.start() if (self.blocking_channel_hashes.intersection(all_channel_keys) or self.filtering_channel_hashes.intersection(all_channel_keys)): self.update_blocked_and_filtered_claims() sub_timer.stop()