treplace TLSLite dependency with minimal RSA implementation - electrum - Electrum Bitcoin wallet
 (HTM) git clone https://git.parazyd.org/electrum
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Submodules
       ---
 (DIR) commit e8d30129eae10b5efeaa9e95a900576e41dd2bf1
 (DIR) parent 2ba07377daafa337225b4814656b7a9613fd6540
 (HTM) Author: ThomasV <thomasv@gitorious>
       Date:   Tue,  4 Aug 2015 18:16:06 +0200
       
       replace TLSLite dependency with minimal RSA implementation
       
       Diffstat:
         M RELEASE-NOTES                       |       1 +
         M lib/asn1tinydecoder.py              |      47 +++++++++++++++++++++++++++++++
         M lib/dnssec.py                       |      16 ++++++++--------
         M lib/paymentrequest.py               |      29 +++++++++++++++++------------
         A lib/pem.py                          |     164 +++++++++++++++++++++++++++++++
         A lib/rsakey.py                       |     517 +++++++++++++++++++++++++++++++
         M lib/x509.py                         |      90 +++++++++----------------------
         M setup.py                            |       1 -
       
       8 files changed, 780 insertions(+), 85 deletions(-)
       ---
 (DIR) diff --git a/RELEASE-NOTES b/RELEASE-NOTES
       t@@ -1,6 +1,7 @@
        # Release 2.4.1
         * Use ssl.PROTOCOL_TLSv1
         * Fix DNSSEC issues with ECDSA signatures
       + * Replace TLSLite dependency with minimal RSA implementation
        
        # Release 2.4
         * Payment to DNS names storing a Bitcoin addresses (OpenAlias) is
 (DIR) diff --git a/lib/asn1tinydecoder.py b/lib/asn1tinydecoder.py
       t@@ -121,3 +121,50 @@ def asn1_read_length(der,ix):
        ####################### END ASN1 DECODER ############################
        
        
       +def decode_OID(s):
       +    s = map(ord, s)
       +    r = []
       +    r.append(s[0] / 40)
       +    r.append(s[0] % 40)
       +    k = 0
       +    for i in s[1:]:
       +        if i < 128:
       +            r.append(i + 128*k)
       +            k = 0
       +        else:
       +            k = (i - 128) + 128*k
       +    return '.'.join(map(str, r))
       +
       +def encode_OID(oid):
       +    x = map(int, oid.split('.'))
       +    s = chr(x[0]*40 + x[1])
       +    for i in x[2:]:
       +        ss = chr(i % 128)
       +        while i > 128:
       +            i = i / 128
       +            ss = chr(128 + i % 128) + ss
       +        s += ss
       +    return s
       +
       +def asn1_get_children(der, i):
       +    nodes = []
       +    ii = asn1_node_first_child(der,i)
       +    nodes.append(ii)
       +    while ii[2]<i[2]:
       +        ii = asn1_node_next(der,ii)
       +        nodes.append(ii)
       +    return nodes
       +
       +def asn1_get_sequence(s):
       +    return map(lambda j: asn1_get_value(s, j), asn1_get_children(s, asn1_node_root(s)))
       +
       +def asn1_get_dict(der, i):
       +    p = {}
       +    for ii in asn1_get_children(der, i):
       +        for iii in asn1_get_children(der, ii):
       +            iiii = asn1_node_first_child(der, iii)
       +            oid = decode_OID(asn1_get_value_of_type(der, iiii, 'OBJECT IDENTIFIER'))
       +            iiii = asn1_node_next(der, iiii)
       +            value = asn1_get_value(der, iiii)
       +            p[oid] = value
       +    return p
 (DIR) diff --git a/lib/dnssec.py b/lib/dnssec.py
       t@@ -57,16 +57,16 @@ from dns.exception import DNSException
        
        """
        Pure-Python version of dns.dnssec._validate_rsig
       -Uses tlslite instead of PyCrypto
        """
       +
       +import ecdsa
       +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
        
       -    import ecdsa
       -    from tlslite.utils.keyfactory import _createPublicRSAKey
       -    from tlslite.utils.cryptomath import bytesToNumber
       -
            if isinstance(origin, (str, unicode)):
                origin = dns.name.from_text(origin, dns.name.root)
        
       t@@ -101,9 +101,9 @@ def python_validate_rrsig(rrset, rrsig, keys, origin=None, now=None):
                        keyptr = keyptr[2:]
                    rsa_e = keyptr[0:bytes]
                    rsa_n = keyptr[bytes:]
       -            n = bytesToNumber(bytearray(rsa_n))
       -            e = bytesToNumber(bytearray(rsa_e))
       -            pubkey = _createPublicRSAKey(n, e)
       +            n = ecdsa.util.string_to_number(rsa_n)
       +            e = ecdsa.util.string_to_number(rsa_e)
       +            pubkey = rsakey.RSAKey(n, e)
                    sig = rrsig.signature
        
                elif _is_ecdsa(rrsig.algorithm):
 (DIR) diff --git a/lib/paymentrequest.py b/lib/paymentrequest.py
       t@@ -25,6 +25,7 @@ import threading
        import time
        import traceback
        import urlparse
       +import json
        import requests
        
        try:
       t@@ -34,9 +35,11 @@ except ImportError:
        
        import bitcoin
        import util
       +from util import print_error
        import transaction
        import x509
       -from util import print_error
       +import rsakey
       +
        
        REQUEST_HEADERS = {'Accept': 'application/bitcoin-paymentrequest', 'User-Agent': 'Electrum'}
        ACK_HEADERS = {'Content-Type':'application/bitcoin-payment','Accept':'application/bitcoin-paymentack','User-Agent':'Electrum'}
       t@@ -52,7 +55,6 @@ PR_UNKNOWN = 2     # sent but not propagated
        PR_PAID    = 3     # send and propagated
        PR_ERROR   = 4     # could not parse
        
       -import json
        
        
        def get_payment_request(url):
       t@@ -163,7 +165,9 @@ class PaymentRequest:
                    prev_x = x509_chain[i-1]
                    algo, sig, data = prev_x.get_signature()
                    sig = bytearray(sig)
       -            pubkey = x.publicKey
       +
       +            pubkey = rsakey.RSAKey(x.modulus, x.exponent)
       +
                    if algo == x509.ALGO_RSA_SHA1:
                        verify = pubkey.hashAndVerify(sig, data)
                    elif algo == x509.ALGO_RSA_SHA256:
       t@@ -183,7 +187,8 @@ class PaymentRequest:
                        self.error = "Certificate not Signed by Provided CA Certificate Chain"
                        return False
                # verify the BIP70 signature
       -        pubkey0 = x509_chain[0].publicKey
       +        x = x509_chain[0]
       +        pubkey0 = rsakey.RSAKey(x.modulus, x.exponent)
                sig = paymntreq.signature
                paymntreq.signature = ''
                s = paymntreq.SerializeToString()
       t@@ -324,20 +329,22 @@ def sign_request_with_alias(pr, alias, alias_privkey):
            pr.signature = ec_key.sign_message(message, compressed, address)
        
        
       +
        def sign_request_with_x509(pr, key_path, cert_path):
       -    import tlslite
       +    import pem
            with open(key_path, 'r') as f:
       -        rsakey = tlslite.utils.python_rsakey.Python_RSAKey.parsePEM(f.read())
       +        params = pem.parse_private_key(f.read())
       +        privkey = rsakey.RSAKey(*params)
            with open(cert_path, 'r') as f:
       -        chain = tlslite.X509CertChain()
       -        chain.parsePemList(f.read())
       +        s = f.read()
       +        bList = pem.dePemList(s, "CERTIFICATE")
            certificates = pb2.X509Certificates()
       -    certificates.certificate.extend(map(lambda x: str(x.bytes), chain.x509List))
       +    certificates.certificate.extend(map(str, bList))
            pr.pki_type = 'x509+sha256'
            pr.pki_data = certificates.SerializeToString()
            msgBytes = bytearray(pr.SerializeToString())
            hashBytes = bytearray(hashlib.sha256(msgBytes).digest())
       -    sig = rsakey.sign(x509.PREFIX_RSA_SHA256 + hashBytes)
       +    sig = privkey.sign(x509.PREFIX_RSA_SHA256 + hashBytes)
            pr.signature = bytes(sig)
        
        
       t@@ -362,8 +369,6 @@ def make_request(config, req):
        
        
        
       -
       -
        class InvoiceStore(object):
        
            def __init__(self, config):
 (DIR) diff --git a/lib/pem.py b/lib/pem.py
       t@@ -0,0 +1,164 @@
       +# This module uses code from TLSLlite
       +# TLSLite Author: Trevor Perrin)
       +
       +
       +import binascii
       +
       +from asn1tinydecoder import *
       +
       +
       +def a2b_base64(s):
       +    try:
       +        b = bytearray(binascii.a2b_base64(s))
       +    except Exception as e:
       +        raise SyntaxError("base64 error: %s" % e)
       +    return b
       +
       +def b2a_base64(b):
       +    return binascii.b2a_base64(b)
       +
       +
       +def dePem(s, name):
       +    """Decode a PEM string into a bytearray of its payload.
       +    
       +    The input must contain an appropriate PEM prefix and postfix
       +    based on the input name string, e.g. for name="CERTIFICATE":
       +
       +    -----BEGIN CERTIFICATE-----
       +    MIIBXDCCAUSgAwIBAgIBADANBgkqhkiG9w0BAQUFADAPMQ0wCwYDVQQDEwRUQUNL
       +    ...
       +    KoZIhvcNAQEFBQADAwA5kw==
       +    -----END CERTIFICATE-----    
       +
       +    The first such PEM block in the input will be found, and its
       +    payload will be base64 decoded and returned.
       +    """
       +    prefix  = "-----BEGIN %s-----" % name
       +    postfix = "-----END %s-----" % name    
       +    start = s.find(prefix)
       +    if start == -1:
       +        raise SyntaxError("Missing PEM prefix")
       +    end = s.find(postfix, start+len(prefix))
       +    if end == -1:
       +        raise SyntaxError("Missing PEM postfix")
       +    s = s[start+len("-----BEGIN %s-----" % name) : end]
       +    retBytes = a2b_base64(s) # May raise SyntaxError
       +    return retBytes
       +
       +def dePemList(s, name):
       +    """Decode a sequence of PEM blocks into a list of bytearrays.
       +
       +    The input must contain any number of PEM blocks, each with the appropriate
       +    PEM prefix and postfix based on the input name string, e.g. for
       +    name="TACK BREAK SIG".  Arbitrary text can appear between and before and
       +    after the PEM blocks.  For example:
       +
       +    " Created by TACK.py 0.9.3 Created at 2012-02-01T00:30:10Z -----BEGIN TACK
       +    BREAK SIG-----
       +    ATKhrz5C6JHJW8BF5fLVrnQss6JnWVyEaC0p89LNhKPswvcC9/s6+vWLd9snYTUv
       +    YMEBdw69PUP8JB4AdqA3K6Ap0Fgd9SSTOECeAKOUAym8zcYaXUwpk0+WuPYa7Zmm
       +    SkbOlK4ywqt+amhWbg9txSGUwFO5tWUHT3QrnRlE/e3PeNFXLx5Bckg= -----END TACK
       +    BREAK SIG----- Created by TACK.py 0.9.3 Created at 2012-02-01T00:30:11Z
       +    -----BEGIN TACK BREAK SIG-----
       +    ATKhrz5C6JHJW8BF5fLVrnQss6JnWVyEaC0p89LNhKPswvcC9/s6+vWLd9snYTUv
       +    YMEBdw69PUP8JB4AdqA3K6BVCWfcjN36lx6JwxmZQncS6sww7DecFO/qjSePCxwM
       +    +kdDqX/9/183nmjx6bf0ewhPXkA0nVXsDYZaydN8rJU1GaMlnjcIYxY= -----END TACK
       +    BREAK SIG----- "
       +    
       +    All such PEM blocks will be found, decoded, and return in an ordered list
       +    of bytearrays, which may have zero elements if not PEM blocks are found.
       +     """
       +    bList = []
       +    prefix  = "-----BEGIN %s-----" % name
       +    postfix = "-----END %s-----" % name
       +    while 1:
       +        start = s.find(prefix)
       +        if start == -1:
       +            return bList
       +        end = s.find(postfix, start+len(prefix))
       +        if end == -1:
       +            raise SyntaxError("Missing PEM postfix")
       +        s2 = s[start+len(prefix) : end]
       +        retBytes = a2b_base64(s2) # May raise SyntaxError
       +        bList.append(retBytes)
       +        s = s[end+len(postfix) : ]
       +
       +def pem(b, name):
       +    """Encode a payload bytearray into a PEM string.
       +    
       +    The input will be base64 encoded, then wrapped in a PEM prefix/postfix
       +    based on the name string, e.g. for name="CERTIFICATE":
       +    
       +    -----BEGIN CERTIFICATE-----
       +    MIIBXDCCAUSgAwIBAgIBADANBgkqhkiG9w0BAQUFADAPMQ0wCwYDVQQDEwRUQUNL
       +    ...
       +    KoZIhvcNAQEFBQADAwA5kw==
       +    -----END CERTIFICATE-----    
       +    """
       +    s1 = b2a_base64(b)[:-1] # remove terminating \n
       +    s2 = ""
       +    while s1:
       +        s2 += s1[:64] + "\n"
       +        s1 = s1[64:]
       +    s = ("-----BEGIN %s-----\n" % name) + s2 + \
       +        ("-----END %s-----\n" % name)     
       +    return s
       +
       +def pemSniff(inStr, name):
       +    searchStr = "-----BEGIN %s-----" % name
       +    return searchStr in inStr
       +
       +
       +def parse_private_key(s):
       +    """Parse a string containing a PEM-encoded <privateKey>."""
       +    if pemSniff(s, "PRIVATE KEY"):
       +        bytes = dePem(s, "PRIVATE KEY")
       +        return _parsePKCS8(bytes)
       +    elif pemSniff(s, "RSA PRIVATE KEY"):
       +        bytes = dePem(s, "RSA PRIVATE KEY")
       +        return _parseSSLeay(bytes)
       +    else:
       +        raise SyntaxError("Not a PEM private key file")
       +
       +
       +def _parsePKCS8(bytes):
       +    s = str(bytes)
       +    root = asn1_node_root(s)
       +    version_node = asn1_node_first_child(s, root)
       +    version = bytestr_to_int(asn1_get_value_of_type(s, version_node, 'INTEGER'))
       +    if version != 0:
       +        raise SyntaxError("Unrecognized PKCS8 version")
       +    rsaOID_node = asn1_node_next(s, version_node)
       +    ii = asn1_node_first_child(s, rsaOID_node)
       +    rsaOID = decode_OID(asn1_get_value_of_type(s, ii, 'OBJECT IDENTIFIER'))
       +    if rsaOID != '1.2.840.113549.1.1.1':
       +        raise SyntaxError("Unrecognized AlgorithmIdentifier")
       +    privkey_node = asn1_node_next(s, rsaOID_node)
       +    value = asn1_get_value_of_type(s, privkey_node, 'OCTET STRING')
       +    return _parseASN1PrivateKey(value)
       +
       +
       +def _parseSSLeay(bytes):
       +    return _parseASN1PrivateKey(str(bytes))
       +
       +
       +def bytesToNumber(s):
       +    return int(binascii.hexlify(s), 16)
       +
       +
       +def _parseASN1PrivateKey(s):
       +    root = asn1_node_root(s)
       +    version_node = asn1_node_first_child(s, root)
       +    version = bytestr_to_int(asn1_get_value_of_type(s, version_node, 'INTEGER'))
       +    if version != 0:
       +        raise SyntaxError("Unrecognized RSAPrivateKey version")
       +    n = asn1_node_next(s, version_node)
       +    e = asn1_node_next(s, n)
       +    d = asn1_node_next(s, e)
       +    p = asn1_node_next(s, d)
       +    q = asn1_node_next(s, p)
       +    dP = asn1_node_next(s, q)
       +    dQ = asn1_node_next(s, dP)
       +    qInv = asn1_node_next(s, dQ)
       +    return map(lambda x: bytesToNumber(asn1_get_value_of_type(s, x, 'INTEGER')), [n, e, d, p, q, dP, dQ, qInv])
       +
 (DIR) diff --git a/lib/rsakey.py b/lib/rsakey.py
       t@@ -0,0 +1,517 @@
       +# This module uses functions from TLSLite (public domain)
       +#
       +# TLSLite Authors: 
       +#   Trevor Perrin
       +#   Martin von Loewis - python 3 port
       +#   Yngve Pettersen (ported by Paul Sokolovsky) - TLS 1.2
       +#
       +
       +"""Pure-Python RSA implementation."""
       +
       +
       +from __future__ import print_function
       +import os
       +import math
       +import base64
       +import binascii
       +
       +from pem import *
       +
       +
       +# **************************************************************************
       +# PRNG Functions
       +# **************************************************************************
       +
       +# Check that os.urandom works
       +import zlib
       +length = len(zlib.compress(os.urandom(1000)))
       +assert(length > 900)
       +
       +def getRandomBytes(howMany):
       +    b = bytearray(os.urandom(howMany))
       +    assert(len(b) == howMany)
       +    return b
       +
       +prngName = "os.urandom"
       +
       +
       +# **************************************************************************
       +# Converter Functions
       +# **************************************************************************
       +
       +def bytesToNumber(b):
       +    total = 0
       +    multiplier = 1
       +    for count in range(len(b)-1, -1, -1):
       +        byte = b[count]
       +        total += multiplier * byte
       +        multiplier *= 256
       +    return total
       +
       +def numberToByteArray(n, howManyBytes=None):
       +    """Convert an integer into a bytearray, zero-pad to howManyBytes.
       +
       +    The returned bytearray may be smaller than howManyBytes, but will
       +    not be larger.  The returned bytearray will contain a big-endian
       +    encoding of the input integer (n).
       +    """    
       +    if howManyBytes == None:
       +        howManyBytes = numBytes(n)
       +    b = bytearray(howManyBytes)
       +    for count in range(howManyBytes-1, -1, -1):
       +        b[count] = int(n % 256)
       +        n >>= 8
       +    return b
       +
       +def mpiToNumber(mpi): #mpi is an openssl-format bignum string
       +    if (ord(mpi[4]) & 0x80) !=0: #Make sure this is a positive number
       +        raise AssertionError()
       +    b = bytearray(mpi[4:])
       +    return bytesToNumber(b)
       +
       +def numberToMPI(n):
       +    b = numberToByteArray(n)
       +    ext = 0
       +    #If the high-order bit is going to be set,
       +    #add an extra byte of zeros
       +    if (numBits(n) & 0x7)==0:
       +        ext = 1
       +    length = numBytes(n) + ext
       +    b = bytearray(4+ext) + b
       +    b[0] = (length >> 24) & 0xFF
       +    b[1] = (length >> 16) & 0xFF
       +    b[2] = (length >> 8) & 0xFF
       +    b[3] = length & 0xFF
       +    return bytes(b)
       +
       +
       +# **************************************************************************
       +# Misc. Utility Functions
       +# **************************************************************************
       +
       +def numBits(n):
       +    if n==0:
       +        return 0
       +    s = "%x" % n
       +    return ((len(s)-1)*4) + \
       +    {'0':0, '1':1, '2':2, '3':2,
       +     '4':3, '5':3, '6':3, '7':3,
       +     '8':4, '9':4, 'a':4, 'b':4,
       +     'c':4, 'd':4, 'e':4, 'f':4,
       +     }[s[0]]
       +    return int(math.floor(math.log(n, 2))+1)
       +
       +def numBytes(n):
       +    if n==0:
       +        return 0
       +    bits = numBits(n)
       +    return int(math.ceil(bits / 8.0))
       +
       +# **************************************************************************
       +# Big Number Math
       +# **************************************************************************
       +
       +def getRandomNumber(low, high):
       +    if low >= high:
       +        raise AssertionError()
       +    howManyBits = numBits(high)
       +    howManyBytes = numBytes(high)
       +    lastBits = howManyBits % 8
       +    while 1:
       +        bytes = getRandomBytes(howManyBytes)
       +        if lastBits:
       +            bytes[0] = bytes[0] % (1 << lastBits)
       +        n = bytesToNumber(bytes)
       +        if n >= low and n < high:
       +            return n
       +
       +def gcd(a,b):
       +    a, b = max(a,b), min(a,b)
       +    while b:
       +        a, b = b, a % b
       +    return a
       +
       +def lcm(a, b):
       +    return (a * b) // gcd(a, b)
       +
       +#Returns inverse of a mod b, zero if none
       +#Uses Extended Euclidean Algorithm
       +def invMod(a, b):
       +    c, d = a, b
       +    uc, ud = 1, 0
       +    while c != 0:
       +        q = d // c
       +        c, d = d-(q*c), c
       +        uc, ud = ud - (q * uc), uc
       +    if d == 1:
       +        return ud % b
       +    return 0
       +
       +
       +def powMod(base, power, modulus):
       +    if power < 0:
       +        result = pow(base, power*-1, modulus)
       +        result = invMod(result, modulus)
       +        return result
       +    else:
       +        return pow(base, power, modulus)
       +
       +#Pre-calculate a sieve of the ~100 primes < 1000:
       +def makeSieve(n):
       +    sieve = list(range(n))
       +    for count in range(2, int(math.sqrt(n))+1):
       +        if sieve[count] == 0:
       +            continue
       +        x = sieve[count] * 2
       +        while x < len(sieve):
       +            sieve[x] = 0
       +            x += sieve[count]
       +    sieve = [x for x in sieve[2:] if x]
       +    return sieve
       +
       +sieve = makeSieve(1000)
       +
       +def isPrime(n, iterations=5, display=False):
       +    #Trial division with sieve
       +    for x in sieve:
       +        if x >= n: return True
       +        if n % x == 0: return False
       +    #Passed trial division, proceed to Rabin-Miller
       +    #Rabin-Miller implemented per Ferguson & Schneier
       +    #Compute s, t for Rabin-Miller
       +    if display: print("*", end=' ')
       +    s, t = n-1, 0
       +    while s % 2 == 0:
       +        s, t = s//2, t+1
       +    #Repeat Rabin-Miller x times
       +    a = 2 #Use 2 as a base for first iteration speedup, per HAC
       +    for count in range(iterations):
       +        v = powMod(a, s, n)
       +        if v==1:
       +            continue
       +        i = 0
       +        while v != n-1:
       +            if i == t-1:
       +                return False
       +            else:
       +                v, i = powMod(v, 2, n), i+1
       +        a = getRandomNumber(2, n)
       +    return True
       +
       +def getRandomPrime(bits, display=False):
       +    if bits < 10:
       +        raise AssertionError()
       +    #The 1.5 ensures the 2 MSBs are set
       +    #Thus, when used for p,q in RSA, n will have its MSB set
       +    #
       +    #Since 30 is lcm(2,3,5), we'll set our test numbers to
       +    #29 % 30 and keep them there
       +    low = ((2 ** (bits-1)) * 3) // 2
       +    high = 2 ** bits - 30
       +    p = getRandomNumber(low, high)
       +    p += 29 - (p % 30)
       +    while 1:
       +        if display: print(".", end=' ')
       +        p += 30
       +        if p >= high:
       +            p = getRandomNumber(low, high)
       +            p += 29 - (p % 30)
       +        if isPrime(p, display=display):
       +            return p
       +
       +#Unused at the moment...
       +def getRandomSafePrime(bits, display=False):
       +    if bits < 10:
       +        raise AssertionError()
       +    #The 1.5 ensures the 2 MSBs are set
       +    #Thus, when used for p,q in RSA, n will have its MSB set
       +    #
       +    #Since 30 is lcm(2,3,5), we'll set our test numbers to
       +    #29 % 30 and keep them there
       +    low = (2 ** (bits-2)) * 3//2
       +    high = (2 ** (bits-1)) - 30
       +    q = getRandomNumber(low, high)
       +    q += 29 - (q % 30)
       +    while 1:
       +        if display: print(".", end=' ')
       +        q += 30
       +        if (q >= high):
       +            q = getRandomNumber(low, high)
       +            q += 29 - (q % 30)
       +        #Ideas from Tom Wu's SRP code
       +        #Do trial division on p and q before Rabin-Miller
       +        if isPrime(q, 0, display=display):
       +            p = (2 * q) + 1
       +            if isPrime(p, display=display):
       +                if isPrime(q, display=display):
       +                    return p
       +
       +
       +class RSAKey(object):
       +
       +    def __init__(self, n=0, e=0, d=0, p=0, q=0, dP=0, dQ=0, qInv=0):
       +        if (n and not e) or (e and not n):
       +            raise AssertionError()
       +        self.n = n
       +        self.e = e
       +        self.d = d
       +        self.p = p
       +        self.q = q
       +        self.dP = dP
       +        self.dQ = dQ
       +        self.qInv = qInv
       +        self.blinder = 0
       +        self.unblinder = 0
       +
       +    def __len__(self):
       +        """Return the length of this key in bits.
       +
       +        @rtype: int
       +        """
       +        return numBits(self.n)
       +
       +    def hasPrivateKey(self):
       +        return self.d != 0
       +
       +    def hashAndSign(self, bytes):
       +        """Hash and sign the passed-in bytes.
       +
       +        This requires the key to have a private component.  It performs
       +        a PKCS1-SHA1 signature on the passed-in data.
       +
       +        @type bytes: str or L{bytearray} of unsigned bytes
       +        @param bytes: The value which will be hashed and signed.
       +
       +        @rtype: L{bytearray} of unsigned bytes.
       +        @return: A PKCS1-SHA1 signature on the passed-in data.
       +        """
       +        hashBytes = SHA1(bytearray(bytes))
       +        prefixedHashBytes = self._addPKCS1SHA1Prefix(hashBytes)
       +        sigBytes = self.sign(prefixedHashBytes)
       +        return sigBytes
       +
       +    def hashAndVerify(self, sigBytes, bytes):
       +        """Hash and verify the passed-in bytes with the signature.
       +
       +        This verifies a PKCS1-SHA1 signature on the passed-in data.
       +
       +        @type sigBytes: L{bytearray} of unsigned bytes
       +        @param sigBytes: A PKCS1-SHA1 signature.
       +
       +        @type bytes: str or L{bytearray} of unsigned bytes
       +        @param bytes: The value which will be hashed and verified.
       +
       +        @rtype: bool
       +        @return: Whether the signature matches the passed-in data.
       +        """
       +        hashBytes = SHA1(bytearray(bytes))
       +        
       +        # Try it with/without the embedded NULL
       +        prefixedHashBytes1 = self._addPKCS1SHA1Prefix(hashBytes, False)
       +        prefixedHashBytes2 = self._addPKCS1SHA1Prefix(hashBytes, True)
       +        result1 = self.verify(sigBytes, prefixedHashBytes1)
       +        result2 = self.verify(sigBytes, prefixedHashBytes2)
       +        return (result1 or result2)
       +
       +    def sign(self, bytes):
       +        """Sign the passed-in bytes.
       +
       +        This requires the key to have a private component.  It performs
       +        a PKCS1 signature on the passed-in data.
       +
       +        @type bytes: L{bytearray} of unsigned bytes
       +        @param bytes: The value which will be signed.
       +
       +        @rtype: L{bytearray} of unsigned bytes.
       +        @return: A PKCS1 signature on the passed-in data.
       +        """
       +        if not self.hasPrivateKey():
       +            raise AssertionError()
       +        paddedBytes = self._addPKCS1Padding(bytes, 1)
       +        m = bytesToNumber(paddedBytes)
       +        if m >= self.n:
       +            raise ValueError()
       +        c = self._rawPrivateKeyOp(m)
       +        sigBytes = numberToByteArray(c, numBytes(self.n))
       +        return sigBytes
       +
       +    def verify(self, sigBytes, bytes):
       +        """Verify the passed-in bytes with the signature.
       +
       +        This verifies a PKCS1 signature on the passed-in data.
       +
       +        @type sigBytes: L{bytearray} of unsigned bytes
       +        @param sigBytes: A PKCS1 signature.
       +
       +        @type bytes: L{bytearray} of unsigned bytes
       +        @param bytes: The value which will be verified.
       +
       +        @rtype: bool
       +        @return: Whether the signature matches the passed-in data.
       +        """
       +        if len(sigBytes) != numBytes(self.n):
       +            return False
       +        paddedBytes = self._addPKCS1Padding(bytes, 1)
       +        c = bytesToNumber(sigBytes)
       +        if c >= self.n:
       +            return False
       +        m = self._rawPublicKeyOp(c)
       +        checkBytes = numberToByteArray(m, numBytes(self.n))
       +        return checkBytes == paddedBytes
       +
       +    def encrypt(self, bytes):
       +        """Encrypt the passed-in bytes.
       +
       +        This performs PKCS1 encryption of the passed-in data.
       +
       +        @type bytes: L{bytearray} of unsigned bytes
       +        @param bytes: The value which will be encrypted.
       +
       +        @rtype: L{bytearray} of unsigned bytes.
       +        @return: A PKCS1 encryption of the passed-in data.
       +        """
       +        paddedBytes = self._addPKCS1Padding(bytes, 2)
       +        m = bytesToNumber(paddedBytes)
       +        if m >= self.n:
       +            raise ValueError()
       +        c = self._rawPublicKeyOp(m)
       +        encBytes = numberToByteArray(c, numBytes(self.n))
       +        return encBytes
       +
       +    def decrypt(self, encBytes):
       +        """Decrypt the passed-in bytes.
       +
       +        This requires the key to have a private component.  It performs
       +        PKCS1 decryption of the passed-in data.
       +
       +        @type encBytes: L{bytearray} of unsigned bytes
       +        @param encBytes: The value which will be decrypted.
       +
       +        @rtype: L{bytearray} of unsigned bytes or None.
       +        @return: A PKCS1 decryption of the passed-in data or None if
       +        the data is not properly formatted.
       +        """
       +        if not self.hasPrivateKey():
       +            raise AssertionError()
       +        if len(encBytes) != numBytes(self.n):
       +            return None
       +        c = bytesToNumber(encBytes)
       +        if c >= self.n:
       +            return None
       +        m = self._rawPrivateKeyOp(c)
       +        decBytes = numberToByteArray(m, numBytes(self.n))
       +        #Check first two bytes
       +        if decBytes[0] != 0 or decBytes[1] != 2:
       +            return None
       +        #Scan through for zero separator
       +        for x in range(1, len(decBytes)-1):
       +            if decBytes[x]== 0:
       +                break
       +        else:
       +            return None
       +        return decBytes[x+1:] #Return everything after the separator
       +
       +
       +
       +
       +    # **************************************************************************
       +    # Helper Functions for RSA Keys
       +    # **************************************************************************
       +
       +    def _addPKCS1SHA1Prefix(self, bytes, withNULL=True):
       +        # There is a long history of confusion over whether the SHA1 
       +        # algorithmIdentifier should be encoded with a NULL parameter or 
       +        # with the parameter omitted.  While the original intention was 
       +        # apparently to omit it, many toolkits went the other way.  TLS 1.2
       +        # specifies the NULL should be included, and this behavior is also
       +        # mandated in recent versions of PKCS #1, and is what tlslite has
       +        # always implemented.  Anyways, verification code should probably 
       +        # accept both.  However, nothing uses this code yet, so this is 
       +        # all fairly moot.
       +        if not withNULL:
       +            prefixBytes = bytearray(\
       +            [0x30,0x1f,0x30,0x07,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x04,0x14])            
       +        else:
       +            prefixBytes = bytearray(\
       +            [0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00,0x04,0x14])            
       +        prefixedBytes = prefixBytes + bytes
       +        return prefixedBytes
       +
       +    def _addPKCS1Padding(self, bytes, blockType):
       +        padLength = (numBytes(self.n) - (len(bytes)+3))
       +        if blockType == 1: #Signature padding
       +            pad = [0xFF] * padLength
       +        elif blockType == 2: #Encryption padding
       +            pad = bytearray(0)
       +            while len(pad) < padLength:
       +                padBytes = getRandomBytes(padLength * 2)
       +                pad = [b for b in padBytes if b != 0]
       +                pad = pad[:padLength]
       +        else:
       +            raise AssertionError()
       +
       +        padding = bytearray([0,blockType] + pad + [0])
       +        paddedBytes = padding + bytes
       +        return paddedBytes
       +
       +
       +
       +
       +    def _rawPrivateKeyOp(self, m):
       +        #Create blinding values, on the first pass:
       +        if not self.blinder:
       +            self.unblinder = getRandomNumber(2, self.n)
       +            self.blinder = powMod(invMod(self.unblinder, self.n), self.e,
       +                                  self.n)
       +
       +        #Blind the input
       +        m = (m * self.blinder) % self.n
       +
       +        #Perform the RSA operation
       +        c = self._rawPrivateKeyOpHelper(m)
       +
       +        #Unblind the output
       +        c = (c * self.unblinder) % self.n
       +
       +        #Update blinding values
       +        self.blinder = (self.blinder * self.blinder) % self.n
       +        self.unblinder = (self.unblinder * self.unblinder) % self.n
       +
       +        #Return the output
       +        return c
       +
       +
       +    def _rawPrivateKeyOpHelper(self, m):
       +        #Non-CRT version
       +        #c = powMod(m, self.d, self.n)
       +
       +        #CRT version  (~3x faster)
       +        s1 = powMod(m, self.dP, self.p)
       +        s2 = powMod(m, self.dQ, self.q)
       +        h = ((s1 - s2) * self.qInv) % self.p
       +        c = s2 + self.q * h
       +        return c
       +
       +    def _rawPublicKeyOp(self, c):
       +        m = powMod(c, self.e, self.n)
       +        return m
       +
       +    def acceptsPassword(self):
       +        return False
       +
       +    def generate(bits):
       +        key = Python_RSAKey()
       +        p = getRandomPrime(bits//2, False)
       +        q = getRandomPrime(bits//2, False)
       +        t = lcm(p-1, q-1)
       +        key.n = p * q
       +        key.e = 65537
       +        key.d = invMod(key.e, t)
       +        key.p = p
       +        key.q = q
       +        key.dP = key.d % (p-1)
       +        key.dQ = key.d % (q-1)
       +        key.qInv = invMod(q, p)
       +        return key
       +    generate = staticmethod(generate)
       +
 (DIR) diff --git a/lib/x509.py b/lib/x509.py
       t@@ -20,17 +20,12 @@
        from datetime import datetime
        import sys
        
       -import tlslite
        import util
        from util import profiler, print_error
        
       -from asn1tinydecoder import asn1_node_root, asn1_get_all, asn1_get_value, \
       -                        asn1_get_value_of_type, asn1_node_next, asn1_node_first_child, \
       -                        asn1_read_length, asn1_node_is_child_of, \
       -                        bytestr_to_int, bitstr_to_bytestr
       -
       -# workaround https://github.com/trevp/tlslite/issues/15
       -tlslite.utils.cryptomath.pycryptoLoaded = False
       +from asn1tinydecoder import *
       +import ecdsa
       +import hashlib
        
        
        # algo OIDs
       t@@ -50,61 +45,13 @@ class CertificateError(Exception):
            pass
        
        
       -def decode_OID(s):
       -    s = map(ord, s)
       -    r = []
       -    r.append(s[0] / 40)
       -    r.append(s[0] % 40)
       -    k = 0 
       -    for i in s[1:]:
       -        if i < 128:
       -            r.append(i + 128*k)
       -            k = 0
       -        else:
       -            k = (i - 128) + 128*k
       -    return '.'.join(map(str, r))
       -
       -def encode_OID(oid):
       -    x = map(int, oid.split('.'))
       -    s = chr(x[0]*40 + x[1])
       -    for i in x[2:]:
       -        ss = chr(i % 128)
       -        while i > 128:
       -            i = i / 128
       -            ss = chr(128 + i % 128) + ss
       -        s += ss
       -    return s
       -
       -def asn1_get_children(der, i):
       -    nodes = []
       -    ii = asn1_node_first_child(der,i)
       -    nodes.append(ii)
       -    while ii[2]<i[2]:
       -        ii = asn1_node_next(der,ii)
       -        nodes.append(ii)
       -    return nodes
       -
       -def asn1_get_sequence(s):
       -    return map(lambda j: asn1_get_value(s, j), asn1_get_children(s, asn1_node_root(s)))
       -
       -def asn1_get_dict(der, i):
       -    p = {}
       -    for ii in asn1_get_children(der, i):
       -        for iii in asn1_get_children(der, ii):
       -            iiii = asn1_node_first_child(der, iii)
       -            oid = decode_OID(asn1_get_value_of_type(der, iiii, 'OBJECT IDENTIFIER'))
       -            iiii = asn1_node_next(der, iiii)
       -            value = asn1_get_value(der, iiii)
       -            p[oid] = value
       -    return p
        
        
       -class X509(tlslite.X509):
       +class X509(object):
        
            def parseBinary(self, b):
        
       -        # call tlslite method first
       -        tlslite.X509.parseBinary(self, b)
       +        self.bytes = bytearray(b)
        
                der = str(b)
                root = asn1_node_root(der)
       t@@ -139,8 +86,25 @@ class X509(tlslite.X509):
                # subject
                subject = asn1_node_next(der, validity)
                self.subject = asn1_get_dict(der, subject)
       +
                subject_pki = asn1_node_next(der, subject)
        
       +        public_key_algo = asn1_node_first_child(der, subject_pki)
       +        ii = asn1_node_first_child(der, public_key_algo)
       +        self.public_key_algo = decode_OID(asn1_get_value_of_type(der, ii, 'OBJECT IDENTIFIER'))
       +
       +        # pubkey modulus and exponent
       +        subject_public_key = asn1_node_next(der, public_key_algo)
       +        spk = asn1_get_value_of_type(der, subject_public_key, 'BIT STRING')
       +        spk = bitstr_to_bytestr(spk)
       +        r = asn1_node_root(spk)
       +        modulus = asn1_node_first_child(spk, r)
       +        exponent = asn1_node_next(spk, modulus)
       +        rsa_n = asn1_get_value_of_type(spk, modulus, 'INTEGER')
       +        rsa_e = asn1_get_value_of_type(spk, exponent, 'INTEGER')
       +        self.modulus = ecdsa.util.string_to_number(rsa_n)
       +        self.exponent = ecdsa.util.string_to_number(rsa_e)
       +
                # extensions
                self.CA = False
                self.AKI = None
       t@@ -198,10 +162,8 @@ class X509(tlslite.X509):
                if not_after <= now:
                    raise CertificateError('Certificate has expired.')
        
       -
       -
       -class X509CertChain(tlslite.X509CertChain):
       -    pass
       +    def getFingerprint(self):
       +        return hashlib.sha1(self.bytes).digest()
        
        
        
       t@@ -209,11 +171,12 @@ class X509CertChain(tlslite.X509CertChain):
        
        @profiler
        def load_certificates(ca_path):
       +    import pem
            ca_list = {}
            ca_keyID = {}
            with open(ca_path, 'r') as f:
                s = f.read()
       -    bList = tlslite.utils.pem.dePemList(s, "CERTIFICATE")
       +    bList = pem.dePemList(s, "CERTIFICATE")
            for b in bList:
                x = X509()
                try:
       t@@ -238,7 +201,6 @@ def int_to_bytestr(i):
            return s
        
        def create_csr(commonName, challenge, k):
       -    import ecdsa, hashlib
            from bitcoin import point_to_ser
            private_key = ecdsa.SigningKey.from_string(k, curve = ecdsa.SECP256k1)
            public_key = private_key.get_verifying_key()
 (DIR) diff --git a/setup.py b/setup.py
       t@@ -35,7 +35,6 @@ setup(
                'requests',
                'qrcode',
                'protobuf',
       -        'tlslite',
                'dnspython',
            ],
            package_dir={