async def user_auth(self, username, token, totp, psbt_hash): if len(token) == 6 and token.isdigit(): # assume TOTP if token (password) is 6-numeric digits totp_time = totp or int(time.time() // 30) token = token.encode('ascii') else: # assume it's a raw password. need to hash it up # TODO: move this hashing into browser secret = self.dev.hash_password(token.encode('utf8')) token = HMAC(secret, msg=psbt_hash, digestmod=sha256).digest() totp_time = 0 await self.send_recv( CCProtocolPacker.user_auth(username.encode('ascii'), token, totp_time))
def user_auth(username, token=None, password=None, prompt=None, totp=None, psbt_file=None, debug=False, version3=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 with get_device() as dev: 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'), v3=version3) 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}')