dependencies: support and require dnspython 2.0, rm monkey patches
- dnspython 2.0 requires cryptography 2.6 so we now always require that (no longer a choice between cryptography and pycryptodomex) - test_dnssec.py is deleted as it was testing the monkey-patch related: #6538
This commit is contained in:
@@ -55,9 +55,8 @@ libsecp256k1 yourself::
|
||||
sudo apt-get install automake libtool
|
||||
./contrib/make_libsecp256k1.sh
|
||||
|
||||
Due to the need for fast symmetric ciphers, either one of `pycryptodomex`_
|
||||
or `cryptography`_ is required. Install from your package manager
|
||||
(or from pip)::
|
||||
Due to the need for fast symmetric ciphers, `cryptography`_ is required.
|
||||
Install from your package manager (or from pip)::
|
||||
|
||||
sudo apt-get install python3-cryptography
|
||||
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
PyQt5<5.15
|
||||
cryptography>=2.1
|
||||
|
||||
# we need at least cryptography>=2.1 for electrum.crypto,
|
||||
# and at least cryptography>=2.6 for dnspython[DNSSEC]
|
||||
cryptography>=2.6
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
ecdsa>=0.14
|
||||
qrcode
|
||||
protobuf>=3.12
|
||||
dnspython<2.0
|
||||
qdarkstyle<2.9
|
||||
aiorpcx>=0.18,<0.19
|
||||
aiohttp>=3.3.0,<4.0.0
|
||||
@@ -9,3 +8,7 @@ aiohttp_socks>=0.3
|
||||
certifi
|
||||
bitstring
|
||||
attrs>=19.2.0
|
||||
|
||||
# Note that we also need the dnspython[DNSSEC] extra which pulls in cryptography,
|
||||
# but as that is not pure-python it cannot be listed in this file!
|
||||
dnspython>=2.0
|
||||
|
||||
@@ -58,135 +58,6 @@ import dns.rdtypes.ANY.TXT
|
||||
import dns.rdtypes.IN.A
|
||||
import dns.rdtypes.IN.AAAA
|
||||
|
||||
|
||||
# Pure-Python version of dns.dnssec._validate_rsig
|
||||
import ecdsa
|
||||
from . import rsakey
|
||||
|
||||
|
||||
def python_validate_rrsig(rrset, rrsig, keys, origin=None, now=None):
|
||||
from dns.dnssec import ValidationFailure, ECDSAP256SHA256, ECDSAP384SHA384
|
||||
from dns.dnssec import _find_candidate_keys, _make_hash, _is_ecdsa, _is_rsa, _to_rdata, _make_algorithm_id
|
||||
|
||||
if isinstance(origin, str):
|
||||
origin = dns.name.from_text(origin, dns.name.root)
|
||||
|
||||
for candidate_key in _find_candidate_keys(keys, rrsig):
|
||||
if not candidate_key:
|
||||
raise ValidationFailure('unknown key')
|
||||
|
||||
# For convenience, allow the rrset to be specified as a (name, rdataset)
|
||||
# tuple as well as a proper rrset
|
||||
if isinstance(rrset, tuple):
|
||||
rrname = rrset[0]
|
||||
rdataset = rrset[1]
|
||||
else:
|
||||
rrname = rrset.name
|
||||
rdataset = rrset
|
||||
|
||||
if now is None:
|
||||
now = time.time()
|
||||
if rrsig.expiration < now:
|
||||
raise ValidationFailure('expired')
|
||||
if rrsig.inception > now:
|
||||
raise ValidationFailure('not yet valid')
|
||||
|
||||
hash = _make_hash(rrsig.algorithm)
|
||||
|
||||
if _is_rsa(rrsig.algorithm):
|
||||
keyptr = candidate_key.key
|
||||
(bytes,) = struct.unpack('!B', keyptr[0:1])
|
||||
keyptr = keyptr[1:]
|
||||
if bytes == 0:
|
||||
(bytes,) = struct.unpack('!H', keyptr[0:2])
|
||||
keyptr = keyptr[2:]
|
||||
rsa_e = keyptr[0:bytes]
|
||||
rsa_n = keyptr[bytes:]
|
||||
n = int.from_bytes(rsa_n, byteorder='big', signed=False)
|
||||
e = int.from_bytes(rsa_e, byteorder='big', signed=False)
|
||||
pubkey = rsakey.RSAKey(n, e)
|
||||
sig = rrsig.signature
|
||||
|
||||
elif _is_ecdsa(rrsig.algorithm):
|
||||
if rrsig.algorithm == ECDSAP256SHA256:
|
||||
curve = ecdsa.curves.NIST256p
|
||||
key_len = 32
|
||||
elif rrsig.algorithm == ECDSAP384SHA384:
|
||||
curve = ecdsa.curves.NIST384p
|
||||
key_len = 48
|
||||
else:
|
||||
# shouldn't happen
|
||||
raise ValidationFailure('unknown ECDSA curve')
|
||||
keyptr = candidate_key.key
|
||||
x = int.from_bytes(keyptr[0:key_len], byteorder='big', signed=False)
|
||||
y = int.from_bytes(keyptr[key_len:key_len * 2], byteorder='big', signed=False)
|
||||
assert ecdsa.ecdsa.point_is_valid(curve.generator, x, y)
|
||||
point = ecdsa.ellipticcurve.Point(curve.curve, x, y, curve.order)
|
||||
verifying_key = ecdsa.keys.VerifyingKey.from_public_point(point, curve)
|
||||
r = rrsig.signature[:key_len]
|
||||
s = rrsig.signature[key_len:]
|
||||
sig = ecdsa.ecdsa.Signature(int.from_bytes(r, byteorder='big', signed=False),
|
||||
int.from_bytes(s, byteorder='big', signed=False))
|
||||
|
||||
else:
|
||||
raise ValidationFailure('unknown algorithm %u' % rrsig.algorithm)
|
||||
|
||||
hash.update(_to_rdata(rrsig, origin)[:18])
|
||||
hash.update(rrsig.signer.to_digestable(origin))
|
||||
|
||||
if rrsig.labels < len(rrname) - 1:
|
||||
suffix = rrname.split(rrsig.labels + 1)[1]
|
||||
rrname = dns.name.from_text('*', suffix)
|
||||
rrnamebuf = rrname.to_digestable(origin)
|
||||
rrfixed = struct.pack('!HHI', rdataset.rdtype, rdataset.rdclass,
|
||||
rrsig.original_ttl)
|
||||
rrlist = sorted(rdataset)
|
||||
for rr in rrlist:
|
||||
hash.update(rrnamebuf)
|
||||
hash.update(rrfixed)
|
||||
rrdata = rr.to_digestable(origin)
|
||||
rrlen = struct.pack('!H', len(rrdata))
|
||||
hash.update(rrlen)
|
||||
hash.update(rrdata)
|
||||
|
||||
digest = hash.digest()
|
||||
|
||||
if _is_rsa(rrsig.algorithm):
|
||||
digest = _make_algorithm_id(rrsig.algorithm) + digest
|
||||
if pubkey.verify(bytearray(sig), bytearray(digest)):
|
||||
return
|
||||
|
||||
elif _is_ecdsa(rrsig.algorithm):
|
||||
diglong = int.from_bytes(digest, byteorder='big', signed=False)
|
||||
if verifying_key.pubkey.verifies(diglong, sig):
|
||||
return
|
||||
|
||||
else:
|
||||
raise ValidationFailure('unknown algorithm %s' % rrsig.algorithm)
|
||||
|
||||
raise ValidationFailure('verify failure')
|
||||
|
||||
|
||||
class PyCryptodomexHashAlike:
|
||||
def __init__(self, hashlib_func):
|
||||
self._hash = hashlib_func
|
||||
def new(self):
|
||||
return self._hash()
|
||||
|
||||
|
||||
# replace validate_rrsig
|
||||
dns.dnssec._validate_rrsig = python_validate_rrsig
|
||||
dns.dnssec.validate_rrsig = python_validate_rrsig
|
||||
dns.dnssec.validate = dns.dnssec._validate
|
||||
dns.dnssec._have_ecdsa = True
|
||||
dns.dnssec.MD5 = PyCryptodomexHashAlike(hashlib.md5)
|
||||
dns.dnssec.SHA1 = PyCryptodomexHashAlike(hashlib.sha1)
|
||||
dns.dnssec.SHA256 = PyCryptodomexHashAlike(hashlib.sha256)
|
||||
dns.dnssec.SHA384 = PyCryptodomexHashAlike(hashlib.sha384)
|
||||
dns.dnssec.SHA512 = PyCryptodomexHashAlike(hashlib.sha512)
|
||||
|
||||
|
||||
|
||||
from .logging import get_logger
|
||||
|
||||
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import dns
|
||||
|
||||
from electrum import dnssec
|
||||
|
||||
from . import ElectrumTestCase
|
||||
|
||||
|
||||
class TestDnsSec(ElectrumTestCase):
|
||||
|
||||
def test_python_validate_rrsig_ecdsa(self):
|
||||
rrset = dns.rrset.from_text("getmonero.org.", 3599, 1, 48,
|
||||
"257 3 13 mdsswUyr3DPW132mOi8V9xESWE8jTo0d xCjjnopKl+GqJxpVXckHAeF+KkxLbxIL fDLUT0rAK9iUzy1L53eKGQ==",
|
||||
"256 3 13 koPbw9wmYZ7ggcjnQ6ayHyhHaDNMYELK TqT+qRGrZpWSccr/lBcrm10Z1PuQHB3A zhii+sb0PYFkH1ruxLhe5g==")
|
||||
rrsig = dns.rdtypes.ANY.RRSIG.RRSIG.from_text(1, 46,
|
||||
dns.tokenizer.Tokenizer("DNSKEY 13 2 3600 20180612115508 20180413115508 2371 getmonero.org. SSjtP2jCtXPukps7E3kum709xq2TH6Lt Ur32UhE7WKwSUfLTZ4EAoD5g22mi1fpB GDGb30kCMndDVjnHAEBDWw=="))
|
||||
keys = {dns.name.Name([b'getmonero', b'org', b'']): rrset}
|
||||
origin = None
|
||||
now = 1527185178.7842247
|
||||
|
||||
# 'None' means it is valid
|
||||
self.assertEqual(None, dnssec.python_validate_rrsig(rrset, rrsig, keys, origin, now))
|
||||
|
||||
def test_python_validate_rrsig_rsa(self):
|
||||
rrset = dns.rrset.from_text("getmonero.org.", 12698, 1, 43,
|
||||
"2371 13 2 3b7f818a879ecb9931dae983d4529afedeb53993759d8080735083f954d40bc8")
|
||||
rrsig = dns.rdtypes.ANY.RRSIG.RRSIG.from_text(1, 46,
|
||||
dns.tokenizer.Tokenizer("DS 7 2 86400 20180609010045 20180519000045 1862 org. SgdGsY4BAm7c3qpwzVLy3ua4orvrsJQO 0rUQDDrrXR6lElnbF+AS0gEEfdZfDv11 65AuNil/+kT2Qh/ExgstvhWQ88XdDnHB ouvRMf9pg3p/q5Otet/StRzf33SMPgC1 zLzkfkSBCjJkwVmwde8saGnjdcW522ra Ge/6JcsryRw="))
|
||||
|
||||
rrset2 = dns.rrset.from_text("org.", 866, 1, 48,
|
||||
"256 3 7 AwEAAXxsMmN/JgpEE9Y4uFNRJm7Q9GBw mEYUCsCxuKlgBU9WrQEFRrvAeMamUBeX 4SE8s3V/TEk/TgGmPPp0pMkKD7mseluK 6Ard2HZ6O3nPAzL4i8py/UDRUmYNSCxw fdfjUWRmcB9H+NKWMsJoDhAkLFqg5HS7 f0j4Vb99Wac24Fk7",
|
||||
"256 3 7 AwEAAcLdAPt3vn/ND00zZlyTx7OBko+9 YeCrSl2eGuEXjef0Lqf0tKGikoHwnmTH tT8J/aGqkZImLMVByJbknE0wKDnbvbKD oTQxPwUQZLH6k3sTdsPKESKDSBSc6VFM q35gx6CeuRYZ9KkGWiUsKqJhXPo6tyJF CBxfaNQQyrzBnv4/",
|
||||
"257 3 7 AwEAAZTjbIO5kIpxWUtyXc8avsKyHIIZ +LjC2Dv8naO+Tz6X2fqzDC1bdq7HlZwt kaqTkMVVJ+8gE9FIreGJ4c8G1GdbjQgb P1OyYIG7OHTc4hv5T2NlyWr6k6QFz98Q 4zwFIGTFVvwBhmrMDYsOTtXakK6QwHov A1+83BsUACxlidpwB0hQacbD6x+I2RCD zYuTzj64Jv0/9XsX6AYV3ebcgn4hL1jI R2eJYyXlrAoWxdzxcW//5yeL5RVWuhRx ejmnSVnCuxkfS4AQ485KH2tpdbWcCopL JZs6tw8q3jWcpTGzdh/v3xdYfNpQNcPI mFlxAun3BtORPA2r8ti6MNoJEHU=",
|
||||
"257 3 7 AwEAAcMnWBKLuvG/LwnPVykcmpvnntwx fshHlHRhlY0F3oz8AMcuF8gw9McCw+Bo C2YxWaiTpNPuxjSNhUlBtcJmcdkz3/r7 PIn0oDf14ept1Y9pdPh8SbIBIWx50ZPf VRlj8oQXv2Y6yKiQik7bi3MT37zMRU2k w2oy3cgrsGAzGN4s/C6SFYon5N1Q2O4h GDbeOq538kATOy0GFELjuauV9guX/431 msYu4Rgb5lLuQ3Mx5FSIxXpI/RaAn2mh M4nEZ/5IeRPKZVGydcuLBS8GZlxW4qbb 8MgRZ8bwMg0pqWRHmhirGmJIt3UuzvN1 pSFBfX7ysI9PPhSnwXCNDXk0kk0=")
|
||||
keys = {dns.name.Name([b'org', b'']): rrset2}
|
||||
origin = None
|
||||
now = 1527191953.6527798
|
||||
|
||||
# 'None' means it is valid
|
||||
self.assertEqual(None, dnssec.python_validate_rrsig(rrset, rrsig, keys, origin, now))
|
||||
4
setup.py
4
setup.py
@@ -53,8 +53,8 @@ if platform.system() in ['Linux', 'FreeBSD', 'DragonFly']:
|
||||
extras_require = {
|
||||
'hardware': requirements_hw,
|
||||
'gui': ['pyqt5'],
|
||||
'crypto': ['cryptography>=2.1'],
|
||||
'tests': ['pycryptodomex>=3.7', 'cryptography>=2.1', 'pyaes>=0.1a1'],
|
||||
'crypto': ['cryptography>=2.6'],
|
||||
'tests': ['pycryptodomex>=3.7', 'cryptography>=2.6', 'pyaes>=0.1a1'],
|
||||
}
|
||||
# 'full' extra that tries to grab everything an enduser would need (except for libsecp256k1...)
|
||||
extras_require['full'] = [pkg for sublist in
|
||||
|
||||
Reference in New Issue
Block a user