URI: 
       tadd dnssec verification to payment requests - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit f3c4a55e77ef648310b835f95197776cb2584661
   DIR parent 652f0d0b7fc33f53679628ac73e9e43062e50319
  HTML Author: ThomasV <thomasv@gitorious>
       Date:   Tue,  7 Jul 2015 08:59:03 +0200
       
       add dnssec verification to payment requests
       
       Diffstat:
         M gui/qt/main_window.py               |      18 ++++++++++++++----
         M lib/bitcoin.py                      |      12 ++++++++----
         M lib/paymentrequest.py               |      70 +++++++++++++++++++++++--------
       
       3 files changed, 75 insertions(+), 25 deletions(-)
       ---
   DIR diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
       t@@ -720,8 +720,18 @@ class ElectrumWindow(QMainWindow):
                self.save_request_button.setEnabled(False)
        
            def export_payment_request(self, addr):
       +        alias = str(self.config.get('alias'))
       +        alias_privkey = None
       +        if alias:
       +            alias_info = self.contacts.resolve_openalias(alias)
       +            if alias_info:
       +                alias_addr, alias_name = alias_info
       +                if alias_addr and self.wallet.is_mine(alias_addr):
       +                    password = self.password_dialog()
       +                    alias_privkey = self.wallet.get_private_key(alias_addr, password)[0]
       +
                r = self.wallet.get_payment_request(addr, self.config)
       -        pr = paymentrequest.make_request(self.config, r)
       +        pr = paymentrequest.make_request(self.config, r, alias, alias_privkey)
                name = r['id'] + '.bip70'
                fileName = self.getSaveFileName(_("Select where to save your payment request"), name, "*.bip70")
                if fileName:
       t@@ -1272,7 +1282,7 @@ class ElectrumWindow(QMainWindow):
        
                def get_payment_request_thread():
                    self.payment_request = get_payment_request(request_url)
       -            if self.payment_request.verify():
       +            if self.payment_request.verify(self.contacts):
                        self.emit(SIGNAL('payment_request_ok'))
                    else:
                        self.emit(SIGNAL('payment_request_error'))
       t@@ -1485,7 +1495,7 @@ class ElectrumWindow(QMainWindow):
        
            def show_invoice(self, key):
                pr = self.invoices.get(key)
       -        pr.verify()
       +        pr.verify(self.contacts)
                self.show_pr_details(pr)
        
            def show_pr_details(self, pr):
       t@@ -1521,7 +1531,7 @@ class ElectrumWindow(QMainWindow):
                pr = self.invoices.get(key)
                self.payment_request = pr
                self.prepare_for_payment_request()
       -        if pr.verify():
       +        if pr.verify(self.contacts):
                    self.payment_request_ok()
                else:
                    self.payment_request_error()
   DIR diff --git a/lib/bitcoin.py b/lib/bitcoin.py
       t@@ -478,11 +478,15 @@ class EC_KEY(object):
            def get_public_key(self, compressed=True):
                return point_to_ser(self.pubkey.point, compressed).encode('hex')
        
       -    def sign_message(self, message, compressed, address):
       -        private_key = ecdsa.SigningKey.from_secret_exponent( self.secret, curve = SECP256k1 )
       +    def sign(self, msg_hash):
       +        private_key = ecdsa.SigningKey.from_secret_exponent(self.secret, curve = SECP256k1)
                public_key = private_key.get_verifying_key()
       -        signature = private_key.sign_digest_deterministic( Hash( msg_magic(message) ), hashfunc=hashlib.sha256, sigencode = ecdsa.util.sigencode_string )
       -        assert public_key.verify_digest( signature, Hash( msg_magic(message) ), sigdecode = ecdsa.util.sigdecode_string)
       +        signature = private_key.sign_digest_deterministic(msg_hash, hashfunc=hashlib.sha256, sigencode = ecdsa.util.sigencode_string)
       +        assert public_key.verify_digest(signature, msg_hash, sigdecode = ecdsa.util.sigdecode_string)
       +        return signature
       +
       +    def sign_message(self, message, compressed, address):
       +        signature = self.sign(Hash(msg_magic(message)))
                for i in range(4):
                    sig = base64.b64encode(chr(27 + i + (4 if compressed else 0)) + signature)
                    try:
   DIR diff --git a/lib/paymentrequest.py b/lib/paymentrequest.py
       t@@ -98,19 +98,29 @@ class PaymentRequest:
                self.memo = self.details.memo
                self.payment_url = self.details.payment_url
        
       -    def verify(self):
       -        """ verify chain of certificates. The last certificate is the CA"""
       -        if not ca_list:
       -            self.error = "Trusted certificate authorities list not found"
       -            return False
       +    def verify(self, contacts):
                if not self.raw:
                    self.error = "Empty request"
                    return
       -        paymntreq = pb2.PaymentRequest()
       -        paymntreq.ParseFromString(self.raw)
       -        if not paymntreq.signature:
       +        pr = pb2.PaymentRequest()
       +        pr.ParseFromString(self.raw)
       +        if not pr.signature:
                    self.error = "No signature"
                    return
       +
       +        if pr.pki_type in ["x509+sha256", "x509+sha1"]:
       +            return self.verify_x509(pr)
       +        elif pr.pki_type in ["dnssec+btc", "dnssec+ecdsa"]:
       +            return self.verify_dnssec(pr, contacts)
       +        else:
       +            self.error = "ERROR: Unsupported PKI Type for Message Signature"
       +            return False
       +
       +    def verify_x509(self, paymntreq):
       +        """ verify chain of certificates. The last certificate is the CA"""
       +        if not ca_list:
       +            self.error = "Trusted certificate authorities list not found"
       +            return False
                cert = pb2.X509Certificates()
                cert.ParseFromString(paymntreq.pki_data)
                cert_num = len(cert.certificate)
       t@@ -184,9 +194,6 @@ class PaymentRequest:
                    verify = pubkey0.verify(sigBytes, x509.PREFIX_RSA_SHA256 + hashBytes)
                elif paymntreq.pki_type == "x509+sha1":
                    verify = pubkey0.hashAndVerify(sigBytes, msgBytes)
       -        else:
       -            self.error = "ERROR: Unsupported PKI Type for Message Signature"
       -            return False
                if not verify:
                    self.error = "ERROR: Invalid Signature for Payment Request Data"
                    return False
       t@@ -194,6 +201,28 @@ class PaymentRequest:
                self.error = 'Signed by Trusted CA: ' + ca.get_common_name()
                return True
        
       +    def verify_dnssec(self, pr, contacts):
       +        sig = pr.signature
       +        alias = pr.pki_data
       +        info = contacts.resolve(alias)
       +        if info.get('validated') is not True:
       +            self.error = "Alias verification failed (DNSSEC)"
       +            return False
       +        if pr.pki_type == "dnssec+btc":
       +            self.requestor = alias
       +            address = info.get('address')
       +            pr.signature = ''
       +            message = pr.SerializeToString()
       +            if bitcoin.verify_message(address, sig, message):
       +                self.error = 'Verified with DNSSEC'
       +                return True
       +            else:
       +                self.error = "verify failed"
       +                return False
       +        else:
       +            self.error = "unknown algo"
       +            return False
       +
            def has_expired(self):
                return self.details.expires and self.details.expires < int(time.time())
        
       t@@ -258,7 +287,7 @@ class PaymentRequest:
        
        
        
       -def make_payment_request(outputs, memo, time, expires, key_path, cert_path):
       +def make_payment_request(outputs, memo, time, expires, key_path, cert_path, alias, alias_privkey):
            pd = pb2.PaymentDetails()
            for script, amount in outputs:
                pd.outputs.add(amount=amount, script=script)
       t@@ -268,9 +297,16 @@ def make_payment_request(outputs, memo, time, expires, key_path, cert_path):
            pr = pb2.PaymentRequest()
            pr.serialized_payment_details = pd.SerializeToString()
            pr.signature = ''
       -    pr = pb2.PaymentRequest()
       -    pr.serialized_payment_details = pd.SerializeToString()
       -    pr.signature = ''
       +
       +    if alias and alias_privkey:
       +        pr.pki_type = 'dnssec+btc'
       +        pr.pki_data = str(alias)
       +        message = pr.SerializeToString()
       +        ec_key = bitcoin.regenerate_key(alias_privkey)
       +        address = bitcoin.address_from_private_key(alias_privkey)
       +        compressed = bitcoin.is_compressed(alias_privkey)
       +        pr.signature = ec_key.sign_message(message, compressed, address)
       +
            if key_path and cert_path:
                import tlslite
                with open(key_path, 'r') as f:
       t@@ -289,7 +325,7 @@ def make_payment_request(outputs, memo, time, expires, key_path, cert_path):
            return pr.SerializeToString()
        
        
       -def make_request(config, req):
       +def make_request(config, req, alias=None, alias_privkey=None):
            from transaction import Transaction
            addr = req['address']
            time = req['timestamp']
       t@@ -300,7 +336,7 @@ def make_request(config, req):
            outputs = [(script, amount)]
            key_path = config.get('ssl_privkey')
            cert_path = config.get('ssl_chain')
       -    return make_payment_request(outputs, message, time, time + expiration if expiration else None, key_path, cert_path)
       +    return make_payment_request(outputs, message, time, time + expiration if expiration else None, key_path, cert_path, alias, alias_privkey)