Exemplo n.º 1
0
async def startup(setup_mode, force_local_mode, config_file, first_psbt):
    # All startup/operation code

    loop = asyncio.get_running_loop()
    if loop.get_debug():
        # quiet noise about slow stuff
        loop.slow_callback_duration = 10

    from utils import setup_logging
    setup_logging()

    from persist import Settings
    Settings.startup(config_file)

    aws = []

    # copy some args into status area
    from status import STATUS
    STATUS.force_local_mode = force_local_mode
    STATUS.setup_mode = setup_mode

    # preload the contents of a PSBT
    if first_psbt:
        STATUS.import_psbt(first_psbt)

    from torsion import TOR
    aws.append(TOR.startup())

    from conn import Connection
    aws.append(Connection(force_serial).run())

    import webapp
    aws.append(webapp.startup(setup_mode))

    await asyncio.gather(*aws)
Exemplo n.º 2
0
async def setup_page(request):
    # HSM policy setup

    # get latest status
    dev = Connection()
    await dev.hsm_status()

    return await add_shared_ctx(request)
Exemplo n.º 3
0
def server():
    """Server listening on named pipes and serving forever."""
    downlink = Connection('downlink', 'w+')
    uplink = Connection('uplink', 'r')

    cur_phase = 'INIT'
    ffs_counter = [0]
    # Listen for messages
    for request in uplink.receive():
        logger.debug('IN\t ' + request)

        response, cur_phase = serve_request(request, cur_phase, ffs_counter)
        if not response:
            continue

        downlink.send(response)
        logger.debug('OUT\t ' + response)

    downlink.close()
    uplink.close()
Exemplo n.º 4
0
async def bunker_page(request):

    # Bunker config and setup

    # get latest status
    dev = Connection()
    await dev.hsm_status()

    from torsion import TOR

    return await add_shared_ctx(request, BP=BP, force_local_mode=STATUS.force_local_mode)
Exemplo n.º 5
0
 def __init__(self):
     self.connection = Connection()
     self.connection.connect_database()
Exemplo n.º 6
0
def client():
    """Client mode work flow."""
    downlink = Connection('downlink', 'r')
    uplink = Connection('uplink', 'w+')

    # Establish connection
    dh = DiffieHellman()
    p, g = dh.get_pg()
    uplink.send('INIT,{0},{1}'.format(p, g))

    response = get_response(downlink, 'MY_PUBLIC', 2,
                            'Failed to authenticate via DH')

    public = dh.get_my_public()
    uplink.send('MY_PUBLIC,{0}'.format(public))
    key = dh.compute_shared(response[1])

    logging.debug('Key established: {0}'.format(key.hexdigest()))

    # Start encrypted communication
    response = get_response(downlink, 'AES', 2, 'Failed to start AES')
    aes = AES.new(key.digest(), AES.MODE_CBC, b64decode(response[1]))

    # Verify identity using FFS
    while True:
        ffs = FFSProver()
        # Send X
        msg = msg_encrypt(aes, str(ffs.get_x()))
        uplink.send('FFS_X,{0}'.format(msg))
        # Get A vector
        response = get_response(downlink, 'FFS_A', 2,
                                'Failed to verify identity via FFS')
        a_vector = [int(a) for a in msg_decrypt(aes, response[1])]
        # Solve Y
        msg = msg_encrypt(aes, str(ffs.solve(a_vector)))
        uplink.send('FFS_Y,{0}'.format(msg))
        # Get result
        response = next(downlink.receive())
        if response not in ('FFS_OK', 'FFS_DONE'):
            raise RuntimeError('Failed to verify identity via FFS')
        if response == 'FFS_DONE':
            break

    # Send messages
    print('Connection established, communication channel open.')
    print('If you want to end this program press CTRL(C')
    while True:
        # Snd message
        raw_msg = raw_input('Enter message: ')
        msg = msg_encrypt(aes, raw_msg)
        uplink.send('MSG,{0}'.format(msg))

        # Get response
        response = next(downlink.receive())
        response = response.split(',')

        if len(response) != 2 or response[0] != 'SHA256':
            raise RuntimeError('Channel compromised')

        # Compare Hashes
        our_hash = sha256(raw_msg).hexdigest()
        their_hash = msg_decrypt(aes, response[1])
        print('Recieved: ' + their_hash)

        if their_hash != our_hash:
            print('Hash mismatch!')
            raise RuntimeError('Channel compromised')
        else:
            print('Hash matches')
