def make_payment_request(outputs, memo, time, expires, key_path, cert_path):
    pd = pb2.PaymentDetails()
    for script, amount in outputs:
        pd.outputs.add(amount=amount, script=script)
    pd.time = time
    pd.expires = expires
    pd.memo = memo
    pr = pb2.PaymentRequest()
    pr.serialized_payment_details = pd.SerializeToString()
    pr.signature = ''
    pr = pb2.PaymentRequest()
    pr.serialized_payment_details = pd.SerializeToString()
    pr.signature = ''
    if key_path and cert_path:
        import tlslite
        with open(key_path, 'r') as f:
            rsakey = tlslite.utils.python_rsakey.Python_RSAKey.parsePEM(
                f.read())
        with open(cert_path, 'r') as f:
            chain = tlslite.X509CertChain()
            chain.parsePemList(f.read())
        certificates = pb2.X509Certificates()
        certificates.certificate.extend(
            map(lambda x: str(x.bytes), chain.x509List))
        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)
        pr.signature = bytes(sig)
    return pr.SerializeToString()
Example #2
0
def make_unsigned_request(req):
    from transaction import Transaction
    addr = req['address']
    time = req.get('time', 0)
    exp = req.get('exp', 0)
    if time and type(time) != int:
        time = 0
    if exp and type(exp) != int:
        exp = 0
    amount = req['amount']
    if amount is None:
        amount = 0
    memo = req['memo']
    script = Transaction.pay_script(TYPE_ADDRESS, addr).decode('hex')
    outputs = [(script, amount)]
    pd = pb2.PaymentDetails()
    for script, amount in outputs:
        pd.outputs.add(amount=amount, script=script)
    pd.time = time
    pd.expires = time + exp if exp else 0
    pd.memo = memo
    pr = pb2.PaymentRequest()
    pr.serialized_payment_details = pd.SerializeToString()
    pr.signature = ''
    return pr
Example #3
0
 def parse(self, r):
     if self.error:
         return
     self.id = bh2u(util.sha256(r)[0:16])
     try:
         self.data = pb2.PaymentRequest()
         self.data.ParseFromString(r)
     except:
         self.error = "cannot parse payment request"
         return
     self.details = pb2.PaymentDetails()
     self.details.ParseFromString(self.data.serialized_payment_details)
     if self.details.network == 'test':
         NetworkConstants.set_testnet()
     elif self.details.network == 'main':
         NetworkConstants.set_mainnet()
     else:
         self.error = "unknown network " + self.details.network
         return
     self.outputs = []
     for o in self.details.outputs:
         out_type, addr = util.get_address_from_output_script(o.script)
         self.outputs.append((out_type, addr, o.amount))
     self.memo = self.details.memo
     self.payment_url = self.details.payment_url
Example #4
0
def payment_request():
    """Generates a http PaymentRequest object"""

    bc = RPCCaller(allow_default_conf=True)
    btc = CCoinAddress(bc.getnewaddress())

    #   Setting the 'amount' field to 0 (zero) should prompt the user to enter
    #   the amount for us but a bug in bitcoin core qt version 0.9.1 (at time of
    #   writing) wrongly informs us that the value is too small and aborts.
    #   https://github.com/bitcoin/bitcoin/issues/3095
    #   Also there can be no leading 0's (zeros).
    btc_amount = 100000
    serialized_pubkey = btc.to_scriptPubKey()

    pdo = o.PaymentDetails(network="regtest")
    # pdo.network = 'test'
    pdo.outputs.add(amount=btc_amount, script=serialized_pubkey)
    pdo.time = int(time())
    pdo.memo = 'String shown to user before confirming payment'
    pdo.payment_url = 'http://{}:{}/{}'.format(listen_on['host'],
                                               listen_on['port'], ack_url_path)

    pro = o.PaymentRequest()
    pro.serialized_payment_details = pdo.SerializeToString()

    sds_pr = pro.SerializeToString()

    headers = {
        'Content-Type': 'application/bitcoin-payment',
        'Accept': 'application/bitcoin-paymentrequest'
    }

    return sds_pr, headers
