#!/usr/bin/env python """Exchange SPAKE2 keys and print out the session key. This does the ``pake`` phase of the magic-wormhole client over stdin and stdout. Once the SPAKE2 exchange is done, it prints the session key. It sends its side of the exchange first and then expects the other side's. The logic is a best-effort attempt to reproduce magic-wormhole's own. It might be wrong. """ import argparse import attr import json import sys try: import wormhole except ImportError as e: print("Could not find Magic Wormhole: %s" % (e,)) sys.exit(0) from wormhole import util from spake2 import SPAKE2_Symmetric wormhole # Be still, pyflakes def main(): parser = argparse.ArgumentParser(prog='spake2_exchange') parser.add_argument( '--code', dest='code', type=str, help='Password to use to connect to other side') parser.add_argument( '--side', dest='side', type=str, help='Identifier for this side of the exchange') parser.add_argument( '--app-id', dest='app_id', type=str, help='Identifier for the application') params = parser.parse_args(sys.argv[1:]) transport = Transport(input_stream=sys.stdin, output_stream=sys.stdout) run_exchange(transport, params.code, params.app_id, params.side) def run_exchange(transport, code, app_id, side): # Send the SPAKE2 message spake = SPAKE2_Symmetric( util.to_bytes(code), idSymmetric=util.to_bytes(app_id)) outbound = spake.start() transport.send_json({ 'phase': 'pake', 'body': util.bytes_to_hexstr( util.dict_to_bytes({ 'pake_v1': util.bytes_to_hexstr(outbound), }) ), 'side': side, 'type': 'message', }) # Receive SPAKE2 message pake_msg = transport.receive_json() inbound = util.hexstr_to_bytes( util.bytes_to_dict( util.hexstr_to_bytes(pake_msg['body']) )['pake_v1'] ) spake_key = spake.finish(inbound) transport.send_line(util.bytes_to_hexstr(spake_key)) @attr.s class Transport(object): # XXX: Duplicated with version_exchange.py input_stream = attr.ib() output_stream = attr.ib() def send_line(self, line): self.output_stream.write(line.rstrip()) self.output_stream.write('\n') self.output_stream.flush() def send_json(self, json_value): self.send_line(json.dumps(json_value)) def receive_line(self): return self.input_stream.readline().strip() def receive_json(self): return json.loads(self.receive_line()) if __name__ == '__main__': main()