forked from LBRYCommunity/lbry-sdk
add tests and fix verification of old signatures
This commit is contained in:
parent
0830917afb
commit
a9e0eeba7e
4 changed files with 87 additions and 25 deletions
|
@ -50,7 +50,7 @@ class MainNetLedger(BaseLedger):
|
|||
@property
|
||||
def resolver(self):
|
||||
return Resolver(self.headers.claim_trie_root, self.headers.height, self.transaction_class,
|
||||
hash160_to_address=self.hash160_to_address, network=self.network)
|
||||
hash160_to_address=self.hash160_to_address, network=self.network, ledger=self)
|
||||
|
||||
async def resolve(self, page, page_size, *uris):
|
||||
for uri in uris:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import logging
|
||||
|
||||
from ecdsa import BadSignatureError
|
||||
from cryptography.exceptions import InvalidSignature
|
||||
from binascii import unhexlify, hexlify
|
||||
from lbrynet.wallet.dewies import dewies_to_lbc
|
||||
from lbrynet.error import UnknownNameError, UnknownClaimID, UnknownURI, UnknownOutpoint
|
||||
|
@ -15,12 +15,13 @@ log = logging.getLogger(__name__)
|
|||
|
||||
class Resolver:
|
||||
|
||||
def __init__(self, claim_trie_root, height, transaction_class, hash160_to_address, network):
|
||||
def __init__(self, claim_trie_root, height, transaction_class, hash160_to_address, network, ledger):
|
||||
self.claim_trie_root = claim_trie_root
|
||||
self.height = height
|
||||
self.transaction_class = transaction_class
|
||||
self.hash160_to_address = hash160_to_address
|
||||
self.network = network
|
||||
self.ledger = ledger
|
||||
|
||||
async def _handle_resolutions(self, resolutions, requested_uris, page, page_size):
|
||||
results = {}
|
||||
|
@ -188,8 +189,7 @@ class Resolver:
|
|||
claim_result['has_signature'] = True
|
||||
claim_result['signature_is_valid'] = False
|
||||
validated, channel_name = validate_claim_signature_and_get_channel_name(
|
||||
claim_result, certificate, claim_result['address'], claim_result['name'],
|
||||
claim_tx=claim_tx, cert_tx=cert_tx
|
||||
claim_result, certificate, self.ledger, claim_tx=claim_tx, cert_tx=cert_tx
|
||||
)
|
||||
claim_result['channel_name'] = channel_name
|
||||
if validated:
|
||||
|
@ -389,33 +389,21 @@ def _verify_proof(name, claim_trie_root, result, height, depth, transaction_clas
|
|||
return {'error': "proof not in result"}
|
||||
|
||||
|
||||
def validate_claim_signature_and_get_channel_name(claim_result, certificate_claim,
|
||||
claim_address, name, decoded_certificate=None,
|
||||
def validate_claim_signature_and_get_channel_name(claim_result, certificate_claim, ledger,
|
||||
claim_tx=None, cert_tx=None):
|
||||
if cert_tx and certificate_claim and claim_tx and claim_result:
|
||||
tx = Transaction(unhexlify(claim_tx))
|
||||
cert_tx = Transaction(unhexlify(cert_tx))
|
||||
is_signed = tx.outputs[claim_result['nout']].is_signed_by(cert_tx.outputs[certificate_claim['nout']])
|
||||
try:
|
||||
is_signed = tx.outputs[claim_result['nout']].is_signed_by(
|
||||
cert_tx.outputs[certificate_claim['nout']], ledger
|
||||
)
|
||||
except InvalidSignature:
|
||||
return False, None
|
||||
return is_signed, certificate_claim['name']
|
||||
return False, None
|
||||
|
||||
|
||||
def _validate_signed_claim(claim, claim_address, name, certificate):
|
||||
if not claim.has_signature:
|
||||
raise Exception("Claim is not signed")
|
||||
try:
|
||||
if claim.validate_signature(claim_address, certificate.protobuf, name):
|
||||
return True
|
||||
except BadSignatureError:
|
||||
# print_msg("Signature for %s is invalid" % claim_id)
|
||||
return False
|
||||
except Exception as err:
|
||||
log.error("Signature for %s is invalid, reason: %s - %s", claim_address,
|
||||
str(type(err)), err)
|
||||
return False
|
||||
return False
|
||||
|
||||
|
||||
# TODO: The following came from code handling lbryum results. Now that it's all in one place a refactor should unify it.
|
||||
def _decode_claim_result(claim):
|
||||
if 'has_signature' in claim and claim['has_signature']:
|
||||
|
|
|
@ -96,6 +96,7 @@ class Output(BaseOutput):
|
|||
def is_signed_by(self, channel: 'Output', ledger=None):
|
||||
if self.claim.unsigned_payload:
|
||||
pieces = [
|
||||
self.claim_name.lower().encode(),
|
||||
Base58.decode(self.get_address(ledger)),
|
||||
self.claim.unsigned_payload,
|
||||
self.claim.signing_channel_hash
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
import hashlib
|
||||
import tempfile
|
||||
from binascii import unhexlify
|
||||
|
||||
from lbrynet.wallet.transaction import Transaction
|
||||
import ecdsa
|
||||
|
||||
from lbrynet.wallet.transaction import Transaction, Output
|
||||
from lbrynet.error import InsufficientFundsError
|
||||
from lbrynet.schema.claim import Claim
|
||||
from lbrynet.schema.compat import OldClaimMessage
|
||||
|
||||
from integration.testcase import CommandTestCase
|
||||
from torba.client.hash import sha256, Base58
|
||||
|
||||
|
||||
class ClaimCommands(CommandTestCase):
|
||||
|
@ -402,3 +406,72 @@ class ClaimCommands(CommandTestCase):
|
|||
self.assertEqual(c2['claim_id'], r4c)
|
||||
self.assertEqual(r3c, r4c)
|
||||
self.assertEqual(r3n, r4n)
|
||||
|
||||
async def test_resolve_old_claim(self):
|
||||
channel = await self.daemon.jsonrpc_channel_new('@olds', "1.0")
|
||||
self.assertTrue(channel['success'])
|
||||
await self.confirm_tx(channel['tx'].id)
|
||||
address = channel['output'].get_address(self.account.ledger)
|
||||
claim = generate_signed_legacy('example', address, channel['output'])
|
||||
tx = await Transaction.claim('example', claim.SerializeToString(), 1, address, [self.account], self.account)
|
||||
await tx.sign([self.account])
|
||||
await self.broadcast(tx)
|
||||
await self.ledger.wait(tx)
|
||||
await self.generate(1)
|
||||
await self.ledger.wait(tx)
|
||||
|
||||
response = await self.daemon.jsonrpc_resolve(urls='@olds/example')
|
||||
self.assertTrue(response['@olds/example']['claim']['signature_is_valid'])
|
||||
|
||||
claim.publisherSignature.signature = bytes(reversed(claim.publisherSignature.signature))
|
||||
tx = await Transaction.claim(
|
||||
'bad_example', claim.SerializeToString(), 1, address, [self.account], self.account
|
||||
)
|
||||
await tx.sign([self.account])
|
||||
await self.broadcast(tx)
|
||||
await self.ledger.wait(tx)
|
||||
await self.generate(1)
|
||||
await self.ledger.wait(tx)
|
||||
|
||||
response = await self.daemon.jsonrpc_resolve(urls='bad_example')
|
||||
self.assertFalse(response['bad_example']['claim']['signature_is_valid'], response)
|
||||
response = await self.daemon.jsonrpc_resolve(urls='@olds/bad_example')
|
||||
self.assertEqual('URI lbry://@olds/bad_example cannot be resolved', response['@olds/bad_example']['error'])
|
||||
|
||||
|
||||
def generate_signed_legacy(name: str, address: bytes, output: Output):
|
||||
decoded_address = Base58.decode(address)
|
||||
claim = OldClaimMessage()
|
||||
claim.ParseFromString(unhexlify(
|
||||
'080110011aee04080112a604080410011a2b4865726520617265203520526561736f6e73204920e29da4e'
|
||||
'fb88f204e657874636c6f7564207c20544c4722920346696e64206f7574206d6f72652061626f7574204e'
|
||||
'657874636c6f75643a2068747470733a2f2f6e657874636c6f75642e636f6d2f0a0a596f752063616e206'
|
||||
'6696e64206d65206f6e20746865736520736f6369616c733a0a202a20466f72756d733a2068747470733a'
|
||||
'2f2f666f72756d2e6865617679656c656d656e742e696f2f0a202a20506f64636173743a2068747470733'
|
||||
'a2f2f6f6666746f706963616c2e6e65740a202a2050617472656f6e3a2068747470733a2f2f7061747265'
|
||||
'6f6e2e636f6d2f7468656c696e757867616d65720a202a204d657263683a2068747470733a2f2f7465657'
|
||||
'37072696e672e636f6d2f73746f7265732f6f6666696369616c2d6c696e75782d67616d65720a202a2054'
|
||||
'77697463683a2068747470733a2f2f7477697463682e74762f786f6e64616b0a202a20547769747465723'
|
||||
'a2068747470733a2f2f747769747465722e636f6d2f7468656c696e757867616d65720a0a2e2e2e0a6874'
|
||||
'7470733a2f2f7777772e796f75747562652e636f6d2f77617463683f763d4672546442434f535f66632a0'
|
||||
'f546865204c696e75782047616d6572321c436f7079726967687465642028636f6e746163742061757468'
|
||||
'6f722938004a2968747470733a2f2f6265726b2e6e696e6a612f7468756d626e61696c732f46725464424'
|
||||
'34f535f666352005a001a41080110011a30040e8ac6e89c061f982528c23ad33829fd7146435bf7a4cc22'
|
||||
'f0bff70c4fe0b91fd36da9a375e3e1c171db825bf5d1f32209766964656f2f6d70342a5c080110031a406'
|
||||
'2b2dd4c45e364030fbfad1a6fefff695ebf20ea33a5381b947753e2a0ca359989a5cc7d15e5392a0d354c'
|
||||
'0b68498382b2701b22c03beb8dcb91089031b871e72214feb61536c007cdf4faeeaab4876cb397feaf6b51'
|
||||
))
|
||||
claim.ClearField("publisherSignature")
|
||||
digest = sha256(b''.join([
|
||||
name.lower().encode(),
|
||||
decoded_address,
|
||||
claim.SerializeToString(),
|
||||
output.claim_hash
|
||||
]))
|
||||
private_key = ecdsa.SigningKey.from_pem(output.private_key, hashfunc=hashlib.sha256)
|
||||
signature = private_key.sign_digest_deterministic(digest, hashfunc=hashlib.sha256)
|
||||
claim.publisherSignature.version = 1
|
||||
claim.publisherSignature.signatureType = 1
|
||||
claim.publisherSignature.signature = signature
|
||||
claim.publisherSignature.certificateId = output.claim_hash
|
||||
return claim
|
Loading…
Reference in a new issue