Exemplo n.º 7
0
async def ws_api_handler(ses, send_json, req, orig_request):     # handle_api
    #
    # Handle incoming requests over websocket; send back results.
    # req = already json parsed request coming in
    # send_json() = means to send the response back
    #
    action = req.action
    args = getattr(req, 'args', None)

    #logging.warn("API action=%s (%r)" % (action, args))        # MAJOR info leak XXX
    logging.debug(f"API action={action}")

    if action == '_connected':
        logging.info("Websocket connected: %r" % args)

        # can send special state update at this point, depending on the page

    elif action == 'start_hsm_btn':
        await Connection().hsm_start()
        await send_json(show_flash_msg=APPROVE_CTA)
        
    elif action == 'delete_user':
        name, = args
        assert 1 <= len(name) <= MAX_USERNAME_LEN, "bad username length"
        await Connection().delete_user(name.encode('utf8'))

        # assume it worked, so UX updates right away
        try:
            STATUS.hsm.users.remove(name)
        except ValueError:
            pass
        STATUS.notify_watchers()

    elif action == 'create_user':
        name, authmode, new_pw = args

        assert 1 <= len(name) <= MAX_USERNAME_LEN, "bad username length"
        assert ',' not in name, "no commas in names"

        if authmode == 'totp':
            mode = USER_AUTH_TOTP | USER_AUTH_SHOW_QR
            new_pw = ''
        elif authmode == 'rand_pw':
            mode = USER_AUTH_HMAC | USER_AUTH_SHOW_QR
            new_pw = ''
        elif authmode == 'give_pw':
            mode = USER_AUTH_HMAC
        else:
            raise ValueError(authmode)

        await Connection().create_user(name.encode('utf8'), mode, new_pw)

        # assume it worked, so UX updates right away
        try:
            STATUS.hsm.users = list(set(STATUS.hsm.users + [name]))
        except ValueError:
            pass
        STATUS.notify_watchers()

    elif action == 'submit_policy':
        # get some JSON w/ everything the user entered.
        p, save_copy = args

        proposed = policy.web_cleanup(json_loads(p))

        policy.update_sl(proposed)

        await Connection().hsm_start(proposed)

        STATUS.notify_watchers()

        await send_json(show_flash_msg=APPROVE_CTA)

        if save_copy:
            d = policy.desensitize(proposed)
            await send_json(local_download=dict(data=json_dumps(d, indent=2),
                                filename=f'hsm-policy-{STATUS.xfp}.json.txt'))

    elif action == 'download_policy':

        proposed = policy.web_cleanup(json_loads(args[0]))
        await send_json(local_download=dict(data=json_dumps(proposed, indent=2),
                                filename=f'hsm-policy-{STATUS.xfp}.json.txt'))

    elif action == 'import_policy':
        # they are uploading a JSON capture, but need values we can load in Vue
        proposed = args[0]
        cooked = policy.web_cookup(proposed)
        await send_json(vue_app_cb=dict(update_policy=cooked),
                        show_flash_msg="Policy file imported.")

    elif action == 'pick_onion_addr':
        from torsion import TOR
        addr, pk = await TOR.pick_onion_addr()
        await send_json(vue_app_cb=dict(new_onion_addr=[addr, pk]))

    elif action == 'pick_master_pw':
        pw = b64encode(os.urandom(12)).decode('ascii')
        pw = pw.replace('/', 'S').replace('+', 'p')
        assert '=' not in pw

        await send_json(vue_app_cb=dict(new_master_pw=pw))

    elif action == 'new_bunker_config':
        from torsion import TOR
        # save and apply config values
        nv = json_loads(args[0])

        assert 4 <= len(nv.master_pw) < 200, "Master password must be at least 4 chars long"

        # copy in simple stuff
        for fn in [ 'tor_enabled', 'master_pw', 'easy_captcha', 'allow_reboots']:
            if fn in nv:
                BP[fn] = nv[fn]


        # update onion stuff only if PK is known (ie. they changed it)
        if nv.get('onion_pk', False) or False:
            for fn in [ 'onion_addr', 'onion_pk']:
                if fn in nv:
                    BP[fn] = nv[fn]

        BP.save()

        await send_json(show_flash_msg="Bunker settings encrypted and saved to disk.")

        STATUS.tor_enabled = BP['tor_enabled']
        STATUS.notify_watchers()

        if not BP['tor_enabled']:
            await TOR.stop_tunnel()
        elif BP.get('onion_pk') and not (STATUS.force_local_mode or STATUS.setup_mode) \
            and TOR.get_current_addr() != BP.get('onion_addr'):
                # disconnect/reconnect
                await TOR.start_tunnel()

    elif action == 'sign_message':
        # sign a short text message
        # - lots more checking could be done here, but CC does it anyway
        msg_text, path, addr_fmt = args

        addr_fmt = AF_P2WPKH if addr_fmt != 'classic' else AF_CLASSIC

        try:
            sig, addr = await Connection().sign_text_msg(msg_text, path, addr_fmt)
        except:
            # get the spinner to stop: error msg will be "refused by policy" typically
            await send_json(vue_app_cb=dict(msg_signing_result='(failed)'))
            raise

        sig = b64encode(sig).decode('ascii').replace('\n', '')

        await send_json(vue_app_cb=dict(msg_signing_result=f'{sig}\n{addr}'))

    elif action == 'upload_psbt':
        # receiving a PSBT for signing

        size, digest, contents = args
        psbt = b64decode(contents)
        assert len(psbt) == size, "truncated/padded in transit"
        assert sha256(psbt).hexdigest() == digest, "corrupted in transit"

        STATUS.import_psbt(psbt)
        STATUS.notify_watchers()

    elif action == 'clear_psbt':
        STATUS.clear_psbt()
        STATUS.notify_watchers()

    elif action == 'preview_psbt':
        STATUS.psbt_preview = 'Wait...'
        STATUS.notify_watchers()
        try:
            txt = await Connection().sign_psbt(STATUS._pending_psbt, flags=STXN_VISUALIZE)
            txt = txt.decode('ascii')
            # force some line splits, especially for bech32, 32-byte values (p2wsh)
            probs = re.findall(r'([a-zA-Z0-9]{36,})', txt)
            for p in probs:
                txt = txt.replace(p, p[0:30] + '\u22ef\n\u22ef' + p[30:])
            STATUS.psbt_preview = txt
        except:
            # like if CC doesn't like the keys, whatever ..
            STATUS.psbt_preview = None
            raise
        finally:
            STATUS.notify_watchers()

    elif action == 'auth_set_name':
        idx, name = args

        assert 0 <= len(name) <= MAX_USERNAME_LEN
        assert 0 <= idx < len(STATUS.pending_auth)

        STATUS.pending_auth[idx].name = name
        STATUS.notify_watchers()

    elif action == 'auth_offer_guess':
        idx, ts, guess = args
        assert 0 <= idx < len(STATUS.pending_auth)
        STATUS.pending_auth[idx].totp = ts
        STATUS.pending_auth[idx].has_guess = 'x'*len(guess)
        STATUS._auth_guess[idx] = guess
        STATUS.notify_watchers()

    elif action == 'submit_psbt':
        # they want to sign it now
        expect_hash, send_immediately, finalize, wants_dl = args

        assert expect_hash == STATUS.psbt_hash, "hash mismatch"
        if send_immediately: assert finalize, "must finalize b4 send"

        logging.info("Starting to sign...")
        STATUS.busy_signing = True
        STATUS.notify_watchers()

        try:
            dev = Connection()

            # do auth steps first (no feedback given)
            for pa, guess in zip(STATUS.pending_auth, STATUS._auth_guess):
                if pa.name and guess:
                    await dev.user_auth(pa.name, guess, int(pa.totp), a2b_hex(STATUS.psbt_hash))

            STATUS.reset_pending_auth()

            try:
                result = await dev.sign_psbt(STATUS._pending_psbt, finalize=finalize)
                logging.info("Done signing")

                msg = "Transaction signed."

                if send_immediately:
                    msg += '<br><br>' + broadcast_txn(result)

                await send_json(show_modal=True, html=Markup(msg), selector='.js-api-success')

                result = (b2a_hex(result) if finalize else b64encode(result)).decode('ascii')
                fname = 'transaction.txt' if finalize else ('signed-%s.psbt'%STATUS.psbt_hash[-6:])

                if wants_dl:
                    await send_json(local_download=dict(data=result, filename=fname,
                                                        is_b64=(not finalize)))

                await dev.hsm_status()
            except CCUserRefused:
                logging.error("Coldcard refused to sign txn")
                await dev.hsm_status()
                r = STATUS.hsm.get('last_refusal', None)
                if not r: 
                    raise HTMLErroMsg('Refused by local user.')
                else:
                    raise HTMLErrorMsg(f"Rejected by Coldcard.<br><br>{r}")

        finally:
            STATUS.busy_signing = False
            STATUS.notify_watchers()

    elif action == 'shutdown_bunker':
        await send_json(show_flash_msg="Bunker is shutdown.")
        await asyncio.sleep(0.25)
        logging.warn("User-initiated shutdown")
        asyncio.get_running_loop().stop()
        sys.exit(0)

    elif action == 'leave_setup_mode':
        # During setup process, they want to go Tor mode; which I consider leaving
        # setup mode ... in particular, logins are required.
        # - button label is "Start Tor" tho ... so user doesn't see it that way
        assert STATUS.setup_mode, 'not in setup mode?'
        assert BP['tor_enabled'], 'Tor not enabled (need to save?)'
        addr = BP['onion_addr']
        assert addr and '.onion' in addr, "Bad address?"

        STATUS.setup_mode = False
        await send_json(show_flash_msg="Tor hidden service has been enabled. "
                            "It may take a few minutes for the website to become available")
        STATUS.notify_watchers()

        from torsion import TOR
        logging.info(f"Starting hidden service: %s" % addr)
        asyncio.create_task(TOR.start_tunnel())

    elif action == 'logout_everyone':
        # useful for running battles...
        # - changes crypto key for cookies, so they are all invalid immediately.
        from aiohttp_session.nacl_storage import NaClCookieStorage
        import nacl

        logging.warning("Logout of everyone!")

        # reset all session cookies
        storage = orig_request.get('aiohttp_session_storage')
        assert isinstance(storage, NaClCookieStorage)
        storage._secretbox = nacl.secret.SecretBox(os.urandom(32))

        # kick everyone off (bonus step)
        for w in web_sockets:
            try:
                await send_json(redirect='/logout', _ws=w)
                await w.close()
            except:
                pass

    else:
        raise NotImplementedError(action)