Example #5
0
 def parse(self, r):
     self.id = bitcoin.sha256(r)[0:16].encode('hex')
     try:
         self.data = pb2.PaymentRequest()
         self.data.ParseFromString(r)
     except:
         self.error = "cannot parse payment request"
         return
     self.details = pb2.PaymentDetails()
     self.details.ParseFromString(self.data.serialized_payment_details)
     self.outputs = []
     for o in self.details.outputs:
         addr = transaction.get_address_from_output_script(o.script)[1]
         self.outputs.append((TYPE_ADDRESS, addr, o.amount))
     self.memo = self.details.memo
     self.payment_url = self.details.payment_url
Example #6
0
def make_unsigned_request(req):
    from transaction import Transaction
    addr = req['address']
    time = req['timestamp']
    amount = req['amount']
    if amount is None:
        amount = 0
    expires = req['expiration']
    memo = req['memo']
    script = Transaction.pay_script('address', addr).decode('hex')
    outputs = [(script, amount)]
    pd = pb2.PaymentDetails()
    for script, amount in outputs:
        pd.outputs.add(amount=amount, script=script)
    pd.time = time
    pd.expires = time + expires if expires else 0
    pd.memo = memo
    pr = pb2.PaymentRequest()
    pr.serialized_payment_details = pd.SerializeToString()
    pr.signature = ''
    return pr
