hirsch-securefido 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,24 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+ .venv/
9
+ .venv-*/
10
+ venv/
11
+
12
+ # Testing
13
+ .pytest_cache/
14
+ .coverage
15
+ htmlcov/
16
+ .ruff_cache/
17
+
18
+ # macOS
19
+ .DS_Store
20
+
21
+ # Editors
22
+ .idea/
23
+ .vscode/
24
+ *.swp
@@ -0,0 +1,82 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ # Homebrew formula for the Hirsch SecureFIDO device-configuration CLI.
5
+ #
6
+ # Regenerate the `resource` blocks after a dependency bump with:
7
+ # ./scripts/brew_release.sh <version>
8
+ #
9
+ # The top-level `sha256` is a placeholder until the sdist is published to
10
+ # PyPI; `scripts/brew_release.sh` fills in the real digest. See HOMEBREW.md.
11
+ class HirschSecurefido < Formula
12
+ include Language::Python::Virtualenv
13
+
14
+ desc "Device configuration CLI for Hirsch SecureKey FIDO2 authenticators"
15
+ homepage "https://github.com/hirschsecure/hirsch-securefido"
16
+ url "https://files.pythonhosted.org/packages/source/h/hirsch-securefido/hirsch_securefido-1.0.0.tar.gz"
17
+ sha256 "REPLACE_WITH_SDIST_SHA256"
18
+ license "BSD-3-Clause"
19
+
20
+ depends_on "python@3.12"
21
+
22
+ # cryptography ships prebuilt wheels, but Homebrew builds from source, so the
23
+ # Rust toolchain is required to compile its extension modules.
24
+ depends_on "rust" => :build
25
+
26
+ resource "cffi" do
27
+ url "https://files.pythonhosted.org/packages/9e/ef/008a1939e372c06329a3fce4279c02f328488f3526744906eeec3da7ad5f/cffi-2.1.1.tar.gz"
28
+ sha256 "dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be"
29
+ end
30
+
31
+ resource "pycparser" do
32
+ url "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz"
33
+ sha256 "600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29"
34
+ end
35
+
36
+ resource "cryptography" do
37
+ url "https://files.pythonhosted.org/packages/bb/ad/5d6702db60b1e40b41ef513b6967ff5848f307d50f8449baf1634f5908f1/cryptography-50.0.1.tar.gz"
38
+ sha256 "5dd9bda1c12b4162f6ff568eeb5e0ff956c28d14406e875cfe8a63a2d414ff20"
39
+ end
40
+
41
+ resource "fido2" do
42
+ url "https://files.pythonhosted.org/packages/ba/ea/6f08c354b7aeb8019249d46a86c2153f8218499cced4d21bf16b6d49fc16/fido2-2.2.1.tar.gz"
43
+ sha256 "85787428a94c3f8eaf72f0ff30afba983b559a1b1b795c93318c81b4ad4062c4"
44
+ end
45
+
46
+ def install
47
+ virtualenv_install_with_resources
48
+ end
49
+
50
+ def caveats
51
+ <<~EOS
52
+ hirsch-securefido talks to FIDO2 authenticators over USB HID.
53
+ No sudo is required on macOS: HID access is granted to the console user.
54
+
55
+ Verify your setup with:
56
+ hirsch-securefido list
57
+ hirsch-securefido info
58
+
59
+ NFC / smart-card readers need the optional pyscard extra:
60
+ $(brew --prefix)/opt/hirsch-securefido/libexec/bin/pip install pyscard
61
+ EOS
62
+ end
63
+
64
+ test do
65
+ # Version reporting must work with no hardware attached.
66
+ assert_match version.to_s, shell_output("#{bin}/hirsch-securefido --version")
67
+
68
+ # Help must advertise exactly the four device-configuration commands.
69
+ help = shell_output("#{bin}/hirsch-securefido --help")
70
+ ["info", "set-pin", "change-pin", "reset"].each do |cmd|
71
+ assert_match cmd, help
72
+ end
73
+
74
+ # With no authenticator present the tool must exit 2 (EXIT_NO_DEVICE)
75
+ # rather than crash with a traceback.
76
+ output = shell_output("#{bin}/hirsch-securefido info 2>&1", 2)
77
+ assert_match "No Hirsch FIDO2 token found", output
78
+
79
+ # The library must import cleanly inside the virtualenv.
80
+ system libexec/"bin/python", "-c", "import hirsch_securefido"
81
+ end
82
+ end
@@ -0,0 +1,207 @@
1
+ # Publishing to Homebrew
2
+
3
+ How to get `brew install hirsch-securefido` working on macOS.
4
+
5
+ ## The ordering constraint
6
+
7
+ Homebrew does not host code. The formula points at a **PyPI sdist** and builds
8
+ from it, so the order is fixed:
9
+
10
+ ```
11
+ 1. publish to PyPI -> 2. get the sdist sha256 -> 3. update the formula -> 4. push the tap
12
+ ```
13
+
14
+ Step 3 cannot happen first. Until the version exists on PyPI there is nothing
15
+ to checksum, which is why `Formula/hirsch-securefido.rb` currently carries
16
+ `REPLACE_WITH_SDIST_SHA256`.
17
+
18
+ ## Which distribution channel?
19
+
20
+ | | Formula (`brew install`) | Cask (`brew install --cask`) |
21
+ | --- | --- | --- |
22
+ | For | CLI tools built from source | prebuilt `.app` bundles, binaries |
23
+ | Ours | **yes** | no |
24
+
25
+ This is a command-line tool, so it is a **formula**. A cask would only apply if
26
+ you later ship a `.app`, which would additionally need codesigning and
27
+ notarization.
28
+
29
+ ## Which tap?
30
+
31
+ **Your own tap** (`hirschsecure/homebrew-tap`) is the right choice here:
32
+
33
+ - You control releases; no third-party review queue.
34
+ - homebrew-core requires notability (roughly 30+ GitHub stars, 30+ forks, or
35
+ equivalent usage) and rejects vendor-specific hardware tools that only work
36
+ with one company's device. This tool is gated to Hirsch VID `04E6`, so it
37
+ would very likely be declined.
38
+
39
+ Users then run:
40
+
41
+ ```bash
42
+ brew tap hirschsecure/tap
43
+ brew install hirsch-securefido
44
+ ```
45
+
46
+ `brew tap hirschsecure/tap` resolves to `github.com/hirschsecure/homebrew-tap`.
47
+ The `homebrew-` prefix on the repo name is required; the tap name drops it.
48
+
49
+ ---
50
+
51
+ ## Step 1 - Publish to PyPI
52
+
53
+ One-time: register the project name and configure Trusted Publishing at
54
+ <https://pypi.org/manage/account/publishing/>
55
+
56
+ | Field | Value |
57
+ | --- | --- |
58
+ | PyPI project | `hirsch-securefido` |
59
+ | Owner | `hirschsecure` |
60
+ | Repository | `hirsch-securefido` |
61
+ | Workflow | `publish.yml` |
62
+ | Environment | `pypi` |
63
+
64
+ Then release:
65
+
66
+ ```bash
67
+ git tag v1.0.0
68
+ git push origin v1.0.0
69
+ ```
70
+
71
+ The `Publish` workflow verifies the tag matches `pyproject.toml`, builds, and
72
+ uploads with no stored API token. Confirm it worked:
73
+
74
+ ```bash
75
+ pip download --no-deps --no-binary :all: hirsch-securefido==1.0.0
76
+ ```
77
+
78
+ ### Manual alternative
79
+
80
+ ```bash
81
+ python -m build
82
+ twine upload --repository testpypi dist/* # rehearse
83
+ twine upload dist/*
84
+ ```
85
+
86
+ ## Step 2 - Create the tap repository
87
+
88
+ Once, on GitHub: create a **public** repo named `homebrew-tap` under the
89
+ `hirschsecure` org.
90
+
91
+ ```bash
92
+ git clone https://github.com/hirschsecure/homebrew-tap
93
+ cd homebrew-tap
94
+ mkdir -p Formula
95
+ ```
96
+
97
+ The `Formula/` subdirectory name matters; Homebrew looks there.
98
+
99
+ ## Step 3 - Update the formula for the release
100
+
101
+ On macOS, from this repository:
102
+
103
+ ```bash
104
+ ./scripts/brew_release.sh 1.0.0
105
+ ```
106
+
107
+ That script:
108
+
109
+ 1. confirms `1.0.0` is actually on PyPI, and stops if not
110
+ 2. rewrites the formula's `url` and `sha256` from the real sdist
111
+ 3. regenerates the Python `resource` blocks via `brew update-python-resources`
112
+ 4. runs `ruby -c` and `brew style`
113
+
114
+ It edits **only** the formula's own url/sha256; the four vendored resource
115
+ digests are left untouched.
116
+
117
+ ## Step 4 - Validate locally before publishing
118
+
119
+ Do not skip this. It is the first time the formula is genuinely exercised.
120
+
121
+ ```bash
122
+ brew install --build-from-source Formula/hirsch-securefido.rb
123
+ brew test hirsch-securefido
124
+ brew audit --strict --new hirsch-securefido
125
+ ```
126
+
127
+ - `install` proves the virtualenv builds and every resource resolves. Expect a
128
+ few minutes: `cryptography` compiles Rust extensions from source.
129
+ - `test` runs the formula's test block: `--version`, the four commands in
130
+ `--help`, and the exit-2 no-device contract.
131
+ - `audit --strict --new` applies the rules a new formula must satisfy.
132
+
133
+ Then confirm it behaves with hardware attached:
134
+
135
+ ```bash
136
+ hirsch-securefido # interactive menu
137
+ hirsch-securefido info # must work WITHOUT sudo
138
+ ```
139
+
140
+ ## Step 5 - Push the tap
141
+
142
+ ```bash
143
+ cp Formula/hirsch-securefido.rb ../homebrew-tap/Formula/
144
+ cd ../homebrew-tap
145
+ git add Formula/hirsch-securefido.rb
146
+ git commit -m "hirsch-securefido 1.0.0"
147
+ git push
148
+ ```
149
+
150
+ Verify as a user would, from a clean state:
151
+
152
+ ```bash
153
+ brew untap hirschsecure/tap 2>/dev/null || true
154
+ brew tap hirschsecure/tap
155
+ brew install hirsch-securefido
156
+ hirsch-securefido --version
157
+ ```
158
+
159
+ ## Subsequent releases
160
+
161
+ ```bash
162
+ # 1. bump the version in pyproject.toml AND src/hirsch_securefido/__init__.py
163
+ # (tests/test_packaging.py fails if they disagree)
164
+ git tag v1.1.0 && git push origin v1.1.0 # publishes to PyPI
165
+
166
+ # 2. refresh and validate the formula
167
+ ./scripts/brew_release.sh 1.1.0
168
+ brew install --build-from-source Formula/hirsch-securefido.rb
169
+ brew test hirsch-securefido
170
+
171
+ # 3. ship it
172
+ cp Formula/hirsch-securefido.rb ../homebrew-tap/Formula/
173
+ cd ../homebrew-tap && git commit -am "hirsch-securefido 1.1.0" && git push
174
+ ```
175
+
176
+ Users upgrade with `brew update && brew upgrade hirsch-securefido`.
177
+
178
+ ## Troubleshooting
179
+
180
+ **`SHA256 mismatch`** - the formula's checksum does not match what PyPI served.
181
+ Re-run `./scripts/brew_release.sh <version>`. Never edit a digest by hand.
182
+
183
+ **`Could not find a version that satisfies the requirement`** - a transitive
184
+ dependency lacks a `resource` block. Re-run `brew update-python-resources`.
185
+
186
+ **`cryptography` fails to build** - the Rust toolchain is missing. The formula
187
+ declares `depends_on "rust" => :build`; confirm `brew install rust` succeeds.
188
+
189
+ **`Error: Formula is not in the tap`** - the file must be at
190
+ `Formula/hirsch-securefido.rb`, and the class name `HirschSecurefido` must match
191
+ the filename. `tests/test_formula.py` checks that pairing.
192
+
193
+ **`No such keg`** on `brew test` - install before testing.
194
+
195
+ ## What is verified, and what is not
196
+
197
+ Automated in CI (`.github/workflows/ci.yml`, `formula` job on macOS):
198
+ `ruby -c` parses the formula, and `brew style` audits it.
199
+
200
+ Also checked offline by `tests/test_formula.py`: block balance, required
201
+ fields, audit-sensitive `desc` style, version agreement, full-length resource
202
+ digests, and that the test block asserts real behavior. Resource URLs are
203
+ confirmed reachable on PyPI over the network.
204
+
205
+ **Not verified here**, because this repo has no macOS host, no Ruby, and no
206
+ Hirsch key: `brew install`, `brew test`, `brew audit`, and every USB HID path.
207
+ Step 4 is the first time those run. Treat it as required, not optional.
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2026, Hirsch Secure, Inc.
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+
10
+ 2. Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+
14
+ 3. Neither the name of the copyright holder nor the names of its
15
+ contributors may be used to endorse or promote products derived from
16
+ this software without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,233 @@
1
+ Metadata-Version: 2.5
2
+ Name: hirsch-securefido
3
+ Version: 1.0.0
4
+ Summary: Device configuration for Hirsch SecureKey FIDO2 authenticators: device info, set PIN, change PIN, factory reset.
5
+ Project-URL: Homepage, https://github.com/hirschsecure/hirsch-securefido
6
+ Project-URL: Repository, https://github.com/hirschsecure/hirsch-securefido
7
+ Project-URL: Issues, https://github.com/hirschsecure/hirsch-securefido/issues
8
+ Author: Hirsch Secure, Inc.
9
+ License: Copyright (c) 2026, Hirsch Secure, Inc.
10
+ All rights reserved.
11
+
12
+ Redistribution and use in source and binary forms, with or without
13
+ modification, are permitted provided that the following conditions are met:
14
+
15
+ 1. Redistributions of source code must retain the above copyright notice, this
16
+ list of conditions and the following disclaimer.
17
+
18
+ 2. Redistributions in binary form must reproduce the above copyright notice,
19
+ this list of conditions and the following disclaimer in the documentation
20
+ and/or other materials provided with the distribution.
21
+
22
+ 3. Neither the name of the copyright holder nor the names of its
23
+ contributors may be used to endorse or promote products derived from
24
+ this software without specific prior written permission.
25
+
26
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
29
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
30
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
34
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36
+ License-File: LICENSE
37
+ Keywords: ctap,fido2,hirsch,macos,passkey,security-key,webauthn
38
+ Classifier: Development Status :: 5 - Production/Stable
39
+ Classifier: Environment :: Console
40
+ Classifier: Intended Audience :: System Administrators
41
+ Classifier: License :: OSI Approved :: BSD License
42
+ Classifier: Operating System :: MacOS :: MacOS X
43
+ Classifier: Programming Language :: Python :: 3
44
+ Classifier: Programming Language :: Python :: 3.10
45
+ Classifier: Programming Language :: Python :: 3.11
46
+ Classifier: Programming Language :: Python :: 3.12
47
+ Classifier: Programming Language :: Python :: 3.13
48
+ Classifier: Topic :: Security
49
+ Classifier: Topic :: System :: Hardware
50
+ Requires-Python: >=3.10
51
+ Requires-Dist: fido2>=1.1.0
52
+ Provides-Extra: dev
53
+ Requires-Dist: build>=1.0; extra == 'dev'
54
+ Requires-Dist: pytest-cov>=4.1; extra == 'dev'
55
+ Requires-Dist: pytest>=7.4; extra == 'dev'
56
+ Requires-Dist: ruff>=0.4; extra == 'dev'
57
+ Requires-Dist: twine>=5.0; extra == 'dev'
58
+ Provides-Extra: pcsc
59
+ Requires-Dist: pyscard>=2.0.0; extra == 'pcsc'
60
+ Description-Content-Type: text/markdown
61
+
62
+ # Hirsch SecureFIDO Device Config
63
+
64
+ Command-line device configuration for Hirsch SecureKey / SecureKey GOV FIDO2
65
+ authenticators on macOS.
66
+
67
+ This is the **device configuration subset** of the Windows
68
+ *Hirsch SecureFIDO Cred Manager*. It provides exactly four operations:
69
+
70
+ | Command | Description | Origin |
71
+ | --- | --- | --- |
72
+ | `info` | Read authenticator info: AAGUID, versions, options, limits, PIN retries | ported from the Windows app |
73
+ | `set-pin` | Set the initial PIN on a factory-fresh token | new |
74
+ | `change-pin` | Change an existing PIN | new |
75
+ | `reset` | Factory reset: erase all credentials and clear the PIN | new |
76
+
77
+ Only the device-info view existed in the Windows original. It read
78
+ authenticator info and rendered the AAGUID, versions, options, and limits;
79
+ that logic, including the option labels and COSE algorithm names, is carried
80
+ over here. The Windows app could *report* on the PIN (retry count, whether one
81
+ was set) and could surface PIN errors, but it had no way to set, change, or
82
+ clear one: it only ever called `getPinToken` to authenticate credential
83
+ management. Set PIN, Change PIN, and Factory Reset are therefore newly
84
+ implemented against CTAP `authenticatorClientPIN` and `authenticatorReset`.
85
+
86
+ Credential enumeration and deletion, which were the Windows app's main
87
+ purpose, are intentionally **not** included.
88
+
89
+ ## Install
90
+
91
+ ### Homebrew
92
+
93
+ ```bash
94
+ brew tap hirschsecure/tap
95
+ brew install hirsch-securefido
96
+ ```
97
+
98
+ ### PyPI
99
+
100
+ ```bash
101
+ pip install hirsch-securefido
102
+
103
+ # with NFC / CCID reader support
104
+ pip install "hirsch-securefido[pcsc]"
105
+ ```
106
+
107
+ ## Usage
108
+
109
+ Run it with no arguments for an interactive menu:
110
+
111
+ ```
112
+ $ hirsch-securefido
113
+
114
+ Hirsch SecureFIDO Device Config v1.0.0
115
+ --------------------------------------------------------
116
+ Hirsch SecureKey USB HID (VID 04E6) PIN set, 8 attempt(s) left
117
+ --------------------------------------------------------
118
+
119
+ 1 Device information
120
+ 2 Set PIN
121
+ 3 Change PIN
122
+ 4 Factory reset
123
+ r Refresh device status
124
+ q Quit
125
+
126
+ Select an option:
127
+ ```
128
+
129
+ The header shows live device status and refreshes after every action. The
130
+ menu appears **only** on an interactive terminal: with a piped stdin or
131
+ redirected stdout the tool prints help and exits, so scripts never block on a
132
+ prompt.
133
+
134
+ Every operation is also available directly:
135
+
136
+ ```bash
137
+ hirsch-securefido info # human-readable device report
138
+ hirsch-securefido info --json # machine-readable, for MDM tooling
139
+ hirsch-securefido list # enumerate connected authenticators
140
+
141
+ hirsch-securefido set-pin # prompts for a new PIN, twice
142
+ hirsch-securefido change-pin # prompts for current then new PIN
143
+ hirsch-securefido reset # requires typing RESET, then a touch
144
+ ```
145
+
146
+ By default only Hirsch authenticators (USB Vendor ID `04E6`) are accepted.
147
+ Pass `--any-vendor` to work with other FIDO2 keys.
148
+
149
+ ### Unattended use
150
+
151
+ PINs may be supplied by environment variable for provisioning scripts:
152
+
153
+ ```bash
154
+ HIRSCH_PIN=oldpin HIRSCH_NEW_PIN=newpin hirsch-securefido change-pin
155
+ hirsch-securefido reset --yes
156
+ ```
157
+
158
+ PINs are also read from stdin when it is not a TTY:
159
+
160
+ ```bash
161
+ echo "123456" | hirsch-securefido set-pin
162
+ ```
163
+
164
+ ### Exit codes
165
+
166
+ | Code | Meaning |
167
+ | --- | --- |
168
+ | `0` | Success |
169
+ | `1` | General device error |
170
+ | `2` | No authenticator found |
171
+ | `3` | PIN rejected or blocked |
172
+ | `4` | Operation unsupported by the token |
173
+ | `130` | Cancelled by the user |
174
+
175
+ ## Factory reset
176
+
177
+ CTAP requires a factory reset to arrive within a few seconds of the token
178
+ powering up, confirmed by a physical touch. The `reset` command therefore
179
+ prompts you to unplug and re-insert the token first. If you see
180
+ "a factory reset must be started within a few seconds", re-insert the key and
181
+ run the command again promptly.
182
+
183
+ A reset destroys every passkey on the device and clears the PIN. You will lose
184
+ access to any account that relies on this key as its only factor.
185
+
186
+ ## macOS notes
187
+
188
+ The Windows original auto-elevated to Administrator, because non-elevated
189
+ Windows processes cannot open FIDO HID authenticators. macOS grants HID access
190
+ to the console user, so **no `sudo` is required**. The Windows-only
191
+ `winscard.dll` / `cfgmgr32.dll` hardware gate and disconnect watchdog have been
192
+ replaced by a direct USB Vendor ID check on the CTAP HID descriptor.
193
+
194
+ `pyscard` is optional. Install the `pcsc` extra only if you need NFC or
195
+ smart-card readers; it requires the PCSC-Lite build toolchain.
196
+
197
+ ## Development
198
+
199
+ ```bash
200
+ pip install -e ".[dev]"
201
+ pytest
202
+ ruff check src tests
203
+ ```
204
+
205
+ The test suite runs entirely against software authenticators, so no hardware is
206
+ needed. It has two layers:
207
+
208
+ - `tests/test_device.py`, `tests/test_cli.py` stub out `fido2` to test error
209
+ handling and CLI behavior in isolation.
210
+ - `tests/test_integration.py` runs the **real** python-fido2 stack (CBOR, ECDH
211
+ key agreement, AES-CBC PIN encryption, HMAC `pinUvAuthParam`) against the
212
+ virtual authenticator in `tests/virtual_authenticator.py`, then asserts on
213
+ the resulting device state.
214
+
215
+ To confirm the suite actually detects bugs rather than passing vacuously:
216
+
217
+ ```bash
218
+ python scripts/mutation_check.py
219
+ ```
220
+
221
+ This injects plausible defects (swapped PIN arguments, a skipped guard, a reset
222
+ that never fires) and requires the suite to fail on each one.
223
+
224
+ ### What software cannot verify
225
+
226
+ USB HID transport, the CTAP reset power-up window, and the physical touch
227
+ confirmation have no software equivalent. `brew install` likewise needs macOS.
228
+ These are covered by the manual hardware checklist in
229
+ [PUBLISHING.md](PUBLISHING.md).
230
+
231
+ ## License
232
+
233
+ BSD 3-Clause. See [LICENSE](LICENSE).
@@ -0,0 +1,118 @@
1
+ # Publishing
2
+
3
+ Release checklist for `hirsch-securefido`.
4
+
5
+ ## 1. Pre-flight
6
+
7
+ ```bash
8
+ pip install -e ".[dev]"
9
+ ruff check src tests
10
+ pytest -q
11
+ python scripts/mutation_check.py # suite must detect all injected bugs
12
+ python -m build
13
+ twine check dist/*
14
+ ```
15
+
16
+ Confirm the version in `pyproject.toml` and `src/hirsch_securefido/__init__.py`
17
+ match, and that `CHANGELOG` / README reflect the release. `tests/test_packaging.py`
18
+ and `tests/test_formula.py` enforce that agreement automatically.
19
+
20
+ ## 2. PyPI
21
+
22
+ ### Automated (recommended)
23
+
24
+ The `Publish` workflow uses PyPI **Trusted Publishing** (OIDC), so no API token
25
+ is stored in the repository.
26
+
27
+ One-time setup at
28
+ <https://pypi.org/manage/project/hirsch-securefido/settings/publishing/>:
29
+
30
+ | Field | Value |
31
+ | --- | --- |
32
+ | Owner | `hirschsecure` |
33
+ | Repository | `hirsch-securefido` |
34
+ | Workflow | `publish.yml` |
35
+ | Environment | `pypi` |
36
+
37
+ Then release by tagging:
38
+
39
+ ```bash
40
+ git tag v1.0.0
41
+ git push origin v1.0.0
42
+ ```
43
+
44
+ The workflow refuses to publish if the tag does not match the packaged version.
45
+
46
+ ### Manual
47
+
48
+ ```bash
49
+ python -m build
50
+ twine upload --repository testpypi dist/* # rehearse first
51
+ twine upload dist/*
52
+ ```
53
+
54
+ Verify the published artifact installs cleanly:
55
+
56
+ ```bash
57
+ python -m venv /tmp/verify && /tmp/verify/bin/pip install hirsch-securefido
58
+ /tmp/verify/bin/hirsch-securefido --version
59
+ ```
60
+
61
+ ## 3. Homebrew
62
+
63
+ See **[HOMEBREW.md](HOMEBREW.md)** for the full walkthrough: tap setup,
64
+ formula-vs-cask, local validation, and troubleshooting.
65
+
66
+ Short version, after the PyPI release is live:
67
+
68
+ ```bash
69
+ ./scripts/brew_release.sh 1.0.0 # rewrites url + sha256, refreshes resources
70
+ brew install --build-from-source Formula/hirsch-securefido.rb
71
+ brew test hirsch-securefido
72
+ brew audit --strict --new hirsch-securefido
73
+ ```
74
+
75
+ Then publish to the tap:
76
+
77
+ ```bash
78
+ cp Formula/hirsch-securefido.rb ../homebrew-tap/Formula/
79
+ cd ../homebrew-tap && git commit -am "hirsch-securefido 1.0.0" && git push
80
+ ```
81
+
82
+ Users install with:
83
+
84
+ ```bash
85
+ brew tap hirschsecure/tap
86
+ brew install hirsch-securefido
87
+ ```
88
+
89
+ ## 4. Post-release
90
+
91
+ ```bash
92
+ brew uninstall hirsch-securefido && brew install hirschsecure/tap/hirsch-securefido
93
+ hirsch-securefido info # with a real Hirsch key attached
94
+ ```
95
+
96
+ ## Manual hardware verification
97
+
98
+ Everything below is **outside** what the automated suite can reach. The tests
99
+ use a virtual authenticator that speaks real CTAP2 CBOR, so protocol logic is
100
+ covered, but USB HID transport, the reset power-up window, and the physical
101
+ touch have no software equivalent.
102
+
103
+ Run this once per release on macOS with a real Hirsch SecureKey:
104
+
105
+ - [ ] `hirsch-securefido list` shows the key over USB HID
106
+ - [ ] `hirsch-securefido info` reports the true AAGUID and firmware version
107
+ - [ ] `info` runs **without `sudo`** (the core macOS port claim)
108
+ - [ ] `set-pin` on a factory-fresh token
109
+ - [ ] `change-pin` with the correct current PIN
110
+ - [ ] `change-pin` with a wrong PIN decrements the retry counter shown by `info`
111
+ - [ ] `reset` refused when run outside the power-up window
112
+ - [ ] `reset` succeeds when the token is re-inserted, and the touch prompt appears
113
+ - [ ] `reset` cancelled by declining the touch, leaving credentials intact
114
+ - [ ] After a successful `reset`, `info` reports `PIN Set: No`
115
+ - [ ] NFC path with `pip install pyscard` and a card on a reader, if in scope
116
+
117
+ Record the macOS version, the key's firmware version, and the fido2 version
118
+ used, so a regression can be attributed later.