예제 #1
0
 def test_modhex_decode(self):
     self.assertEqual(b"", modhex_decode(""))
     self.assertEqual(b"\x2d\x34\x4e\x83", modhex_decode("dteffuje"))
     self.assertEqual(
         b"\x69\xb6\x48\x1c\x8b\xab\xa2\xb6\x0e\x8f\x22\x17\x9b\x58\xcd\x56",
         modhex_decode("hknhfjbrjnlnldnhcujvddbikngjrtgh"),
     )
예제 #2
0
    def program_otp(self, slot, public_id, private_id, key, upload=False,
                    app_version='unknown'):
        key = a2b_hex(key)
        public_id = modhex_decode(public_id)
        private_id = a2b_hex(private_id)

        upload_url = None

        with self._open_device([OtpConnection]) as conn:
            if upload:
                try:
                    upload_url = prepare_upload_key(
                        key, public_id, private_id,
                        serial=self._dev_info['serial'],
                        user_agent='ykman-qt/' + app_version)
                except PrepareUploadFailed as e:
                    logger.debug('YubiCloud upload failed', exc_info=e)
                    return failure('upload_failed',
                                   {'upload_errors': [err.name
                                                      for err in e.errors]})
            try:
                session = YubiOtpSession(conn)
                session.put_configuration(
                    slot,
                    YubiOtpSlotConfiguration(public_id, private_id, key)
                )
            except CommandError as e:
                logger.debug("Failed to program YubiOTP", exc_info=e)
                return failure("write error")

        logger.debug('YubiOTP successfully programmed.')
        if upload_url:
            logger.debug('Upload url: %s', upload_url)

        return success({'upload_url': upload_url})
예제 #3
0
def parse_modhex_or_bcd(value):
    try:
        return True, modhex_decode(value)
    except ValueError:
        try:
            int(value)
            return False, bytes.fromhex(value)
        except ValueError:
            raise ValueError("value must be modhex or decimal")
예제 #4
0
def yubiotp(
    ctx,
    slot,
    public_id,
    private_id,
    key,
    no_enter,
    force,
    serial_public_id,
    generate_private_id,
    generate_key,
    upload,
):
    """
    Program a Yubico OTP credential.
    """

    info = ctx.obj["info"]
    session = ctx.obj["session"]

    if public_id and serial_public_id:
        ctx.fail(
            "Invalid options: --public-id conflicts with --serial-public-id.")

    if private_id and generate_private_id:
        ctx.fail(
            "Invalid options: --private-id conflicts with --generate-public-id."
        )

    if upload and force:
        ctx.fail("Invalid options: --upload conflicts with --force.")

    if key and generate_key:
        ctx.fail("Invalid options: --key conflicts with --generate-key.")

    if not public_id:
        if serial_public_id:
            serial = session.get_serial()
            if serial is None:
                cli_fail("Serial number not set, public ID must be provided")
            public_id = modhex_encode(b"\xff\x00" + struct.pack(b">I", serial))
            click.echo(f"Using YubiKey serial as public ID: {public_id}")
        elif force:
            ctx.fail("Public ID not given. Please remove the --force flag, or "
                     "add the --serial-public-id flag or --public-id option.")
        else:
            public_id = click_prompt("Enter public ID")

    try:
        public_id = modhex_decode(public_id)
    except KeyError:
        ctx.fail("Invalid public ID, must be modhex.")

    if not private_id:
        if generate_private_id:
            private_id = os.urandom(6)
            click.echo(
                f"Using a randomly generated private ID: {private_id.hex()}")
        elif force:
            ctx.fail(
                "Private ID not given. Please remove the --force flag, or "
                "add the --generate-private-id flag or --private-id option.")
        else:
            private_id = click_prompt("Enter private ID")
            private_id = bytes.fromhex(private_id)

    if not key:
        if generate_key:
            key = os.urandom(16)
            click.echo(f"Using a randomly generated secret key: {key.hex()}")
        elif force:
            ctx.fail(
                "Secret key not given. Please remove the --force flag, or "
                "add the --generate-key flag or --key option.")
        else:
            key = click_prompt("Enter secret key")
            key = bytes.fromhex(key)

    if not upload and not force:
        upload = click.confirm("Upload credential to YubiCloud?",
                               abort=False,
                               err=True)
    if upload:
        try:
            upload_url = prepare_upload_key(
                key,
                public_id,
                private_id,
                serial=info.serial,
                user_agent="ykman/" + __version__,
            )
            click.echo("Upload to YubiCloud initiated successfully.")
        except PrepareUploadFailed as e:
            error_msg = "\n".join(e.messages())
            cli_fail("Upload to YubiCloud failed.\n" + error_msg)

    force or click.confirm(
        f"Program a YubiOTP credential in slot {slot}?", abort=True, err=True)

    try:
        session.put_configuration(
            slot,
            YubiOtpSlotConfiguration(public_id, private_id,
                                     key).append_cr(not no_enter),
            ctx.obj["access_code"],
            ctx.obj["access_code"],
        )
    except CommandError as e:
        _failed_to_write_msg(ctx, e)

    if upload:
        click.echo("Opening upload form in browser: " + upload_url)
        webbrowser.open_new_tab(upload_url)