Exemplo n.º 8
0
    my_url = f"http://localhost:{settings.PORT_NUMBER}" + ('/setup' if setup_mode else '')
    logging.info(f"Web server at:    {my_url}")

    # meh; kinda annoying.
    if 0:
        def popup():
            try:
                # see <https://github.com/jupyter/notebook/issues/3746#issuecomment-444957821>
                # if this doesn't happen on MacOS
                from webbrowser import open as open_browser
                open_browser(my_url)
            except:
                logging.error("Unable to pop browser open", exc_info=1)
        asyncio.get_running_loop().call_later(3, popup)

    from aiohttp.abc import AbstractAccessLogger
    class AccessLogger(AbstractAccessLogger):
        def log(self, request, response, time):
            self.logger.info(f'{response.status} <= {request.method} {request.path}')

    return web._run_app(app, port=settings.PORT_NUMBER, print=None, access_log_class=AccessLogger)

if __name__ == "__main__":
    from utils import setup_logging
    setup_logging()
    dev = Connection(None)      # won't do anything tho, because async dev.run not called
    asyncio.run(startup())

# EOF
Exemplo n.º 9
0
from conn import Connection

# create object for class "Connection"
connection = Connection()

# create database "terntop"
try:
    connection.cursor.execute("CREATE DATABASE terntop")
    print('database created successfully')
except Exception as e:
    print('Error: ', str(e))

# connect to sql database "terntop"
connection.connect_database()

# create table "interns"
try:
    connection.cursor.execute(
        "CREATE TABLE interns (user_id VARCHAR(10) NOT NULL UNIQUE PRIMARY KEY, "
        "email_id VARCHAR(50), pw_hash VARCHAR(100) NOT NULL, "
        "first_name VARCHAR(20), last_name VARCHAR(40), full_name VARCHAR(20),"
        "image_url TEXT,phone BIGINT(10),country_code VARCHAR(2), city VARCHAR(20),"
        "state VARCHAR(20), country VARCHAR(20) ,date_registered TIMESTAMP, "
        "date_updated TIMESTAMP, skills TEXT, education TEXT,"
        "experience TEXT, applications TEXT)")
    print('"interns" table created successfully')
except Exception as e:
    print(str(e))

# create table "employers"
try: