├── .gitignore ├── LICENSE.md ├── README.md ├── paymentrequest.proto ├── tui.py ├── msqr.py ├── setup.py ├── segwit_addr.py ├── pem.py ├── gui.py ├── x509.py ├── paymentrequest.py ├── rsakey.py └── util.py /.gitignore: -------------------------------------------------------------------------------- 1 | paymentrequest_pb2.py 2 | __pycache__ 3 | venv/ 4 | *.pyc -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bitcoin Payment Protocol Interface 2 | 3 | This is a simple application which allows you to interact with the Bitcoin payment protocol manually. It displays all of the information from the protocol and lets you perform the actions that your wallet would otherwise do automatically. This lets you use services that use the payment protocol exclusively without having to switch to a new wallet. 4 | 5 | ## Running 6 | 7 | Install all of the dependencies 8 | 9 | python3 setup.py install 10 | 11 | Compile the Protobuf descriptors 12 | 13 | sudo apt-get install protobuf-compiler 14 | protoc --proto_path=./ --python_out=./ ./paymentrequest.proto 15 | 16 | Use the Text User Interface 17 | 18 | python tui.py 19 | 20 | Or use the Graphical User Interface 21 | 22 | python gui.py 23 | 24 | ## Building for release 25 | 26 | To build a binary package for a release, you must have a machine of the target OS and bit-ness (e.g. Windows 64-bit) to build on. 27 | 28 | First install `pyinstaller`: 29 | 30 | pip3 install pyinstaller 31 | 32 | Then create the executables: 33 | 34 | pyinstaller -F -n btcpp-cli tui.py 35 | pyinstaller -F -n btcpp-qt --windowed gui.py 36 | 37 | This will create the executables `btcpp-cli` and `btcpp-qt` in the `dist/` folder 38 | 39 | ## License 40 | 41 | This project is Copyright (c) 2017 Andrew Chow under the MIT License. 42 | 43 | Parts of this project are taken from Electrum; those are Copyright (c) The Electrum Developers under the MIT License 44 | 45 | ## Disclaimer 46 | 47 | I do not guarantee that any part of this software actually works and you use it at your own risk. While I try the best I can to make quality software, I am only human and mistakes can happen. I am not liable for any losses or potential losses caused by this software. 48 | -------------------------------------------------------------------------------- /paymentrequest.proto: -------------------------------------------------------------------------------- 1 | // 2 | // Simple Bitcoin Payment Protocol messages 3 | // 4 | // Use fields 1000+ for extensions; 5 | // to avoid conflicts, register extensions via pull-req at 6 | // https://github.com/bitcoin/bips/bip-0070/extensions.mediawiki 7 | // 8 | 9 | syntax = "proto2"; 10 | package payments; 11 | option java_package = "org.bitcoin.protocols.payments"; 12 | option java_outer_classname = "Protos"; 13 | 14 | // Generalized form of "send payment to this/these bitcoin addresses" 15 | message Output { 16 | optional uint64 amount = 1 [default = 0]; // amount is integer-number-of-satoshis 17 | required bytes script = 2; // usually one of the standard Script forms 18 | } 19 | message PaymentDetails { 20 | optional string network = 1 [default = "main"]; // "main" or "test" 21 | repeated Output outputs = 2; // Where payment should be sent 22 | required uint64 time = 3; // Timestamp; when payment request created 23 | optional uint64 expires = 4; // Timestamp; when this request should be considered invalid 24 | optional string memo = 5; // Human-readable description of request for the customer 25 | optional string payment_url = 6; // URL to send Payment and get PaymentACK 26 | optional bytes merchant_data = 7; // Arbitrary data to include in the Payment message 27 | } 28 | message PaymentRequest { 29 | optional uint32 payment_details_version = 1 [default = 1]; 30 | optional string pki_type = 2 [default = "none"]; // none / x509+sha256 / x509+sha1 31 | optional bytes pki_data = 3; // depends on pki_type 32 | required bytes serialized_payment_details = 4; // PaymentDetails 33 | optional bytes signature = 5; // pki-dependent signature 34 | } 35 | message X509Certificates { 36 | repeated bytes certificate = 1; // DER-encoded X.509 certificate chain 37 | } 38 | message Payment { 39 | optional bytes merchant_data = 1; // From PaymentDetails.merchant_data 40 | repeated bytes transactions = 2; // Signed transactions that satisfy PaymentDetails.outputs 41 | repeated Output refund_to = 3; // Where to send refunds, if a refund is necessary 42 | optional string memo = 4; // Human-readable message for the merchant 43 | } 44 | message PaymentACK { 45 | required Payment payment = 1; // Payment message that triggered this ACK 46 | optional string memo = 2; // human-readable message for customer 47 | } 48 | -------------------------------------------------------------------------------- /tui.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (C) 2017 Andrew Chow 4 | 5 | import util 6 | 7 | def print_pr(pr): 8 | if pr.error: 9 | print(pr.error) 10 | exit() 11 | else: 12 | pr.verify() 13 | print() 14 | print("Payment request data") 15 | print("Network: " + pr.details.network) 16 | print("Requestor: " + pr.get_requestor()) 17 | print("Memo: " + pr.get_memo()) 18 | print("Expiration: " + util.format_time(pr.get_expiration_date())) 19 | print("Creation Time: " + util.format_time(pr.details.time)) 20 | print("Verification Status: " + pr.get_verify_status()) 21 | print("Merchant Data: " + str(pr.details.merchant_data)) 22 | print("Outputs:") 23 | for out in pr.get_outputs(): 24 | if out[0] == util.TYPE_ADDRESS: 25 | print(" Type: Address") 26 | print(" Address: " + out[1]) 27 | elif out[0] == util.TYPE_PUBKEY: 28 | print(" Type: Public Key") 29 | print(" Public Key: " + out[1]) 30 | elif out[0] == util.TYPE_SCRIPT: 31 | print(" Type: Script") 32 | print(" Script: " + out[1]) 33 | else: 34 | print(" Type: Unknown") 35 | print(" Data: " + out[1]) 36 | print(" Amount: " + util.format_satoshis(out[2]) + " BTC") 37 | 38 | # Prompt to send transaction 39 | print("To continue, send the necessary amounts of Bitcoin to the addresses specified in the 'Outputs' field above. Once broadcast, press ENTER to continue or CTRL+C to exit.") 40 | input() 41 | 42 | # Only do this if there is a Payment URL 43 | if pr.details.payment_url: 44 | # Get raw tx and refund address for Payment message 45 | raw_tx = input("Enter the hex of the transaction that was just made: ").strip() 46 | ref_addr = input("Enter a refund address: ").strip() 47 | 48 | # Send payment message and wait for ACK 49 | result = pr.send_ack(raw_tx, ref_addr) 50 | if result[0]: 51 | print(result[1]) 52 | else: 53 | print(result[1]) 54 | exit() 55 | 56 | if __name__ == '__main__': 57 | # Command line interface, print welcome message 58 | print("Bitcoin Payment Protocol Interface") 59 | print() 60 | 61 | # Get the payment request 62 | uri = input("Enter a Bitcoin URI: ").strip() 63 | print() 64 | util.parse_URI(uri, print_pr) 65 | print() 66 | print("Payment complete!") 67 | -------------------------------------------------------------------------------- /msqr.py: -------------------------------------------------------------------------------- 1 | # from http://eli.thegreenplace.net/2009/03/07/computing-modular-square-roots-in-python/ 2 | 3 | def modular_sqrt(a, p): 4 | """ Find a quadratic residue (mod p) of 'a'. p 5 | must be an odd prime. 6 | 7 | Solve the congruence of the form: 8 | x^2 = a (mod p) 9 | And returns x. Note that p - x is also a root. 10 | 11 | 0 is returned is no square root exists for 12 | these a and p. 13 | 14 | The Tonelli-Shanks algorithm is used (except 15 | for some simple cases in which the solution 16 | is known from an identity). This algorithm 17 | runs in polynomial time (unless the 18 | generalized Riemann hypothesis is false). 19 | """ 20 | # Simple cases 21 | # 22 | if legendre_symbol(a, p) != 1: 23 | return 0 24 | elif a == 0: 25 | return 0 26 | elif p == 2: 27 | return p 28 | elif p % 4 == 3: 29 | return pow(a, (p + 1) // 4, p) 30 | 31 | # Partition p-1 to s * 2^e for an odd s (i.e. 32 | # reduce all the powers of 2 from p-1) 33 | # 34 | s = p - 1 35 | e = 0 36 | while s % 2 == 0: 37 | s //= 2 38 | e += 1 39 | 40 | # Find some 'n' with a legendre symbol n|p = -1. 41 | # Shouldn't take long. 42 | # 43 | n = 2 44 | while legendre_symbol(n, p) != -1: 45 | n += 1 46 | 47 | # Here be dragons! 48 | # Read the paper "Square roots from 1; 24, 51, 49 | # 10 to Dan Shanks" by Ezra Brown for more 50 | # information 51 | # 52 | 53 | # x is a guess of the square root that gets better 54 | # with each iteration. 55 | # b is the "fudge factor" - by how much we're off 56 | # with the guess. The invariant x^2 = ab (mod p) 57 | # is maintained throughout the loop. 58 | # g is used for successive powers of n to update 59 | # both a and b 60 | # r is the exponent - decreases with each update 61 | # 62 | x = pow(a, (s + 1) // 2, p) 63 | b = pow(a, s, p) 64 | g = pow(n, s, p) 65 | r = e 66 | 67 | while True: 68 | t = b 69 | m = 0 70 | for m in range(r): 71 | if t == 1: 72 | break 73 | t = pow(t, 2, p) 74 | 75 | if m == 0: 76 | return x 77 | 78 | gs = pow(g, 2 ** (r - m - 1), p) 79 | g = (gs * gs) % p 80 | x = (x * gs) % p 81 | b = (b * g) % p 82 | r = m 83 | 84 | def legendre_symbol(a, p): 85 | """ Compute the Legendre symbol a|p using 86 | Euler's criterion. p is a prime, a is 87 | relatively prime to p (if p divides 88 | a, then a|p = 0) 89 | 90 | Returns 1 if a has a square root modulo 91 | p, -1 otherwise. 92 | """ 93 | ls = pow(a, (p - 1) // 2, p) 94 | return -1 if ls == p - 1 else ls 95 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """A setuptools based setup module. 2 | 3 | See: 4 | https://packaging.python.org/en/latest/distributing.html 5 | https://github.com/pypa/sampleproject 6 | """ 7 | 8 | # Always prefer setuptools over distutils 9 | from setuptools import setup, find_packages 10 | # To use a consistent encoding 11 | from codecs import open 12 | from os import path 13 | 14 | here = path.abspath(path.dirname(__file__)) 15 | 16 | # Get the long description from the README file 17 | with open(path.join(here, 'README.md'), encoding='utf-8') as f: 18 | long_description = f.read() 19 | 20 | # Arguments marked as "Required" below must be included for upload to PyPI. 21 | # Fields marked as "Optional" may be commented out. 22 | 23 | setup( 24 | # This is the name of your project. The first time you publish this 25 | # package, this name will be registered for you. It will determine how 26 | # users can install this project, e.g.: 27 | # 28 | # $ pip install sampleproject 29 | # 30 | # And where it will live on PyPI: https://pypi.org/project/sampleproject/ 31 | # 32 | # There are some restrictions on what makes a valid project name 33 | # specification here: 34 | # https://packaging.python.org/specifications/core-metadata/#name 35 | name='Bitcoin-Payment-Protocol-Interface', # Required 36 | 37 | # Versions should comply with PEP 440: 38 | # https://www.python.org/dev/peps/pep-0440/ 39 | # 40 | # For a discussion on single-sourcing the version across setup.py and the 41 | # project code, see 42 | # https://packaging.python.org/en/latest/single_source_version.html 43 | version='0.1', # Required 44 | 45 | # This is a one-line description or tagline of what your project does. This 46 | # corresponds to the "Summary" metadata field: 47 | # https://packaging.python.org/specifications/core-metadata/#summary 48 | description='User interfaces for interacting with the Bitcoin Payment Protocol (BIP 70) manually', # Required 49 | 50 | # This should be your name or the name of the organization which owns the 51 | # project. 52 | author='Andrew Chow', # Optional 53 | 54 | # This should be a valid email address corresponding to the author listed 55 | # above. 56 | author_email='achow101@gmail.com', # Optional 57 | 58 | # Classifiers help users find your project by categorizing it. 59 | # 60 | # For a list of valid classifiers, see 61 | # https://pypi.python.org/pypi?%3Aaction=list_classifiers 62 | classifiers=[ # Optional 63 | # How mature is this project? Common values are 64 | # 3 - Alpha 65 | # 4 - Beta 66 | # 5 - Production/Stable 67 | 'Development Status :: 3 - Alpha', 68 | 69 | # Indicate who your project is intended for 70 | 'Intended Audience :: Users' 71 | 72 | # Pick your license as you wish 73 | 'License :: OSI Approved :: MIT License', 74 | 75 | # Specify the Python versions you support here. In particular, ensure 76 | # that you indicate whether you support Python 2, Python 3 or both. 77 | 'Programming Language :: Python :: 3.6', 78 | ], 79 | 80 | # This field adds keywords for your project which will appear on the 81 | # project page. What does your project relate to? 82 | # 83 | # Note that this is a string of words separated by whitespace, not a list. 84 | keywords='bitcoin payment protocol', # Optional 85 | 86 | # You can just specify package directories manually here if your project is 87 | # simple. Or you can use find_packages(). 88 | # 89 | # Alternatively, if you just want to distribute a single Python file, use 90 | # the `py_modules` argument instead as follows, which will expect a file 91 | # called `my_module.py` to exist: 92 | # 93 | # py_modules=["my_module"], 94 | # 95 | packages=find_packages(exclude=['contrib', 'docs', 'tests']), # Required 96 | 97 | # This field lists other packages that your project depends on to run. 98 | # Any package you put here will be installed by pip when your project is 99 | # installed, so they must be valid existing projects. 100 | # 101 | # For an analysis of "install_requires" vs pip's requirements files see: 102 | # https://packaging.python.org/en/latest/requirements.html 103 | install_requires=[ 104 | 'requests', 105 | 'ecdsa', 106 | 'protobuf', 107 | 'pyqt5', 108 | ], # Optional 109 | ) -------------------------------------------------------------------------------- /segwit_addr.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017 Pieter Wuille 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to deal 5 | # in the Software without restriction, including without limitation the rights 6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | # copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | # THE SOFTWARE. 20 | 21 | """Reference implementation for Bech32 and segwit addresses.""" 22 | 23 | 24 | CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" 25 | 26 | 27 | def bech32_polymod(values): 28 | """Internal function that computes the Bech32 checksum.""" 29 | generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3] 30 | chk = 1 31 | for value in values: 32 | top = chk >> 25 33 | chk = (chk & 0x1ffffff) << 5 ^ value 34 | for i in range(5): 35 | chk ^= generator[i] if ((top >> i) & 1) else 0 36 | return chk 37 | 38 | 39 | def bech32_hrp_expand(hrp): 40 | """Expand the HRP into values for checksum computation.""" 41 | return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp] 42 | 43 | 44 | def bech32_verify_checksum(hrp, data): 45 | """Verify a checksum given HRP and converted data characters.""" 46 | return bech32_polymod(bech32_hrp_expand(hrp) + data) == 1 47 | 48 | 49 | def bech32_create_checksum(hrp, data): 50 | """Compute the checksum values given HRP and data.""" 51 | values = bech32_hrp_expand(hrp) + data 52 | polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ 1 53 | return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)] 54 | 55 | 56 | def bech32_encode(hrp, data): 57 | """Compute a Bech32 string given HRP and data values.""" 58 | combined = data + bech32_create_checksum(hrp, data) 59 | return hrp + '1' + ''.join([CHARSET[d] for d in combined]) 60 | 61 | 62 | def bech32_decode(bech): 63 | """Validate a Bech32 string, and determine HRP and data.""" 64 | if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or 65 | (bech.lower() != bech and bech.upper() != bech)): 66 | return (None, None) 67 | bech = bech.lower() 68 | pos = bech.rfind('1') 69 | if pos < 1 or pos + 7 > len(bech) or len(bech) > 90: 70 | return (None, None) 71 | if not all(x in CHARSET for x in bech[pos+1:]): 72 | return (None, None) 73 | hrp = bech[:pos] 74 | data = [CHARSET.find(x) for x in bech[pos+1:]] 75 | if not bech32_verify_checksum(hrp, data): 76 | return (None, None) 77 | return (hrp, data[:-6]) 78 | 79 | 80 | def convertbits(data, frombits, tobits, pad=True): 81 | """General power-of-2 base conversion.""" 82 | acc = 0 83 | bits = 0 84 | ret = [] 85 | maxv = (1 << tobits) - 1 86 | max_acc = (1 << (frombits + tobits - 1)) - 1 87 | for value in data: 88 | if value < 0 or (value >> frombits): 89 | return None 90 | acc = ((acc << frombits) | value) & max_acc 91 | bits += frombits 92 | while bits >= tobits: 93 | bits -= tobits 94 | ret.append((acc >> bits) & maxv) 95 | if pad: 96 | if bits: 97 | ret.append((acc << (tobits - bits)) & maxv) 98 | elif bits >= frombits or ((acc << (tobits - bits)) & maxv): 99 | return None 100 | return ret 101 | 102 | 103 | def decode(hrp, addr): 104 | """Decode a segwit address.""" 105 | hrpgot, data = bech32_decode(addr) 106 | if hrpgot != hrp: 107 | return (None, None) 108 | decoded = convertbits(data[1:], 5, 8, False) 109 | if decoded is None or len(decoded) < 2 or len(decoded) > 40: 110 | return (None, None) 111 | if data[0] > 16: 112 | return (None, None) 113 | if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32: 114 | return (None, None) 115 | return (data[0], decoded) 116 | 117 | 118 | def encode(hrp, witver, witprog): 119 | """Encode a segwit address.""" 120 | ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5)) 121 | assert decode(hrp, ret) is not (None, None) 122 | return ret 123 | -------------------------------------------------------------------------------- /pem.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Electrum - lightweight Bitcoin client 4 | # Copyright (C) 2015 Thomas Voegtlin 5 | # 6 | # Permission is hereby granted, free of charge, to any person 7 | # obtaining a copy of this software and associated documentation files 8 | # (the "Software"), to deal in the Software without restriction, 9 | # including without limitation the rights to use, copy, modify, merge, 10 | # publish, distribute, sublicense, and/or sell copies of the Software, 11 | # and to permit persons to whom the Software is furnished to do so, 12 | # subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be 15 | # included in all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | # SOFTWARE. 25 | 26 | 27 | # This module uses code from TLSLlite 28 | # TLSLite Author: Trevor Perrin) 29 | 30 | 31 | import binascii 32 | 33 | from x509 import ASN1_Node, bytestr_to_int, decode_OID 34 | 35 | 36 | def a2b_base64(s): 37 | try: 38 | b = bytearray(binascii.a2b_base64(s)) 39 | except Exception as e: 40 | raise SyntaxError("base64 error: %s" % e) 41 | return b 42 | 43 | def b2a_base64(b): 44 | return binascii.b2a_base64(b) 45 | 46 | 47 | def dePem(s, name): 48 | """Decode a PEM string into a bytearray of its payload. 49 | 50 | The input must contain an appropriate PEM prefix and postfix 51 | based on the input name string, e.g. for name="CERTIFICATE": 52 | 53 | -----BEGIN CERTIFICATE----- 54 | MIIBXDCCAUSgAwIBAgIBADANBgkqhkiG9w0BAQUFADAPMQ0wCwYDVQQDEwRUQUNL 55 | ... 56 | KoZIhvcNAQEFBQADAwA5kw== 57 | -----END CERTIFICATE----- 58 | 59 | The first such PEM block in the input will be found, and its 60 | payload will be base64 decoded and returned. 61 | """ 62 | prefix = "-----BEGIN %s-----" % name 63 | postfix = "-----END %s-----" % name 64 | start = s.find(prefix) 65 | if start == -1: 66 | raise SyntaxError("Missing PEM prefix") 67 | end = s.find(postfix, start+len(prefix)) 68 | if end == -1: 69 | raise SyntaxError("Missing PEM postfix") 70 | s = s[start+len("-----BEGIN %s-----" % name) : end] 71 | retBytes = a2b_base64(s) # May raise SyntaxError 72 | return retBytes 73 | 74 | def dePemList(s, name): 75 | """Decode a sequence of PEM blocks into a list of bytearrays. 76 | 77 | The input must contain any number of PEM blocks, each with the appropriate 78 | PEM prefix and postfix based on the input name string, e.g. for 79 | name="TACK BREAK SIG". Arbitrary text can appear between and before and 80 | after the PEM blocks. For example: 81 | 82 | " Created by TACK.py 0.9.3 Created at 2012-02-01T00:30:10Z -----BEGIN TACK 83 | BREAK SIG----- 84 | ATKhrz5C6JHJW8BF5fLVrnQss6JnWVyEaC0p89LNhKPswvcC9/s6+vWLd9snYTUv 85 | YMEBdw69PUP8JB4AdqA3K6Ap0Fgd9SSTOECeAKOUAym8zcYaXUwpk0+WuPYa7Zmm 86 | SkbOlK4ywqt+amhWbg9txSGUwFO5tWUHT3QrnRlE/e3PeNFXLx5Bckg= -----END TACK 87 | BREAK SIG----- Created by TACK.py 0.9.3 Created at 2012-02-01T00:30:11Z 88 | -----BEGIN TACK BREAK SIG----- 89 | ATKhrz5C6JHJW8BF5fLVrnQss6JnWVyEaC0p89LNhKPswvcC9/s6+vWLd9snYTUv 90 | YMEBdw69PUP8JB4AdqA3K6BVCWfcjN36lx6JwxmZQncS6sww7DecFO/qjSePCxwM 91 | +kdDqX/9/183nmjx6bf0ewhPXkA0nVXsDYZaydN8rJU1GaMlnjcIYxY= -----END TACK 92 | BREAK SIG----- " 93 | 94 | All such PEM blocks will be found, decoded, and return in an ordered list 95 | of bytearrays, which may have zero elements if not PEM blocks are found. 96 | """ 97 | bList = [] 98 | prefix = "-----BEGIN %s-----" % name 99 | postfix = "-----END %s-----" % name 100 | while 1: 101 | start = s.find(prefix) 102 | if start == -1: 103 | return bList 104 | end = s.find(postfix, start+len(prefix)) 105 | if end == -1: 106 | raise SyntaxError("Missing PEM postfix") 107 | s2 = s[start+len(prefix) : end] 108 | retBytes = a2b_base64(s2) # May raise SyntaxError 109 | bList.append(retBytes) 110 | s = s[end+len(postfix) : ] 111 | 112 | def pem(b, name): 113 | """Encode a payload bytearray into a PEM string. 114 | 115 | The input will be base64 encoded, then wrapped in a PEM prefix/postfix 116 | based on the name string, e.g. for name="CERTIFICATE": 117 | 118 | -----BEGIN CERTIFICATE----- 119 | MIIBXDCCAUSgAwIBAgIBADANBgkqhkiG9w0BAQUFADAPMQ0wCwYDVQQDEwRUQUNL 120 | ... 121 | KoZIhvcNAQEFBQADAwA5kw== 122 | -----END CERTIFICATE----- 123 | """ 124 | s1 = b2a_base64(b)[:-1] # remove terminating \n 125 | s2 = b"" 126 | while s1: 127 | s2 += s1[:64] + b"\n" 128 | s1 = s1[64:] 129 | s = ("-----BEGIN %s-----\n" % name).encode('ascii') + s2 + \ 130 | ("-----END %s-----\n" % name).encode('ascii') 131 | return s 132 | 133 | def pemSniff(inStr, name): 134 | searchStr = "-----BEGIN %s-----" % name 135 | return searchStr in inStr 136 | 137 | 138 | def parse_private_key(s): 139 | """Parse a string containing a PEM-encoded .""" 140 | if pemSniff(s, "PRIVATE KEY"): 141 | bytes = dePem(s, "PRIVATE KEY") 142 | return _parsePKCS8(bytes) 143 | elif pemSniff(s, "RSA PRIVATE KEY"): 144 | bytes = dePem(s, "RSA PRIVATE KEY") 145 | return _parseSSLeay(bytes) 146 | else: 147 | raise SyntaxError("Not a PEM private key file") 148 | 149 | 150 | def _parsePKCS8(_bytes): 151 | s = ASN1_Node(_bytes) 152 | root = s.root() 153 | version_node = s.first_child(root) 154 | version = bytestr_to_int(s.get_value_of_type(version_node, 'INTEGER')) 155 | if version != 0: 156 | raise SyntaxError("Unrecognized PKCS8 version") 157 | rsaOID_node = s.next_node(version_node) 158 | ii = s.first_child(rsaOID_node) 159 | rsaOID = decode_OID(s.get_value_of_type(ii, 'OBJECT IDENTIFIER')) 160 | if rsaOID != '1.2.840.113549.1.1.1': 161 | raise SyntaxError("Unrecognized AlgorithmIdentifier") 162 | privkey_node = s.next_node(rsaOID_node) 163 | value = s.get_value_of_type(privkey_node, 'OCTET STRING') 164 | return _parseASN1PrivateKey(value) 165 | 166 | 167 | def _parseSSLeay(bytes): 168 | return _parseASN1PrivateKey(ASN1_Node(str(bytes))) 169 | 170 | 171 | def bytesToNumber(s): 172 | return int(binascii.hexlify(s), 16) 173 | 174 | 175 | def _parseASN1PrivateKey(s): 176 | s = ASN1_Node(s) 177 | root = s.root() 178 | version_node = s.first_child(root) 179 | version = bytestr_to_int(s.get_value_of_type(version_node, 'INTEGER')) 180 | if version != 0: 181 | raise SyntaxError("Unrecognized RSAPrivateKey version") 182 | n = s.next_node(version_node) 183 | e = s.next_node(n) 184 | d = s.next_node(e) 185 | p = s.next_node(d) 186 | q = s.next_node(p) 187 | dP = s.next_node(q) 188 | dQ = s.next_node(dP) 189 | qInv = s.next_node(dQ) 190 | return list(map(lambda x: bytesToNumber(s.get_value_of_type(x, 'INTEGER')), [n, e, d, p, q, dP, dQ, qInv])) 191 | 192 | -------------------------------------------------------------------------------- /gui.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (C) 2017 Andrew Chow 4 | 5 | import util 6 | 7 | import sys 8 | from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLineEdit, QMessageBox, QInputDialog, QGroupBox, QHBoxLayout, QVBoxLayout, QGridLayout, QLabel 9 | from PyQt5.QtGui import QIcon 10 | from PyQt5.QtCore import pyqtSlot, Qt 11 | 12 | class App(QWidget): 13 | 14 | def __init__(self): 15 | super().__init__() 16 | self.title = 'Bitcoin Payment Protocol Interface' 17 | self.left = 10 18 | self.top = 10 19 | self.width = 700 20 | self.height = 500 21 | self.initUI() 22 | 23 | def initUI(self): 24 | self.setWindowTitle(self.title) 25 | self.setGeometry(self.left, self.top, self.width, self.height) 26 | 27 | self.uri_box = QLineEdit(self) 28 | go_button = QPushButton('Go!', self) 29 | go_button.clicked.connect(self.handle_entered_uri) 30 | 31 | self.main_box = QGroupBox("Bitcoin Payment Protocol Interface") 32 | main_layout = QGridLayout() 33 | main_layout.addWidget(QLabel("Bitcoin URI:"), 0, 0) 34 | main_layout.addWidget(self.uri_box, 0, 1) 35 | main_layout.addWidget(go_button, 0, 2) 36 | 37 | self.payment_data_box = QGroupBox() 38 | main_layout.addWidget(self.payment_data_box, 1, 1) 39 | 40 | self.main_box.setLayout(main_layout) 41 | 42 | windowLayout = QVBoxLayout() 43 | windowLayout.addWidget(self.main_box) 44 | self.setLayout(windowLayout) 45 | 46 | self.show() 47 | 48 | def display_pr(self, pr): 49 | if pr.error: 50 | print(pr.error) 51 | exit() 52 | else: 53 | pr.verify() 54 | self.payment_data_box.setTitle("Payment Request Data") 55 | pr_data_layout = QGridLayout() 56 | 57 | pr_data_layout.addWidget(QLabel("Network:"), 0, 0) 58 | network_lbl = QLabel(pr.details.network) 59 | network_lbl.setTextInteractionFlags(Qt.TextSelectableByMouse) 60 | pr_data_layout.addWidget(network_lbl, 0, 1) 61 | 62 | pr_data_layout.addWidget(QLabel("Requestor:"), 1, 0) 63 | requestor_lbl = QLabel(pr.get_requestor()) 64 | requestor_lbl.setTextInteractionFlags(Qt.TextSelectableByMouse) 65 | pr_data_layout.addWidget(requestor_lbl, 1, 1) 66 | 67 | pr_data_layout.addWidget(QLabel("Memo:"), 2, 0) 68 | memo_lbl = QLabel(pr.get_memo()) 69 | memo_lbl.setTextInteractionFlags(Qt.TextSelectableByMouse) 70 | pr_data_layout.addWidget(memo_lbl, 2, 1) 71 | 72 | pr_data_layout.addWidget(QLabel("Expiration:"), 3, 0) 73 | expire_lbl = QLabel(util.format_time(pr.get_expiration_date())) 74 | expire_lbl.setTextInteractionFlags(Qt.TextSelectableByMouse) 75 | pr_data_layout.addWidget(expire_lbl, 3, 1) 76 | 77 | pr_data_layout.addWidget(QLabel("Creation Time:"), 4, 0) 78 | creation_lbl = QLabel(util.format_time(pr.details.time)) 79 | creation_lbl.setTextInteractionFlags(Qt.TextSelectableByMouse) 80 | pr_data_layout.addWidget(creation_lbl, 4, 1) 81 | 82 | pr_data_layout.addWidget(QLabel("Verification status:"), 5, 0) 83 | verification_lbl = QLabel(pr.get_verify_status()) 84 | verification_lbl.setTextInteractionFlags(Qt.TextSelectableByMouse) 85 | pr_data_layout.addWidget(verification_lbl, 5, 1) 86 | 87 | pr_data_layout.addWidget(QLabel("Merchant Data:"), 6, 0) 88 | merch_lbl = QLabel(str(pr.details.merchant_data)) 89 | merch_lbl.setTextInteractionFlags(Qt.TextSelectableByMouse) 90 | pr_data_layout.addWidget(merch_lbl, 6, 1) 91 | 92 | pr_data_layout.addWidget(QLabel("Outputs:"), 7, 0) 93 | i = 0 94 | for out in pr.get_outputs(): 95 | type_lbl = QLabel() 96 | if out[0] == util.TYPE_ADDRESS: 97 | pr_data_layout.addWidget(QLabel(" Type:"), 8 + i, 0) 98 | type_lbl.setText("Address") 99 | pr_data_layout.addWidget(QLabel(" Address:"), 8 + i + 1, 0) 100 | elif out[0] == util.TYPE_PUBKEY: 101 | pr_data_layout.addWidget(QLabel(" Type:"), 8 + i, 0) 102 | type_lbl.setText("Public Key") 103 | pr_data_layout.addWidget(QLabel(" Public Key:"), 8 + i + 1, 0) 104 | elif out[0] == util.TYPE_SCRIPT: 105 | pr_data_layout.addWidget(QLabel(" Type:"), 8 + i, 0) 106 | type_lbl.setText("Script") 107 | pr_data_layout.addWidget(QLabel(" Script:"), 8 + i + 1, 0) 108 | else: 109 | pr_data_layout.addWidget(QLabel(" Type:"), 8 + i, 0) 110 | type_lbl.setText("Unknown") 111 | pr_data_layout.addWidget(QLabel(" Data:"), 8 + i + 1, 0) 112 | 113 | type_lbl.setTextInteractionFlags(Qt.TextSelectableByMouse) 114 | pr_data_layout.addWidget(type_lbl, 8 + i, 1) 115 | 116 | data_lbl = QLabel(out[1]) 117 | data_lbl.setTextInteractionFlags(Qt.TextSelectableByMouse) 118 | pr_data_layout.addWidget(data_lbl, 8 + i + 1, 1) 119 | 120 | amt_lbl = QLabel(util.format_satoshis(out[2]) + " BTC") 121 | amt_lbl.setTextInteractionFlags(Qt.TextSelectableByMouse) 122 | pr_data_layout.addWidget(QLabel(" Amount:"), 8 + i + 2, 0) 123 | pr_data_layout.addWidget(amt_lbl, 8 + i + 2, 1) 124 | 125 | i += 3 126 | next_button = QPushButton("Next") 127 | next_button.clicked.connect(self.make_further_instructions(pr)) 128 | pr_data_layout.addWidget(next_button, 8 + i, 0) 129 | self.payment_data_box.setLayout(pr_data_layout) 130 | 131 | @pyqtSlot() 132 | def handle_entered_uri(self): 133 | uri = self.uri_box.text().strip() 134 | util.parse_URI(uri, self.display_pr) 135 | 136 | def make_further_instructions(self, pr): 137 | def further_instructions(): 138 | response = QMessageBox.information(self, "Next Step", "To continue, send the necessary amounts of Bitcoin to the addresses specified in the 'Outputs' field above. Once broadcast, press Yes to Continue or Cancel to quit.", QMessageBox.Cancel | QMessageBox.Yes, QMessageBox.Cancel) 139 | if response == QMessageBox.Cancel: 140 | sys.exit() 141 | elif response == QMessageBox.Yes: 142 | if pr.details.payment_url: 143 | raw_tx, okPressed1 = QInputDialog.getText(self, "Enter Raw Transaction","Enter the hex of the transaction that was just made:", QLineEdit.Normal, "") 144 | if okPressed1 and raw_tx != '': 145 | ref_addr, okPressed2 = QInputDialog.getText(self, "Enter Refund Address","Enter a refund address:", QLineEdit.Normal, "") 146 | if okPressed2 and ref_addr != '': 147 | try: 148 | result = pr.send_ack(raw_tx.strip(), ref_addr.strip()) 149 | if result[0]: 150 | QMessageBox.information(self, "Complete!", "Payment request successful: " + result[1] + "\n\nClick Ok to exit", QMessageBox.Ok, QMessageBox.Ok) 151 | sys.exit() 152 | else: 153 | QMessageBox.error(self, "Error!", "Payment request was not successful: " + result[1] + "\n\nClick Ok to exit", QMessageBox.Ok, QMessageBox.Ok) 154 | sys.exit() 155 | except: 156 | QMessageBox.error(self, "Error!", "There was an error parsing the raw transaction or address. Please restart and try again.\n\nClick Ok to exit", QMessageBox.Ok, QMessageBox.Ok) 157 | sys.exit() 158 | 159 | return further_instructions 160 | 161 | if __name__ == '__main__': 162 | app = QApplication(sys.argv) 163 | ex = App() 164 | sys.exit(app.exec_()) 165 | -------------------------------------------------------------------------------- /x509.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Modified from Electrum 4 | # Copyright (C) 2017 Andrew Chow 5 | # 6 | # Electrum - lightweight Bitcoin client 7 | # Copyright (C) 2014 Thomas Voegtlin 8 | # 9 | # Permission is hereby granted, free of charge, to any person 10 | # obtaining a copy of this software and associated documentation files 11 | # (the "Software"), to deal in the Software without restriction, 12 | # including without limitation the rights to use, copy, modify, merge, 13 | # publish, distribute, sublicense, and/or sell copies of the Software, 14 | # and to permit persons to whom the Software is furnished to do so, 15 | # subject to the following conditions: 16 | # 17 | # The above copyright notice and this permission notice shall be 18 | # included in all copies or substantial portions of the Software. 19 | # 20 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 22 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 24 | # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 25 | # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 26 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | # SOFTWARE. 28 | import util 29 | from util import bh2u 30 | import ecdsa 31 | import hashlib 32 | 33 | # algo OIDs 34 | ALGO_RSA_SHA1 = '1.2.840.113549.1.1.5' 35 | ALGO_RSA_SHA256 = '1.2.840.113549.1.1.11' 36 | ALGO_RSA_SHA384 = '1.2.840.113549.1.1.12' 37 | ALGO_RSA_SHA512 = '1.2.840.113549.1.1.13' 38 | ALGO_ECDSA_SHA256 = '1.2.840.10045.4.3.2' 39 | 40 | # prefixes, see http://stackoverflow.com/questions/3713774/c-sharp-how-to-calculate-asn-1-der-encoding-of-a-particular-hash-algorithm 41 | PREFIX_RSA_SHA256 = bytearray( 42 | [0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20]) 43 | PREFIX_RSA_SHA384 = bytearray( 44 | [0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30]) 45 | PREFIX_RSA_SHA512 = bytearray( 46 | [0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40]) 47 | 48 | # types used in ASN1 structured data 49 | ASN1_TYPES = { 50 | 'BOOLEAN' : 0x01, 51 | 'INTEGER' : 0x02, 52 | 'BIT STRING' : 0x03, 53 | 'OCTET STRING' : 0x04, 54 | 'NULL' : 0x05, 55 | 'OBJECT IDENTIFIER': 0x06, 56 | 'SEQUENCE' : 0x70, 57 | 'SET' : 0x71, 58 | 'PrintableString' : 0x13, 59 | 'IA5String' : 0x16, 60 | 'UTCTime' : 0x17, 61 | 'GeneralizedTime' : 0x18, 62 | 'ENUMERATED' : 0x0A, 63 | 'UTF8String' : 0x0C, 64 | } 65 | 66 | 67 | class CertificateError(Exception): 68 | pass 69 | 70 | 71 | # helper functions 72 | def bitstr_to_bytestr(s): 73 | if s[0] != 0x00: 74 | raise TypeError('no padding') 75 | return s[1:] 76 | 77 | 78 | def bytestr_to_int(s): 79 | i = 0 80 | for char in s: 81 | i <<= 8 82 | i |= char 83 | return i 84 | 85 | 86 | def decode_OID(s): 87 | r = [] 88 | r.append(s[0] // 40) 89 | r.append(s[0] % 40) 90 | k = 0 91 | for i in s[1:]: 92 | if i < 128: 93 | r.append(i + 128 * k) 94 | k = 0 95 | else: 96 | k = (i - 128) + 128 * k 97 | return '.'.join(map(str, r)) 98 | 99 | 100 | def encode_OID(oid): 101 | x = [int(i) for i in oid.split('.')] 102 | s = chr(x[0] * 40 + x[1]) 103 | for i in x[2:]: 104 | ss = chr(i % 128) 105 | while i > 128: 106 | i //= 128 107 | ss = chr(128 + i % 128) + ss 108 | s += ss 109 | return s 110 | 111 | 112 | class ASN1_Node(bytes): 113 | def get_node(self, ix): 114 | # return index of first byte, first content byte and last byte. 115 | first = self[ix + 1] 116 | if (first & 0x80) == 0: 117 | length = first 118 | ixf = ix + 2 119 | ixl = ixf + length - 1 120 | else: 121 | lengthbytes = first & 0x7F 122 | length = bytestr_to_int(self[ix + 2:ix + 2 + lengthbytes]) 123 | ixf = ix + 2 + lengthbytes 124 | ixl = ixf + length - 1 125 | return ix, ixf, ixl 126 | 127 | def root(self): 128 | return self.get_node(0) 129 | 130 | def next_node(self, node): 131 | ixs, ixf, ixl = node 132 | return self.get_node(ixl + 1) 133 | 134 | def first_child(self, node): 135 | ixs, ixf, ixl = node 136 | if self[ixs] & 0x20 != 0x20: 137 | raise TypeError('Can only open constructed types.', hex(self[ixs])) 138 | return self.get_node(ixf) 139 | 140 | def is_child_of(node1, node2): 141 | ixs, ixf, ixl = node1 142 | jxs, jxf, jxl = node2 143 | return ((ixf <= jxs) and (jxl <= ixl)) or ((jxf <= ixs) and (ixl <= jxl)) 144 | 145 | def get_all(self, node): 146 | # return type + length + value 147 | ixs, ixf, ixl = node 148 | return self[ixs:ixl + 1] 149 | 150 | def get_value_of_type(self, node, asn1_type): 151 | # verify type byte and return content 152 | ixs, ixf, ixl = node 153 | if ASN1_TYPES[asn1_type] != self[ixs]: 154 | raise TypeError('Wrong type:', hex(self[ixs]), hex(ASN1_TYPES[asn1_type])) 155 | return self[ixf:ixl + 1] 156 | 157 | def get_value(self, node): 158 | ixs, ixf, ixl = node 159 | return self[ixf:ixl + 1] 160 | 161 | def get_children(self, node): 162 | nodes = [] 163 | ii = self.first_child(node) 164 | nodes.append(ii) 165 | while ii[2] < node[2]: 166 | ii = self.next_node(ii) 167 | nodes.append(ii) 168 | return nodes 169 | 170 | def get_sequence(self): 171 | return list(map(lambda j: self.get_value(j), self.get_children(self.root()))) 172 | 173 | def get_dict(self, node): 174 | p = {} 175 | for ii in self.get_children(node): 176 | for iii in self.get_children(ii): 177 | iiii = self.first_child(iii) 178 | oid = decode_OID(self.get_value_of_type(iiii, 'OBJECT IDENTIFIER')) 179 | iiii = self.next_node(iiii) 180 | value = self.get_value(iiii) 181 | p[oid] = value 182 | return p 183 | 184 | 185 | class X509(object): 186 | def __init__(self, b): 187 | 188 | self.bytes = bytearray(b) 189 | 190 | der = ASN1_Node(b) 191 | root = der.root() 192 | cert = der.first_child(root) 193 | # data for signature 194 | self.data = der.get_all(cert) 195 | 196 | # optional version field 197 | if der.get_value(cert)[0] == 0xa0: 198 | version = der.first_child(cert) 199 | serial_number = der.next_node(version) 200 | else: 201 | serial_number = der.first_child(cert) 202 | self.serial_number = bytestr_to_int(der.get_value_of_type(serial_number, 'INTEGER')) 203 | 204 | # signature algorithm 205 | sig_algo = der.next_node(serial_number) 206 | ii = der.first_child(sig_algo) 207 | self.sig_algo = decode_OID(der.get_value_of_type(ii, 'OBJECT IDENTIFIER')) 208 | 209 | # issuer 210 | issuer = der.next_node(sig_algo) 211 | self.issuer = der.get_dict(issuer) 212 | 213 | # validity 214 | validity = der.next_node(issuer) 215 | ii = der.first_child(validity) 216 | try: 217 | self.notBefore = der.get_value_of_type(ii, 'UTCTime') 218 | except TypeError: 219 | self.notBefore = der.get_value_of_type(ii, 'GeneralizedTime')[2:] # strip year 220 | ii = der.next_node(ii) 221 | try: 222 | self.notAfter = der.get_value_of_type(ii, 'UTCTime') 223 | except TypeError: 224 | self.notAfter = der.get_value_of_type(ii, 'GeneralizedTime')[2:] # strip year 225 | 226 | # subject 227 | subject = der.next_node(validity) 228 | self.subject = der.get_dict(subject) 229 | subject_pki = der.next_node(subject) 230 | public_key_algo = der.first_child(subject_pki) 231 | ii = der.first_child(public_key_algo) 232 | self.public_key_algo = decode_OID(der.get_value_of_type(ii, 'OBJECT IDENTIFIER')) 233 | 234 | if self.public_key_algo != '1.2.840.10045.2.1': # for non EC public key 235 | # pubkey modulus and exponent 236 | subject_public_key = der.next_node(public_key_algo) 237 | spk = der.get_value_of_type(subject_public_key, 'BIT STRING') 238 | spk = ASN1_Node(bitstr_to_bytestr(spk)) 239 | r = spk.root() 240 | modulus = spk.first_child(r) 241 | exponent = spk.next_node(modulus) 242 | rsa_n = spk.get_value_of_type(modulus, 'INTEGER') 243 | rsa_e = spk.get_value_of_type(exponent, 'INTEGER') 244 | self.modulus = ecdsa.util.string_to_number(rsa_n) 245 | self.exponent = ecdsa.util.string_to_number(rsa_e) 246 | else: 247 | subject_public_key = der.next_node(public_key_algo) 248 | spk = der.get_value_of_type(subject_public_key, 'BIT STRING') 249 | self.ec_public_key = spk 250 | 251 | # extensions 252 | self.CA = False 253 | self.AKI = None 254 | self.SKI = None 255 | i = subject_pki 256 | while i[2] < cert[2]: 257 | i = der.next_node(i) 258 | d = der.get_dict(i) 259 | for oid, value in d.items(): 260 | value = ASN1_Node(value) 261 | if oid == '2.5.29.19': 262 | # Basic Constraints 263 | self.CA = bool(value) 264 | elif oid == '2.5.29.14': 265 | # Subject Key Identifier 266 | r = value.root() 267 | value = value.get_value_of_type(r, 'OCTET STRING') 268 | self.SKI = bh2u(value) 269 | elif oid == '2.5.29.35': 270 | # Authority Key Identifier 271 | self.AKI = bh2u(value.get_sequence()[0]) 272 | else: 273 | pass 274 | 275 | # cert signature 276 | cert_sig_algo = der.next_node(cert) 277 | ii = der.first_child(cert_sig_algo) 278 | self.cert_sig_algo = decode_OID(der.get_value_of_type(ii, 'OBJECT IDENTIFIER')) 279 | cert_sig = der.next_node(cert_sig_algo) 280 | self.signature = der.get_value(cert_sig)[1:] 281 | 282 | def get_keyID(self): 283 | # http://security.stackexchange.com/questions/72077/validating-an-ssl-certificate-chain-according-to-rfc-5280-am-i-understanding-th 284 | return self.SKI if self.SKI else repr(self.subject) 285 | 286 | def get_issuer_keyID(self): 287 | return self.AKI if self.AKI else repr(self.issuer) 288 | 289 | def get_common_name(self): 290 | return self.subject.get('2.5.4.3', 'unknown'.encode()).decode() 291 | 292 | def get_signature(self): 293 | return self.cert_sig_algo, self.signature, self.data 294 | 295 | def check_ca(self): 296 | return self.CA 297 | 298 | def check_date(self): 299 | import time 300 | now = time.time() 301 | TIMESTAMP_FMT = '%y%m%d%H%M%SZ' 302 | not_before = time.mktime(time.strptime(self.notBefore.decode('ascii'), TIMESTAMP_FMT)) 303 | not_after = time.mktime(time.strptime(self.notAfter.decode('ascii'), TIMESTAMP_FMT)) 304 | if not_before > now: 305 | raise CertificateError('Certificate has not entered its valid date range. (%s)' % self.get_common_name()) 306 | if not_after <= now: 307 | raise CertificateError('Certificate has expired. (%s)' % self.get_common_name()) 308 | 309 | def getFingerprint(self): 310 | return hashlib.sha1(self.bytes).digest() 311 | 312 | def load_certificates(ca_path): 313 | import pem 314 | ca_list = {} 315 | ca_keyID = {} 316 | # ca_path = '/tmp/tmp.txt' 317 | with open(ca_path, 'r') as f: 318 | s = f.read() 319 | bList = pem.dePemList(s, "CERTIFICATE") 320 | for b in bList: 321 | try: 322 | x = X509(b) 323 | x.check_date() 324 | except BaseException as e: 325 | # with open('/tmp/tmp.txt', 'w') as f: 326 | # f.write(pem.pem(b, 'CERTIFICATE').decode('ascii')) 327 | util.print_error("cert error:", e) 328 | continue 329 | 330 | fp = x.getFingerprint() 331 | ca_list[fp] = x 332 | ca_keyID[x.get_keyID()] = fp 333 | 334 | return ca_list, ca_keyID 335 | 336 | 337 | if __name__ == "__main__": 338 | import requests 339 | 340 | ca_path = requests.certs.where() 341 | ca_list, ca_keyID = load_certificates(ca_path) 342 | -------------------------------------------------------------------------------- /paymentrequest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Electrum - lightweight Bitcoin client 4 | # Copyright (C) 2014 Thomas Voegtlin 5 | # 6 | # Permission is hereby granted, free of charge, to any person 7 | # obtaining a copy of this software and associated documentation files 8 | # (the "Software"), to deal in the Software without restriction, 9 | # including without limitation the rights to use, copy, modify, merge, 10 | # publish, distribute, sublicense, and/or sell copies of the Software, 11 | # and to permit persons to whom the Software is furnished to do so, 12 | # subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be 15 | # included in all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | # SOFTWARE. 25 | import hashlib 26 | import sys 27 | import time 28 | import traceback 29 | import json 30 | import requests 31 | 32 | import urllib.parse 33 | 34 | try: 35 | import paymentrequest_pb2 as pb2 36 | except ImportError: 37 | sys.exit("Error: could not find paymentrequest_pb2.py. Create it with 'protoc --proto_path=./ --python_out=./ paymentrequest.proto'") 38 | 39 | import util 40 | from util import print_error, bh2u, bfh, TYPE_ADDRESS, NetworkConstants 41 | import x509 42 | import rsakey 43 | 44 | REQUEST_HEADERS = {'Accept': 'application/bitcoin-paymentrequest', 'User-Agent': 'Payment Protocol Interface'} 45 | ACK_HEADERS = {'Content-Type':'application/bitcoin-payment','Accept':'application/bitcoin-paymentack','User-Agent':'Payment Protocol Interface'} 46 | 47 | ca_path = requests.certs.where() 48 | ca_list = None 49 | ca_keyID = None 50 | 51 | def load_ca_list(): 52 | global ca_list, ca_keyID 53 | if ca_list is None: 54 | ca_list, ca_keyID = x509.load_certificates(ca_path) 55 | 56 | 57 | 58 | # status of payment requests 59 | PR_UNPAID = 0 60 | PR_EXPIRED = 1 61 | PR_UNKNOWN = 2 # sent but not propagated 62 | PR_PAID = 3 # send and propagated 63 | 64 | 65 | 66 | def get_payment_request(url): 67 | u = urllib.parse.urlparse(url) 68 | error = None 69 | if u.scheme in ['http', 'https']: 70 | try: 71 | response = requests.request('GET', url, headers=REQUEST_HEADERS) 72 | response.raise_for_status() 73 | # Guard against `bitcoin:`-URIs with invalid payment request URLs 74 | if "Content-Type" not in response.headers \ 75 | or response.headers["Content-Type"] != "application/bitcoin-paymentrequest": 76 | data = None 77 | error = "payment URL not pointing to a payment request handling server" 78 | else: 79 | data = response.content 80 | print_error('fetched payment request', url, len(response.content)) 81 | except requests.exceptions.RequestException: 82 | data = None 83 | error = "payment URL not pointing to a valid server" 84 | elif u.scheme == 'file': 85 | try: 86 | with open(u.path, 'r') as f: 87 | data = f.read() 88 | except IOError: 89 | data = None 90 | error = "payment URL not pointing to a valid file" 91 | else: 92 | raise BaseException("unknown scheme", url) 93 | pr = PaymentRequest(data, error) 94 | return pr 95 | 96 | 97 | class PaymentRequest: 98 | 99 | def __init__(self, data, error=None): 100 | self.raw = data 101 | self.error = error 102 | self.parse(data) 103 | self.requestor = None # known after verify 104 | self.tx = None 105 | 106 | def __str__(self): 107 | return self.raw 108 | 109 | def parse(self, r): 110 | if self.error: 111 | return 112 | self.id = bh2u(util.sha256(r)[0:16]) 113 | try: 114 | self.data = pb2.PaymentRequest() 115 | self.data.ParseFromString(r) 116 | except: 117 | self.error = "cannot parse payment request" 118 | return 119 | self.details = pb2.PaymentDetails() 120 | self.details.ParseFromString(self.data.serialized_payment_details) 121 | if self.details.network == 'test': 122 | NetworkConstants.set_testnet() 123 | elif self.details.network == 'main': 124 | NetworkConstants.set_mainnet() 125 | else: 126 | self.error = "unknown network " + self.details.network 127 | return 128 | self.outputs = [] 129 | for o in self.details.outputs: 130 | out_type, addr = util.get_address_from_output_script(o.script) 131 | self.outputs.append((out_type, addr, o.amount)) 132 | self.memo = self.details.memo 133 | self.payment_url = self.details.payment_url 134 | 135 | def is_pr(self): 136 | return self.get_amount() != 0 137 | #return self.get_outputs() != [(TYPE_ADDRESS, self.get_requestor(), self.get_amount())] 138 | 139 | def verify(self): 140 | if self.error: 141 | return False 142 | if not self.raw: 143 | self.error = "Empty request" 144 | return False 145 | pr = pb2.PaymentRequest() 146 | try: 147 | pr.ParseFromString(self.raw) 148 | except: 149 | self.error = "Error: Cannot parse payment request" 150 | return False 151 | if not pr.signature: 152 | # the address will be dispayed as requestor 153 | self.requestor = None 154 | return True 155 | if pr.pki_type in ["x509+sha256", "x509+sha1"]: 156 | return self.verify_x509(pr) 157 | elif pr.pki_type in ["dnssec+btc", "dnssec+ecdsa"]: 158 | return self.verify_dnssec(pr) 159 | else: 160 | self.error = "ERROR: Unsupported PKI Type for Message Signature" 161 | return False 162 | 163 | def verify_x509(self, paymntreq): 164 | load_ca_list() 165 | if not ca_list: 166 | self.error = "Trusted certificate authorities list not found" 167 | return False 168 | cert = pb2.X509Certificates() 169 | cert.ParseFromString(paymntreq.pki_data) 170 | # verify the chain of certificates 171 | try: 172 | x, ca = verify_cert_chain(cert.certificate) 173 | except BaseException as e: 174 | traceback.print_exc(file=sys.stderr) 175 | self.error = str(e) 176 | return False 177 | # get requestor name 178 | self.requestor = x.get_common_name() 179 | if self.requestor.startswith('*.'): 180 | self.requestor = self.requestor[2:] 181 | # verify the BIP70 signature 182 | pubkey0 = rsakey.RSAKey(x.modulus, x.exponent) 183 | sig = paymntreq.signature 184 | paymntreq.signature = b'' 185 | s = paymntreq.SerializeToString() 186 | sigBytes = bytearray(sig) 187 | msgBytes = bytearray(s) 188 | if paymntreq.pki_type == "x509+sha256": 189 | hashBytes = bytearray(hashlib.sha256(msgBytes).digest()) 190 | verify = pubkey0.verify(sigBytes, x509.PREFIX_RSA_SHA256 + hashBytes) 191 | elif paymntreq.pki_type == "x509+sha1": 192 | verify = pubkey0.hashAndVerify(sigBytes, msgBytes) 193 | if not verify: 194 | self.error = "ERROR: Invalid Signature for Payment Request Data" 195 | return False 196 | ### SIG Verified 197 | self.error = 'Signed by Trusted CA: ' + ca.get_common_name() 198 | return True 199 | 200 | def verify_dnssec(self, pr): 201 | sig = pr.signature 202 | alias = pr.pki_data 203 | info = contacts.resolve(alias) 204 | if info.get('validated') is not True: 205 | self.error = "Alias verification failed (DNSSEC)" 206 | return False 207 | if pr.pki_type == "dnssec+btc": 208 | self.requestor = alias 209 | address = info.get('address') 210 | pr.signature = '' 211 | message = pr.SerializeToString() 212 | if bitcoin.verify_message(address, sig, message): 213 | self.error = 'Verified with DNSSEC' 214 | return True 215 | else: 216 | self.error = "verify failed" 217 | return False 218 | else: 219 | self.error = "unknown algo" 220 | return False 221 | 222 | def has_expired(self): 223 | return self.details.expires and self.details.expires < int(time.time()) 224 | 225 | def get_expiration_date(self): 226 | return self.details.expires 227 | 228 | def get_amount(self): 229 | return sum(map(lambda x:x[2], self.outputs)) 230 | 231 | def get_address(self): 232 | o = self.outputs[0] 233 | assert o[0] == TYPE_ADDRESS 234 | return o[1] 235 | 236 | def get_requestor(self): 237 | return self.requestor if self.requestor else self.get_address() 238 | 239 | def get_verify_status(self): 240 | return self.error if self.requestor else "No Signature" 241 | 242 | def get_memo(self): 243 | return self.memo 244 | 245 | def get_dict(self): 246 | return { 247 | 'requestor': self.get_requestor(), 248 | 'memo':self.get_memo(), 249 | 'exp': self.get_expiration_date(), 250 | 'amount': self.get_amount(), 251 | 'signature': self.get_verify_status(), 252 | 'txid': self.tx, 253 | 'outputs': self.get_outputs() 254 | } 255 | 256 | def get_id(self): 257 | return self.id if self.requestor else self.get_address() 258 | 259 | def get_outputs(self): 260 | return self.outputs[:] 261 | 262 | def send_ack(self, raw_tx, refund_addr): 263 | pay_det = self.details 264 | if not self.details.payment_url: 265 | return False, "no url" 266 | paymnt = pb2.Payment() 267 | paymnt.merchant_data = pay_det.merchant_data 268 | paymnt.transactions.append(bfh(raw_tx)) 269 | ref_out = paymnt.refund_to.add() 270 | ref_out.script = util.bfh(util.pay_script(TYPE_ADDRESS, refund_addr)) 271 | paymnt.memo = "Paid with the Bitcoin Payment Protocol Interface" 272 | pm = paymnt.SerializeToString() 273 | payurl = urllib.parse.urlparse(pay_det.payment_url) 274 | try: 275 | r = requests.post(payurl.geturl(), data=pm, headers=ACK_HEADERS, verify=ca_path) 276 | except requests.exceptions.SSLError: 277 | print("Payment Message/PaymentACK verify Failed") 278 | try: 279 | r = requests.post(payurl.geturl(), data=pm, headers=ACK_HEADERS, verify=False) 280 | except Exception as e: 281 | print(e) 282 | return False, "Payment Message/PaymentACK Failed" 283 | if r.status_code >= 500: 284 | return False, r.reason 285 | try: 286 | paymntack = pb2.PaymentACK() 287 | paymntack.ParseFromString(r.content) 288 | except Exception: 289 | return False, "PaymentACK could not be processed. Payment was sent; please manually verify that payment was received." 290 | return True, paymntack.memo 291 | 292 | 293 | def make_unsigned_request(req): 294 | addr = req['address'] 295 | time = req.get('time', 0) 296 | exp = req.get('exp', 0) 297 | if time and type(time) != int: 298 | time = 0 299 | if exp and type(exp) != int: 300 | exp = 0 301 | amount = req['amount'] 302 | if amount is None: 303 | amount = 0 304 | memo = req['memo'] 305 | script = bfh(util.pay_script(TYPE_ADDRESS, addr)) 306 | outputs = [(script, amount)] 307 | pd = pb2.PaymentDetails() 308 | for script, amount in outputs: 309 | pd.outputs.add(amount=amount, script=script) 310 | pd.time = time 311 | pd.expires = time + exp if exp else 0 312 | pd.memo = memo 313 | pr = pb2.PaymentRequest() 314 | pr.serialized_payment_details = pd.SerializeToString() 315 | pr.signature = util.to_bytes('') 316 | return pr 317 | 318 | 319 | def sign_request_with_alias(pr, alias, alias_privkey): 320 | pr.pki_type = 'dnssec+btc' 321 | pr.pki_data = str(alias) 322 | message = pr.SerializeToString() 323 | ec_key = bitcoin.regenerate_key(alias_privkey) 324 | address = bitcoin.address_from_private_key(alias_privkey) 325 | compressed = bitcoin.is_compressed(alias_privkey) 326 | pr.signature = ec_key.sign_message(message, compressed, address) 327 | 328 | 329 | def verify_cert_chain(chain): 330 | """ Verify a chain of certificates. The last certificate is the CA""" 331 | load_ca_list() 332 | # parse the chain 333 | cert_num = len(chain) 334 | x509_chain = [] 335 | for i in range(cert_num): 336 | x = x509.X509(bytearray(chain[i])) 337 | x509_chain.append(x) 338 | if i == 0: 339 | x.check_date() 340 | else: 341 | if not x.check_ca(): 342 | raise BaseException("ERROR: Supplied CA Certificate Error") 343 | if not cert_num > 1: 344 | raise BaseException("ERROR: CA Certificate Chain Not Provided by Payment Processor") 345 | # if the root CA is not supplied, add it to the chain 346 | ca = x509_chain[cert_num-1] 347 | if ca.getFingerprint() not in ca_list: 348 | keyID = ca.get_issuer_keyID() 349 | f = ca_keyID.get(keyID) 350 | if f: 351 | root = ca_list[f] 352 | x509_chain.append(root) 353 | else: 354 | raise BaseException("Supplied CA Not Found in Trusted CA Store.") 355 | # verify the chain of signatures 356 | cert_num = len(x509_chain) 357 | for i in range(1, cert_num): 358 | x = x509_chain[i] 359 | prev_x = x509_chain[i-1] 360 | algo, sig, data = prev_x.get_signature() 361 | sig = bytearray(sig) 362 | pubkey = rsakey.RSAKey(x.modulus, x.exponent) 363 | if algo == x509.ALGO_RSA_SHA1: 364 | verify = pubkey.hashAndVerify(sig, data) 365 | elif algo == x509.ALGO_RSA_SHA256: 366 | hashBytes = bytearray(hashlib.sha256(data).digest()) 367 | verify = pubkey.verify(sig, x509.PREFIX_RSA_SHA256 + hashBytes) 368 | elif algo == x509.ALGO_RSA_SHA384: 369 | hashBytes = bytearray(hashlib.sha384(data).digest()) 370 | verify = pubkey.verify(sig, x509.PREFIX_RSA_SHA384 + hashBytes) 371 | elif algo == x509.ALGO_RSA_SHA512: 372 | hashBytes = bytearray(hashlib.sha512(data).digest()) 373 | verify = pubkey.verify(sig, x509.PREFIX_RSA_SHA512 + hashBytes) 374 | else: 375 | raise BaseException("Algorithm not supported") 376 | util.print_error(self.error, algo.getComponentByName('algorithm')) 377 | if not verify: 378 | raise BaseException("Certificate not Signed by Provided CA Certificate Chain") 379 | 380 | return x509_chain[0], ca 381 | 382 | 383 | def check_ssl_config(config): 384 | import pem 385 | key_path = config.get('ssl_privkey') 386 | cert_path = config.get('ssl_chain') 387 | with open(key_path, 'r') as f: 388 | params = pem.parse_private_key(f.read()) 389 | with open(cert_path, 'r') as f: 390 | s = f.read() 391 | bList = pem.dePemList(s, "CERTIFICATE") 392 | # verify chain 393 | x, ca = verify_cert_chain(bList) 394 | # verify that privkey and pubkey match 395 | privkey = rsakey.RSAKey(*params) 396 | pubkey = rsakey.RSAKey(x.modulus, x.exponent) 397 | assert x.modulus == params[0] 398 | assert x.exponent == params[1] 399 | # return requestor 400 | requestor = x.get_common_name() 401 | if requestor.startswith('*.'): 402 | requestor = requestor[2:] 403 | return requestor 404 | 405 | def sign_request_with_x509(pr, key_path, cert_path): 406 | import pem 407 | with open(key_path, 'r') as f: 408 | params = pem.parse_private_key(f.read()) 409 | privkey = rsakey.RSAKey(*params) 410 | with open(cert_path, 'r') as f: 411 | s = f.read() 412 | bList = pem.dePemList(s, "CERTIFICATE") 413 | certificates = pb2.X509Certificates() 414 | certificates.certificate.extend(map(bytes, bList)) 415 | pr.pki_type = 'x509+sha256' 416 | pr.pki_data = certificates.SerializeToString() 417 | msgBytes = bytearray(pr.SerializeToString()) 418 | hashBytes = bytearray(hashlib.sha256(msgBytes).digest()) 419 | sig = privkey.sign(x509.PREFIX_RSA_SHA256 + hashBytes) 420 | pr.signature = bytes(sig) 421 | 422 | 423 | def serialize_request(req): 424 | pr = make_unsigned_request(req) 425 | signature = req.get('sig') 426 | requestor = req.get('name') 427 | if requestor and signature: 428 | pr.signature = bfh(signature) 429 | pr.pki_type = 'dnssec+btc' 430 | pr.pki_data = str(requestor) 431 | return pr 432 | 433 | 434 | def make_request(config, req): 435 | pr = make_unsigned_request(req) 436 | key_path = config.get('ssl_privkey') 437 | cert_path = config.get('ssl_chain') 438 | if key_path and cert_path: 439 | sign_request_with_x509(pr, key_path, cert_path) 440 | return pr 441 | -------------------------------------------------------------------------------- /rsakey.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Electrum - lightweight Bitcoin client 4 | # Copyright (C) 2015 Thomas Voegtlin 5 | # 6 | # Permission is hereby granted, free of charge, to any person 7 | # obtaining a copy of this software and associated documentation files 8 | # (the "Software"), to deal in the Software without restriction, 9 | # including without limitation the rights to use, copy, modify, merge, 10 | # publish, distribute, sublicense, and/or sell copies of the Software, 11 | # and to permit persons to whom the Software is furnished to do so, 12 | # subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be 15 | # included in all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 21 | # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 22 | # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | # SOFTWARE. 25 | 26 | # This module uses functions from TLSLite (public domain) 27 | # 28 | # TLSLite Authors: 29 | # Trevor Perrin 30 | # Martin von Loewis - python 3 port 31 | # Yngve Pettersen (ported by Paul Sokolovsky) - TLS 1.2 32 | # 33 | 34 | """Pure-Python RSA implementation.""" 35 | 36 | import os 37 | import math 38 | import hashlib 39 | 40 | from pem import * 41 | 42 | 43 | def SHA1(x): 44 | return hashlib.sha1(x).digest() 45 | 46 | 47 | # ************************************************************************** 48 | # PRNG Functions 49 | # ************************************************************************** 50 | 51 | # Check that os.urandom works 52 | import zlib 53 | length = len(zlib.compress(os.urandom(1000))) 54 | assert(length > 900) 55 | 56 | def getRandomBytes(howMany): 57 | b = bytearray(os.urandom(howMany)) 58 | assert(len(b) == howMany) 59 | return b 60 | 61 | prngName = "os.urandom" 62 | 63 | 64 | # ************************************************************************** 65 | # Converter Functions 66 | # ************************************************************************** 67 | 68 | def bytesToNumber(b): 69 | total = 0 70 | multiplier = 1 71 | for count in range(len(b)-1, -1, -1): 72 | byte = b[count] 73 | total += multiplier * byte 74 | multiplier *= 256 75 | return total 76 | 77 | def numberToByteArray(n, howManyBytes=None): 78 | """Convert an integer into a bytearray, zero-pad to howManyBytes. 79 | 80 | The returned bytearray may be smaller than howManyBytes, but will 81 | not be larger. The returned bytearray will contain a big-endian 82 | encoding of the input integer (n). 83 | """ 84 | if howManyBytes == None: 85 | howManyBytes = numBytes(n) 86 | b = bytearray(howManyBytes) 87 | for count in range(howManyBytes-1, -1, -1): 88 | b[count] = int(n % 256) 89 | n >>= 8 90 | return b 91 | 92 | def mpiToNumber(mpi): #mpi is an openssl-format bignum string 93 | if (ord(mpi[4]) & 0x80) !=0: #Make sure this is a positive number 94 | raise AssertionError() 95 | b = bytearray(mpi[4:]) 96 | return bytesToNumber(b) 97 | 98 | def numberToMPI(n): 99 | b = numberToByteArray(n) 100 | ext = 0 101 | #If the high-order bit is going to be set, 102 | #add an extra byte of zeros 103 | if (numBits(n) & 0x7)==0: 104 | ext = 1 105 | length = numBytes(n) + ext 106 | b = bytearray(4+ext) + b 107 | b[0] = (length >> 24) & 0xFF 108 | b[1] = (length >> 16) & 0xFF 109 | b[2] = (length >> 8) & 0xFF 110 | b[3] = length & 0xFF 111 | return bytes(b) 112 | 113 | 114 | # ************************************************************************** 115 | # Misc. Utility Functions 116 | # ************************************************************************** 117 | 118 | def numBits(n): 119 | if n==0: 120 | return 0 121 | s = "%x" % n 122 | return ((len(s)-1)*4) + \ 123 | {'0':0, '1':1, '2':2, '3':2, 124 | '4':3, '5':3, '6':3, '7':3, 125 | '8':4, '9':4, 'a':4, 'b':4, 126 | 'c':4, 'd':4, 'e':4, 'f':4, 127 | }[s[0]] 128 | return int(math.floor(math.log(n, 2))+1) 129 | 130 | def numBytes(n): 131 | if n==0: 132 | return 0 133 | bits = numBits(n) 134 | return int(math.ceil(bits / 8.0)) 135 | 136 | # ************************************************************************** 137 | # Big Number Math 138 | # ************************************************************************** 139 | 140 | def getRandomNumber(low, high): 141 | if low >= high: 142 | raise AssertionError() 143 | howManyBits = numBits(high) 144 | howManyBytes = numBytes(high) 145 | lastBits = howManyBits % 8 146 | while 1: 147 | bytes = getRandomBytes(howManyBytes) 148 | if lastBits: 149 | bytes[0] = bytes[0] % (1 << lastBits) 150 | n = bytesToNumber(bytes) 151 | if n >= low and n < high: 152 | return n 153 | 154 | def gcd(a,b): 155 | a, b = max(a,b), min(a,b) 156 | while b: 157 | a, b = b, a % b 158 | return a 159 | 160 | def lcm(a, b): 161 | return (a * b) // gcd(a, b) 162 | 163 | #Returns inverse of a mod b, zero if none 164 | #Uses Extended Euclidean Algorithm 165 | def invMod(a, b): 166 | c, d = a, b 167 | uc, ud = 1, 0 168 | while c != 0: 169 | q = d // c 170 | c, d = d-(q*c), c 171 | uc, ud = ud - (q * uc), uc 172 | if d == 1: 173 | return ud % b 174 | return 0 175 | 176 | 177 | def powMod(base, power, modulus): 178 | if power < 0: 179 | result = pow(base, power*-1, modulus) 180 | result = invMod(result, modulus) 181 | return result 182 | else: 183 | return pow(base, power, modulus) 184 | 185 | #Pre-calculate a sieve of the ~100 primes < 1000: 186 | def makeSieve(n): 187 | sieve = list(range(n)) 188 | for count in range(2, int(math.sqrt(n))+1): 189 | if sieve[count] == 0: 190 | continue 191 | x = sieve[count] * 2 192 | while x < len(sieve): 193 | sieve[x] = 0 194 | x += sieve[count] 195 | sieve = [x for x in sieve[2:] if x] 196 | return sieve 197 | 198 | sieve = makeSieve(1000) 199 | 200 | def isPrime(n, iterations=5, display=False): 201 | #Trial division with sieve 202 | for x in sieve: 203 | if x >= n: return True 204 | if n % x == 0: return False 205 | #Passed trial division, proceed to Rabin-Miller 206 | #Rabin-Miller implemented per Ferguson & Schneier 207 | #Compute s, t for Rabin-Miller 208 | if display: print("*", end=' ') 209 | s, t = n-1, 0 210 | while s % 2 == 0: 211 | s, t = s//2, t+1 212 | #Repeat Rabin-Miller x times 213 | a = 2 #Use 2 as a base for first iteration speedup, per HAC 214 | for count in range(iterations): 215 | v = powMod(a, s, n) 216 | if v==1: 217 | continue 218 | i = 0 219 | while v != n-1: 220 | if i == t-1: 221 | return False 222 | else: 223 | v, i = powMod(v, 2, n), i+1 224 | a = getRandomNumber(2, n) 225 | return True 226 | 227 | def getRandomPrime(bits, display=False): 228 | if bits < 10: 229 | raise AssertionError() 230 | #The 1.5 ensures the 2 MSBs are set 231 | #Thus, when used for p,q in RSA, n will have its MSB set 232 | # 233 | #Since 30 is lcm(2,3,5), we'll set our test numbers to 234 | #29 % 30 and keep them there 235 | low = ((2 ** (bits-1)) * 3) // 2 236 | high = 2 ** bits - 30 237 | p = getRandomNumber(low, high) 238 | p += 29 - (p % 30) 239 | while 1: 240 | if display: print(".", end=' ') 241 | p += 30 242 | if p >= high: 243 | p = getRandomNumber(low, high) 244 | p += 29 - (p % 30) 245 | if isPrime(p, display=display): 246 | return p 247 | 248 | #Unused at the moment... 249 | def getRandomSafePrime(bits, display=False): 250 | if bits < 10: 251 | raise AssertionError() 252 | #The 1.5 ensures the 2 MSBs are set 253 | #Thus, when used for p,q in RSA, n will have its MSB set 254 | # 255 | #Since 30 is lcm(2,3,5), we'll set our test numbers to 256 | #29 % 30 and keep them there 257 | low = (2 ** (bits-2)) * 3//2 258 | high = (2 ** (bits-1)) - 30 259 | q = getRandomNumber(low, high) 260 | q += 29 - (q % 30) 261 | while 1: 262 | if display: print(".", end=' ') 263 | q += 30 264 | if (q >= high): 265 | q = getRandomNumber(low, high) 266 | q += 29 - (q % 30) 267 | #Ideas from Tom Wu's SRP code 268 | #Do trial division on p and q before Rabin-Miller 269 | if isPrime(q, 0, display=display): 270 | p = (2 * q) + 1 271 | if isPrime(p, display=display): 272 | if isPrime(q, display=display): 273 | return p 274 | 275 | 276 | class RSAKey(object): 277 | 278 | def __init__(self, n=0, e=0, d=0, p=0, q=0, dP=0, dQ=0, qInv=0): 279 | if (n and not e) or (e and not n): 280 | raise AssertionError() 281 | self.n = n 282 | self.e = e 283 | self.d = d 284 | self.p = p 285 | self.q = q 286 | self.dP = dP 287 | self.dQ = dQ 288 | self.qInv = qInv 289 | self.blinder = 0 290 | self.unblinder = 0 291 | 292 | def __len__(self): 293 | """Return the length of this key in bits. 294 | 295 | @rtype: int 296 | """ 297 | return numBits(self.n) 298 | 299 | def hasPrivateKey(self): 300 | return self.d != 0 301 | 302 | def hashAndSign(self, bytes): 303 | """Hash and sign the passed-in bytes. 304 | 305 | This requires the key to have a private component. It performs 306 | a PKCS1-SHA1 signature on the passed-in data. 307 | 308 | @type bytes: str or L{bytearray} of unsigned bytes 309 | @param bytes: The value which will be hashed and signed. 310 | 311 | @rtype: L{bytearray} of unsigned bytes. 312 | @return: A PKCS1-SHA1 signature on the passed-in data. 313 | """ 314 | hashBytes = SHA1(bytearray(bytes)) 315 | prefixedHashBytes = self._addPKCS1SHA1Prefix(hashBytes) 316 | sigBytes = self.sign(prefixedHashBytes) 317 | return sigBytes 318 | 319 | def hashAndVerify(self, sigBytes, bytes): 320 | """Hash and verify the passed-in bytes with the signature. 321 | 322 | This verifies a PKCS1-SHA1 signature on the passed-in data. 323 | 324 | @type sigBytes: L{bytearray} of unsigned bytes 325 | @param sigBytes: A PKCS1-SHA1 signature. 326 | 327 | @type bytes: str or L{bytearray} of unsigned bytes 328 | @param bytes: The value which will be hashed and verified. 329 | 330 | @rtype: bool 331 | @return: Whether the signature matches the passed-in data. 332 | """ 333 | hashBytes = SHA1(bytearray(bytes)) 334 | 335 | # Try it with/without the embedded NULL 336 | prefixedHashBytes1 = self._addPKCS1SHA1Prefix(hashBytes, False) 337 | prefixedHashBytes2 = self._addPKCS1SHA1Prefix(hashBytes, True) 338 | result1 = self.verify(sigBytes, prefixedHashBytes1) 339 | result2 = self.verify(sigBytes, prefixedHashBytes2) 340 | return (result1 or result2) 341 | 342 | def sign(self, bytes): 343 | """Sign the passed-in bytes. 344 | 345 | This requires the key to have a private component. It performs 346 | a PKCS1 signature on the passed-in data. 347 | 348 | @type bytes: L{bytearray} of unsigned bytes 349 | @param bytes: The value which will be signed. 350 | 351 | @rtype: L{bytearray} of unsigned bytes. 352 | @return: A PKCS1 signature on the passed-in data. 353 | """ 354 | if not self.hasPrivateKey(): 355 | raise AssertionError() 356 | paddedBytes = self._addPKCS1Padding(bytes, 1) 357 | m = bytesToNumber(paddedBytes) 358 | if m >= self.n: 359 | raise ValueError() 360 | c = self._rawPrivateKeyOp(m) 361 | sigBytes = numberToByteArray(c, numBytes(self.n)) 362 | return sigBytes 363 | 364 | def verify(self, sigBytes, bytes): 365 | """Verify the passed-in bytes with the signature. 366 | 367 | This verifies a PKCS1 signature on the passed-in data. 368 | 369 | @type sigBytes: L{bytearray} of unsigned bytes 370 | @param sigBytes: A PKCS1 signature. 371 | 372 | @type bytes: L{bytearray} of unsigned bytes 373 | @param bytes: The value which will be verified. 374 | 375 | @rtype: bool 376 | @return: Whether the signature matches the passed-in data. 377 | """ 378 | if len(sigBytes) != numBytes(self.n): 379 | return False 380 | paddedBytes = self._addPKCS1Padding(bytes, 1) 381 | c = bytesToNumber(sigBytes) 382 | if c >= self.n: 383 | return False 384 | m = self._rawPublicKeyOp(c) 385 | checkBytes = numberToByteArray(m, numBytes(self.n)) 386 | return checkBytes == paddedBytes 387 | 388 | def encrypt(self, bytes): 389 | """Encrypt the passed-in bytes. 390 | 391 | This performs PKCS1 encryption of the passed-in data. 392 | 393 | @type bytes: L{bytearray} of unsigned bytes 394 | @param bytes: The value which will be encrypted. 395 | 396 | @rtype: L{bytearray} of unsigned bytes. 397 | @return: A PKCS1 encryption of the passed-in data. 398 | """ 399 | paddedBytes = self._addPKCS1Padding(bytes, 2) 400 | m = bytesToNumber(paddedBytes) 401 | if m >= self.n: 402 | raise ValueError() 403 | c = self._rawPublicKeyOp(m) 404 | encBytes = numberToByteArray(c, numBytes(self.n)) 405 | return encBytes 406 | 407 | def decrypt(self, encBytes): 408 | """Decrypt the passed-in bytes. 409 | 410 | This requires the key to have a private component. It performs 411 | PKCS1 decryption of the passed-in data. 412 | 413 | @type encBytes: L{bytearray} of unsigned bytes 414 | @param encBytes: The value which will be decrypted. 415 | 416 | @rtype: L{bytearray} of unsigned bytes or None. 417 | @return: A PKCS1 decryption of the passed-in data or None if 418 | the data is not properly formatted. 419 | """ 420 | if not self.hasPrivateKey(): 421 | raise AssertionError() 422 | if len(encBytes) != numBytes(self.n): 423 | return None 424 | c = bytesToNumber(encBytes) 425 | if c >= self.n: 426 | return None 427 | m = self._rawPrivateKeyOp(c) 428 | decBytes = numberToByteArray(m, numBytes(self.n)) 429 | #Check first two bytes 430 | if decBytes[0] != 0 or decBytes[1] != 2: 431 | return None 432 | #Scan through for zero separator 433 | for x in range(1, len(decBytes)-1): 434 | if decBytes[x]== 0: 435 | break 436 | else: 437 | return None 438 | return decBytes[x+1:] #Return everything after the separator 439 | 440 | 441 | 442 | 443 | # ************************************************************************** 444 | # Helper Functions for RSA Keys 445 | # ************************************************************************** 446 | 447 | def _addPKCS1SHA1Prefix(self, bytes, withNULL=True): 448 | # There is a long history of confusion over whether the SHA1 449 | # algorithmIdentifier should be encoded with a NULL parameter or 450 | # with the parameter omitted. While the original intention was 451 | # apparently to omit it, many toolkits went the other way. TLS 1.2 452 | # specifies the NULL should be included, and this behavior is also 453 | # mandated in recent versions of PKCS #1, and is what tlslite has 454 | # always implemented. Anyways, verification code should probably 455 | # accept both. However, nothing uses this code yet, so this is 456 | # all fairly moot. 457 | if not withNULL: 458 | prefixBytes = bytearray(\ 459 | [0x30,0x1f,0x30,0x07,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x04,0x14]) 460 | else: 461 | prefixBytes = bytearray(\ 462 | [0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00,0x04,0x14]) 463 | prefixedBytes = prefixBytes + bytes 464 | return prefixedBytes 465 | 466 | def _addPKCS1Padding(self, bytes, blockType): 467 | padLength = (numBytes(self.n) - (len(bytes)+3)) 468 | if blockType == 1: #Signature padding 469 | pad = [0xFF] * padLength 470 | elif blockType == 2: #Encryption padding 471 | pad = bytearray(0) 472 | while len(pad) < padLength: 473 | padBytes = getRandomBytes(padLength * 2) 474 | pad = [b for b in padBytes if b != 0] 475 | pad = pad[:padLength] 476 | else: 477 | raise AssertionError() 478 | 479 | padding = bytearray([0,blockType] + pad + [0]) 480 | paddedBytes = padding + bytes 481 | return paddedBytes 482 | 483 | 484 | 485 | 486 | def _rawPrivateKeyOp(self, m): 487 | #Create blinding values, on the first pass: 488 | if not self.blinder: 489 | self.unblinder = getRandomNumber(2, self.n) 490 | self.blinder = powMod(invMod(self.unblinder, self.n), self.e, 491 | self.n) 492 | 493 | #Blind the input 494 | m = (m * self.blinder) % self.n 495 | 496 | #Perform the RSA operation 497 | c = self._rawPrivateKeyOpHelper(m) 498 | 499 | #Unblind the output 500 | c = (c * self.unblinder) % self.n 501 | 502 | #Update blinding values 503 | self.blinder = (self.blinder * self.blinder) % self.n 504 | self.unblinder = (self.unblinder * self.unblinder) % self.n 505 | 506 | #Return the output 507 | return c 508 | 509 | 510 | def _rawPrivateKeyOpHelper(self, m): 511 | #Non-CRT version 512 | #c = powMod(m, self.d, self.n) 513 | 514 | #CRT version (~3x faster) 515 | s1 = powMod(m, self.dP, self.p) 516 | s2 = powMod(m, self.dQ, self.q) 517 | h = ((s1 - s2) * self.qInv) % self.p 518 | c = s2 + self.q * h 519 | return c 520 | 521 | def _rawPublicKeyOp(self, c): 522 | m = powMod(c, self.e, self.n) 523 | return m 524 | 525 | def acceptsPassword(self): 526 | return False 527 | 528 | def generate(bits): 529 | key = RSAKey() 530 | p = getRandomPrime(bits//2, False) 531 | q = getRandomPrime(bits//2, False) 532 | t = lcm(p-1, q-1) 533 | key.n = p * q 534 | key.e = 65537 535 | key.d = invMod(key.e, t) 536 | key.p = p 537 | key.q = q 538 | key.dP = key.d % (p-1) 539 | key.dQ = key.d % (q-1) 540 | key.qInv = invMod(q, p) 541 | return key 542 | generate = staticmethod(generate) 543 | -------------------------------------------------------------------------------- /util.py: -------------------------------------------------------------------------------- 1 | # Modified from Electrum 2 | # Copyright (C) 2017 Andrew Chow 3 | # 4 | # Electrum - lightweight Bitcoin client 5 | # Copyright (C) 2011 Thomas Voegtlin 6 | # 7 | # Permission is hereby granted, free of charge, to any person 8 | # obtaining a copy of this software and associated documentation files 9 | # (the "Software"), to deal in the Software without restriction, 10 | # including without limitation the rights to use, copy, modify, merge, 11 | # publish, distribute, sublicense, and/or sell copies of the Software, 12 | # and to permit persons to whom the Software is furnished to do so, 13 | # subject to the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be 16 | # included in all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 22 | # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 23 | # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 24 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | # SOFTWARE. 26 | import binascii 27 | import os, sys, re, json 28 | from collections import defaultdict 29 | from datetime import datetime 30 | from decimal import Decimal 31 | import traceback 32 | import urllib 33 | import threading 34 | import hashlib 35 | import ecdsa 36 | import segwit_addr 37 | 38 | import urllib.request, urllib.parse, urllib.error 39 | import queue 40 | 41 | 42 | base_units = {'BTC':8, 'mBTC':5, 'uBTC':2} 43 | 44 | def normalize_version(v): 45 | return [int(x) for x in re.sub(r'(\.0+)*$','', v).split(".")] 46 | 47 | class PrintError(object): 48 | '''A handy base class''' 49 | def diagnostic_name(self): 50 | return self.__class__.__name__ 51 | 52 | def print_error(self, *msg): 53 | print_error("[%s]" % self.diagnostic_name(), *msg) 54 | 55 | def print_msg(self, *msg): 56 | print_msg("[%s]" % self.diagnostic_name(), *msg) 57 | 58 | class ThreadJob(PrintError): 59 | """A job that is run periodically from a thread's main loop. run() is 60 | called from that thread's context. 61 | """ 62 | 63 | def run(self): 64 | """Called periodically from the thread""" 65 | pass 66 | 67 | # TODO: disable 68 | is_verbose = True 69 | def set_verbosity(b): 70 | global is_verbose 71 | is_verbose = b 72 | 73 | def print_error(*args): 74 | if not is_verbose: return 75 | print_stderr(*args) 76 | 77 | def print_stderr(*args): 78 | args = [str(item) for item in args] 79 | sys.stderr.write(" ".join(args) + "\n") 80 | sys.stderr.flush() 81 | 82 | def print_msg(*args): 83 | # Stringify args 84 | args = [str(item) for item in args] 85 | sys.stdout.write(" ".join(args) + "\n") 86 | sys.stdout.flush() 87 | 88 | def json_encode(obj): 89 | try: 90 | s = json.dumps(obj, sort_keys = True, indent = 4, cls=MyEncoder) 91 | except TypeError: 92 | s = repr(obj) 93 | return s 94 | 95 | def json_decode(x): 96 | try: 97 | return json.loads(x, parse_float=Decimal) 98 | except: 99 | return x 100 | 101 | def assert_bytes(*args): 102 | """ 103 | porting helper, assert args type 104 | """ 105 | try: 106 | for x in args: 107 | assert isinstance(x, (bytes, bytearray)) 108 | except: 109 | print('assert bytes failed', list(map(type, args))) 110 | raise 111 | 112 | 113 | def assert_str(*args): 114 | """ 115 | porting helper, assert args type 116 | """ 117 | for x in args: 118 | assert isinstance(x, str) 119 | 120 | 121 | 122 | def to_string(x, enc): 123 | if isinstance(x, (bytes, bytearray)): 124 | return x.decode(enc) 125 | if isinstance(x, str): 126 | return x 127 | else: 128 | raise TypeError("Not a string or bytes like object") 129 | 130 | def to_bytes(something, encoding='utf8'): 131 | """ 132 | cast string to bytes() like object, but for python2 support it's bytearray copy 133 | """ 134 | if isinstance(something, bytes): 135 | return something 136 | if isinstance(something, str): 137 | return something.encode(encoding) 138 | elif isinstance(something, bytearray): 139 | return bytes(something) 140 | else: 141 | raise TypeError("Not a string or bytes like object") 142 | 143 | 144 | bfh = bytes.fromhex 145 | hfu = binascii.hexlify 146 | 147 | 148 | def bh2u(x): 149 | """ 150 | str with hex representation of a bytes-like object 151 | 152 | >>> x = bytes((1, 2, 10)) 153 | >>> bh2u(x) 154 | '01020A' 155 | 156 | :param x: bytes 157 | :rtype: str 158 | """ 159 | return hfu(x).decode('ascii') 160 | 161 | def format_satoshis_plain(x, decimal_point = 8): 162 | """Display a satoshi amount scaled. Always uses a '.' as a decimal 163 | point and has no thousands separator""" 164 | scale_factor = pow(10, decimal_point) 165 | return "{:.8f}".format(Decimal(x) / scale_factor).rstrip('0').rstrip('.') 166 | 167 | 168 | def format_satoshis(x, is_diff=False, num_zeros = 0, decimal_point = 8, whitespaces=False): 169 | from locale import localeconv 170 | if x is None: 171 | return 'unknown' 172 | x = int(x) # Some callers pass Decimal 173 | scale_factor = pow (10, decimal_point) 174 | integer_part = "{:n}".format(int(abs(x) / scale_factor)) 175 | if x < 0: 176 | integer_part = '-' + integer_part 177 | elif is_diff: 178 | integer_part = '+' + integer_part 179 | dp = localeconv()['decimal_point'] 180 | fract_part = ("{:0" + str(decimal_point) + "}").format(abs(x) % scale_factor) 181 | fract_part = fract_part.rstrip('0') 182 | if len(fract_part) < num_zeros: 183 | fract_part += "0" * (num_zeros - len(fract_part)) 184 | result = integer_part + dp + fract_part 185 | if whitespaces: 186 | result += " " * (decimal_point - len(fract_part)) 187 | result = " " * (15 - len(result)) + result 188 | return result 189 | 190 | def timestamp_to_datetime(timestamp): 191 | try: 192 | return datetime.fromtimestamp(timestamp) 193 | except: 194 | return None 195 | 196 | def format_time(timestamp): 197 | date = timestamp_to_datetime(timestamp) 198 | return date.isoformat(' ')[:-3] if date else _("Unknown") 199 | 200 | # URL decode 201 | #_ud = re.compile('%([0-9a-hA-H]{2})', re.MULTILINE) 202 | #urldecode = lambda x: _ud.sub(lambda m: chr(int(m.group(1), 16)), x) 203 | 204 | # Copied from Electrum's lib/bitcoin.py 205 | def is_segwit_address(addr): 206 | try: 207 | witver, witprog = segwit_addr.decode(NetworkConstants.SEGWIT_HRP, addr) 208 | except Exception as e: 209 | return False 210 | return witprog is not None 211 | 212 | def is_b58_address(addr): 213 | try: 214 | addrtype, h = b58_address_to_hash160(addr) 215 | except Exception as e: 216 | return False 217 | if addrtype not in [NetworkConstants.ADDRTYPE_P2PKH, NetworkConstants.ADDRTYPE_P2SH]: 218 | return False 219 | return addr == hash160_to_b58_address(h, addrtype) 220 | 221 | def is_address(addr): 222 | return is_segwit_address(addr) or is_b58_address(addr) 223 | 224 | def base_decode(v, length, base): 225 | """ decode v into a string of len bytes.""" 226 | # assert_bytes(v) 227 | v = to_bytes(v, 'ascii') 228 | assert base in (58, 43) 229 | chars = __b58chars 230 | if base == 43: 231 | chars = __b43chars 232 | long_value = 0 233 | for (i, c) in enumerate(v[::-1]): 234 | long_value += chars.find(bytes([c])) * (base**i) 235 | result = bytearray() 236 | while long_value >= 256: 237 | div, mod = divmod(long_value, 256) 238 | result.append(mod) 239 | long_value = div 240 | result.append(long_value) 241 | nPad = 0 242 | for c in v: 243 | if c == chars[0]: 244 | nPad += 1 245 | else: 246 | break 247 | result.extend(b'\x00' * nPad) 248 | if length is not None and len(result) != length: 249 | return None 250 | result.reverse() 251 | return bytes(result) 252 | # End copy 253 | 254 | def parse_URI(uri, on_pr=None): 255 | COIN = 100000000 256 | 257 | if ':' not in uri: 258 | if not is_address(uri): 259 | raise BaseException("Not a bitcoin address") 260 | return {'address': uri} 261 | 262 | u = urllib.parse.urlparse(uri) 263 | if u.scheme != 'bitcoin': 264 | raise BaseException("Not a bitcoin URI") 265 | address = u.path 266 | 267 | # python for android fails to parse query 268 | if address.find('?') > 0: 269 | address, query = u.path.split('?') 270 | pq = urllib.parse.parse_qs(query) 271 | else: 272 | pq = urllib.parse.parse_qs(u.query) 273 | 274 | for k, v in pq.items(): 275 | if len(v)!=1: 276 | raise Exception('Duplicate Key', k) 277 | 278 | out = {k: v[0] for k, v in pq.items()} 279 | if address: 280 | if not is_address(address): 281 | raise BaseException("Invalid bitcoin address:" + address) 282 | out['address'] = address 283 | if 'amount' in out: 284 | am = out['amount'] 285 | m = re.match('([0-9\.]+)X([0-9])', am) 286 | if m: 287 | k = int(m.group(2)) - 8 288 | amount = Decimal(m.group(1)) * pow( Decimal(10) , k) 289 | else: 290 | amount = Decimal(am) * COIN 291 | out['amount'] = int(amount) 292 | if 'message' in out: 293 | out['message'] = out['message'] 294 | out['memo'] = out['message'] 295 | if 'time' in out: 296 | out['time'] = int(out['time']) 297 | if 'exp' in out: 298 | out['exp'] = int(out['exp']) 299 | if 'sig' in out: 300 | out['sig'] = bh2u(base_decode(out['sig'], None, base=58)) 301 | 302 | r = out.get('r') 303 | sig = out.get('sig') 304 | name = out.get('name') 305 | if on_pr and (r or (name and sig)): 306 | import paymentrequest as pr 307 | if name and sig: 308 | s = pr.serialize_request(out).SerializeToString() 309 | request = pr.PaymentRequest(s) 310 | else: 311 | request = pr.get_payment_request(r) 312 | if on_pr: 313 | on_pr(request) 314 | 315 | return out 316 | 317 | 318 | def create_URI(addr, amount, message): 319 | if not is_address(addr): 320 | return "" 321 | query = [] 322 | if amount: 323 | query.append('amount=%s'%format_satoshis_plain(amount)) 324 | if message: 325 | query.append('message=%s'%urllib.parse.quote(message)) 326 | p = urllib.parse.ParseResult(scheme='bitcoin', netloc='', path=addr, params='', query='&'.join(query), fragment='') 327 | return urllib.parse.urlunparse(p) 328 | 329 | 330 | # Python bug (http://bugs.python.org/issue1927) causes raw_input 331 | # to be redirected improperly between stdin/stderr on Unix systems 332 | #TODO: py3 333 | def raw_input(prompt=None): 334 | if prompt: 335 | sys.stdout.write(prompt) 336 | return builtin_raw_input() 337 | 338 | import builtins 339 | builtin_raw_input = builtins.input 340 | builtins.input = raw_input 341 | 342 | 343 | def parse_json(message): 344 | # TODO: check \r\n pattern 345 | n = message.find(b'\n') 346 | if n==-1: 347 | return None, message 348 | try: 349 | j = json.loads(message[0:n].decode('utf8')) 350 | except: 351 | j = None 352 | return j, message[n+1:] 353 | 354 | # Copied from Electrum's lib/bitcoin.py 355 | class NetworkConstants: 356 | 357 | @classmethod 358 | def set_mainnet(cls): 359 | cls.TESTNET = False 360 | cls.WIF_PREFIX = 0x80 361 | cls.ADDRTYPE_P2PKH = 0 362 | cls.ADDRTYPE_P2SH = 5 363 | cls.SEGWIT_HRP = "bc" 364 | cls.GENESIS = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f" 365 | 366 | @classmethod 367 | def set_testnet(cls): 368 | cls.TESTNET = True 369 | cls.WIF_PREFIX = 0xef 370 | cls.ADDRTYPE_P2PKH = 111 371 | cls.ADDRTYPE_P2SH = 196 372 | cls.SEGWIT_HRP = "tb" 373 | cls.GENESIS = "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943" 374 | 375 | # supported types of transction outputs 376 | TYPE_ADDRESS = 0 377 | TYPE_PUBKEY = 1 378 | TYPE_SCRIPT = 2 379 | 380 | def rev_hex(s): 381 | return bh2u(bfh(s)[::-1]) 382 | 383 | def int_to_hex(i, length=1): 384 | assert isinstance(i, int) 385 | s = hex(i)[2:].rstrip('L') 386 | s = "0"*(2*length - len(s)) + s 387 | return rev_hex(s) 388 | def op_push(i): 389 | if i<0x4c: 390 | return int_to_hex(i) 391 | elif i<0xff: 392 | return '4c' + int_to_hex(i) 393 | elif i<0xffff: 394 | return '4d' + int_to_hex(i,2) 395 | else: 396 | return '4e' + int_to_hex(i,4) 397 | 398 | def push_script(x): 399 | return op_push(len(x)//2) + x 400 | 401 | def sha256(x): 402 | x = to_bytes(x, 'utf8') 403 | return bytes(hashlib.sha256(x).digest()) 404 | 405 | def Hash(x): 406 | x = to_bytes(x, 'utf8') 407 | out = bytes(sha256(sha256(x))) 408 | return out 409 | 410 | def msg_magic(message): 411 | length = bfh(var_int(len(message))) 412 | return b"\x18Bitcoin Signed Message:\n" + length + message 413 | 414 | class MyVerifyingKey(ecdsa.VerifyingKey): 415 | @classmethod 416 | def from_signature(klass, sig, recid, h, curve): 417 | """ See http://www.secg.org/download/aid-780/sec1-v2.pdf, chapter 4.1.6 """ 418 | from ecdsa import util, numbertheory 419 | import msqr 420 | curveFp = curve.curve 421 | G = curve.generator 422 | order = G.order() 423 | # extract r,s from signature 424 | r, s = util.sigdecode_string(sig, order) 425 | # 1.1 426 | x = r + (recid//2) * order 427 | # 1.3 428 | alpha = ( x * x * x + curveFp.a() * x + curveFp.b() ) % curveFp.p() 429 | beta = msqr.modular_sqrt(alpha, curveFp.p()) 430 | y = beta if (beta - recid) % 2 == 0 else curveFp.p() - beta 431 | # 1.4 the constructor checks that nR is at infinity 432 | R = Point(curveFp, x, y, order) 433 | # 1.5 compute e from message: 434 | e = string_to_number(h) 435 | minus_e = -e % order 436 | # 1.6 compute Q = r^-1 (sR - eG) 437 | inv_r = numbertheory.inverse_mod(r,order) 438 | Q = inv_r * ( s * R + minus_e * G ) 439 | return klass.from_public_point( Q, curve ) 440 | 441 | def pubkey_from_signature(sig, h): 442 | if len(sig) != 65: 443 | raise Exception("Wrong encoding") 444 | nV = sig[0] 445 | if nV < 27 or nV >= 35: 446 | raise Exception("Bad encoding") 447 | if nV >= 31: 448 | compressed = True 449 | nV -= 4 450 | else: 451 | compressed = False 452 | recid = nV - 27 453 | return MyVerifyingKey.from_signature(sig[1:], recid, h, curve = SECP256k1), compressed 454 | 455 | def point_to_ser(P, comp=True ): 456 | if comp: 457 | return bfh( ('%02x'%(2+(P.y()&1)))+('%064x'%P.x()) ) 458 | return bfh( '04'+('%064x'%P.x())+('%064x'%P.y()) ) 459 | 460 | def pubkey_to_address(txin_type, pubkey): 461 | if txin_type == 'p2pkh': 462 | return public_key_to_p2pkh(bfh(pubkey)) 463 | elif txin_type == 'p2wpkh': 464 | return hash_to_segwit_addr(hash_160(bfh(pubkey))) 465 | elif txin_type == 'p2wpkh-p2sh': 466 | scriptSig = p2wpkh_nested_script(pubkey) 467 | return hash160_to_p2sh(hash_160(bfh(scriptSig))) 468 | else: 469 | raise NotImplementedError(txin_type) 470 | 471 | def verify_message(address, sig, message): 472 | assert_bytes(sig, message) 473 | try: 474 | h = Hash(msg_magic(message)) 475 | public_key, compressed = pubkey_from_signature(sig, h) 476 | # check public key using the address 477 | pubkey = point_to_ser(public_key.pubkey.point, compressed) 478 | for txin_type in ['p2pkh','p2wpkh','p2wpkh-p2sh']: 479 | addr = pubkey_to_address(txin_type, bh2u(pubkey)) 480 | if address == addr: 481 | break 482 | else: 483 | raise Exception("Bad signature") 484 | # check message 485 | public_key.verify_digest(sig[1:], h, sigdecode = ecdsa.util.sigdecode_string) 486 | return True 487 | except Exception as e: 488 | print_error("Verification error: {0}".format(e)) 489 | return False 490 | 491 | def address_to_script(addr): 492 | witver, witprog = segwit_addr.decode(NetworkConstants.SEGWIT_HRP, addr) 493 | if witprog is not None: 494 | assert (0 <= witver <= 16) 495 | OP_n = witver + 0x50 if witver > 0 else 0 496 | script = bh2u(bytes([OP_n])) 497 | script += push_script(bh2u(bytes(witprog))) 498 | return script 499 | addrtype, hash_160 = b58_address_to_hash160(addr) 500 | if addrtype == NetworkConstants.ADDRTYPE_P2PKH: 501 | script = '76a9' # op_dup, op_hash_160 502 | script += push_script(bh2u(hash_160)) 503 | script += '88ac' # op_equalverify, op_checksig 504 | elif addrtype == NetworkConstants.ADDRTYPE_P2SH: 505 | script = 'a9' # op_hash_160 506 | script += push_script(bh2u(hash_160)) 507 | script += '87' # op_equal 508 | else: 509 | raise BaseException('unknown address type') 510 | return script 511 | 512 | def public_key_to_p2pk_script(pubkey): 513 | script = push_script(pubkey) 514 | script += 'ac' # op_checksig 515 | return script 516 | 517 | ############ functions from pywallet ##################### 518 | def hash_160(public_key): 519 | md = hashlib.new('ripemd160') 520 | md.update(sha256(public_key)) 521 | return md.digest() 522 | 523 | 524 | def hash160_to_b58_address(h160, addrtype, witness_program_version=1): 525 | s = bytes([addrtype]) 526 | s += h160 527 | return base_encode(s+Hash(s)[0:4], base=58) 528 | 529 | 530 | def b58_address_to_hash160(addr): 531 | addr = to_bytes(addr, 'ascii') 532 | _bytes = base_decode(addr, 25, base=58) 533 | return _bytes[0], _bytes[1:21] 534 | 535 | 536 | def hash160_to_p2pkh(h160): 537 | return hash160_to_b58_address(h160, NetworkConstants.ADDRTYPE_P2PKH) 538 | 539 | def hash160_to_p2sh(h160): 540 | return hash160_to_b58_address(h160, NetworkConstants.ADDRTYPE_P2SH) 541 | 542 | def public_key_to_p2pkh(public_key): 543 | return hash160_to_p2pkh(hash_160(public_key)) 544 | 545 | def hash_to_segwit_addr(h): 546 | return segwit_addr.encode(NetworkConstants.SEGWIT_HRP, 0, h) 547 | 548 | def public_key_to_p2wpkh(public_key): 549 | return hash_to_segwit_addr(hash_160(public_key)) 550 | 551 | def script_to_p2wsh(script): 552 | return hash_to_segwit_addr(sha256(bfh(script))) 553 | 554 | def p2wpkh_nested_script(pubkey): 555 | pkh = bh2u(hash_160(bfh(pubkey))) 556 | return '00' + push_script(pkh) 557 | 558 | def p2wsh_nested_script(witness_script): 559 | wsh = bh2u(sha256(bfh(witness_script))) 560 | return '00' + push_script(wsh) 561 | 562 | def pubkey_to_address(txin_type, pubkey): 563 | if txin_type == 'p2pkh': 564 | return public_key_to_p2pkh(bfh(pubkey)) 565 | elif txin_type == 'p2wpkh': 566 | return hash_to_segwit_addr(hash_160(bfh(pubkey))) 567 | elif txin_type == 'p2wpkh-p2sh': 568 | scriptSig = p2wpkh_nested_script(pubkey) 569 | return hash160_to_p2sh(hash_160(bfh(scriptSig))) 570 | else: 571 | raise NotImplementedError(txin_type) 572 | 573 | def redeem_script_to_address(txin_type, redeem_script): 574 | if txin_type == 'p2sh': 575 | return hash160_to_p2sh(hash_160(bfh(redeem_script))) 576 | elif txin_type == 'p2wsh': 577 | return script_to_p2wsh(redeem_script) 578 | elif txin_type == 'p2wsh-p2sh': 579 | scriptSig = p2wsh_nested_script(redeem_script) 580 | return hash160_to_p2sh(hash_160(bfh(scriptSig))) 581 | else: 582 | raise NotImplementedError(txin_type) 583 | 584 | 585 | def script_to_address(script): 586 | t, addr = get_address_from_output_script(bfh(script)) 587 | assert t == TYPE_ADDRESS 588 | return addr 589 | 590 | def address_to_script(addr): 591 | witver, witprog = segwit_addr.decode(NetworkConstants.SEGWIT_HRP, addr) 592 | if witprog is not None: 593 | assert (0 <= witver <= 16) 594 | OP_n = witver + 0x50 if witver > 0 else 0 595 | script = bh2u(bytes([OP_n])) 596 | script += push_script(bh2u(bytes(witprog))) 597 | return script 598 | addrtype, hash_160 = b58_address_to_hash160(addr) 599 | if addrtype == NetworkConstants.ADDRTYPE_P2PKH: 600 | script = '76a9' # op_dup, op_hash_160 601 | script += push_script(bh2u(hash_160)) 602 | script += '88ac' # op_equalverify, op_checksig 603 | elif addrtype == NetworkConstants.ADDRTYPE_P2SH: 604 | script = 'a9' # op_hash_160 605 | script += push_script(bh2u(hash_160)) 606 | script += '87' # op_equal 607 | else: 608 | raise BaseException('unknown address type') 609 | return script 610 | 611 | def address_to_scripthash(addr): 612 | script = address_to_script(addr) 613 | return script_to_scripthash(script) 614 | 615 | def script_to_scripthash(script): 616 | h = sha256(bytes.fromhex(script))[0:32] 617 | return bh2u(bytes(reversed(h))) 618 | 619 | def public_key_to_p2pk_script(pubkey): 620 | script = push_script(pubkey) 621 | script += 'ac' # op_checksig 622 | return script 623 | 624 | __b58chars = b'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' 625 | assert len(__b58chars) == 58 626 | 627 | __b43chars = b'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ$*+-./:' 628 | assert len(__b43chars) == 43 629 | 630 | 631 | def base_encode(v, base): 632 | """ encode v, which is a string of bytes, to base58.""" 633 | assert_bytes(v) 634 | assert base in (58, 43) 635 | chars = __b58chars 636 | if base == 43: 637 | chars = __b43chars 638 | long_value = 0 639 | for (i, c) in enumerate(v[::-1]): 640 | long_value += (256**i) * c 641 | result = bytearray() 642 | while long_value >= base: 643 | div, mod = divmod(long_value, base) 644 | result.append(chars[mod]) 645 | long_value = div 646 | result.append(chars[long_value]) 647 | # Bitcoin does a little leading-zero-compression: 648 | # leading 0-bytes in the input become leading-1s 649 | nPad = 0 650 | for c in v: 651 | if c == 0x00: 652 | nPad += 1 653 | else: 654 | break 655 | result.extend([chars[0]] * nPad) 656 | result.reverse() 657 | return result.decode('ascii') 658 | 659 | 660 | def base_decode(v, length, base): 661 | """ decode v into a string of len bytes.""" 662 | # assert_bytes(v) 663 | v = to_bytes(v, 'ascii') 664 | assert base in (58, 43) 665 | chars = __b58chars 666 | if base == 43: 667 | chars = __b43chars 668 | long_value = 0 669 | for (i, c) in enumerate(v[::-1]): 670 | long_value += chars.find(bytes([c])) * (base**i) 671 | result = bytearray() 672 | while long_value >= 256: 673 | div, mod = divmod(long_value, 256) 674 | result.append(mod) 675 | long_value = div 676 | result.append(long_value) 677 | nPad = 0 678 | for c in v: 679 | if c == chars[0]: 680 | nPad += 1 681 | else: 682 | break 683 | result.extend(b'\x00' * nPad) 684 | if length is not None and len(result) != length: 685 | return None 686 | result.reverse() 687 | return bytes(result) 688 | 689 | 690 | def EncodeBase58Check(vchIn): 691 | hash = Hash(vchIn) 692 | return base_encode(vchIn + hash[0:4], base=58) 693 | 694 | 695 | def DecodeBase58Check(psz): 696 | vchRet = base_decode(psz, None, base=58) 697 | key = vchRet[0:-4] 698 | csum = vchRet[-4:] 699 | hash = Hash(key) 700 | cs32 = hash[0:4] 701 | if cs32 != csum: 702 | return None 703 | else: 704 | return key 705 | # End copy 706 | 707 | # Copied from Electrum's lib/transaction.py 708 | class Enumeration: 709 | def __init__(self, name, enumList): 710 | self.__doc__ = name 711 | lookup = { } 712 | reverseLookup = { } 713 | i = 0 714 | uniqueNames = [ ] 715 | uniqueValues = [ ] 716 | for x in enumList: 717 | if isinstance(x, tuple): 718 | x, i = x 719 | if not isinstance(x, str): 720 | raise EnumException("enum name is not a string: " + x) 721 | if not isinstance(i, int): 722 | raise EnumException("enum value is not an integer: " + i) 723 | if x in uniqueNames: 724 | raise EnumException("enum name is not unique: " + x) 725 | if i in uniqueValues: 726 | raise EnumException("enum value is not unique for " + x) 727 | uniqueNames.append(x) 728 | uniqueValues.append(i) 729 | lookup[x] = i 730 | reverseLookup[i] = x 731 | i = i + 1 732 | self.lookup = lookup 733 | self.reverseLookup = reverseLookup 734 | 735 | def __getattr__(self, attr): 736 | if attr not in self.lookup: 737 | raise AttributeError 738 | return self.lookup[attr] 739 | def whatis(self, value): 740 | return self.reverseLookup[value] 741 | 742 | opcodes = Enumeration("Opcodes", [ 743 | ("OP_0", 0), ("OP_PUSHDATA1",76), "OP_PUSHDATA2", "OP_PUSHDATA4", "OP_1NEGATE", "OP_RESERVED", 744 | "OP_1", "OP_2", "OP_3", "OP_4", "OP_5", "OP_6", "OP_7", 745 | "OP_8", "OP_9", "OP_10", "OP_11", "OP_12", "OP_13", "OP_14", "OP_15", "OP_16", 746 | "OP_NOP", "OP_VER", "OP_IF", "OP_NOTIF", "OP_VERIF", "OP_VERNOTIF", "OP_ELSE", "OP_ENDIF", "OP_VERIFY", 747 | "OP_RETURN", "OP_TOALTSTACK", "OP_FROMALTSTACK", "OP_2DROP", "OP_2DUP", "OP_3DUP", "OP_2OVER", "OP_2ROT", "OP_2SWAP", 748 | "OP_IFDUP", "OP_DEPTH", "OP_DROP", "OP_DUP", "OP_NIP", "OP_OVER", "OP_PICK", "OP_ROLL", "OP_ROT", 749 | "OP_SWAP", "OP_TUCK", "OP_CAT", "OP_SUBSTR", "OP_LEFT", "OP_RIGHT", "OP_SIZE", "OP_INVERT", "OP_AND", 750 | "OP_OR", "OP_XOR", "OP_EQUAL", "OP_EQUALVERIFY", "OP_RESERVED1", "OP_RESERVED2", "OP_1ADD", "OP_1SUB", "OP_2MUL", 751 | "OP_2DIV", "OP_NEGATE", "OP_ABS", "OP_NOT", "OP_0NOTEQUAL", "OP_ADD", "OP_SUB", "OP_MUL", "OP_DIV", 752 | "OP_MOD", "OP_LSHIFT", "OP_RSHIFT", "OP_BOOLAND", "OP_BOOLOR", 753 | "OP_NUMEQUAL", "OP_NUMEQUALVERIFY", "OP_NUMNOTEQUAL", "OP_LESSTHAN", 754 | "OP_GREATERTHAN", "OP_LESSTHANOREQUAL", "OP_GREATERTHANOREQUAL", "OP_MIN", "OP_MAX", 755 | "OP_WITHIN", "OP_RIPEMD160", "OP_SHA1", "OP_SHA256", "OP_HASH160", 756 | "OP_HASH256", "OP_CODESEPARATOR", "OP_CHECKSIG", "OP_CHECKSIGVERIFY", "OP_CHECKMULTISIG", 757 | "OP_CHECKMULTISIGVERIFY", 758 | ("OP_SINGLEBYTE_END", 0xF0), 759 | ("OP_DOUBLEBYTE_BEGIN", 0xF000), 760 | "OP_PUBKEY", "OP_PUBKEYHASH", 761 | ("OP_INVALIDOPCODE", 0xFFFF), 762 | ]) 763 | 764 | 765 | def script_GetOp(_bytes): 766 | i = 0 767 | while i < len(_bytes): 768 | vch = None 769 | opcode = _bytes[i] 770 | i += 1 771 | if opcode >= opcodes.OP_SINGLEBYTE_END: 772 | opcode <<= 8 773 | opcode |= _bytes[i] 774 | i += 1 775 | 776 | if opcode <= opcodes.OP_PUSHDATA4: 777 | nSize = opcode 778 | if opcode == opcodes.OP_PUSHDATA1: 779 | nSize = _bytes[i] 780 | i += 1 781 | elif opcode == opcodes.OP_PUSHDATA2: 782 | (nSize,) = struct.unpack_from('0: 797 | continue # Opcodes below OP_PUSHDATA4 all just push data onto stack, and are equivalent. 798 | if to_match[i] != decoded[i][0]: 799 | return False 800 | return True 801 | 802 | def get_address_from_output_script(_bytes): 803 | decoded = [x for x in script_GetOp(_bytes)] 804 | 805 | # The Genesis Block, self-payments, and pay-by-IP-address payments look like: 806 | # 65 BYTES:... CHECKSIG 807 | match = [ opcodes.OP_PUSHDATA4, opcodes.OP_CHECKSIG ] 808 | if match_decoded(decoded, match): 809 | return TYPE_PUBKEY, bh2u(decoded[0][1]) 810 | 811 | # Pay-by-Bitcoin-address TxOuts look like: 812 | # DUP HASH160 20 BYTES:... EQUALVERIFY CHECKSIG 813 | match = [ opcodes.OP_DUP, opcodes.OP_HASH160, opcodes.OP_PUSHDATA4, opcodes.OP_EQUALVERIFY, opcodes.OP_CHECKSIG ] 814 | if match_decoded(decoded, match): 815 | return TYPE_ADDRESS, hash160_to_p2pkh(decoded[2][1]) 816 | 817 | # p2sh 818 | match = [ opcodes.OP_HASH160, opcodes.OP_PUSHDATA4, opcodes.OP_EQUAL ] 819 | if match_decoded(decoded, match): 820 | return TYPE_ADDRESS, hash160_to_p2sh(decoded[1][1]) 821 | 822 | # segwit address 823 | match = [ opcodes.OP_0, opcodes.OP_PUSHDATA4 ] 824 | if match_decoded(decoded, match): 825 | return TYPE_ADDRESS, hash_to_segwit_addr(decoded[1][1]) 826 | 827 | return TYPE_SCRIPT, bh2u(_bytes) 828 | 829 | def pay_script(output_type, addr): 830 | if output_type == TYPE_SCRIPT: 831 | return addr 832 | elif output_type == TYPE_ADDRESS: 833 | return address_to_script(addr) 834 | elif output_type == TYPE_PUBKEY: 835 | return public_key_to_p2pk_script(addr) 836 | else: 837 | raise TypeError('Unknown output type') 838 | # End copy --------------------------------------------------------------------------------