Example #7
0
    def verify(self):

        if not ca_list:
            self.error = "Trusted certificate authorities list not found"
            return False

        paymntreq = self.data
        if not paymntreq.signature:
            self.error = "No signature"
            return

        cert = paymentrequest_pb2.X509Certificates()
        cert.ParseFromString(paymntreq.pki_data)
        cert_num = len(cert.certificate)

        x509_chain = []
        for i in range(cert_num):
            x = x509.X509()
            x.parseBinary(bytearray(cert.certificate[i]))
            x.slow_parse()
            x509_chain.append(x)
            if i == 0:
                try:
                    x.check_date()
                    x.check_name(self.domain)
                except Exception as e:
                    self.error = str(e)
                    return
            else:
                if not x.check_ca():
                    self.error = "ERROR: Supplied CA Certificate Error"
                    return

        if not cert_num > 1:
            self.error = "ERROR: CA Certificate Chain Not Provided by Payment Processor"
            return False

        for i in range(1, cert_num):
            x = x509_chain[i]
            prev_x = x509_chain[i - 1]

            algo, sig, data = prev_x.extract_sig()
            sig = bytearray(sig[5:])
            pubkey = x.publicKey
            if algo.getComponentByName('algorithm') == x509.ALGO_RSA_SHA1:
                verify = pubkey.hashAndVerify(sig, data)
            elif algo.getComponentByName('algorithm') == x509.ALGO_RSA_SHA256:
                hashBytes = bytearray(hashlib.sha256(data).digest())
                prefixBytes = bytearray([
                    0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
                    0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20
                ])
                verify = pubkey.verify(sig, prefixBytes + hashBytes)
            else:
                self.error = "Algorithm not supported"
                util.print_error(self.error,
                                 algo.getComponentByName('algorithm'))
                return

            if not verify:
                self.error = "Certificate not Signed by Provided CA Certificate Chain"
                return

        ca = x509_chain[cert_num - 1]
        supplied_CA_fingerprint = ca.getFingerprint()
        supplied_CA_names = ca.extract_names()
        CA_OU = supplied_CA_names['OU']

        x = ca_list.get(supplied_CA_fingerprint)
        if x:
            x.slow_parse()
            names = x.extract_names()
            CA_match = True
            if names['CN'] != supplied_CA_names['CN']:
                print "ERROR: Trusted CA CN Mismatch; however CA has trusted fingerprint"
                print "Payment will continue with manual verification."
        else:
            CA_match = False

        pubkey0 = x509_chain[0].publicKey
        sig = paymntreq.signature
        paymntreq.signature = ''
        s = paymntreq.SerializeToString()
        sigBytes = bytearray(sig)
        msgBytes = bytearray(s)

        if paymntreq.pki_type == "x509+sha256":
            hashBytes = bytearray(hashlib.sha256(msgBytes).digest())
            prefixBytes = bytearray([
                0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
                0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20
            ])
            verify = pubkey0.verify(sigBytes, prefixBytes + 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

        ### SIG Verified
        self.details = pay_det = paymentrequest_pb2.PaymentDetails()
        self.details.ParseFromString(paymntreq.serialized_payment_details)

        for o in pay_det.outputs:
            addr = transaction.get_address_from_output_script(o.script)[1]
            self.outputs.append(('address', addr, o.amount))

        self.memo = self.details.memo

        if CA_match:
            self.status = 'Signed by Trusted CA:\n' + CA_OU
        else:
            self.status = "Supplied CA Not Found in Trusted CA Store."

        self.payment_url = self.details.payment_url

        return True
Example #8
0
class PaymentRequest:
    def __init__(self, url):
        self.url = url
        self.outputs = []
        self.error = ""

    def get_amount(self):
        return sum(map(lambda x: x[1], self.outputs))

    def verify(self):
        u = urlparse.urlparse(self.url)
        self.domain = u.netloc

        try:
            connection = httplib.HTTPConnection(
                u.netloc) if u.scheme == 'http' else httplib.HTTPSConnection(
                    u.netloc)
            connection.request("GET", u.geturl(), headers=REQUEST_HEADERS)
            resp = connection.getresponse()
        except:
            self.error = "cannot read url"
            return

        paymntreq = paymentrequest_pb2.PaymentRequest()
        try:
            r = resp.read()
            paymntreq.ParseFromString(r)
        except:
            self.error = "cannot parse payment request"
            return

        sig = paymntreq.signature
        if not sig:
            self.error = "No signature"
            return

        cert = paymentrequest_pb2.X509Certificates()
        cert.ParseFromString(paymntreq.pki_data)
        cert_num = len(cert.certificate)

        x509_1 = X509.load_cert_der_string(cert.certificate[0])
        if self.domain != x509_1.get_subject().CN:
            validcert = False
            try:
                SANs = x509_1.get_ext("subjectAltName").get_value().split(",")
                for s in SANs:
                    s = s.strip()
                    if s.startswith("DNS:") and s[4:] == self.domain:
                        validcert = True
                        print "Match SAN DNS"
                    elif s.startswith("IP:") and s[3:] == self.domain:
                        validcert = True
                        print "Match SAN IP"
                    elif s.startswith("email:") and s[6:] == self.domain:
                        validcert = True
                        print "Match SAN email"
            except Exception, e:
                print "ERROR: No SAN data"
            if not validcert:
                ###TODO: check for wildcards
                self.error = "ERROR: Certificate Subject Domain Mismatch and SAN Mismatch"
                return

        x509 = []
        CA_OU = ''

        if cert_num > 1:
            for i in range(cert_num - 1):
                x509.append(X509.load_cert_der_string(cert.certificate[i + 1]))
                if x509[i].check_ca() == 0:
                    self.error = "ERROR: Supplied CA Certificate Error"
                    return
            for i in range(cert_num - 1):
                if i == 0:
                    if x509_1.verify(x509[i].get_pubkey()) != 1:
                        self.error = "ERROR: Certificate not Signed by Provided CA Certificate Chain"
                        return
                else:
                    if x509[i - 1].verify(x509[i].get_pubkey()) != 1:
                        self.error = "ERROR: CA Certificate not Signed by Provided CA Certificate Chain"
                        return

            supplied_CA_fingerprint = x509[cert_num - 2].get_fingerprint()
            supplied_CA_CN = x509[cert_num - 2].get_subject().CN
            CA_match = False

            x = ca_list.get(supplied_CA_fingerprint)
            if x:
                CA_OU = x.get_subject().OU
                CA_match = True
                if x.get_subject().CN != supplied_CA_CN:
                    print "ERROR: Trusted CA CN Mismatch; however CA has trusted fingerprint"
                    print "Payment will continue with manual verification."
            else:
                print "ERROR: Supplied CA Not Found in Trusted CA Store."
                print "Payment will continue with manual verification."
        else:
            self.error = "ERROR: CA Certificate Chain Not Provided by Payment Processor"
            return False

        paymntreq.signature = ''
        s = paymntreq.SerializeToString()
        pubkey_1 = x509_1.get_pubkey()

        if paymntreq.pki_type == "x509+sha256":
            pubkey_1.reset_context(md="sha256")
        elif paymntreq.pki_type == "x509+sha1":
            pubkey_1.reset_context(md="sha1")
        else:
            self.error = "ERROR: Unsupported PKI Type for Message Signature"
            return False

        pubkey_1.verify_init()
        pubkey_1.verify_update(s)
        if pubkey_1.verify_final(sig) != 1:
            self.error = "ERROR: Invalid Signature for Payment Request Data"
            return False

        ### SIG Verified

        self.payment_details = pay_det = paymentrequest_pb2.PaymentDetails()
        pay_det.ParseFromString(paymntreq.serialized_payment_details)

        if pay_det.expires and pay_det.expires < int(time.time()):
            self.error = "ERROR: Payment Request has Expired."
            return False

        for o in pay_det.outputs:
            addr = transaction.get_address_from_output_script(o.script)[1]
            self.outputs.append((addr, o.amount))

        self.memo = pay_det.memo

        if CA_match:
            print 'Signed By Trusted CA: ', CA_OU

        print "payment url", pay_det.payment_url
        return True
Example #9
0
class PaymentRequest:
    def __init__(self, config):
        self.config = config
        self.outputs = []
        self.error = ""
        self.dir_path = os.path.join(self.config.path, 'requests')
        if not os.path.exists(self.dir_path):
            os.mkdir(self.dir_path)

    def read(self, url):
        self.url = url
        u = urlparse.urlparse(url)
        self.domain = u.netloc
        try:
            connection = httplib.HTTPConnection(
                u.netloc) if u.scheme == 'http' else httplib.HTTPSConnection(
                    u.netloc)
            connection.request("GET", u.geturl(), headers=REQUEST_HEADERS)
            response = connection.getresponse()
        except:
            self.error = "cannot read url"
            return

        try:
            r = response.read()
        except:
            self.error = "cannot read"
            return

        self.id = bitcoin.sha256(r)[0:16].encode('hex')
        filename = os.path.join(self.dir_path, self.id)
        with open(filename, 'w') as f:
            f.write(r)

        return self.parse(r)

    def get_status(self):
        if self.error:
            return self.error
        else:
            return self.status

    def read_file(self, key):
        filename = os.path.join(self.dir_path, key)
        with open(filename, 'r') as f:
            r = f.read()

        self.parse(r)

    def parse(self, r):
        try:
            self.data = paymentrequest_pb2.PaymentRequest()
            self.data.ParseFromString(r)
        except:
            self.error = "cannot parse payment request"
            return

    def verify(self):
        try:
            from M2Crypto import X509
        except:
            self.error = "cannot import M2Crypto"
            return False

        if not ca_list:
            self.error = "Trusted certificate authorities list not found"
            return False

        paymntreq = self.data
        sig = paymntreq.signature
        if not sig:
            self.error = "No signature"
            return

        cert = paymentrequest_pb2.X509Certificates()
        cert.ParseFromString(paymntreq.pki_data)
        cert_num = len(cert.certificate)

        x509_1 = X509.load_cert_der_string(cert.certificate[0])
        if self.domain != x509_1.get_subject().CN:
            validcert = False
            try:
                SANs = x509_1.get_ext("subjectAltName").get_value().split(",")
                for s in SANs:
                    s = s.strip()
                    if s.startswith("DNS:") and s[4:] == self.domain:
                        validcert = True
                        print "Match SAN DNS"
                    elif s.startswith("IP:") and s[3:] == self.domain:
                        validcert = True
                        print "Match SAN IP"
                    elif s.startswith("email:") and s[6:] == self.domain:
                        validcert = True
                        print "Match SAN email"
            except Exception, e:
                print "ERROR: No SAN data"
            if not validcert:
                ###TODO: check for wildcards
                self.error = "ERROR: Certificate Subject Domain Mismatch and SAN Mismatch"
                return

        x509 = []
        CA_OU = ''

        if cert_num > 1:
            for i in range(cert_num - 1):
                x509.append(X509.load_cert_der_string(cert.certificate[i + 1]))
                if x509[i].check_ca() == 0:
                    self.error = "ERROR: Supplied CA Certificate Error"
                    return
            for i in range(cert_num - 1):
                if i == 0:
                    if x509_1.verify(x509[i].get_pubkey()) != 1:
                        self.error = "ERROR: Certificate not Signed by Provided CA Certificate Chain"
                        return
                else:
                    if x509[i - 1].verify(x509[i].get_pubkey()) != 1:
                        self.error = "ERROR: CA Certificate not Signed by Provided CA Certificate Chain"
                        return

            supplied_CA_fingerprint = x509[cert_num - 2].get_fingerprint()
            supplied_CA_CN = x509[cert_num - 2].get_subject().CN
            CA_match = False

            x = ca_list.get(supplied_CA_fingerprint)
            if x:
                CA_OU = x.get_subject().OU
                CA_match = True
                if x.get_subject().CN != supplied_CA_CN:
                    print "ERROR: Trusted CA CN Mismatch; however CA has trusted fingerprint"
                    print "Payment will continue with manual verification."
            else:
                print "ERROR: Supplied CA Not Found in Trusted CA Store."
                print "Payment will continue with manual verification."
        else:
            self.error = "ERROR: CA Certificate Chain Not Provided by Payment Processor"
            return False

        paymntreq.signature = ''
        s = paymntreq.SerializeToString()
        pubkey_1 = x509_1.get_pubkey()

        if paymntreq.pki_type == "x509+sha256":
            pubkey_1.reset_context(md="sha256")
        elif paymntreq.pki_type == "x509+sha1":
            pubkey_1.reset_context(md="sha1")
        else:
            self.error = "ERROR: Unsupported PKI Type for Message Signature"
            return False

        pubkey_1.verify_init()
        pubkey_1.verify_update(s)
        if pubkey_1.verify_final(sig) != 1:
            self.error = "ERROR: Invalid Signature for Payment Request Data"
            return False

        ### SIG Verified

        self.details = pay_det = paymentrequest_pb2.PaymentDetails()
        self.details.ParseFromString(paymntreq.serialized_payment_details)

        for o in pay_det.outputs:
            addr = transaction.get_address_from_output_script(o.script)[1]
            self.outputs.append((addr, o.amount))

        self.memo = self.details.memo

        if CA_match:
            self.status = 'Signed by Trusted CA:\n' + CA_OU

        self.payment_url = self.details.payment_url

        if self.has_expired():
            self.error = "ERROR: Payment Request has Expired."
            return False

        return True
Example #10
0
def decode_pr(addr):
    dat = http.request('GET', url_from_input(addr), headers={'Accept': 'application/bitcoin-paymentrequest'}).data
    req = paymentrequest_pb2.PaymentRequest().FromString(dat)
    return paymentrequest_pb2.PaymentDetails().FromString(req.serialized_payment_details)
Example #11
0
def decode_pr(addr):
    req = urllib.request.Request(url_from_input(addr), headers={'Accept': 'application/bitcoin-paymentrequest'})
    with urllib.request.urlopen(req) as f:
        dat = f.read()
        req = paymentrequest_pb2.PaymentRequest().FromString(dat)
        return paymentrequest_pb2.PaymentDetails().FromString(req.serialized_payment_details)