pax_global_header00006660000000000000000000000064147413067540014525gustar00rootroot0000000000000052 comment=3dff1807e811778e872796c28817ca5320194a26 nitrokey-udev-rules-1.1.0/000077500000000000000000000000001474130675400154615ustar00rootroot00000000000000nitrokey-udev-rules-1.1.0/.github/000077500000000000000000000000001474130675400170215ustar00rootroot00000000000000nitrokey-udev-rules-1.1.0/.github/workflows/000077500000000000000000000000001474130675400210565ustar00rootroot00000000000000nitrokey-udev-rules-1.1.0/.github/workflows/ci.yaml000066400000000000000000000013661474130675400223430ustar00rootroot00000000000000name: Continuous integration on: [push, pull_request] jobs: check: name: Run checks runs-on: ubuntu-latest container: python:3.11 steps: - name: Checkout repository uses: actions/checkout@v4 - name: Install dependencies run: | pip install poetry poetry install - name: Run checks run: make check PYRIGHT="poetry run pyright" RUFF="poetry run ruff" validate: name: Validate rules runs-on: ubuntu-latest container: python:3.11 steps: - name: Checkout repository uses: actions/checkout@v4 - name: Validate rules run: | cp 41-nitrokey.rules original make generate diff original 41-nitrokey.rules nitrokey-udev-rules-1.1.0/41-nitrokey.rules000066400000000000000000000055021474130675400206250ustar00rootroot00000000000000# Copyright (c) Nitrokey GmbH # SPDX-License-Identifier: CC0-1.0 # Here rules in new style should be provided. Matching devices should be tagged with 'uaccess'. # File prefix number should be lower than 73, to be correctly processed by the Udev. # Recommended udev version: >= 188. ACTION!="add|change", GOTO="u2f_end" # Nitrokey U2F KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2581", ATTRS{idProduct}=="f1d0", TAG+="uaccess" SUBSYSTEMS=="usb", ATTRS{idVendor}=="2581", ATTRS{idProduct}=="f1d0", TAG+="uaccess" # Nitrokey FIDO U2F KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="4287", TAG+="uaccess" SUBSYSTEMS=="usb", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="4287", TAG+="uaccess" # Nitrokey FIDO2 KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42b1", TAG+="uaccess" SUBSYSTEMS=="usb", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42b1", TAG+="uaccess" # Nitrokey 3A Mini/3A NFC/3C NFC KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42b2", TAG+="uaccess" SUBSYSTEMS=="usb", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42b2", TAG+="uaccess" # Nitrokey 3A NFC Bootloader/3C NFC Bootloader KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42dd", TAG+="uaccess" SUBSYSTEMS=="usb", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42dd", TAG+="uaccess" # Nitrokey 3A Mini Bootloader ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42e8", TAG+="uaccess" # Nitrokey Passkey KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42f3", TAG+="uaccess" SUBSYSTEMS=="usb", ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42f3", TAG+="uaccess" # Nitrokey Passkey Bootloader ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42f4", TAG+="uaccess" LABEL="u2f_end" SUBSYSTEM!="usb", GOTO="gnupg_rules_end" ACTION!="add", GOTO="gnupg_rules_end" # CryptoStick 1.2 ATTR{idVendor}=="20a0", ATTR{idProduct}=="4107", ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg", TAG+="uaccess" # Nitrokey Pro ATTR{idVendor}=="20a0", ATTR{idProduct}=="4108", ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg", TAG+="uaccess" # Nitrokey Pro Bootloader ATTRS{idVendor}=="20a0", ATTRS{idProduct}=="42b4", TAG+="uaccess" # Nitrokey Storage ATTR{idVendor}=="20a0", ATTR{idProduct}=="4109", ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg", TAG+="uaccess" # Nitrokey Storage Bootloader ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2ff1", TAG+="uaccess" # Nitrokey Start ATTR{idVendor}=="20a0", ATTR{idProduct}=="4211", ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg", TAG+="uaccess" # Nitrokey HSM ATTR{idVendor}=="20a0", ATTR{idProduct}=="4230", ENV{ID_SMARTCARD_READER}="1", ENV{ID_SMARTCARD_READER_DRIVER}="gnupg", TAG+="uaccess" LABEL="gnupg_rules_end" nitrokey-udev-rules-1.1.0/CHANGELOG.md000066400000000000000000000013631474130675400172750ustar00rootroot00000000000000# Changelog ## Unreleased - ## [v1.1.0][] (2025-01-14) [v1.1.0]: https://github.com/Nitrokey/nitrokey-udev-rules/releases/tag/v1.1.0 - Remove symlink rule for the Nitrokey Storage. Users are advised to use label- or UUID-based mounting or setup a a custom rule for their device instead. - Add rules for accessing HID devices with libusb. ## [v1.0.0][] (2024-01-29) [v1.0.0]: https://github.com/Nitrokey/nitrokey-udev-rules/releases/tag/v1.0.0 - Import [`41-nitrokey.rules`][] from libnitrokey. - Add rules for the Nitrokey Passkey: - Nitrokey Passkey (20a0:42f3) - Nitrokey Passkey Bootloader (20a0:42f4) [`41-nitrokey.rules`]: https://github.com/Nitrokey/libnitrokey/blob/834937476cf3aa551ed5ab6e766e9dc522d35c36/data/41-nitrokey.rules nitrokey-udev-rules-1.1.0/LICENSE000066400000000000000000000156101474130675400164710ustar00rootroot00000000000000Creative Commons Legal Code CC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; ii. moral rights retained by the original author(s) and/or performer(s); iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; v. rights protecting the extraction, dissemination, use and reuse of data in a Work; vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. nitrokey-udev-rules-1.1.0/Makefile000066400000000000000000000004051474130675400171200ustar00rootroot00000000000000PYTHON3 ?= python3 PYRIGHT ?= pyright RUFF ?= ruff .PHONY: check check: $(RUFF) check $(RUFF) format --diff $(PYRIGHT) .PHONY: fix fix: $(RUFF) check --fix $(RUFF) format .PHONY: generate generate: $(PYTHON3) generate.py devices.toml 41-nitrokey.rules nitrokey-udev-rules-1.1.0/README.md000066400000000000000000000016731474130675400167470ustar00rootroot00000000000000# Nitrokey udev Rules This repository contains udev rules for Nitrokey devices. Previously, these rules were maintained as a part of [libnitrokey][]. [libnitrokey]: https://github.com/Nitrokey/libnitrokey ## Usage and Requirements The [`41-nitrokey.rules`][] file contains udev rules for these devices: - Nitrokey 3 - Nitrokey HSM - Nitrokey FIDO U2F - Nitrokey FIDO2 - Nitrokey Passkey - Nitrokey Pro - Nitrokey Storage - Nitrokey Start - Nitrokey U2F [`41-nitrokey.rules`]: ./41-nitrokey.rules It requires udev 188 or later. For older udev versions, use the [`41-nitrokey_old.rules`][] from libnitrokey. [`41-nitrokey_old.rules`]: https://github.com/Nitrokey/libnitrokey/blob/master/data/41-nitrokey_old.rules The rules use the `uaccess` tag which is a systemd mechanism. To install the rules file, place it in `/etc/udev/rules.d`. The file prefix should be lower than 73 because the rules must be applied before udev’s `73-seat-late.rules`. nitrokey-udev-rules-1.1.0/devices.toml000066400000000000000000000022121474130675400177750ustar00rootroot00000000000000[[u2f]] name = "Nitrokey U2F" vid = 0x2581 pid = 0xf1d0 hid = true [[u2f]] name = "Nitrokey FIDO U2F" vid = 0x20a0 pid = 0x4287 hid = true [[u2f]] name = "Nitrokey FIDO2" vid = 0x20a0 pid = 0x42b1 hid = true [[u2f]] name = "Nitrokey 3A Mini/3A NFC/3C NFC" vid = 0x20a0 pid = 0x42b2 hid = true [[u2f]] name = "Nitrokey 3A NFC Bootloader/3C NFC Bootloader" vid = 0x20a0 pid = 0x42dd hid = true [[u2f]] name = "Nitrokey 3A Mini Bootloader" vid = 0x20a0 pid = 0x42e8 all = true [[u2f]] name = "Nitrokey Passkey" vid = 0x20a0 pid = 0x42f3 hid = true [[u2f]] name = "Nitrokey Passkey Bootloader" vid = 0x20a0 pid = 0x42f4 all = true [[ccid]] name = "CryptoStick 1.2" vid = 0x20a0 pid = 0x4107 gnupg = true [[ccid]] name = "Nitrokey Pro" vid = 0x20a0 pid = 0x4108 gnupg = true [[ccid]] name = "Nitrokey Pro Bootloader" vid = 0x20a0 pid = 0x42b4 all = true [[ccid]] name = "Nitrokey Storage" vid = 0x20a0 pid = 0x4109 gnupg = true [[ccid]] name = "Nitrokey Storage Bootloader" vid = 0x03eb pid = 0x2ff1 all = true [[ccid]] name = "Nitrokey Start" vid = 0x20a0 pid = 0x4211 gnupg = true [[ccid]] name = "Nitrokey HSM" vid = 0x20a0 pid = 0x4230 gnupg = true nitrokey-udev-rules-1.1.0/generate.py000066400000000000000000000072711474130675400176340ustar00rootroot00000000000000import argparse import dataclasses import textwrap import tomllib import typing @dataclasses.dataclass(frozen=True) class Device: name: str vid: int pid: int hid: bool = False gnupg: bool = False all: bool = False def generate(self) -> str: s = f"# {self.name}\n" attr_vid_pid = [ ("ATTR{idVendor}", "==", f"{self.vid:04x}"), ("ATTR{idProduct}", "==", f"{self.pid:04x}"), ] attrs_vid_pid = [ ("ATTRS{idVendor}", "==", f"{self.vid:04x}"), ("ATTRS{idProduct}", "==", f"{self.pid:04x}"), ] uaccess = [("TAG", "+=", "uaccess")] if self.hid: s += generate_rule( [("KERNEL", "==", "hidraw*"), ("SUBSYSTEM", "==", "hidraw")] + attrs_vid_pid + uaccess ) s += generate_rule([("SUBSYSTEMS", "==", "usb")] + attrs_vid_pid + uaccess) if self.gnupg: s += generate_rule( attr_vid_pid + [ ("ENV{ID_SMARTCARD_READER}", "=", "1"), ("ENV{ID_SMARTCARD_READER_DRIVER}", "=", "gnupg"), ] + uaccess ) if self.all: s += generate_rule(attrs_vid_pid + uaccess) return s @classmethod def from_dict(cls, data: dict[str, typing.Any]) -> "Device": return cls(**data) def generate_rule(matches: typing.Sequence[tuple[str, str, str]]) -> str: rules = [f'{key}{op}"{value}"' for (key, op, value) in matches] return ", ".join(rules) + "\n" def generate_u2f(devices: list[Device]) -> str: output = 'ACTION!="add|change", GOTO="u2f_end"\n' output += "\n" for device in devices: output += device.generate() output += "\n" output += 'LABEL="u2f_end"\n' return output def generate_ccid(devices: list[Device]) -> str: output = "" output += 'SUBSYSTEM!="usb", GOTO="gnupg_rules_end"\n' output += 'ACTION!="add", GOTO="gnupg_rules_end"\n' output += "\n" for device in devices: output += device.generate() output += "\n" output += 'LABEL="gnupg_rules_end"\n' return output def generate(u2f_devices: list[Device], ccid_devices: list[Device]) -> str: header = """\ # Copyright (c) Nitrokey GmbH # SPDX-License-Identifier: CC0-1.0 # Here rules in new style should be provided. Matching devices should be tagged with 'uaccess'. # File prefix number should be lower than 73, to be correctly processed by the Udev. # Recommended udev version: >= 188. """ sections = [] if u2f_devices: sections.append(generate_u2f(u2f_devices)) if ccid_devices: sections.append(generate_ccid(ccid_devices)) output = textwrap.dedent(header) output += "\n\n".join(sections) return output def run(input: str, output: str) -> None: with open(input, "rb") as f: data = tomllib.load(f) u2f_devices = [] if "u2f" in data: assert isinstance(data["u2f"], list) for device in data["u2f"]: assert isinstance(device, dict) u2f_devices.append(Device.from_dict(device)) ccid_devices = [] if "ccid" in data: assert isinstance(data["ccid"], list) for device in data["ccid"]: assert isinstance(device, dict) ccid_devices.append(Device.from_dict(device)) rules = generate(u2f_devices, ccid_devices) with open(output, "w") as f: f.write(rules) if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("input") parser.add_argument("output") args = parser.parse_args() run(args.input, args.output) nitrokey-udev-rules-1.1.0/poetry.lock000066400000000000000000000107561474130675400176660ustar00rootroot00000000000000# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "nodeenv" version = "1.9.1" description = "Node.js virtual environment builder" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, ] [[package]] name = "pyright" version = "1.1.390" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" files = [ {file = "pyright-1.1.390-py3-none-any.whl", hash = "sha256:ecebfba5b6b50af7c1a44c2ba144ba2ab542c227eb49bc1f16984ff714e0e110"}, {file = "pyright-1.1.390.tar.gz", hash = "sha256:aad7f160c49e0fbf8209507a15e17b781f63a86a1facb69ca877c71ef2e9538d"}, ] [package.dependencies] nodeenv = ">=1.6.0" typing-extensions = ">=4.1" [package.extras] all = ["nodejs-wheel-binaries", "twine (>=3.4.1)"] dev = ["twine (>=3.4.1)"] nodejs = ["nodejs-wheel-binaries"] [[package]] name = "ruff" version = "0.8.3" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ {file = "ruff-0.8.3-py3-none-linux_armv6l.whl", hash = "sha256:8d5d273ffffff0acd3db5bf626d4b131aa5a5ada1276126231c4174543ce20d6"}, {file = "ruff-0.8.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e4d66a21de39f15c9757d00c50c8cdd20ac84f55684ca56def7891a025d7e939"}, {file = "ruff-0.8.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c356e770811858bd20832af696ff6c7e884701115094f427b64b25093d6d932d"}, {file = "ruff-0.8.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c0a60a825e3e177116c84009d5ebaa90cf40dfab56e1358d1df4e29a9a14b13"}, {file = "ruff-0.8.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:75fb782f4db39501210ac093c79c3de581d306624575eddd7e4e13747e61ba18"}, {file = "ruff-0.8.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f26bc76a133ecb09a38b7868737eded6941b70a6d34ef53a4027e83913b6502"}, {file = "ruff-0.8.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:01b14b2f72a37390c1b13477c1c02d53184f728be2f3ffc3ace5b44e9e87b90d"}, {file = "ruff-0.8.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53babd6e63e31f4e96ec95ea0d962298f9f0d9cc5990a1bbb023a6baf2503a82"}, {file = "ruff-0.8.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ae441ce4cf925b7f363d33cd6570c51435972d697e3e58928973994e56e1452"}, {file = "ruff-0.8.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7c65bc0cadce32255e93c57d57ecc2cca23149edd52714c0c5d6fa11ec328cd"}, {file = "ruff-0.8.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5be450bb18f23f0edc5a4e5585c17a56ba88920d598f04a06bd9fd76d324cb20"}, {file = "ruff-0.8.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8faeae3827eaa77f5721f09b9472a18c749139c891dbc17f45e72d8f2ca1f8fc"}, {file = "ruff-0.8.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:db503486e1cf074b9808403991663e4277f5c664d3fe237ee0d994d1305bb060"}, {file = "ruff-0.8.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6567be9fb62fbd7a099209257fef4ad2c3153b60579818b31a23c886ed4147ea"}, {file = "ruff-0.8.3-py3-none-win32.whl", hash = "sha256:19048f2f878f3ee4583fc6cb23fb636e48c2635e30fb2022b3a1cd293402f964"}, {file = "ruff-0.8.3-py3-none-win_amd64.whl", hash = "sha256:f7df94f57d7418fa7c3ffb650757e0c2b96cf2501a0b192c18e4fb5571dfada9"}, {file = "ruff-0.8.3-py3-none-win_arm64.whl", hash = "sha256:fe2756edf68ea79707c8d68b78ca9a58ed9af22e430430491ee03e718b5e4936"}, {file = "ruff-0.8.3.tar.gz", hash = "sha256:5e7558304353b84279042fc584a4f4cb8a07ae79b2bf3da1a7551d960b5626d3"}, ] [[package]] name = "typing-extensions" version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] [metadata] lock-version = "2.0" python-versions = "^3.11" content-hash = "ffa1adbfa456ece54e1ece4615f9746cfe02098ab2ca093dc2d939fab9d850f4" nitrokey-udev-rules-1.1.0/pyproject.toml000066400000000000000000000002231474130675400203720ustar00rootroot00000000000000[tool.poetry] package-mode = false [tool.poetry.dependencies] python = "^3.11" [tool.poetry.group.dev.dependencies] pyright = "^1" ruff = "^0.8"