move exception handling into is_signed_by

This commit is contained in:
Lex Berezhny 2019-05-04 22:15:25 -04:00
parent 6946860521
commit 9accbfcf8b
6 changed files with 31 additions and 27 deletions

View file

@ -4,8 +4,6 @@ from binascii import hexlify
from datetime import datetime from datetime import datetime
from json import JSONEncoder from json import JSONEncoder
from ecdsa import BadSignatureError
from cryptography.exceptions import InvalidSignature
from google.protobuf.message import DecodeError from google.protobuf.message import DecodeError
from lbrynet.schema.claim import Claim from lbrynet.schema.claim import Claim
@ -192,15 +190,8 @@ class JSONResponseEncoder(JSONEncoder):
} }
if check_signature and txo.claim.is_signed: if check_signature and txo.claim.is_signed:
output['is_channel_signature_valid'] = False output['is_channel_signature_valid'] = False
try: if txo.channel:
output['is_channel_signature_valid'] = txo.is_signed_by(txo.channel, self.ledger) output['is_channel_signature_valid'] = txo.is_signed_by(txo.channel, self.ledger)
except (BadSignatureError, InvalidSignature):
pass
except ValueError:
log.exception(
'txo.id: %s, txo.channel.id:%s, output: %s',
txo.id, txo.channel.id, output
)
except DecodeError: except DecodeError:
pass pass
return output return output

View file

@ -3,8 +3,12 @@ import logging
from binascii import unhexlify from binascii import unhexlify
from typing import Tuple, List, Dict from typing import Tuple, List, Dict
from ecdsa import BadSignatureError
from cryptography.exceptions import InvalidSignature
from torba.client.baseledger import BaseLedger from torba.client.baseledger import BaseLedger
from lbrynet.schema.result import Outputs from lbrynet.schema.result import Outputs
from lbrynet.schema.url import URL
from lbrynet.wallet.dewies import dewies_to_lbc from lbrynet.wallet.dewies import dewies_to_lbc
from lbrynet.wallet.resolve import Resolver from lbrynet.wallet.resolve import Resolver
from lbrynet.wallet.account import Account from lbrynet.wallet.account import Account
@ -61,7 +65,16 @@ class MainNetLedger(BaseLedger):
async def resolve(self, urls): async def resolve(self, urls):
txos = (await self._inflate_outputs(self.network.resolve(urls)))[0] txos = (await self._inflate_outputs(self.network.resolve(urls)))[0]
assert len(urls) == len(txos), "Mismatch between urls requested for resolve and responses received." assert len(urls) == len(txos), "Mismatch between urls requested for resolve and responses received."
return {url: txo for url, txo in zip(urls, txos)} result = {}
for url, txo in zip(urls, txos):
if txo and URL.parse(url).has_channel:
if not txo.channel or not txo.is_signed_by(txo.channel, self):
txo = None
if txo:
result[url] = txo
else:
result[url] = {'error': f'{url} did not resolve to a claim'}
return result
async def claim_search(self, **kwargs) -> Tuple[List, int, int]: async def claim_search(self, **kwargs) -> Tuple[List, int, int]:
return await self._inflate_outputs(self.network.claim_search(**kwargs)) return await self._inflate_outputs(self.network.claim_search(**kwargs))

View file

@ -2,7 +2,6 @@ import logging
import asyncio import asyncio
from cryptography.exceptions import InvalidSignature
from binascii import unhexlify, hexlify from binascii import unhexlify, hexlify
from lbrynet.utils import lru_cache_concurrent from lbrynet.utils import lru_cache_concurrent
from lbrynet.wallet.account import validate_claim_id from lbrynet.wallet.account import validate_claim_id
@ -340,12 +339,9 @@ def validate_claim_signature_and_get_channel_name(claim_result, certificate_clai
claim_tx=None, cert_tx=None): claim_tx=None, cert_tx=None):
valid_signature = False valid_signature = False
if cert_tx and certificate_claim and claim_tx and claim_result: if cert_tx and certificate_claim and claim_tx and claim_result:
try: valid_signature = claim_tx.outputs[claim_result['nout']].is_signed_by(
valid_signature = claim_tx.outputs[claim_result['nout']].is_signed_by( cert_tx.outputs[certificate_claim['nout']], ledger
cert_tx.outputs[certificate_claim['nout']], ledger )
)
except InvalidSignature:
pass
if not valid_signature: if not valid_signature:
log.warning("lbry://%s#%s has an invalid signature", log.warning("lbry://%s#%s has an invalid signature",
claim_result['name'], claim_result['claim_id']) claim_result['name'], claim_result['claim_id'])

