From 36c7cb9e5113eafffaa3b165e422f9026c60ea58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pascal=20Eng=C3=A9libert?= <tuxmain@zettascript.org> Date: Sat, 26 Oct 2019 20:52:58 +0200 Subject: [PATCH] Add /idtysig --- README.md | 3 +++ client.py | 35 ++++++++++++++++++++++++++++++----- server.py | 52 +++++++++++++++++++++++++++++++++++----------------- utils.py | 26 +++++++++++++++++++++----- 4 files changed, 89 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 1b0c1e6..7ff5c52 100644 --- a/README.md +++ b/README.md @@ -207,6 +207,9 @@ Command: **/getconfirm/_sender\_pubkey_/_in\_seed1_** get mix confirmation CONFIRMS // Each confirm is preceded by its length, as above ) +Command: **/idtysig** update identity signature +Content: Identity signature (must be signed by current server idty) + Command: **/mix/_sender_/_amount_/_base_[/client]** mix a transaction (set "client" option if request is sent to entry node) * If "client": _sender_ is the sender pubkey * Else: _sender_ is the sender peer hash diff --git a/client.py b/client.py index b72594f..4735b65 100644 --- a/client.py +++ b/client.py @@ -350,10 +350,14 @@ Options: --onion use proxy only when connecting to .onion -d <path> config directory (default ~/.config/gmixer-client) --no-tx Do not send transaction (for debug) + --idtysig <pubkey> <host> <port> + Update node's identity signature (no mixing) Example: python3 client.py -h svetsae7j3usrycn.onion 10951 -r 78ZwwgpgdH5uLZLbThUQH7LKwPgjMunYfLiCfUCySkM8 -p 127.0.0.1 9050 --onion - ⤷ send 10Ğ1 to Duniter developers + ⤷ send 10Ğ1 to Duniter developers (through a Tor peer) +python3 client.py -h txmn.tk 10951 -r HVXB7mrnrLDfJVALZiZknFg8FgPPi5k4y1md6GCLYGEK -a 5000 + ⤷ send 50Ğ1 to ĞMixer contributors """) exit() @@ -364,6 +368,31 @@ python3 client.py -h svetsae7j3usrycn.onion 10951 -r 78ZwwgpgdH5uLZLbThUQH7LKwPg conf = read_config(DIR) + proxy = None + if "-p" in sys.argv: + proxy = (utils.getargv("-p", "127.0.0.1", 1), int(utils.getargv("-p", 9050, 2))) + proxy_onion_only = "--onion" in sys.argv + + if "--idtysig" in sys.argv: + loop = True + while loop: + idty_keys = SigningKey.from_credentials( + getpass.getpass("Identity passphrase (salt):"), + getpass.getpass("Identity password:") + ) + print(idty_keys.pubkey) + loop = input("Is that the right pubkey? [yn]: ").lower() != "y" + + print(utils.sdata( + (utils.getargv("--idtysig", n=2), int(utils.getargv("--idtysig", n=3))), + "POST", + "/idtysig", + utils.gen_idty_sig(PublicKey(utils.getargv("--idtysig", n=1)), idty_keys)[0], + proxy=proxy, + proxy_onion_only=proxy_onion_only + )) + exit() + receiver = utils.getargv("-r", "") if receiver == "": print("Error: No receiver set (try option --help)") @@ -375,10 +404,6 @@ python3 client.py -h svetsae7j3usrycn.onion 10951 -r 78ZwwgpgdH5uLZLbThUQH7LKwPg port = int(utils.getargv("-h", "", 2)) amount = int(utils.getargv("-a", "1000")) layers = int(utils.getargv("-l", "3")) - proxy = None - if "-p" in sys.argv: - proxy = (utils.getargv("-p", "127.0.0.1", 1), int(utils.getargv("-p", 9050, 2))) - proxy_onion_only = "--onion" in sys.argv send_tx = not "--no-tx" in sys.argv db_txs = plyvel.DB(DIR+"/client_db_txs", create_if_missing=True) diff --git a/server.py b/server.py index 9d15b5f..034630d 100644 --- a/server.py +++ b/server.py @@ -221,7 +221,7 @@ def read_config(cdir, conf_overwrite={}): return conf class ServerThread(Thread): - def __init__(self, conf, peers, keys, local_peer, pool, tx_in_index, tx_out_index, db_txs): + def __init__(self, conf, peers, keys, local_peer, pool, tx_in_index, tx_out_index, db_txs, timers, client_th): Thread.__init__(self) self.conf = conf @@ -232,6 +232,8 @@ class ServerThread(Thread): self.tx_in_index = tx_in_index self.tx_out_index = tx_out_index self.db_txs = db_txs + self.timers = timers + self.client_th = client_th self.sock = None self.work = True @@ -470,6 +472,26 @@ class ServerThread(Thread): resp["confirm_ok"] = tx.out_seeds[2] peer.up_in = True + elif "idtysig" in url: + doc = utils.verify_idty_sig(self.conf, content, self.conf["idty"]["pubkey"], self.keys.pubkey, utils.self_checktime) + + if not doc: + send_response(client, "403 Forbidden", {"error": "bad_idty_sig"}, resp_format) + continue + + self.conf = read_config(DIR, { + "idty.sig": content.hex(), + "idty.sigtime": doc["sigtime"] + }) + utils.logprint("Updated idty sig: sigtime= {}".format(doc["sigtime"]), utils.LOG_INFO) + + self.timers["next_peer_info"] = time.time() + self.conf["server"]["peer_info_interval"] + self.local_peer = utils.Peer.generate(self.conf, self.keys, self.peers) + utils.logprint("Generated new peer info", utils.LOG_TRACE) + self.client_th.spread_peer_info() + + resp["idtysig_ok"] = doc + if "getconfirm" in url: sender_pubkey = utils.getargv("getconfirm", "", 1, url) try: @@ -539,7 +561,7 @@ class ServerThread(Thread): self.sock.shutdown(socket.SHUT_WR) class ClientThread(Thread): - def __init__(self, conf, peers, keys, local_peer, pool, tx_in_index, tx_out_index, db_txs): + def __init__(self, conf, peers, keys, local_peer, pool, tx_in_index, tx_out_index, db_txs, timers): Thread.__init__(self) self.conf = conf @@ -550,6 +572,7 @@ class ClientThread(Thread): self.tx_in_index = tx_in_index self.tx_out_index = tx_out_index self.db_txs = db_txs + self.timers = timers self.bma_endpoints = conf["client"]["bma_hosts"].copy() self.work = True @@ -740,7 +763,7 @@ class ClientThread(Thread): t = time.time() next_mix = t + self.conf["mix"]["mix_interval"] next_peers_detection = t + self.conf["server"]["peer_detect_interval"] - next_peer_info = t + self.conf["server"]["peer_info_interval"] + self.timers["next_peer_info"] = t + self.conf["server"]["peer_info_interval"] self.detect_peers() local_peer = utils.Peer.generate(self.conf, self.keys, self.peers) @@ -819,11 +842,11 @@ class ClientThread(Thread): utils.logprint("No peer for: "+tx.receiver_pubkey, utils.LOG_WARN) # Generate peer info - if t > next_peer_info: + if t > self.timers["next_peer_info"]: + self.timers["next_peer_info"] = time.time() + self.conf["server"]["peer_info_interval"] self.local_peer = utils.Peer.generate(self.conf, self.keys, self.peers) utils.logprint("Generated new peer info", utils.LOG_TRACE) self.spread_peer_info() - next_peer_info = time.time() + self.conf["server"]["peer_info_interval"] # Remove expired requests expire_txs = [] @@ -850,15 +873,6 @@ def get_credentials(conf): password = getpass.getpass("Enter your password: ") return salt, password -def gen_idty_sig(keys, idty_keys): - doc = { - "doctype": "gmixer/idtysig", - "docver": "1", - "pubkey": keys.pubkey, - "sigtime": time.time() - } - return idty_keys.sign(ubjson.dumpb(doc)) - # Main function def main(): # Load conf @@ -888,9 +902,11 @@ def main(): # Generate peer info local_peer = utils.Peer.generate(conf, keys, {}) + timers = {"next_peer_info": 0} + # Start threads - clientThread = ClientThread(conf, peers, keys, local_peer, pool, tx_in_index, tx_out_index, db_txs) - serverThread = ServerThread(conf, peers, keys, local_peer, pool, tx_in_index, tx_out_index, db_txs) + clientThread = ClientThread(conf, peers, keys, local_peer, pool, tx_in_index, tx_out_index, db_txs, timers) + serverThread = ServerThread(conf, peers, keys, local_peer, pool, tx_in_index, tx_out_index, db_txs, timers, clientThread) clientThread.start() serverThread.start() @@ -965,8 +981,10 @@ if __name__ == "__main__": ) print(idty_keys.pubkey) loop = input("Is that the right pubkey? [yn]: ").lower() != "y" - conf_overwrite["idty.sig"] = gen_idty_sig(keys, idty_keys).hex() + sig, doc = utils.gen_idty_sig(keys, idty_keys) + conf_overwrite["idty.sig"] = sig.hex() conf_overwrite["idty.pubkey"] = idty_keys.pubkey + conf_overwrite["idty.sigtime"] = doc["sigtime"] conf = read_config(DIR, conf_overwrite) diff --git a/utils.py b/utils.py index 409de78..db67a92 100644 --- a/utils.py +++ b/utils.py @@ -132,19 +132,35 @@ def run_async(coro): #-------- ĞMixer -def verify_idty_sig(conf, raw, idty_pubkey, peer_pubkey): +def gen_idty_sig(keys, idty_keys): + doc = { + "doctype": "gmixer/idtysig", + "docver": "1", + "pubkey": keys.pubkey if isinstance(keys, SigningKey) else keys.base58(), + "sigtime": int(time.time()) + } + return idty_keys.sign(ubjson.dumpb(doc)), doc + +def default_checktime(conf, data): + t = time.time() + return data["sigtime"] < t and data["sigtime"] + conf["server"]["idty_sig_age_max"] > t + +def self_checktime(conf, data): + t = time.time() + return data["sigtime"] > conf["idty"]["sigtime"] and data["sigtime"] < t and data["sigtime"] + conf["server"]["idty_sig_age_max"] > t + +def verify_idty_sig(conf, raw, idty_pubkey, peer_pubkey, checktime=default_checktime): try: raw = libnacl.sign.Verifier(PublicKey(idty_pubkey).hex_pk()).verify(raw) data = ubjson.loadb(raw) assert data["doctype"] == "gmixer/idtysig" , "Bad doctype" assert data["docver"] == "1" , "Bad docver" assert data["pubkey"] == peer_pubkey , "Bad pubkey" - t = time.time() - assert data["sigtime"] < t and data["sigtime"] + conf["server"]["idty_sig_age_max"] > t , "Bad sigtime" + assert checktime(conf, data) , "Bad sigtime" except (ValueError, IndexError, ubjson.decoder.DecoderException, AssertionError) as e: logprint("Bad idty sig: "+str(e), LOG_TRACE) - return False - return True + return None + return data class Peer: VERSION = "2" -- GitLab