def test_parse_uri_issuer(self): no_issuer = CredentialData.parse_uri("otpauth://totp/account" "?secret=abba") self.assertIsNone(no_issuer.issuer) from_param = CredentialData.parse_uri("otpauth://totp/account" "?secret=abba&issuer=Test") self.assertEqual("Test", from_param.issuer) from_name = CredentialData.parse_uri("otpauth://totp/Test:account" "?secret=abba") self.assertEqual("Test", from_name.issuer) with_both = CredentialData.parse_uri("otpauth://totp/TestA:account" "?secret=abba&issuer=TestB") self.assertEqual("TestB", with_both.issuer)
def uri(ctx, uri, touch, force): """ Add a new credential from URI. Use a URI to add a new credential to your YubiKey. """ if not uri: while True: uri = click_prompt("Enter an OATH URI") try: uri = CredentialData.parse_uri(uri) break except Exception as e: click.echo(e) ensure_validated(ctx) data = uri # Steam is a special case where we allow the otpauth # URI to contain a 'digits' value of '5'. if data.digits == 5 and is_steam(data): data.digits = 6 _add_cred(ctx, data, touch, force)
def test_vector(self, session, params, digits): key, values = params cred = session.put_credential( CredentialData("test", OATH_TYPE.HOTP, HASH_ALGORITHM.SHA1, key, digits) ) for expected in values: code = session.calculate_code(cred) assert len(code.value) == digits assert expected.endswith(code.value)
def test_vector(self, session, params): key, challenge, hash_algorithm, expected = params if hash_algorithm == HASH_ALGORITHM.SHA512: if session.version < (4, 3, 1) or is_fips_version(session.version): pytest.skip("SHA512 requires (non-FIPS) YubiKey 4.3.1 or later") cred = session.put_credential( CredentialData("test", OATH_TYPE.TOTP, hash_algorithm, key) ) value = session.calculate(cred.id, challenge) assert value == expected
def ccid_add_credential( self, name, secret, issuer, oath_type, algo, digits, period, touch, overwrite=False, ): secret = parse_b32_key(secret) with self._open_oath() as oath_controller: try: self._unlock(oath_controller) cred_data = CredentialData( name, OATH_TYPE[oath_type], HASH_ALGORITHM[algo], secret, int(digits), int(period), 0, issuer, ) if not overwrite: key = cred_data.get_id() if key in [ cred.id for cred in oath_controller.list_credentials() ]: return failure("credential_already_exists") oath_controller.put_credential(cred_data, touch) except ApduError as e: # NEO doesn't return a no space error if full, # but a command aborted error. Assume it's because of # no space in this context. if e.sw in (SW.NO_SPACE, SW.COMMAND_ABORTED): return failure("no_space") else: raise return success()
def test_vector(self, session, params, digits): timestamp, hash_algorithm, value, key = params if hash_algorithm == HASH_ALGORITHM.SHA512: if session.version < (4, 3, 1) or is_fips_version(session.version): pytest.skip("SHA512 requires (non-FIPS) YubiKey 4.3.1 or later") cred = session.put_credential( CredentialData("test", OATH_TYPE.TOTP, hash_algorithm, key, digits) ) code = session.calculate_code(cred, timestamp) assert len(code.value) == digits assert value.endswith(code.value)
def test_parse_uri(self): data = CredentialData.parse_uri("otpauth://totp/Issuer:account" "?secret=abba&issuer=Issuer" "&algorithm=SHA256&digits=7" "&period=20&counter=5") self.assertEqual(b"\0B", data.secret) self.assertEqual("Issuer", data.issuer) self.assertEqual("account", data.name) self.assertEqual(OATH_TYPE.TOTP, data.oath_type) self.assertEqual(HASH_ALGORITHM.SHA256, data.hash_algorithm) self.assertEqual(7, data.digits) self.assertEqual(20, data.period) self.assertEqual(5, data.counter)
def parse_qr(self, screenshot): data = b64decode(screenshot["data"]) image = PixelImage(data, screenshot["width"], screenshot["height"]) for qr in qrparse.parse_qr_codes(image, 2): try: return success( credential_data_to_dict( CredentialData.parse_uri(qrdecode.decode_qr_data(qr)) ) ) except Exception as e: logger.error("Failed to parse uri", exc_info=e) return failure("failed_to_parse_uri") return failure("no_credential_found")
def add( ctx, secret, name, issuer, period, oath_type, digits, touch, algorithm, counter, force, password, remember, ): """ Add a new account. This will add a new OATH account to the YubiKey. \b NAME Human readable name of the account, such as a username or e-mail address. SECRET Base32-encoded secret/key value provided by the server. """ digits = int(digits) if not secret: while True: secret = click_prompt("Enter a secret key (base32)") try: secret = parse_b32_key(secret) break except Exception as e: click.echo(e) _init_session(ctx, password, remember) _add_cred( ctx, CredentialData( name, oath_type, algorithm, secret, digits, period, counter, issuer ), touch, force, )
def add( ctx, secret, name, issuer, period, oath_type, digits, touch, algorithm, counter, force, password, remember, ): """ Add a new account. This will add a new OATH account to the YubiKey. """ digits = int(digits) if not secret: while True: secret = click_prompt("Enter a secret key (base32)") try: secret = parse_b32_key(secret) break except Exception as e: click.echo(e) _init_session(ctx, password, remember) _add_cred( ctx, CredentialData(name, oath_type, algorithm, secret, digits, period, counter, issuer), touch, force, )
def add( ctx, secret, name, issuer, period, oath_type, digits, touch, algorithm, counter, force, ): """ Add a new credential. This will add a new credential to your YubiKey. """ digits = int(digits) if not secret: while True: secret = click_prompt("Enter a secret key (base32)") try: secret = parse_b32_key(secret) break except Exception as e: click.echo(e) ensure_validated(ctx) _add_cred( ctx, CredentialData( name, oath_type, algorithm, secret, digits, period, counter, issuer ), touch, force, )
def uri(ctx, data, touch, force, password, remember): """ Add a new account from an otpauth:// URI. Use a URI to add a new account to the YubiKey. """ if not data: while True: uri = click_prompt("Enter an OATH URI") try: data = CredentialData.parse_uri(uri) break except Exception as e: click.echo(e) # Steam is a special case where we allow the otpauth # URI to contain a 'digits' value of '5'. if data.digits == 5 and is_steam(data): data.digits = 6 _init_session(ctx, password, remember) _add_cred(ctx, data, touch, force)
def click_parse_uri(ctx, param, val): try: return CredentialData.parse_uri(val) except ValueError: raise click.BadParameter("URI seems to have the wrong format.")
from ykman.device import is_fips_version from . import condition KEY = bytes.fromhex("01020304050607080102030405060708") @pytest.fixture @condition.capability(CAPABILITY.OATH) def session(ccid_connection): oath = OathSession(ccid_connection) oath.reset() yield oath CRED_DATA = CredentialData("name", OATH_TYPE.TOTP, HASH_ALGORITHM.SHA1, b"secret") class TestFunctions: @condition.min_version(5, 3) def test_rename(self, session): cred = session.put_credential(CRED_DATA) new_id = session.rename_credential(cred.id, "newname", "newissuer") with pytest.raises(ApduError): session.calculate(cred.id, b"challenge") session.calculate(new_id, b"challenge") @condition.min_version(5, 3) def test_rename_to_existing(self, session): cred = session.put_credential(CRED_DATA) new_id = session.rename_credential(cred.id, "newname", "newissuer")