View file

@ -9,7 +9,7 @@ from cryptography.hazmat.primitives.serialization import load_der_public_key
from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.utils import Prehashed from cryptography.hazmat.primitives.asymmetric.utils import Prehashed
from ecdsa.util import sigencode_der from cryptography.exceptions import InvalidSignature
from torba.client.basetransaction import BaseTransaction, BaseInput, BaseOutput, ReadOnlyList from torba.client.basetransaction import BaseTransaction, BaseInput, BaseOutput, ReadOnlyList
from torba.client.hash import hash160, sha256, Base58 from torba.client.hash import hash160, sha256, Base58
@ -116,9 +116,13 @@ class Output(BaseOutput):
signature = hexlify(self.claim.signature) signature = hexlify(self.claim.signature)
r = int(signature[:int(len(signature)/2)], 16) r = int(signature[:int(len(signature)/2)], 16)
s = int(signature[int(len(signature)/2):], 16) s = int(signature[int(len(signature)/2):], 16)
encoded_sig = sigencode_der(r, s, len(signature)*4) encoded_sig = ecdsa.util.sigencode_der(r, s, len(signature)*4)
public_key.verify(encoded_sig, digest, ec.ECDSA(Prehashed(hash))) try:
return True public_key.verify(encoded_sig, digest, ec.ECDSA(Prehashed(hash)))
return True
except (ValueError, InvalidSignature):
pass
return False
def sign(self, channel: 'Output', first_input_id=None): def sign(self, channel: 'Output', first_input_id=None):
self.channel = channel self.channel = channel

View file

@ -830,7 +830,9 @@ class StreamCommands(CommandTestCase):
response = await self.resolve('bad_example') response = await self.resolve('bad_example')
self.assertFalse(response['bad_example']['is_channel_signature_valid']) self.assertFalse(response['bad_example']['is_channel_signature_valid'])
response = await self.resolve('@olds/bad_example') response = await self.resolve('@olds/bad_example')
self.assertFalse(response['@olds/bad_example']['is_channel_signature_valid']) self.assertEqual(response, {
'@olds/bad_example': {'error': '@olds/bad_example did not resolve to a claim'}
})
def generate_signed_legacy(address: bytes, output: Output): def generate_signed_legacy(address: bytes, output: Output):

View file

@ -49,8 +49,7 @@ class TestSigningAndValidatingClaim(AsyncioTestCase):
def test_fail_to_validate_on_wrong_channel(self): def test_fail_to_validate_on_wrong_channel(self):
stream = self.get_stream() stream = self.get_stream()
stream.sign(self.get_channel()) stream.sign(self.get_channel())
with self.assertRaises(InvalidSignature): self.assertFalse(stream.is_signed_by(self.get_channel()))
self.assertTrue(stream.is_signed_by(self.get_channel()))
def test_fail_to_validate_altered_claim(self): def test_fail_to_validate_altered_claim(self):
channel = self.get_channel() channel = self.get_channel()
@ -58,8 +57,7 @@ class TestSigningAndValidatingClaim(AsyncioTestCase):
stream.sign(channel) stream.sign(channel)
self.assertTrue(stream.is_signed_by(channel)) self.assertTrue(stream.is_signed_by(channel))
stream.claim.stream.title = 'hello' stream.claim.stream.title = 'hello'
with self.assertRaises(InvalidSignature): self.assertFalse(stream.is_signed_by(channel))
self.assertTrue(stream.is_signed_by(channel))
def test_valid_private_key_for_cert(self): def test_valid_private_key_for_cert(self):
channel = self.get_channel() channel = self.get_channel()