コード例 #1
0
ファイル: cli.py プロジェクト: justinmoon/ckcc-protocol
def user_auth(username, token=None, password=None, prompt=None, totp=None, psbt_file=None, debug=False):
    '''
Indicate specific user is present (for HSM).

Username and 2FA (TOTP, 6-digits) value or password are required. To use
password, the PSBT file in question must be provided.
'''
    import time
    from hmac import HMAC
    from hashlib import pbkdf2_hmac, sha256

    dryrun = True
    dev = ColdcardDevice(sn=force_serial)
    dev.check_mitm()

    if psbt_file or password:
        if psbt_file:
            psbt_hash = sha256(psbt_file.read()).digest()
            dryrun = False
        else:
            psbt_hash = bytes(32)

        pw = token or click.prompt('Password (hidden)', hide_input=True)
        secret = dev.hash_password(pw.encode('utf8'))

        token = HMAC(secret, msg=psbt_hash, digestmod=sha256).digest()

        if debug:
            click.echo("  secret = %s" % B2A(secret))
            click.echo("    salt = %s" % B2A(salt))

        totp_time = 0
    else:
        if not token:
            token = click.prompt('2FA Token (6 digits)', hide_input=False)

        if len(token) != 6 or not token.isdigit():
            raise click.UsageError("2FA Token must be 6 decimal digits")

        token = token.encode('ascii')

        now = int(time.time())
        if now % 30 < 5:
            click.echo("NOTE: TOTP was on edge of expiry limit! Might not work.")
        totp_time =  now // 30

    #raise click.UsageError("Need PSBT file as part of HMAC for password")

    assert token and len(token) in {6, 32}
    username = username.encode('ascii')

    if debug:
        click.echo(" username = %s" % username.decode('ascii'))
        click.echo("    token = %s" % (B2A(token) if len(token) > 6 else token.decode('ascii')))
        click.echo("totp_time = %d" % totp_time)

    resp = dev.send_recv(CCProtocolPacker.user_auth(username, token, totp_time))

    if not resp:
        click.echo("Correct or queued")
    else:
        click.echo(f'Problem: {resp}')
コード例 #2
0
ファイル: cli.py プロジェクト: justinmoon/ckcc-protocol
def new_user(username, totp_create=False, totp_secret=None, text_secret=None, ask_pass=False,
                do_delete=False, debug=False, show_qr=False, hotp=False, pick_pass=False):
    '''
Create a new user on the Coldcard for HSM policy (also delete).

You can input a password (interactively), or one can be picked
by the Coldcard. When possible the QR to enrol your 2FA app will
be shown on the Coldcard screen.
'''
    from base64 import b32encode, b32decode

    username = username.encode('ascii')
    assert 1 <= len(username) <= MAX_USERNAME_LEN, "Username length wrong"

    dev = ColdcardDevice(sn=force_serial)
    dev.check_mitm()

    if do_delete:
        dev.send_recv(CCProtocolPacker.delete_user(username))
        click.echo('Deleted, if it was there')
        return

    if ask_pass:
        assert not text_secret, "dont give and ask for password"
        text_secret = click.prompt('Password (hidden)', hide_input=True, confirmation_prompt=True)
        mode = USER_AUTH_HMAC

    if totp_secret:
        secret = b32decode(totp_secret, casefold=True)
        assert len(secret) in {10, 20}
        mode = USER_AUTH_TOTP
    elif hotp:
        mode = USER_AUTH_HOTP
        secret = b''
    elif pick_pass or text_secret:
        mode = USER_AUTH_HMAC
    else:
        # default is TOTP
        secret = b''
        mode = USER_AUTH_TOTP
    
    if mode == USER_AUTH_HMAC:
        # default is text passwords
        secret = dev.hash_password(text_secret.encode('utf8')) if text_secret else b''
        assert not show_qr, 'QR not appropriate for text passwords'

    if not secret and not show_qr:
        # ask the Coldcard to show the QR (for password or TOTP shared secret)
        mode |= USER_AUTH_SHOW_QR

    new_secret = dev.send_recv(CCProtocolPacker.create_user(username, mode, secret))

    if show_qr and new_secret:
        # format the URL thing ... needs a spec
        username = username.decode('ascii')
        secret = new_secret or b32encode(secret).decode('ascii')
        mode = 'hotp' if mode == USER_AUTH_HOTP else 'totp'
        click.echo(f'otpauth://{mode}/{username}?secret={secret}&issuer=Coldcard%20{dev.serial}')
    elif not text_secret and new_secret:
        click.echo(f'New password is: {new_secret}')
    else:
        click.echo('Done')