Example #1
0
def generate_payment_request(crypto_addr, x509_cert, expires, signer=None, amount=0, memo=None, payment_url=None, merchant_data=None):

    # Setup & Populate PaymentDetails
    payment_details = PaymentDetails()

    # Setup Single PaymentDetails Output
    output = payment_details.outputs.add()
    output.amount = amount * 100000000 # BTC to Satoshis

    if crypto_addr[0] == '1':
        output.script = serialize_script([OP_DUP, OP_HASH160, b58check_to_hex(crypto_addr), OP_EQUALVERIFY, OP_CHECKSIG]).decode('hex')
    else:
        try:
            int(crypto_addr, 16)
            output.script = str(crypto_addr).decode('hex')
        except ValueError:
            output.script = str(crypto_addr)

    # Add current and expiration epoch time values
    payment_details.time = int(datetime.utcnow().strftime('%s'))

    payment_details.expires = expires

    # Handle Various Optional Fields in PaymentDetails
    payment_details.memo = memo if memo else ''
    payment_details.payment_url = payment_url if payment_url else ''
    payment_details.merchant_data = str(merchant_data) if merchant_data else ''

    # Setup & Populate PaymentRequest
    payment_request = PaymentRequest()
    payment_request.payment_details_version = 1
    payment_request.serialized_payment_details = payment_details.SerializeToString()

    # Set PKI Type / Data
    if not x509_cert or not signer:
        payment_request.pki_type = 'none'
        payment_request.pki_data = ''
    else:

        payment_request.pki_type = signer.get_pki_type()
        pki_data = X509Certificates()

        for cert in get_certs(x509_cert):
            pki_data.certificate.append(cert)

        payment_request.pki_data = pki_data.SerializeToString()

    # Sign PaymentRequest
    if signer and x509_cert:
        payment_request.signature = ''
        payment_request.signature = signer.sign(payment_request.SerializeToString())

    # Log Payment Request to Logging System
    logger = PluginManager.get_plugin('LOGGER', config.logger_type)
    logger.log_payment_request(crypto_addr, signer.__class__.__name__, amount, expires, memo, payment_url, merchant_data)

    log.debug('Generated Payment Request [Address: %s | Signer: %s | Amount: %s | Expires: %s | Memo: %s | Payment URL: %s | Merchant Data: %s]' %
              (crypto_addr, signer.__class__.__name__, amount, expires, memo, payment_url, merchant_data))

    return payment_request.SerializeToString()
Example #2
0
def get_addrs_from_paymentrequest(pr):

    ret_list = []
    pr_obj = PaymentRequest()
    pr_obj.ParseFromString(pr)

    pd = PaymentDetails()
    pd.ParseFromString(pr_obj.serialized_payment_details)

    for output in pd.outputs:
        script = deserialize_script(output.script)
        if script[0] == OP_DUP and script[1] == OP_HASH160 and script[3] == OP_EQUALVERIFY and script[4] == OP_CHECKSIG:
            ret_list.append(hex_to_b58check(script[2].encode('hex')))

    return ret_list
Example #3
0
    def create_and_send_payment_message(self, payment_request):
        pr = PaymentRequest()
        pr.ParseFromString(payment_request)

        pd = PaymentDetails()
        pd.ParseFromString(pr.serialized_payment_details)

        # Retrieve unique payment_url created by Addressimo and replace with Flask LiveServerTestCase server URL
        payment_uuid = pd.payment_url.rsplit('/', 1)[1]
        payment_url = '%s/payment/%s' % (self.get_server_url(), payment_uuid)

        # Add to payment_uuid Redis cleanup list
        self.redis_pr_store_cleanup.append(payment_uuid)

        # Create Payment output to satisfy PaymentRequest
        priv = sha256('random')
        ins = [{
            'output':
            '35d49e1833a20baffae35fb1423427d0c23e83b428a933eebde95ac7fd1c967a:0',
            'value': 10000,
            'script': 'random_script'
        }]
        outs = [{
            'value': pd.outputs[0].amount,
            'address': '%s' % script_to_address(pd.outputs[0].script)
        }]
        tx = sign(mktx(ins, outs), 0, priv)

        refund_address = '1CpLXM15vjULK3ZPGUTDMUcGATGR9xGitv'

        self.payment = Payment()
        self.payment.merchant_data = pd.merchant_data
        self.payment.memo = 'Payment memo'
        self.payment.transactions.append(tx)

        output = self.payment.refund_to.add()
        output.amount = pd.outputs[0].amount
        output.script = serialize_script([
            OP_DUP, OP_HASH160,
            b58check_to_hex(refund_address), OP_EQUALVERIFY, OP_CHECKSIG
        ]).decode('hex')

        # Set appropriate headers for Payment
        headers = {
            'Content-Type': 'application/bitcoin-payment',
            'Accept': 'application/bitcoin-paymentack',
            'Test-Transaction': True
        }

        response = requests.post(payment_url,
                                 data=self.payment.SerializeToString(),
                                 headers=headers)

        #######################
        # Validation
        #######################

        # PaymentRequest data used for Payment Creation
        self.assertTrue(
            pd.payment_url.startswith('https://%s/payment/' % config.site_url))
        self.assertEqual((33000 * 100000000), pd.outputs[0].amount)
        self.assertEqual(payment_uuid, pd.merchant_data)

        # PaymentRequest meta data used later for Payment validation
        pr_redis = Redis.from_url(config.redis_pr_store)
        pr_data = pr_redis.hgetall(payment_uuid)

        self.assertIsNotNone(pr_data.get('expiration_date'))
        payment_data = json.loads(pr_data.get('payment_validation_data'))
        self.assertEqual(self.test_id_obj.wallet_address,
                         payment_data.keys()[0])
        self.assertEqual((33000 * 100000000),
                         payment_data[self.test_id_obj.wallet_address])

        # Payment meta data used for refund endpoint
        payment_redis = Redis.from_url(config.redis_payment_store)
        payment_data = payment_redis.hgetall('testtxhash')

        self.assertIsNotNone(payment_data.get('expiration_date'))
        self.assertEqual('Payment memo', payment_data.get('memo'))
        self.assertEqual(
            "['76a914819d3b204b99f252da2ef21293c621e75dd1444f88ac']",
            payment_data.get('refund_to'))

        return response
Example #4
0
    def test_bip75_flow(self):

        ###################
        # Load Crypto Keys
        ###################
        self.x509_sender_cert = crypto.load_certificate(crypto.FILETYPE_PEM, SENDER_CERT)
        self.x509_sender_cert_privkey = crypto.load_privatekey(crypto.FILETYPE_PEM, SENDER_CERT_PRIVKEY)

        self.x509_receiver_cert = crypto.load_certificate(crypto.FILETYPE_PEM, RECEIVER_CERT)
        self.x509_receiver_cert_privkey = crypto.load_privatekey(crypto.FILETYPE_PEM, RECEIVER_CERT_PRIVKEY)

        #########################
        # Create InvoiceRequest
        #########################
        log.info("Building InvoiceRequest")

        sender_certs = X509Certificates()
        sender_certs.certificate.append(ssl.PEM_cert_to_DER_cert(crypto.dump_certificate(crypto.FILETYPE_PEM, self.x509_sender_cert)))

        invoice_request = InvoiceRequest()
        invoice_request.sender_public_key = BIP75FunctionalTest.sender_sk.get_verifying_key().to_der()
        invoice_request.amount = 75
        invoice_request.pki_type = 'x509+sha256'
        invoice_request.pki_data = sender_certs.SerializeToString()
        invoice_request.notification_url = 'https://notify.me/longId'
        invoice_request.signature = ""

        # Handle x509 Signature
        sig = crypto.sign(self.x509_sender_cert_privkey, invoice_request.SerializeToString(), 'sha1')
        invoice_request.signature = sig

        ##################################
        # Create Encrypted InvoiceRequest
        ##################################
        eir = self.create_encrypted_protocol_message(
            message=invoice_request,
            receiver_pubkey=BIP75FunctionalTest.receiver_sk.get_verifying_key(),
            sender_pubkey=BIP75FunctionalTest.sender_sk.get_verifying_key(),
            private_key=BIP75FunctionalTest.sender_sk
        )

        BIP75FunctionalTest.identifier = eir.identifier

        #############################
        # Sign & Submit HTTP Request
        #############################
        post_url = "%s/address/testid/resolve" % self.get_server_url()
        msg_sig = BIP75FunctionalTest.sender_sk.sign(post_url + eir.SerializeToString())

        ir_headers = {
            'X-Identity': BIP75FunctionalTest.sender_sk.get_verifying_key().to_der().encode('hex'),
            'X-Signature': msg_sig.encode('hex'),
            'Content-Type': 'application/bitcoin-encrypted-paymentprotocol-message',
            'Content-Transfer-Encoding': 'binary'
        }
        log.info("Submitting InvoiceRequest using an EncryptedProtocolMessage")
        response = requests.post(post_url, headers=ir_headers, data=eir.SerializeToString())

        # Validate Response
        self.assertEqual(202, response.status_code)
        self.assertTrue(response.headers.get('Location').startswith('https://%s/paymentprotocol' % config.site_url))
        self.payment_id = response.headers.get('Location').rsplit('/', 1)[1]
        log.info('Payment ID: %s' % self.payment_id)

        ###############################################
        # Get Pending InvoiceRequests from Addressimo
        ###############################################
        sign_url = "%s/address/testid/paymentprotocol" % self.get_server_url()
        msg_sig = BIP75FunctionalTest.receiver_sk.sign(sign_url)

        ir_req_headers = {
            'X-Identity': BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der().encode('hex'),
            'X-Signature': msg_sig.encode('hex')
        }

        log.info("Retrieving Encrypted InvoiceRequests")
        response = requests.get(sign_url, headers=ir_req_headers)

        log.info("Encrypted InvoiceRequest Retrieval Response [CODE: %d | TEXT: %s]" % (response.status_code, response.text))
        self.assertEqual(200, response.status_code)
        self.assertIsNotNone(response.text)

        ###############################################
        # Retrieve and Decrypt Encrypted InvoiceRequest
        ###############################################
        received_eir = None
        for epm in response.json()['encrypted_protocol_messages']:
            _local_msg = self.parse_protocol_message(epm.decode('hex'))
            if _local_msg.message_type == ProtocolMessageType.Value('INVOICE_REQUEST') and _local_msg.identifier == BIP75FunctionalTest.identifier:
                received_eir = _local_msg

        if not received_eir:
            self.fail('Failed to Retrieve Encrypted InvoiceRequest message')

        sender_vk = VerifyingKey.from_der(received_eir.sender_public_key)
        self.assertEqual(received_eir.receiver_public_key, BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der())

        received_invoice_request = self.get_decrypted_protocol_message(received_eir, sender_vk, BIP75FunctionalTest.receiver_sk)

        #########################
        # Create PaymentRequest
        #########################
        log.info("Building PaymentRequest")

        pd = PaymentDetails()
        pd.network = 'main'
        output = pd.outputs.add()
        output.amount = received_invoice_request.amount
        output.script = 'paymesomemoneyhere'.encode('hex')
        pd.time = int(datetime.utcnow().strftime('%s'))
        pd.expires = int((datetime.utcnow() + timedelta(seconds=3600)).strftime('%s'))
        pd.memo = ''
        pd.payment_url = ''
        pd.merchant_data = ''

        receiver_certs = X509Certificates()
        receiver_certs.certificate.append(ssl.PEM_cert_to_DER_cert(crypto.dump_certificate(crypto.FILETYPE_PEM, self.x509_receiver_cert)))

        pr = PaymentRequest()
        pr.payment_details_version = 1
        pr.pki_type = 'x509+sha256'
        pr.pki_data = receiver_certs.SerializeToString()
        pr.serialized_payment_details = pd.SerializeToString()
        pr.signature = ''

        sig = crypto.sign(self.x509_receiver_cert_privkey, pr.SerializeToString(), 'sha1')
        pr.signature = sig

        log.info('Encapsulating PaymentRequest in EncryptedProtocolMessage')
        epr = self.create_encrypted_protocol_message(
            message=pr,
            receiver_pubkey=BIP75FunctionalTest.receiver_sk.get_verifying_key(),
            sender_pubkey=BIP75FunctionalTest.sender_sk.get_verifying_key(),
            private_key=BIP75FunctionalTest.receiver_sk,
            identifier=BIP75FunctionalTest.identifier
        )

        sign_url = "%s/address/testid/paymentprotocol" % self.get_server_url()
        msg_sig = BIP75FunctionalTest.receiver_sk.sign(sign_url + epr.SerializeToString())

        ir_req_headers = {
            'X-Identity': BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der().encode('hex'),
            'X-Signature': msg_sig.encode('hex'),
            'Content-Type': 'application/bitcoin-encrypted-paymentprotocol-message',
            'Content-Transfer-Encoding': 'binary'
        }

        log.info("Submitting PaymentRequest using an EncryptedProtocolMessage")
        response = requests.post(sign_url, data=epr.SerializeToString(), headers=ir_req_headers)
        log.info('Submit PaymentRequest Response: %s' % response.text)
        self.assertEqual(200, response.status_code)

        ##############################################################################
        # Delete InvoiceRequest after the PaymentRequest was submitted successfully
        delete_url = "%s/address/testid/paymentprotocol/%s/invoice_request" % (self.get_server_url(), received_eir.identifier.encode('hex'))
        msg_sig = BIP75FunctionalTest.receiver_sk.sign(delete_url)

        ir_delete_headers = {
            'X-Identity': BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der().encode('hex'),
            'X-Signature': msg_sig.encode('hex')
        }
        response = requests.delete(delete_url, headers=ir_delete_headers)
        self.assertEqual(response.status_code, requests.codes.no_content)

        #####################################
        # Retrieve Encrypted PaymentRequest
        #####################################
        log.info("Retrieving PaymentRequest")

        sign_url = "%s/paymentprotocol/%s" % (self.get_server_url(), self.payment_id)
        msg_sig = BIP75FunctionalTest.receiver_sk.sign(sign_url)
        get_message_headers = {
            'X-Identity': BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der().encode('hex'),
            'X-Signature': msg_sig.encode('hex')
        }
        response = requests.get(sign_url, headers=get_message_headers)
        self.assertIsNotNone(response)

        self.assertIn('Content-Type', response.headers)
        self.assertEqual('application/json', response.headers.get('Content-Type'))

        received_epr = None
        for epm in response.json()['encrypted_protocol_messages']:
            _local_msg = self.parse_protocol_message(epm.decode('hex'))
            if _local_msg.message_type == ProtocolMessageType.Value('PAYMENT_REQUEST') and _local_msg.identifier == BIP75FunctionalTest.identifier:
                received_epr = _local_msg

        log.info('Received Encrypted PaymentRequest')

        self.assertEqual(BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der(), received_epr.receiver_public_key)
        self.assertEqual(BIP75FunctionalTest.sender_sk.get_verifying_key().to_der(), received_epr.sender_public_key)

        # Decrypt Response
        returned_paymentrequest = self.get_decrypted_protocol_message(
            message=received_epr,
            pubkey=VerifyingKey.from_der(received_epr.receiver_public_key),
            privkey=BIP75FunctionalTest.sender_sk
        )

        self.assertEqual(1, returned_paymentrequest.payment_details_version)
        self.assertEqual(pr.pki_type, returned_paymentrequest.pki_type)
        self.assertEqual(pr.pki_data, returned_paymentrequest.pki_data)
        self.assertEqual(pd.SerializeToString(), returned_paymentrequest.serialized_payment_details)
        self.assertEqual(pr.signature, returned_paymentrequest.signature)

        #######################################
        # Create / Submit Payment
        #######################################
        payment = Payment()
        payment.merchant_data = 'nodusttxs'.encode('hex')
        payment.transactions.append('btc_tx'.encode('hex'))
        out = payment.refund_to.add()
        out.script = 'myp2shaddress'.encode('hex')

        encrypted_payment = self.create_encrypted_protocol_message(
            message=payment,
            receiver_pubkey=VerifyingKey.from_der(received_epr.receiver_public_key),
            sender_pubkey=VerifyingKey.from_der(received_epr.sender_public_key),
            private_key=BIP75FunctionalTest.sender_sk,
            identifier=BIP75FunctionalTest.identifier
        )

        # Submit Payment
        sign_url = "%s/paymentprotocol/%s" % (self.get_server_url(), self.payment_id)
        msg_sig = BIP75FunctionalTest.sender_sk.sign(sign_url + encrypted_payment.SerializeToString())

        ep_req_headers = {
            'X-Identity': BIP75FunctionalTest.sender_sk.get_verifying_key().to_der().encode('hex'),
            'X-Signature': msg_sig.encode('hex'),
            'Content-Type': 'application/bitcoin-encrypted-paymentprotocol-message',
            'Content-Transfer-Encoding': 'binary'
        }

        log.info("Submitting Payment using an EncryptedProtocolMessage")
        response = requests.post(sign_url, data=encrypted_payment.SerializeToString(), headers=ep_req_headers)
        log.info('Submit Payment Response: %s' % response.text)
        self.assertEqual(200, response.status_code)

        ##############################################################################
        # Delete PaymentRequest after the Payment was submitted successfully
        delete_url = "%s/paymentprotocol/%s/%s/payment_request" % (self.get_server_url(), self.payment_id, received_eir.identifier.encode('hex'))
        msg_sig = BIP75FunctionalTest.receiver_sk.sign(delete_url)

        ir_delete_headers = {
            'X-Identity': BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der().encode('hex'),
            'X-Signature': msg_sig.encode('hex')
        }
        response = requests.delete(delete_url, headers=ir_delete_headers)
        self.assertEqual(response.status_code, requests.codes.no_content)

        ############################
        # Retrieve Payment
        ############################
        log.info("Retrieving Payment")

        sign_url = "%s/paymentprotocol/%s" % (self.get_server_url(), self.payment_id)
        msg_sig = BIP75FunctionalTest.receiver_sk.sign(sign_url)
        get_message_headers = {
            'X-Identity': BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der().encode('hex'),
            'X-Signature': msg_sig.encode('hex')
        }

        response = requests.get(sign_url, headers=get_message_headers)
        self.assertIsNotNone(response)

        self.assertIn('Content-Type', response.headers)
        self.assertEqual('application/json', response.headers.get('Content-Type'))

        returned_ep = None
        for epm in response.json()['encrypted_protocol_messages']:
            _local_msg = self.parse_protocol_message(epm.decode('hex'))
            if _local_msg.message_type == ProtocolMessageType.Value('PAYMENT') and _local_msg.identifier == BIP75FunctionalTest.identifier:
                returned_ep = _local_msg

        self.assertEqual(BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der(), returned_ep.receiver_public_key)
        self.assertEqual(BIP75FunctionalTest.sender_sk.get_verifying_key().to_der(), returned_ep.sender_public_key)
        self.assertEqual(encrypted_payment.encrypted_message, returned_ep.encrypted_message)

        payment_msg = self.get_decrypted_protocol_message(
            message=returned_ep,
            pubkey=VerifyingKey.from_der(returned_ep.sender_public_key),
            privkey=BIP75FunctionalTest.receiver_sk
        )

        self.assertEqual('nodusttxs'.encode('hex'), payment_msg.merchant_data)
        self.assertEqual(1, len(payment_msg.transactions))
        self.assertEqual('btc_tx'.encode('hex'), payment_msg.transactions[0])


        #######################################
        # Create / Submit PaymentACK
        #######################################
        paymentack = PaymentACK()
        paymentack.payment.CopyFrom(payment_msg)
        paymentack.memo = 'Payment ACKed'

        encrypted_paymentack = self.create_encrypted_protocol_message(
            message=paymentack,
            receiver_pubkey=VerifyingKey.from_der(epr.receiver_public_key),
            sender_pubkey=VerifyingKey.from_der(epr.sender_public_key),
            private_key=BIP75FunctionalTest.receiver_sk,
            identifier=BIP75FunctionalTest.identifier
        )

        # Submit PaymentAck
        sign_url = "%s/paymentprotocol/%s" % (self.get_server_url(), self.payment_id)
        msg_sig = BIP75FunctionalTest.receiver_sk.sign(sign_url + encrypted_paymentack.SerializeToString())

        ep_req_headers = {
            'X-Identity': BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der().encode('hex'),
            'X-Signature': msg_sig.encode('hex'),
            'Content-Type': 'application/bitcoin-encrypted-paymentprotocol-message',
            'Content-Transfer-Encoding': 'binary'
        }

        log.info("Submitting PaymentAck using an EncryptedProtocolMessage")
        response = requests.post(sign_url, data=encrypted_paymentack.SerializeToString(), headers=ep_req_headers)
        log.info('Submit PaymentAck Response: %s' % response.text)
        self.assertEqual(200, response.status_code)

        ##############################################################################
        # Delete Payment after the PaymentACK was submitted successfully
        delete_url = "%s/address/testid/paymentprotocol/%s/payment" % (self.get_server_url(), received_eir.identifier.encode('hex'))
        msg_sig = BIP75FunctionalTest.receiver_sk.sign(delete_url)

        ir_delete_headers = {
            'X-Identity': BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der().encode('hex'),
            'X-Signature': msg_sig.encode('hex')
        }
        response = requests.delete(delete_url, headers=ir_delete_headers)
        self.assertEqual(response.status_code, requests.codes.no_content)

        ###############################
        # Retrieve PaymentAck
        ###############################
        log.info("Retrieving EncryptedPaymentAck")
        sign_url = "%s/paymentprotocol/%s" % (self.get_server_url(), self.payment_id)
        msg_sig = BIP75FunctionalTest.receiver_sk.sign(sign_url)
        get_message_headers = {
            'X-Identity': BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der().encode('hex'),
            'X-Signature': msg_sig.encode('hex')
        }
        response = requests.get(sign_url, headers=get_message_headers)
        self.assertIsNotNone(response)

        self.assertIn('Content-Type', response.headers)
        self.assertEqual('application/json', response.headers.get('Content-Type'))

        returned_epa = None
        for epm in response.json()['encrypted_protocol_messages']:
            _local_msg = self.parse_protocol_message(epm.decode('hex'))
            if _local_msg.message_type == ProtocolMessageType.Value('PAYMENT_ACK') and _local_msg.identifier == BIP75FunctionalTest.identifier:
                returned_epa = _local_msg

        log.info('Received PaymentACK')

        self.assertEqual(BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der(), returned_ep.receiver_public_key)
        self.assertEqual(BIP75FunctionalTest.sender_sk.get_verifying_key().to_der(), returned_ep.sender_public_key)
        self.assertEqual(encrypted_paymentack.encrypted_message, returned_epa.encrypted_message)

        paymentack_msg = self.get_decrypted_protocol_message(
            message=returned_epa,
            pubkey=VerifyingKey.from_der(returned_epa.sender_public_key),
            privkey=BIP75FunctionalTest.receiver_sk
        )
        self.assertEqual(paymentack_msg, paymentack)

        ##############################################################################
        # Delete PaymentACK after the PaymentACK was retrieved successfully
        delete_url = "%s/address/testid/paymentprotocol/%s/payment_ack" % (self.get_server_url(), received_eir.identifier.encode('hex'))
        msg_sig = BIP75FunctionalTest.receiver_sk.sign(delete_url)

        ir_delete_headers = {
            'X-Identity': BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der().encode('hex'),
            'X-Signature': msg_sig.encode('hex')
        }
        response = requests.delete(delete_url, headers=ir_delete_headers)
        self.assertEqual(response.status_code, requests.codes.no_content)
Example #5
0
    def test_bip75_flow(self):

        ###################
        # Load Crypto Keys
        ###################
        self.x509_sender_cert = crypto.load_certificate(
            crypto.FILETYPE_PEM, SENDER_CERT)
        self.x509_sender_cert_privkey = crypto.load_privatekey(
            crypto.FILETYPE_PEM, SENDER_CERT_PRIVKEY)

        self.x509_receiver_cert = crypto.load_certificate(
            crypto.FILETYPE_PEM, RECEIVER_CERT)
        self.x509_receiver_cert_privkey = crypto.load_privatekey(
            crypto.FILETYPE_PEM, RECEIVER_CERT_PRIVKEY)

        ################################
        # Register Addressimo Endpoint
        ################################
        self.register_endpoint()

        #########################
        # Create InvoiceRequest
        #########################
        log.info("Building InvoiceRequest")

        sender_certs = X509Certificates()
        sender_certs.certificate.append(
            ssl.PEM_cert_to_DER_cert(
                crypto.dump_certificate(crypto.FILETYPE_PEM,
                                        self.x509_sender_cert)))

        invoice_request = InvoiceRequest()
        invoice_request.sender_public_key = BIP75FunctionalTest.sender_sk.get_verifying_key(
        ).to_der()
        invoice_request.amount = 75
        invoice_request.pki_type = 'x509+sha256'
        invoice_request.pki_data = sender_certs.SerializeToString()
        invoice_request.notification_url = 'https://notify.me/longId'
        invoice_request.signature = ""

        # Handle x509 Signature
        sig = crypto.sign(self.x509_sender_cert_privkey,
                          invoice_request.SerializeToString(), 'sha256')
        invoice_request.signature = sig

        ##################################
        # Create Encrypted InvoiceRequest
        ##################################
        eir = self.create_encrypted_protocol_message(
            message=invoice_request,
            receiver_pubkey=BIP75FunctionalTest.receiver_sk.get_verifying_key(
            ),
            sender_pubkey=BIP75FunctionalTest.sender_sk.get_verifying_key(),
            private_key=BIP75FunctionalTest.sender_sk)

        BIP75FunctionalTest.identifier = eir.identifier

        #############################
        # Sign & Submit HTTP Request
        #############################
        post_url = "%s/address/%s/resolve" % (self.get_server_url(),
                                              self.addressimo_endpoint_id)
        msg_sig = BIP75FunctionalTest.sender_sk.sign(self.get_signing_data(
            post_url, 'post', eir.SerializeToString()),
                                                     hashfunc=sha256,
                                                     sigencode=sigencode_der)

        ir_headers = {
            'X-Identity':
            BIP75FunctionalTest.sender_sk.get_verifying_key().to_der().encode(
                'hex'),
            'X-Signature':
            msg_sig.encode('hex'),
            'Content-Type':
            'application/bitcoin-encrypted-paymentprotocol-message',
            'Content-Transfer-Encoding':
            'binary'
        }
        log.info("Submitting InvoiceRequest using an EncryptedProtocolMessage")
        response = requests.post(post_url,
                                 headers=ir_headers,
                                 data=eir.SerializeToString())

        # Validate Response
        self.assertEqual(202, response.status_code)
        self.assertTrue(
            response.headers.get('Location').startswith(
                'https://%s/paymentprotocol' % config.site_url))
        self.payment_id = response.headers.get('Location').rsplit('/', 1)[1]
        log.info('Payment ID: %s' % self.payment_id)

        ###############################################
        # Get Pending InvoiceRequests from Addressimo
        ###############################################
        sign_url = "%s/address/%s/paymentprotocol" % (
            self.get_server_url(), self.addressimo_endpoint_id)
        msg_sig = BIP75FunctionalTest.receiver_sk.sign(self.get_signing_data(
            sign_url, 'get'),
                                                       hashfunc=sha256,
                                                       sigencode=sigencode_der)

        ir_req_headers = {
            'X-Identity':
            BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der().
            encode('hex'),
            'X-Signature':
            msg_sig.encode('hex')
        }

        log.info("Retrieving Encrypted InvoiceRequests")
        response = requests.get(sign_url, headers=ir_req_headers)

        log.info(
            "Encrypted InvoiceRequest Retrieval Response [CODE: %d | TEXT: %s]"
            % (response.status_code, response.text))
        self.assertEqual(200, response.status_code)
        self.assertIsNotNone(response.text)

        ###############################################
        # Retrieve and Decrypt Encrypted InvoiceRequest
        ###############################################
        received_eir = None
        for epm in response.json()['encrypted_protocol_messages']:
            _local_msg = self.parse_protocol_message(epm.decode('hex'))
            if _local_msg.message_type == ProtocolMessageType.Value(
                    'INVOICE_REQUEST'
            ) and _local_msg.identifier == BIP75FunctionalTest.identifier:
                received_eir = _local_msg

        if not received_eir:
            self.fail('Failed to Retrieve Encrypted InvoiceRequest message')

        sender_vk = VerifyingKey.from_der(received_eir.sender_public_key)
        self.assertEqual(
            received_eir.receiver_public_key,
            BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der())

        received_invoice_request = self.get_decrypted_protocol_message(
            received_eir, sender_vk, BIP75FunctionalTest.receiver_sk)

        #########################
        # Create PaymentRequest
        #########################
        log.info("Building PaymentRequest")

        pd = PaymentDetails()
        pd.network = 'main'
        output = pd.outputs.add()
        output.amount = received_invoice_request.amount
        output.script = 'paymesomemoneyhere'.encode('hex')
        pd.time = int(datetime.utcnow().strftime('%s'))
        pd.expires = int(
            (datetime.utcnow() + timedelta(seconds=3600)).strftime('%s'))
        pd.memo = ''
        pd.payment_url = ''
        pd.merchant_data = ''

        receiver_certs = X509Certificates()
        receiver_certs.certificate.append(
            ssl.PEM_cert_to_DER_cert(
                crypto.dump_certificate(crypto.FILETYPE_PEM,
                                        self.x509_receiver_cert)))

        pr = PaymentRequest()
        pr.payment_details_version = 1
        pr.pki_type = 'x509+sha256'
        pr.pki_data = receiver_certs.SerializeToString()
        pr.serialized_payment_details = pd.SerializeToString()
        pr.signature = ''

        sig = crypto.sign(self.x509_receiver_cert_privkey,
                          pr.SerializeToString(), 'sha256')
        pr.signature = sig

        log.info('Encapsulating PaymentRequest in EncryptedProtocolMessage')
        epr = self.create_encrypted_protocol_message(
            message=pr,
            receiver_pubkey=BIP75FunctionalTest.receiver_sk.get_verifying_key(
            ),
            sender_pubkey=BIP75FunctionalTest.sender_sk.get_verifying_key(),
            private_key=BIP75FunctionalTest.receiver_sk,
            identifier=BIP75FunctionalTest.identifier)

        sign_url = "%s/address/%s/paymentprotocol" % (
            self.get_server_url(), self.addressimo_endpoint_id)
        msg_sig = BIP75FunctionalTest.receiver_sk.sign(self.get_signing_data(
            sign_url, 'post', epr.SerializeToString()),
                                                       hashfunc=sha256,
                                                       sigencode=sigencode_der)

        ir_req_headers = {
            'X-Identity':
            BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der().
            encode('hex'),
            'X-Signature':
            msg_sig.encode('hex'),
            'Content-Type':
            'application/bitcoin-encrypted-paymentprotocol-message',
            'Content-Transfer-Encoding':
            'binary'
        }

        log.info("Submitting PaymentRequest using an EncryptedProtocolMessage")
        response = requests.post(sign_url,
                                 data=epr.SerializeToString(),
                                 headers=ir_req_headers)
        log.info('Submit PaymentRequest Response: %s' % response.text)
        self.assertEqual(200, response.status_code)

        ##############################################################################
        # Delete InvoiceRequest after the PaymentRequest was submitted successfully
        ##############################################################################
        delete_url = "%s/address/%s/paymentprotocol/%s/invoice_request" % (
            self.get_server_url(), self.addressimo_endpoint_id,
            received_eir.identifier.encode('hex'))
        msg_sig = BIP75FunctionalTest.receiver_sk.sign(self.get_signing_data(
            delete_url, 'delete'),
                                                       hashfunc=sha256,
                                                       sigencode=sigencode_der)

        ir_delete_headers = {
            'X-Identity':
            BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der().
            encode('hex'),
            'X-Signature':
            msg_sig.encode('hex')
        }
        response = requests.delete(delete_url, headers=ir_delete_headers)
        self.assertEqual(response.status_code, requests.codes.no_content)

        #####################################
        # Retrieve Encrypted PaymentRequest
        #####################################
        log.info("Retrieving PaymentRequest")

        sign_url = "%s/paymentprotocol/%s" % (self.get_server_url(),
                                              self.payment_id)
        msg_sig = BIP75FunctionalTest.sender_sk.sign(self.get_signing_data(
            sign_url, 'get'),
                                                     hashfunc=sha256,
                                                     sigencode=sigencode_der)
        get_message_headers = {
            'X-Identity':
            BIP75FunctionalTest.sender_sk.get_verifying_key().to_der().encode(
                'hex'),
            'X-Signature':
            msg_sig.encode('hex')
        }
        response = requests.get(sign_url, headers=get_message_headers)
        self.assertIsNotNone(response)

        self.assertIn('Content-Type', response.headers)
        self.assertEqual('application/json',
                         response.headers.get('Content-Type'))

        received_epr = None
        for epm in response.json()['encrypted_protocol_messages']:
            _local_msg = self.parse_protocol_message(epm.decode('hex'))
            if _local_msg.message_type == ProtocolMessageType.Value(
                    'PAYMENT_REQUEST'
            ) and _local_msg.identifier == BIP75FunctionalTest.identifier:
                received_epr = _local_msg

        log.info('Received Encrypted PaymentRequest')

        self.assertEqual(
            BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der(),
            received_epr.receiver_public_key)
        self.assertEqual(
            BIP75FunctionalTest.sender_sk.get_verifying_key().to_der(),
            received_epr.sender_public_key)

        # Decrypt Response
        returned_paymentrequest = self.get_decrypted_protocol_message(
            message=received_epr,
            pubkey=VerifyingKey.from_der(received_epr.receiver_public_key),
            privkey=BIP75FunctionalTest.sender_sk)

        self.assertEqual(1, returned_paymentrequest.payment_details_version)
        self.assertEqual(pr.pki_type, returned_paymentrequest.pki_type)
        self.assertEqual(pr.pki_data, returned_paymentrequest.pki_data)
        self.assertEqual(pd.SerializeToString(),
                         returned_paymentrequest.serialized_payment_details)
        self.assertEqual(pr.signature, returned_paymentrequest.signature)

        #######################################
        # Create / Submit Payment
        #######################################
        payment = Payment()
        payment.merchant_data = 'nodusttxs'.encode('hex')
        payment.transactions.append('btc_tx'.encode('hex'))
        out = payment.refund_to.add()
        out.script = 'myp2shaddress'.encode('hex')

        encrypted_payment = self.create_encrypted_protocol_message(
            message=payment,
            receiver_pubkey=VerifyingKey.from_der(
                received_epr.receiver_public_key),
            sender_pubkey=VerifyingKey.from_der(
                received_epr.sender_public_key),
            private_key=BIP75FunctionalTest.sender_sk,
            identifier=BIP75FunctionalTest.identifier)

        # Submit Payment
        sign_url = "%s/paymentprotocol/%s" % (self.get_server_url(),
                                              self.payment_id)
        msg_sig = BIP75FunctionalTest.sender_sk.sign(self.get_signing_data(
            sign_url, 'post', encrypted_payment.SerializeToString()),
                                                     hashfunc=sha256,
                                                     sigencode=sigencode_der)

        ep_req_headers = {
            'X-Identity':
            BIP75FunctionalTest.sender_sk.get_verifying_key().to_der().encode(
                'hex'),
            'X-Signature':
            msg_sig.encode('hex'),
            'Content-Type':
            'application/bitcoin-encrypted-paymentprotocol-message',
            'Content-Transfer-Encoding':
            'binary'
        }

        log.info("Submitting Payment using an EncryptedProtocolMessage")
        response = requests.post(sign_url,
                                 data=encrypted_payment.SerializeToString(),
                                 headers=ep_req_headers)
        log.info('Submit Payment Response: %s' % response.text)
        self.assertEqual(200, response.status_code)

        ##############################################################################
        # Delete PaymentRequest after the Payment was submitted successfully
        ##############################################################################
        delete_url = "%s/paymentprotocol/%s/%s/payment_request" % (
            self.get_server_url(), self.payment_id,
            received_eir.identifier.encode('hex'))
        msg_sig = BIP75FunctionalTest.receiver_sk.sign(self.get_signing_data(
            delete_url, 'delete'),
                                                       hashfunc=sha256,
                                                       sigencode=sigencode_der)

        ir_delete_headers = {
            'X-Identity':
            BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der().
            encode('hex'),
            'X-Signature':
            msg_sig.encode('hex')
        }
        response = requests.delete(delete_url, headers=ir_delete_headers)
        self.assertEqual(response.status_code, requests.codes.no_content)

        ############################
        # Retrieve Payment
        ############################
        log.info("Retrieving Payment")

        sign_url = "%s/paymentprotocol/%s" % (self.get_server_url(),
                                              self.payment_id)
        msg_sig = BIP75FunctionalTest.receiver_sk.sign(self.get_signing_data(
            sign_url, 'get'),
                                                       hashfunc=sha256,
                                                       sigencode=sigencode_der)
        get_message_headers = {
            'X-Identity':
            BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der().
            encode('hex'),
            'X-Signature':
            msg_sig.encode('hex')
        }

        response = requests.get(sign_url, headers=get_message_headers)
        self.assertIsNotNone(response)

        self.assertIn('Content-Type', response.headers)
        self.assertEqual('application/json',
                         response.headers.get('Content-Type'))

        returned_ep = None
        for epm in response.json()['encrypted_protocol_messages']:
            _local_msg = self.parse_protocol_message(epm.decode('hex'))
            if _local_msg.message_type == ProtocolMessageType.Value(
                    'PAYMENT'
            ) and _local_msg.identifier == BIP75FunctionalTest.identifier:
                returned_ep = _local_msg

        self.assertEqual(
            BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der(),
            returned_ep.receiver_public_key)
        self.assertEqual(
            BIP75FunctionalTest.sender_sk.get_verifying_key().to_der(),
            returned_ep.sender_public_key)
        self.assertEqual(encrypted_payment.encrypted_message,
                         returned_ep.encrypted_message)

        payment_msg = self.get_decrypted_protocol_message(
            message=returned_ep,
            pubkey=VerifyingKey.from_der(returned_ep.sender_public_key),
            privkey=BIP75FunctionalTest.receiver_sk)

        self.assertEqual('nodusttxs'.encode('hex'), payment_msg.merchant_data)
        self.assertEqual(1, len(payment_msg.transactions))
        self.assertEqual('btc_tx'.encode('hex'), payment_msg.transactions[0])

        #######################################
        # Create / Submit PaymentACK
        #######################################
        paymentack = PaymentACK()
        paymentack.payment.CopyFrom(payment_msg)
        paymentack.memo = 'Payment ACKed'

        encrypted_paymentack = self.create_encrypted_protocol_message(
            message=paymentack,
            receiver_pubkey=VerifyingKey.from_der(epr.receiver_public_key),
            sender_pubkey=VerifyingKey.from_der(epr.sender_public_key),
            private_key=BIP75FunctionalTest.receiver_sk,
            identifier=BIP75FunctionalTest.identifier)

        # Submit PaymentAck
        sign_url = "%s/paymentprotocol/%s" % (self.get_server_url(),
                                              self.payment_id)
        msg_sig = BIP75FunctionalTest.receiver_sk.sign(self.get_signing_data(
            sign_url, 'post', encrypted_paymentack.SerializeToString()),
                                                       hashfunc=sha256,
                                                       sigencode=sigencode_der)

        ep_req_headers = {
            'X-Identity':
            BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der().
            encode('hex'),
            'X-Signature':
            msg_sig.encode('hex'),
            'Content-Type':
            'application/bitcoin-encrypted-paymentprotocol-message',
            'Content-Transfer-Encoding':
            'binary'
        }

        log.info("Submitting PaymentAck using an EncryptedProtocolMessage")
        response = requests.post(sign_url,
                                 data=encrypted_paymentack.SerializeToString(),
                                 headers=ep_req_headers)
        log.info('Submit PaymentAck Response: %s' % response.text)
        self.assertEqual(200, response.status_code)

        ##############################################################################
        # Delete Payment after the PaymentACK was submitted successfully
        ##############################################################################
        delete_url = "%s/address/%s/paymentprotocol/%s/payment" % (
            self.get_server_url(), self.addressimo_endpoint_id,
            received_eir.identifier.encode('hex'))
        msg_sig = BIP75FunctionalTest.receiver_sk.sign(self.get_signing_data(
            delete_url, 'delete'),
                                                       hashfunc=sha256,
                                                       sigencode=sigencode_der)

        ir_delete_headers = {
            'X-Identity':
            BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der().
            encode('hex'),
            'X-Signature':
            msg_sig.encode('hex')
        }
        response = requests.delete(delete_url, headers=ir_delete_headers)
        self.assertEqual(response.status_code, requests.codes.no_content)

        ###############################
        # Retrieve PaymentAck
        ###############################
        log.info("Retrieving EncryptedPaymentAck")
        sign_url = "%s/paymentprotocol/%s" % (self.get_server_url(),
                                              self.payment_id)
        msg_sig = BIP75FunctionalTest.receiver_sk.sign(self.get_signing_data(
            sign_url, 'get'),
                                                       hashfunc=sha256,
                                                       sigencode=sigencode_der)
        get_message_headers = {
            'X-Identity':
            BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der().
            encode('hex'),
            'X-Signature':
            msg_sig.encode('hex')
        }
        response = requests.get(sign_url, headers=get_message_headers)
        self.assertIsNotNone(response)

        self.assertIn('Content-Type', response.headers)
        self.assertEqual('application/json',
                         response.headers.get('Content-Type'))

        returned_epa = None
        for epm in response.json()['encrypted_protocol_messages']:
            _local_msg = self.parse_protocol_message(epm.decode('hex'))
            if _local_msg.message_type == ProtocolMessageType.Value(
                    'PAYMENT_ACK'
            ) and _local_msg.identifier == BIP75FunctionalTest.identifier:
                returned_epa = _local_msg

        log.info('Received PaymentACK')

        self.assertEqual(
            BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der(),
            returned_ep.receiver_public_key)
        self.assertEqual(
            BIP75FunctionalTest.sender_sk.get_verifying_key().to_der(),
            returned_ep.sender_public_key)
        self.assertEqual(encrypted_paymentack.encrypted_message,
                         returned_epa.encrypted_message)

        paymentack_msg = self.get_decrypted_protocol_message(
            message=returned_epa,
            pubkey=VerifyingKey.from_der(returned_epa.sender_public_key),
            privkey=BIP75FunctionalTest.receiver_sk)
        self.assertEqual(paymentack_msg, paymentack)

        ##############################################################################
        # Delete PaymentACK after the PaymentACK was retrieved successfully
        ##############################################################################
        delete_url = "%s/address/%s/paymentprotocol/%s/payment_ack" % (
            self.get_server_url(), self.addressimo_endpoint_id,
            received_eir.identifier.encode('hex'))
        msg_sig = BIP75FunctionalTest.receiver_sk.sign(self.get_signing_data(
            delete_url, 'delete'),
                                                       hashfunc=sha256,
                                                       sigencode=sigencode_der)

        ir_delete_headers = {
            'X-Identity':
            BIP75FunctionalTest.receiver_sk.get_verifying_key().to_der().
            encode('hex'),
            'X-Signature':
            msg_sig.encode('hex')
        }
        response = requests.delete(delete_url, headers=ir_delete_headers)
        self.assertEqual(response.status_code, requests.codes.no_content)

        ################################
        # Delete Addressimo Endpoint
        ################################
        self.cleanup_endpoint()
Example #6
0
    def add():

        resolver = PluginManager.get_plugin('RESOLVER', config.resolver_type)
        id_obj = resolver.get_id_obj(get_id())
        if not id_obj:
            return create_json_response(False, 'Invalid Identifier', 404)

        rdata = None
        try:
            rdata = request.get_json()
        except Exception as e:
            log.warn("Exception Parsing JSON: %s" % str(e))
            return create_json_response(False, 'Invalid Request', 400)

        if not rdata:
            return create_json_response(False, 'Invalid Request', 400)

        pr_list = rdata.get('presigned_payment_requests')

        if not pr_list:
            return create_json_response(
                False, 'Missing presigned_payment_requests data', 400)

        if not isinstance(pr_list, list):
            return create_json_response(
                False, 'presigned_payment_requests data must be a list', 400)

        # Validate PaymentRequests
        for pr in pr_list:

            try:
                int(pr, 16)
            except ValueError:
                return create_json_response(
                    False, 'Payment Request Must Be Hex Encoded', 400)

            verify_pr = PaymentRequest()
            try:

                hex_decoded_pr = pr.decode('hex')
                if len(hex_decoded_pr) > PAYMENT_REQUEST_SIZE_MAX:
                    log.warn(
                        'Rejecting Payment Request for Size [ACCEPTED: %d bytes | ACTUAL: %d bytes]'
                        % (PAYMENT_REQUEST_SIZE_MAX, len(hex_decoded_pr)))
                    return create_json_response(
                        False, 'Invalid Payment Request Submitted', 400)

                verify_pr.ParseFromString(hex_decoded_pr)
            except Exception as e:
                log.warn(
                    'Unable to Parse Submitted Payment Request [ID: %s]: %s' %
                    (id_obj.id, str(e)))
                return create_json_response(
                    False, 'Invalid Payment Request Submitted', 400)

            verify_pd = PaymentDetails()
            try:
                verify_pd.ParseFromString(verify_pr.serialized_payment_details)
            except Exception as e:
                log.warn(
                    'Unable to Parse Submitted Payment Request [ID: %s]: %s' %
                    (id_obj.id, str(e)))
                return create_json_response(
                    False, 'Invalid Payment Request Submitted', 400)

        # Validated!
        add_count = 0
        for pr in pr_list:
            id_obj.presigned_payment_requests.append(pr)
            add_count += 1
            if add_count == config.presigned_pr_limit:
                log.info('Presigned Payment Limit Reached [ID: %s]' %
                         id_obj.id)
                break

        log.info('Added %d Pre-Signed Payment Requests [ID: %s]' %
                 (add_count, id_obj.id))
        resolver.save(id_obj)

        return create_json_response(data={'payment_requests_added': add_count})