diff --git a/funding/routes.py b/funding/routes.py index 4936c92..dea19fb 100644 --- a/funding/routes.py +++ b/funding/routes.py @@ -10,6 +10,9 @@ import settings from funding.factory import app, db, cache from funding.orm import Address, Slate +import secp256k1 +import base58 + @app.route('/') def index(): @@ -50,11 +53,24 @@ def postSlate(receivingAddress, slate): @app.route('/getSlates', methods=['POST']) @endpoint.api( parameter('receivingAddress', type=str, required=True), + parameter('signature', type=str, required=True) ) -def getSlates(receivingAddress): +def getSlates(receivingAddress, signature): try: - if receivingAddress is None: + if receivingAddress is None or signature is None: return make_response(jsonify({'status': 'failure', 'error': str("missing correct arguments")})) + + # Deserialize the base-58 address to an internal public key format + # NOTE: This assumes that the network version (which is not part of the key) is exactly 2 bytes + public_key = secp256k1.PublicKey(base58.b58decode_check(receivingAddress)[2:], raw=True) + + # Prepare the message bound to the signature: a domain separator and the encoded address + # For some reason, the original client code calls this the "challenge" + message = 'SubscribeRequest_' + receivingAddress + + # Deserialize and verify the provided signature against the message and address public key + if not public_key.ecdsa_verify(message.encode(), public_key.ecdsa_deserialize(bytes(bytearray.fromhex(signature)))): + return make_response(jsonify({'status': 'failure', 'error': str("bad signature")})) slates = Slate.find_slates(address=receivingAddress) return make_response(jsonify({'status': 'success', 'slates': slates})) diff --git a/requirements.txt b/requirements.txt index ec3b4eb..27117cc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,4 +14,6 @@ pypng pillow-simd Flask-Caching flask-sqlalchemy -sqlalchemy_json \ No newline at end of file +sqlalchemy_json +secp256k1 +base58