def create(cls, username, auth_mode, secret): # create new user: # - username must be unique # - if secret is empty, we pick it and return choice # - show QR of secret (for TOTP/HOTP) if qr_mode = bool(auth_mode & USER_AUTH_SHOW_QR) if qr_mode: auth_mode &= ~USER_AUTH_SHOW_QR assert not secret assert auth_mode in {USER_AUTH_TOTP, USER_AUTH_HOTP, USER_AUTH_HMAC} # validate username; assert 1 < len(username) <= MAX_USERNAME_LEN, 'badlen' assert username[0] != '_', 'reserved' # We don't care if it exists, because then it's an update? # - but can safely let them reset the counter/totp level?? # - not sure, so force them to delete on-device first # - this check does not allow brute-force search for names (because existing = cls.lookup(username) assert not existing, "exists" if not secret: secret, picked = cls.pick_secret(auth_mode) else: picked = '' if auth_mode == USER_AUTH_HMAC: assert len(secret) == 32 else: assert len(secret) in {10, 20} # save u = cls.get() assert len(u) < MAX_NUMBER_USERS, 'too many' u[username] = [auth_mode, b32encode(secret), 0] settings.put(KEY, u) if qr_mode: # can only show up to 42 chars, and secret is 16, required overhead is 23 => 39 min # - can't fit any meta data, like username or our serial # in there # - HOTP not compliant because 'counter=0' not included (works in FreeOTP) from ux import abort_and_push, the_ux from qrs import QRDisplaySingle if auth_mode == USER_AUTH_HMAC: qr = picked else: qr = 'otpauth://{m}otp/CC?secret={s}'.format( s=picked, m=('t' if auth_mode == USER_AUTH_TOTP else 'h')) o = QRDisplaySingle([qr], False, sidebar=(picked, 4)) abort_and_push(o) picked = '' return picked
def capture_xpub(self): # track my xpubkey fingerprint & value in settings (not sensitive really) # - we share these on any USB connection from nvstore import settings # Implicit in the values is the BIP-39 encryption passphrase, # which we might not want to actually store. xfp = swab32(self.node.my_fp()) xpub = self.chain.serialize_public(self.node) if self._bip39pw: settings.put_volatile('xfp', xfp) settings.put_volatile('xpub', xpub) else: settings.overrides.clear() settings.put('xfp', xfp) settings.put('xpub', xpub) settings.put('chain', self.chain.ctype) settings.put('words', (self.mode == 'words'))
async def pick_multisig(self, _1, menu_idx, item): ms_wallet = item.arg settings.put('axi', menu_idx) # update last clicked address await self.show_n_addresses(None, None, ms_wallet)
async def pick_single(self, _1, menu_idx, item): settings.put('axi', menu_idx) # update last clicked address path, addr_fmt = item.arg await self.show_n_addresses(path, addr_fmt, None)
def delete(cls, username): # remove a user. simple. no checking u = cls.get() u.pop(username, None) settings.put(KEY, u)