Esempio n. 1
0
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)
Esempio n. 2
0
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
Esempio n. 3
0
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
Esempio n. 4
0
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']
    )))
Esempio n. 5
0
    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()