def _add_otp(self, name=None, otp_type=None, secret=None): if not name: self.q.put([Action.ask_input, _("Add OTP to which password?"), "", "add_otp"]) elif not otp_type: screenshot = pyscreenshot.grab(childprocess=False).convert('L') qr_codes = zbar.Scanner().scan(screenshot) autodetected = 0 for qr_code in qr_codes: qr_data = qr_code.data.decode() try: pyotp.parse_uri(qr_data) except ValueError: continue autodetected += 1 self._append_password(name, qr_data) if autodetected == 0: self.q.put([Action.add_error, _("Could not detect any valid OTP QR codes on your screen. Continuing with manual configuration...")]) self.q.put([Action.ask_choice, _("Use which OTP type?"), ["TOTP", "HOTP"], "add_otp {}".format(name)]) else: self.q.put([Action.add_message, _("Detected and added {} valid OTP QR code(s) on your screen.").format(str(autodetected))]) return elif not secret: self.q.put([Action.ask_input, _("What is the OTP secret?"), "", "add_otp {} {}".format(name, otp_type)]) else: if otp_type == "TOTP": otp_uri = pyotp.TOTP(secret).provisioning_uri() elif otp_type == "HOTP": otp_uri = pyotp.HOTP(secret).provisioning_uri() else: return self._append_password(name, otp_uri)
def _add_otp(self, name=None, otp_type=None, secret=None): if not hasattr(pyotp, "parse_uri"): self.q.put([ Action.critical_error, _("pyotp lib doesn't support this yet") ]) return if not name: self.q.put([ Action.ask_input, _("What password should OTP be added to?"), "", "add_otp" ]) elif not otp_type: screenshot = pyscreenshot.grab(childprocess=False).convert('L') qr_codes = zbar.Scanner().scan(screenshot) autodetected = 0 for qr_code in qr_codes: qr_data = qr_code.data.decode() try: pyotp.parse_uri(qr_data) except ValueError: continue autodetected += 1 self._append_password(name, qr_data) if autodetected == 0: self.q.put([ Action.add_error, _("No valid OTP QR codes detected on your screen. Configuring manually…" ) ]) self.q.put([ Action.ask_choice, _("Which OTP type should be used?"), ["TOTP", "HOTP"], "add_otp {}".format(name) ]) else: self.q.put([ Action.add_message, _("Detected and added {} valid OTP QR code(s) on your screen." ).format(str(autodetected)) ]) return elif not secret: self.q.put([ Action.ask_input, _("What is the OTP secret?"), "", "add_otp {} {}".format(name, otp_type) ]) else: if otp_type == "TOTP": otp_uri = pyotp.TOTP(secret).provisioning_uri() elif otp_type == "HOTP": otp_uri = pyotp.HOTP(secret).provisioning_uri() else: return self._append_password(name, otp_uri)
def __init__(self, db_manager: DatabaseManager, entry: Entry) -> None: """GObject to handle a safe entry. :param DatabaseManager db_manager: database of the entry :param Entry entry: entry to handle """ super().__init__(db_manager, entry) self._entry: Entry = entry self._attachments: list[Attachment] = entry.attachments or [] self._attributes: dict[str, str] = { key: value for key, value in entry.custom_properties.items() if key not in (self._color_key, self._note_key, self._otp_key) } color_value: str = entry.get_custom_property(self._color_key) self._color: str = color_value or EntryColor.NONE.value self._icon_nr: str = entry.icon or "" self._password: str = entry.password or "" self._url: str = entry.url or "" self._username: str = entry.username or "" otp_uri = entry.get_custom_property("otp") if otp_uri: try: self._otp = parse_uri(otp_uri) except ValueError as err: logging.debug(err) self._check_expiration()
def generate(issuer): home = os.environ.get('HOME') with open('{}/totp.yaml'.format(home)) as file: contents = yaml.full_load(file.read()) generator = pyotp.parse_uri(contents['totp'][issuer]['url']) click.secho(generator.now())
def show(args): """Print out the contents of an entry to console""" kp = open_database(**vars(args)) _, path = parse_path(args.path) entry = get_entry(kp, path) # show specified field if args.field: # handle lowercase field input gracefully field = get_field(entry, args.field) print(entry._get_string_field(field), end='') elif args.totp: if entry.otp is None: log.error(red("Entry has no OTP field")) sys.exit(1) import pyotp print(pyotp.parse_uri(entry.otp).now(), end='') # otherwise, show all fields else: print(green("Title: ") + (entry.title or '')) print(green("UserName: "******"Password: "******"URL: ") + (entry.url or '')) if entry.otp is not None: print(green("OTP: ") + (entry.otp or '')) import pyotp import qrcode # generate QR code for seed qr = qrcode.QRCode( error_correction=qrcode.constants.ERROR_CORRECT_L, border=1) qr.add_data(entry.otp) qr.print_ascii() print(green("OTP Code: ") + pyotp.parse_uri(entry.otp).now()) # print custom fields for field_name, field_value in entry.custom_properties.items(): print(green("{}: ".format(field_name)) + str(field_value or '')) print(green("Created: ") + entry.ctime.isoformat()) print(green("Modified: ") + entry.mtime.isoformat())
def generate_otp(username, otp_seed): """Generate a time based OTP using the OTP_SEED for the npm user It is used in 2 Factor Authentication for the user account Args: username (string): The username of the npm user otp_seed (string): The seed generated during 2 factor auth setup for the user """ totp = pyotp.parse_uri( f'otpauth://totp/{username}?secret={otp_seed}&issuer=npm') otp = totp.now() return otp
def get_otp(self): if not self.mfa_string: return None otp = pyotp.parse_uri(self.mfa_string) # the library fails to cast the interval field to a number, # despite requiring that the field be a number otp.interval = int(otp.interval) return otp
async def test_signin(): otp = None if os.environ.get('OTP_URI'): import pyotp otp = pyotp.parse_uri(os.environ.get('OTP_URI')) from v2ex.utils import recognize_captcha_by_human me = await Me.signin(os.environ.get('USERNAME'), os.environ.get('PASSWORD'), recognize_captcha_by_human, otp=otp) print(me.id)
def main(): args = parser.parse_args() f = open(args.password_file) pw = f.read().strip() kp = PyKeePass(args.kdbx, password=pw) entry = kp.find_entries(title=args.entry, first=True) uri = entry.get_custom_property("otp") totp = pyotp.parse_uri(uri) print(totp.now())
def _parse_otp(self, line): if not hasattr(pyotp, "parse_uri"): return try: otp = pyotp.parse_uri(line) except ValueError: return if isinstance(otp, pyotp.TOTP): otp_code = otp.now() else: otp_code = otp.generate_otp() return ("{} - {}".format(otp.issuer, otp.name) if otp.issuer else otp.name, otp_code)
def test_algorithms(self): otp = pyotp.parse_uri( 'otpauth://totp?algorithm=SHA1&secret=GEZDGNBV&algorithm=SHA1') self.assertEqual(hashlib.sha1, otp.digest) self.assertEqual(otp.at(0), '734055') self.assertEqual(otp.at(30), '662488') self.assertEqual(otp.at(60), '289363') self.assertEqual(otp.provisioning_uri(), 'otpauth://totp/Secret?secret=GEZDGNBV') self.assertEqual(otp.provisioning_uri(name='n', issuer_name='i'), 'otpauth://totp/i:n?secret=GEZDGNBV&issuer=i') otp = pyotp.parse_uri( 'otpauth://totp?algorithm=SHA1&secret=GEZDGNBV&algorithm=SHA1&period=60' ) self.assertEqual(hashlib.sha1, otp.digest) self.assertEqual(otp.at(30), '734055') self.assertEqual(otp.at(60), '662488') self.assertEqual( otp.provisioning_uri(name='n', issuer_name='i'), 'otpauth://totp/i:n?secret=GEZDGNBV&issuer=i&period=60') otp = pyotp.parse_uri( 'otpauth://hotp?algorithm=SHA1&secret=GEZDGNBV&algorithm=SHA1') self.assertEqual(hashlib.sha1, otp.digest) self.assertEqual(otp.at(0), '734055') self.assertEqual(otp.at(1), '662488') self.assertEqual(otp.at(2), '289363') self.assertEqual( otp.provisioning_uri(name='n', issuer_name='i'), 'otpauth://hotp/i:n?secret=GEZDGNBV&issuer=i&counter=0') otp = pyotp.parse_uri( 'otpauth://hotp?algorithm=SHA1&secret=GEZDGNBV&algorithm=SHA1&counter=1' ) self.assertEqual(hashlib.sha1, otp.digest) self.assertEqual(otp.at(0), '662488') self.assertEqual(otp.at(1), '289363') self.assertEqual( otp.provisioning_uri(name='n', issuer_name='i'), 'otpauth://hotp/i:n?secret=GEZDGNBV&issuer=i&counter=1') otp = pyotp.parse_uri( 'otpauth://totp?algorithm=SHA1&secret=GEZDGNBV&algorithm=SHA256') self.assertEqual(hashlib.sha256, otp.digest) self.assertEqual(otp.at(0), '918961') self.assertEqual(otp.at(9000), '934470') otp = pyotp.parse_uri( 'otpauth://totp?algorithm=SHA1&secret=GEZDGNBV&algorithm=SHA512') self.assertEqual(hashlib.sha512, otp.digest) self.assertEqual(otp.at(0), '816660') self.assertEqual(otp.at(9000), '524153')
def main(): secret_or_uri = '' line_end = '\n' terminal = sys.stdin.isatty() if not terminal: secret_or_uri = sys.stdin.read().strip() line_end = '' if not secret_or_uri and terminal: secret_or_uri = getpass(prompt='Secret or URI: ', stream=None).strip() try: totp = parse_uri(secret_or_uri) except ValueError: totp = TOTP(secret_or_uri) try: print(totp.now(), end=line_end) except binascii.Error: print('Invalid 2FA secret', end=line_end)
def _display_results(self, results): while self.result_display_active: result_lines = results.rstrip().splitlines() self.passwordEntries = {} # Parse OTP for number, line in enumerate(result_lines): try: otp = pyotp.parse_uri(line) except ValueError: continue if isinstance(otp, pyotp.TOTP): otp_code = otp.now() else: otp_code = otp.generate_otp() otp_description = "{} - {}".format( otp.issuer, otp.name) if otp.issuer else otp.name result_lines[number] = "OTP ({}): {}".format( otp_description, otp_code) # If only a password and no other fields, select password immediately if len(result_lines) == 1: self.q.put([Action.copy_to_clipboard, result_lines[0]]) self.q.put([Action.close]) return for line in result_lines: if len(self.passwordEntries) == 0: self.passwordEntries["********"] = line else: self.passwordEntries[line] = line self.q.put( [Action.replace_entry_list, ["********"] + result_lines[1:]]) if self.result_display_active: sleep(1)
def test_enable_totp_view(self): self.client.login(username='******', password='******') get_response = self.client.get(reverse('accounts:enable_totp')) self.assertEquals(get_response.status_code, 200) self.assertTemplateUsed(get_response, 'accounts/totp_form.html') self.assertEquals(get_response.request['PATH_INFO'], '/accounts/enable-totp/') soup = BeautifulSoup(get_response.content, features="lxml") svg_container = soup.find("div", {"id": "svgcontainer"}) self.assertIsNotNone(svg_container) scsvg = svg_container.findChild("svg", recursive=False) self.assertIsNotNone(scsvg) with open('qr.svg', 'w') as f: f.write("<?xml version='1.0' encoding='utf-8'?>\n" + str(scsvg)) svg2png(url='qr.svg', write_to='qr.png', scale=8) t_image = Image.open('qr.png') t_image.load() background = Image.new("RGB", t_image.size, (255, 255, 255)) background.paste(t_image, mask=t_image.split()[3]) background.save('qr.jpg', "JPEG", quality=100) image = Image.open('qr.jpg') img_data = decode(image) qr_data = img_data[0].data.decode("utf-8") totp_code = pyotp.TOTP(pyotp.parse_uri(qr_data).secret).now() response = self.client.post(reverse('accounts:enable_totp'), {'totp_code': totp_code}, follow=True) self.assertEquals(response.status_code, 200) self.assertTemplateUsed(response, 'base_form.html') self.assertEquals(response.request['PATH_INFO'], '/accounts/edit-profile/') user_a = User.objects.get(username='******') self.assertTrue(user_a.account.use_totp) self.assertEquals(len(user_a.account.totp_key), 32) pathlib.Path('qr.svg').unlink() pathlib.Path('qr.png').unlink() pathlib.Path('qr.jpg').unlink()
def type_entries(args): """Type out password using keyboard Selects an entry using `prog`, then sends the password to the keyboard. If `tabbed` is true, both the username and password are typed, separated by a tab. If `totp` is true, generate and type totp. """ from Xlib.error import DisplayNameError try: from pynput.keyboard import Controller, Key except DisplayNameError: log.error(red("No X11 session found")) # build up a dictionary mapping strings (shown in dmenu) to entries entry_texts = {} # type from all databases if args.name is None: databases = open_database(all=True, **vars(args)) # generate multi-line string to send to dmenu for name, kp in databases: for entry in kp.entries: if entry.title: if len(databases) > 1: entry_text = "@{}/{}".format(name, '/'.join(entry.path)) else: entry_text = '/'.join(entry.path) if args.username: entry_text += " ({})".format(entry.username) entry_texts[entry_text] = entry dmenu_text = '\n'.join(sorted(entry_texts.keys())) # type from specific database else: kp = open_database(**vars(args)) for entry in kp.entries: if entry.title: entry_text = '/'.join(entry.path) if args.username: entry_text += " ({})".format(entry.username) entry_texts[entry_text] = entry dmenu_text = '\n'.join(sorted(entry_texts.keys())) # get the entry from dmenu try: p = subprocess.Popen(args.prog, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True) except FileNotFoundError: log.error(bold(args.prog[0]) + red(" not found.")) sys.exit(1) stdout = p.communicate( input=dmenu_text.encode('utf-8'))[0].decode('utf-8').rstrip('\n') log.debug("text from dmenu: {}".format(stdout)) # if nothing was selected, return None if not stdout: log.warning("No path returned by {}".format(args.prog)) return # kp = entry_texts[stdout] # _, selection_path = parse_path(stdout) # selected_entry = get_entry(kp, selection_path) selected_entry = entry_texts[stdout] log.debug("selected_entry:{}".format(selected_entry)) def call_xdotool(args): try: subprocess.call(["xdotool"] + args) except FileNotFoundError: log.error(bold("xdotool ") + red("not found")) sys.exit(1) # type out username/password k = Controller() # parse OTP field and type if args.totp: otp = None if selected_entry.otp is not None: otp = pyotp.parse_uri(selected_entry.otp) if otp is not None: if args.xdotool: call_xdotool(['type', otp.now()]) else: k.type(otp.now()) else: log.error(red("Selected entry has no OTP field")) sys.exit(1) else: if args.tabbed: if selected_entry.username: if args.xdotool: call_xdotool(['type', selected_entry.username]) call_xdotool(['key', 'Tab']) else: k.type(selected_entry.username) k.press(Key.tab) k.release(Key.tab) else: log.warning("Selected entry does not have a username") # type out password only if selected_entry.password: if args.xdotool: call_xdotool(['type', selected_entry.password]) else: k.type(selected_entry.password) else: log.warning("Selected entry does not have a password")
def selection_made(self, selection): if len(selection) == 0: # We're at the main menu self.passwordEntries = {} self.q.put([Action.set_header]) self.q.put([Action.replace_command_list, []]) self.q.put([Action.replace_entry_list, []]) self._get_entries() elif selection[-1]["type"] == SelectionType.none: # Global context menu option if selection[-1]["context_option"] == _("Create"): self._insert() self.q.put([Action.set_selection, []]) return elif selection[-1]["context_option"] == _("Generate"): self._generate() self.q.put([Action.set_selection, []]) return else: self.q.put([Action.critical_error, _("Unexpected selection_made value: {}").format(selection)]) elif len(selection) == 1: if selection[0]["type"] == SelectionType.entry: if selection[0]["context_option"] == _("Edit"): self._edit(name=selection[0]["value"]) self.q.put([Action.set_selection, []]) return elif selection[0]["context_option"] == _("Copy"): self._copy(name=selection[0]["value"]) self.q.put([Action.set_selection, []]) return elif selection[0]["context_option"] == _("Rename"): self._rename(name=selection[0]["value"]) self.q.put([Action.set_selection, []]) return elif selection[0]["context_option"] == _("Remove"): self._remove(selection[0]["value"]) self.q.put([Action.set_selection, []]) return elif selection[0]["context_option"] == _("Add OTP"): self._add_otp(selection[0]["value"]) self.q.put([Action.set_selection, []]) return results = self.password_store.get_decrypted_password(selection[0]["value"]) if results is None: self.q.put([Action.set_selection, []]) return self.q.put([Action.replace_entry_list, []]) self.q.put([Action.replace_command_list, []]) result_lines = results.rstrip().splitlines() # Parse OTP for number, line in enumerate(result_lines): try: otp = pyotp.parse_uri(line) except ValueError: continue if isinstance(otp, pyotp.TOTP): otp_code = otp.now() else: otp_code = otp.generate_otp() result_lines[number] = "OTP: {}".format(otp_code) # If only a password and no other fields, select password immediately if len(result_lines) == 1: self.q.put([Action.copy_to_clipboard, result_lines[0]]) self.q.put([Action.close]) return for line in result_lines: if len(self.passwordEntries) == 0: self.passwordEntries["********"] = line self.q.put([Action.add_entry, "********"]) else: self.passwordEntries[line] = line self.q.put([Action.add_entry, line]) else: self.q.put([Action.critical_error, _("Unexpected selection_made value: {}").format(selection)]) elif len(selection) == 2: # We're selecting a password if selection[1]["value"] == "********": self.q.put([Action.copy_to_clipboard, self.passwordEntries["********"]]) else: # Get the final part to prepare for copying. For example, if # the entry is named URL: https://example.org/", only copy # "https://example.org/" to the clipboard copyStringParts = self.passwordEntries[selection[1]["value"]].split(": ", 1) copyString = copyStringParts[1] if len(copyStringParts) > 1 else copyStringParts[0] self.q.put([Action.copy_to_clipboard, copyString]) self.passwordEntries = {} self.q.put([Action.close]) else: self.q.put([Action.critical_error, _("Unexpected selection_made value: {}").format(selection)])
def test_algorithms(self): otp = pyotp.parse_uri( 'otpauth://totp?algorithm=SHA1&secret=GEZDGNBV&algorithm=SHA1') self.assertEqual(hashlib.sha1, otp.digest) self.assertEqual(otp.at(0), '734055') self.assertEqual(otp.at(30), '662488') self.assertEqual(otp.at(60), '289363') self.assertEqual(otp.provisioning_uri(), 'otpauth://totp/Secret?secret=GEZDGNBV') self.assertEqual(otp.provisioning_uri(name='n', issuer_name='i'), 'otpauth://totp/i:n?secret=GEZDGNBV&issuer=i') otp = pyotp.parse_uri( 'otpauth://totp?algorithm=SHA1&secret=GEZDGNBV&algorithm=SHA1&period=60' ) self.assertEqual(hashlib.sha1, otp.digest) self.assertEqual(otp.at(30), '734055') self.assertEqual(otp.at(60), '662488') self.assertEqual( otp.provisioning_uri(name='n', issuer_name='i'), 'otpauth://totp/i:n?secret=GEZDGNBV&issuer=i&period=60') otp = pyotp.parse_uri( 'otpauth://hotp?algorithm=SHA1&secret=GEZDGNBV&algorithm=SHA1') self.assertEqual(hashlib.sha1, otp.digest) self.assertEqual(otp.at(0), '734055') self.assertEqual(otp.at(1), '662488') self.assertEqual(otp.at(2), '289363') self.assertEqual( otp.provisioning_uri(name='n', issuer_name='i'), 'otpauth://hotp/i:n?secret=GEZDGNBV&issuer=i&counter=0') otp = pyotp.parse_uri( 'otpauth://hotp?algorithm=SHA1&secret=GEZDGNBV&algorithm=SHA1&counter=1' ) self.assertEqual(hashlib.sha1, otp.digest) self.assertEqual(otp.at(0), '662488') self.assertEqual(otp.at(1), '289363') self.assertEqual( otp.provisioning_uri(name='n', issuer_name='i'), 'otpauth://hotp/i:n?secret=GEZDGNBV&issuer=i&counter=1') otp = pyotp.parse_uri( 'otpauth://totp?algorithm=SHA1&secret=GEZDGNBV&algorithm=SHA256') self.assertEqual(hashlib.sha256, otp.digest) self.assertEqual(otp.at(0), '918961') self.assertEqual(otp.at(9000), '934470') otp = pyotp.parse_uri( 'otpauth://totp?algorithm=SHA1&secret=GEZDGNBV&algorithm=SHA512') self.assertEqual(hashlib.sha512, otp.digest) self.assertEqual(otp.at(0), '816660') self.assertEqual(otp.at(9000), '524153') self.assertEqual( otp.provisioning_uri(name='n', issuer_name='i', image='https://test.net/test.png'), 'otpauth://totp/i:n?secret=GEZDGNBV&issuer=i&algorithm=SHA512&image=https%3A%2F%2Ftest.net%2Ftest.png' ) with self.assertRaises(ValueError): otp.provisioning_uri(name='n', issuer_name='i', image='nourl') otp = pyotp.parse_uri( otp.provisioning_uri(name='n', issuer_name='i', image='https://test.net/test.png')) self.assertEqual(hashlib.sha512, otp.digest)
def test_provisioning_uri(self): hotp = pyotp.HOTP('wrn3pqx5uqxqvnqr', name='mark@percival') url = urlparse(hotp.provisioning_uri()) self.assertEqual(url.scheme, 'otpauth') self.assertEqual(url.netloc, 'hotp') self.assertEqual(url.path, '/mark%40percival') self.assertEqual(dict(parse_qsl(url.query)), { 'secret': 'wrn3pqx5uqxqvnqr', 'counter': '0' }) self.assertEqual( hotp.provisioning_uri(), pyotp.parse_uri(hotp.provisioning_uri()).provisioning_uri()) hotp = pyotp.HOTP('wrn3pqx5uqxqvnqr', name='mark@percival', initial_count=12) url = urlparse(hotp.provisioning_uri()) self.assertEqual(url.scheme, 'otpauth') self.assertEqual(url.netloc, 'hotp') self.assertEqual(url.path, '/mark%40percival') self.assertEqual(dict(parse_qsl(url.query)), { 'secret': 'wrn3pqx5uqxqvnqr', 'counter': '12' }) self.assertEqual( hotp.provisioning_uri(), pyotp.parse_uri(hotp.provisioning_uri()).provisioning_uri()) hotp = pyotp.HOTP('wrn3pqx5uqxqvnqr', name='mark@percival', issuer='FooCorp!') url = urlparse(hotp.provisioning_uri()) self.assertEqual(url.scheme, 'otpauth') self.assertEqual(url.netloc, 'hotp') self.assertEqual(url.path, '/FooCorp%21:mark%40percival') self.assertEqual(dict(parse_qsl(url.query)), { 'secret': 'wrn3pqx5uqxqvnqr', 'counter': '0', 'issuer': 'FooCorp!' }) self.assertEqual( hotp.provisioning_uri(), pyotp.parse_uri(hotp.provisioning_uri()).provisioning_uri()) key = 'c7uxuqhgflpw7oruedmglbrk7u6242vb' hotp = pyotp.HOTP(key, digits=8, digest=hashlib.sha256, name='baco@peperina', issuer='FooCorp') url = urlparse(hotp.provisioning_uri()) self.assertEqual(url.scheme, 'otpauth') self.assertEqual(url.netloc, 'hotp') self.assertEqual(url.path, '/FooCorp:baco%40peperina') self.assertEqual( dict(parse_qsl(url.query)), { 'secret': 'c7uxuqhgflpw7oruedmglbrk7u6242vb', 'counter': '0', 'issuer': 'FooCorp', 'digits': '8', 'algorithm': 'SHA256' }) self.assertEqual( hotp.provisioning_uri(), pyotp.parse_uri(hotp.provisioning_uri()).provisioning_uri()) hotp = pyotp.HOTP(key, digits=8, name='baco@peperina', issuer='Foo Corp', initial_count=10) url = urlparse(hotp.provisioning_uri()) self.assertEqual(url.scheme, 'otpauth') self.assertEqual(url.netloc, 'hotp') self.assertEqual(url.path, '/Foo%20Corp:baco%40peperina') self.assertEqual( dict(parse_qsl(url.query)), { 'secret': 'c7uxuqhgflpw7oruedmglbrk7u6242vb', 'counter': '10', 'issuer': 'Foo Corp', 'digits': '8' }) self.assertEqual( hotp.provisioning_uri(), pyotp.parse_uri(hotp.provisioning_uri()).provisioning_uri()) code = pyotp.totp.TOTP("S46SQCPPTCNPROMHWYBDCTBZXV") self.assertEqual( code.provisioning_uri(), "otpauth://totp/Secret?secret=S46SQCPPTCNPROMHWYBDCTBZXV") code.verify("123456") self.assertEqual( code.provisioning_uri(), "otpauth://totp/Secret?secret=S46SQCPPTCNPROMHWYBDCTBZXV")
def test_invalids(self): with self.assertRaises(ValueError) as cm: pyotp.parse_uri('http://hello.com') self.assertEqual('Not an otpauth URI', str(cm.exception)) with self.assertRaises(ValueError) as cm: pyotp.parse_uri('otpauth://totp') self.assertEqual('No secret found in URI', str(cm.exception)) with self.assertRaises(ValueError) as cm: pyotp.parse_uri('otpauth://derp?secret=foo') self.assertEqual('Not a supported OTP type', str(cm.exception)) with self.assertRaises(ValueError) as cm: pyotp.parse_uri('otpauth://totp?foo=secret') self.assertEqual('foo is not a valid parameter', str(cm.exception)) with self.assertRaises(ValueError) as cm: pyotp.parse_uri('otpauth://totp?digits=-1') self.assertEqual('Digits may only be 6, 7, or 8', str(cm.exception)) with self.assertRaises(ValueError) as cm: pyotp.parse_uri('otpauth://totp/SomeIssuer:?issuer=AnotherIssuer') self.assertEqual( 'If issuer is specified in both label and parameters, it should be equal.', str(cm.exception)) with self.assertRaises(ValueError) as cm: pyotp.parse_uri('otpauth://totp?algorithm=aes') self.assertEqual( 'Invalid value for algorithm, must be SHA1, SHA256 or SHA512', str(cm.exception))
def test_provisioning_uri(self): totp = pyotp.TOTP('wrn3pqx5uqxqvnqr', name='mark@percival') url = urlparse(totp.provisioning_uri()) self.assertEqual(url.scheme, 'otpauth') self.assertEqual(url.netloc, 'totp') self.assertEqual(url.path, '/mark%40percival') self.assertEqual(dict(parse_qsl(url.query)), {'secret': 'wrn3pqx5uqxqvnqr'}) self.assertEqual( totp.provisioning_uri(), pyotp.parse_uri(totp.provisioning_uri()).provisioning_uri()) totp = pyotp.TOTP('wrn3pqx5uqxqvnqr', name='mark@percival', issuer='FooCorp!') url = urlparse(totp.provisioning_uri()) self.assertEqual(url.scheme, 'otpauth') self.assertEqual(url.netloc, 'totp') self.assertEqual(url.path, '/FooCorp%21:mark%40percival') self.assertEqual(dict(parse_qsl(url.query)), { 'secret': 'wrn3pqx5uqxqvnqr', 'issuer': 'FooCorp!' }) self.assertEqual( totp.provisioning_uri(), pyotp.parse_uri(totp.provisioning_uri()).provisioning_uri()) key = 'c7uxuqhgflpw7oruedmglbrk7u6242vb' totp = pyotp.TOTP(key, digits=8, interval=60, digest=hashlib.sha256, name='baco@peperina', issuer='FooCorp') url = urlparse(totp.provisioning_uri()) self.assertEqual(url.scheme, 'otpauth') self.assertEqual(url.netloc, 'totp') self.assertEqual(url.path, '/FooCorp:baco%40peperina') self.assertEqual( dict(parse_qsl(url.query)), { 'secret': 'c7uxuqhgflpw7oruedmglbrk7u6242vb', 'issuer': 'FooCorp', 'digits': '8', 'period': '60', 'algorithm': 'SHA256' }) self.assertEqual( totp.provisioning_uri(), pyotp.parse_uri(totp.provisioning_uri()).provisioning_uri()) totp = pyotp.TOTP(key, digits=8, interval=60, name='baco@peperina', issuer='FooCorp') url = urlparse(totp.provisioning_uri()) self.assertEqual(url.scheme, 'otpauth') self.assertEqual(url.netloc, 'totp') self.assertEqual(url.path, '/FooCorp:baco%40peperina') self.assertEqual( dict(parse_qsl(url.query)), { 'secret': 'c7uxuqhgflpw7oruedmglbrk7u6242vb', 'issuer': 'FooCorp', 'digits': '8', 'period': '60' }) self.assertEqual( totp.provisioning_uri(), pyotp.parse_uri(totp.provisioning_uri()).provisioning_uri()) totp = pyotp.TOTP(key, digits=8, name='baco@peperina', issuer='FooCorp') url = urlparse(totp.provisioning_uri()) self.assertEqual(url.scheme, 'otpauth') self.assertEqual(url.netloc, 'totp') self.assertEqual(url.path, '/FooCorp:baco%40peperina') self.assertEqual( dict(parse_qsl(url.query)), { 'secret': 'c7uxuqhgflpw7oruedmglbrk7u6242vb', 'issuer': 'FooCorp', 'digits': '8' }) self.assertEqual( totp.provisioning_uri(), pyotp.parse_uri(totp.provisioning_uri()).provisioning_uri())
def _get_token(response): totp_uri = response.json()["totp_uri"] totp = pyotp.parse_uri(totp_uri) return totp.now()
def is_valid_otp_string(self, mfa_string): try: pyotp.parse_uri(mfa_string) return True except ValueError: return False