pwn 0.5.785 → 0.5.787

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.
@@ -1,181 +1,123 @@
1
1
  # YubiKey device operations
2
2
 
3
- `PWN::Plugins::YubiKey` is a native Ruby wrapper around Yubico's official
4
- `ykman` executable. It is not a USB driver, a replacement for Yubico's CLI, or a
5
- WebAuthn relying party. The command grammar is based on **YubiKey Manager
6
- 5.9.1**. Hardware capability and firmware still determine which commands work.
3
+ `PWN::Plugins::YubiKey` implements YubiKey application protocols in Ruby. It
4
+ talks to the device through HIDAPI and PC/SC (`::FFI` bindings to those host
5
+ libraries). It does not start `ykman`, another device CLI, a Python helper, or
6
+ any subprocess. It is not a WebAuthn relying party.
7
+
8
+ Hardware capability and firmware still decide which operations succeed. Fixture
9
+ tests are not physical-key interoperability, and this page does not claim every
10
+ model or firmware revision.
7
11
 
8
12
  For encrypted configuration, credential storage, conversion and recovery, see
9
13
  [YubiKey-backed vaults and authentication](YubiKey-Vaults.md).
10
14
 
11
15
  ## Dependency and discovery
12
16
 
13
- Install the official **yubikey-manager** package using Yubico's installation
14
- instructions or your platform package manager. PWN reports `ykman` as an optional
15
- hardware tool; merely loading this module does not install software or access a
16
- device. The official executable has its own Python/native-library dependencies;
17
- PWN does not ship a Python helper. PC/SC, USB permissions and enabled device
18
- interfaces must be configured locally as appropriate.
17
+ `required_bins` is empty. Core setup does not install hardware packages. The
18
+ `hardware` profile maps optional host libraries (`hidapi`, `pcsc`), not an
19
+ executable. Loading the module does not open a device. `available?` only probes
20
+ those libraries. A missing library, a missing device, a permission failure and
21
+ an unsupported application are different conditions. Preflight reports
22
+ `FIDO native transport` and `PC/SC native transport` without discovering
23
+ hardware. See [Plugin Degradation](Plugin-Degradation.md#pwn-plugins-yubikey).
19
24
 
20
25
  ```ruby
21
26
  require 'pwn'
22
27
  yk = PWN::Plugins::YubiKey
23
- yk.required_bins # => ['ykman']
24
- yk.available? # --version only, no device operation
25
- yk.devices # decimal serial Strings, or []
26
- yk.info(serial: '12345678') # example serial; replace locally
28
+ yk.required_bins # => []
29
+ yk.available?(backend: 'hid') # library probe; no device open
30
+ yk.devices # explicit listing; may be []
31
+ yk.info(serial: '12345678') # example serial; replace locally
27
32
  yk.otp(args: ['info'], serial: '12345678')
28
- yk.help # all public methods and option descriptions
29
- yk.cli_help # official --full-help, including sd and apdu
33
+ yk.help # public methods and option descriptions
34
+ yk.cli_help # local native help metadata, not a CLI process
30
35
  yk.cli_help(path: %w[piv keys generate])
31
36
  ```
32
37
 
33
- `ykman: '/trusted/path/to/ykman'` selects an executable explicitly. Do not load
34
- executable paths from untrusted vault metadata. Serial values must be decimal
35
- **Strings**; they are not trimmed, coerced or repaired. Omit the serial only when
36
- device selection is unambiguous. PWN never chooses the first of several devices.
37
- `reader:` selects a smartcard reader instead and cannot be combined with `serial:`;
38
- it is not supported by the upstream `list` command.
38
+ There is no `ykman:` path option. Executable paths, stdin, log files and inline
39
+ credential argv are rejected. Serial values must be decimal Strings; they are
40
+ not trimmed or coerced. Omit the serial only when selection is unambiguous.
41
+ PWN never chooses the first of several devices. `reader:` and `device_path:`
42
+ are mutually exclusive with each other and with `serial:` when more than one
43
+ is set. A caller-owned `connection:` stays open; PWN closes only handles it
44
+ opened.
39
45
 
40
- ## Management coverage
46
+ ## Native application coverage
41
47
 
42
- Every family method accepts `args:` as an argv Array **after the family name**.
43
- With no `args`, it returns that family's official help. `command(args:)` accepts
44
- the complete management argv after global selection/security options. It returns
45
- raw binary-tagged stdout; it does not print or log the result.
48
+ Family methods take `args:` as an argv Array **after the family name**. With
49
+ no `args`, they return that family's local help metadata. `command(args:)`
50
+ takes the complete nonsecret argv, including the family name, and dispatches
51
+ to the matching Ruby application. It returns the application result; it does
52
+ not print or log it.
46
53
 
47
- | Method | Official family | Covered management operations |
54
+ | Method | Application | Covered operations |
48
55
  |---|---|---|
49
- | `config` | `config` | USB/NFC application enablement, mode, lock code, timeouts, reset |
50
- | `otp` | `otp` | Slot inventory, Yubico OTP, static password, HOTP, HMAC/TOTP challenge-response, settings, NDEF, swap, delete |
51
- | `oath` | `oath` | Account list/add/URI/import/rename/delete/code, access password, remembered-password state, reset |
52
- | `fido` | `fido` | PIN changes/verification, minimum PIN length, resident credential list/delete, fingerprints, UV/attestation policy, reset |
53
- | `piv` | `piv` | PIN/PUK/management key, key generation/import/export/move/delete/attestation, certificates, CSR and object management, reset |
54
- | `openpgp` | `openpgp` | PIN/admin/reset code and retry policy, key import/attestation/touch policy, certificates, reset |
55
- | `hsmauth` | `hsmauth` | YubiHSM Auth asymmetric/symmetric/derived credentials, credential passwords, management protection, reset |
56
- | `security_domain` | **`sd`** | SCP key information, generation/import/export/deletion/allowlists and reset |
57
- | `apdu` | `apdu` | Explicit raw APDUs, application selection, full-APDU mode and response formatting |
58
-
59
- The wrapper recognizes the complete 5.9.1 management command tree, including
60
- hidden `sd` and `apdu`. It intentionally does **not** run `ykman script`, arbitrary
61
- Python, diagnostic logging, inline credential options, or unknown future commands.
62
- New command grammar must be reviewed before extending the allowlist. All known
63
- commands remain discoverable through `cli_help`. Protected prompts use the
64
- explicit private-terminal `interact` method rather than secret command arguments.
65
-
66
- Use **long options as separate argv elements**. Short aliases and `--option=value`
67
- are rejected to avoid credential-alias ambiguity; `--` is available before a
68
- positional identifier beginning with a dash. Do not pass shell command strings.
69
- For example, `args: ['keys', 'info', '9a']`, not `args: 'keys info 9a'`.
70
-
71
- ### Confirmation and execution boundaries
72
-
73
- Only a narrow read-only allowlist runs without confirmation: device inventory,
74
- basic application information, interface `--list`, HSM Auth credential listing,
75
- canonical help/version calls, and validated HMAC calculation. Other operations
76
- require **literal `confirm: true`**. This includes key provisioning, reset,
77
- authentication attempts, local exports, unknown commands, OATH account listing
78
- and code generation, and **every raw APDU**, even an APDU the
79
- caller believes is a read. Confirmation does not make unknown grammar acceptable.
80
-
81
- OATH account listing requires consent even without `--remember`: upstream
82
- authenticates with a remembered credential and may delete and persist that
83
- remembered key on authentication failure before prompting.
84
- Device discovery may still update ykman's noncredential local device-history cache;
85
- the allowlist is not a guarantee of no host filesystem writes.
86
-
87
- PWN never adds `--force` to destructive operations. A caller may explicitly supply
88
- that flag after consenting, but `confirm: true` alone does not automatically answer
89
- the CLI's prompts. Normal commands close stdin and capture output; they are not
90
- interactive terminal sessions. A prompt may abort or time out. Do not retry an
91
- uncertain mutation blindly: inspect device state first. The one automated secret
92
- input path is the fixed, version-checked HMAC provisioning method below.
93
-
94
- - Execution uses argv vectors, never a shell. Arguments and outputs are bounded.
95
- A child-only `0077` umask restricts newly created output files; it does not
96
- repair permissions on existing files or suppress the CLI's generated secrets.
97
- - `timeout:` is a finite positive number of seconds, default **30** per child.
98
- OATH retrieval shares one budget across inventory and calculation; explicit
99
- provisioning uses a separate budget for each of its three subprocesses.
100
- - `max_output:` defaults to **1,048,576 bytes**, counting both stdout and stderr.
101
- Exceeding it terminates the operation rather than returning partial output.
102
- - Cleanup sends TERM, then KILL to the child's process group and reaps the direct
103
- child. Unix process-group semantics are required. This is not a sandbox for a
104
- malicious replacement executable or a process that deliberately escapes its group.
105
- - Missing executable, nonzero exit, timeout, output limit and malformed responses
106
- raise `DependencyError`, `CommandError`, `TimeoutError`, `OutputLimitError` or
107
- `ResponseError`. These inherit from `YubiKey::Error`. Guard failures use
108
- `ConfirmationRequired`; invalid caller values use `ArgumentError`.
109
- - Errors suppress stdout/stderr, argv and underlying OS exception causes. Successful
110
- output can still contain generated keys, account identifiers or OTPs: do not
111
- print, persist, put into agent transcripts, or send it to a logger.
112
-
113
- ### Credential and secure-channel handling
114
-
115
- Do not pass PINs, PUKs, passwords, management/access/lock keys, OTP seeds or OATH
116
- URIs in argv. Their known options and positional slots are rejected even with
117
- confirmation. Unknown Ruby options, including arbitrary stdin, are rejected too.
118
- Key imports take existing key **file paths**, not inline secret bytes; protect
119
- those files and remove them according to your organization's retention policy.
120
-
121
- For protected commands, use `interact` in a **private local operator terminal**,
122
- omit credential arguments and answer the official CLI's prompts. Obtain specific
123
- consent first. For example, after approving a PIN change on the selected device:
56
+ | `config` | management | USB/NFC application enablement, mode, lock code, timeouts, reset |
57
+ | `otp` | OTP | Slot inventory, Yubico OTP, static password, HOTP, HMAC challenge-response, settings, NDEF, swap, delete |
58
+ | `oath` | OATH | Account list/add/URI/import/rename/delete/code, access password, caller-owned cache, reset |
59
+ | `fido` | FIDO | PIN changes/verification, minimum PIN length, resident credential list/delete, fingerprints, UV/attestation policy, reset |
60
+ | `piv` | PIV | PIN/PUK/management key, key generation/import/export/move/delete/attestation, certificates, CSR and object management, reset |
61
+ | `openpgp` | OpenPGP | PIN/admin/reset code and retry policy, key import/attestation/touch policy, certificates, reset |
62
+ | `hsmauth` | HSM Auth | Asymmetric/symmetric/derived credentials, credential passwords, management protection, reset |
63
+ | `security_domain` | Security Domain | SCP key information, generation/import/export/deletion/allowlists and reset |
64
+ | `apdu` | smartcard | Explicit APDUs. Mutations require `confirm: true` |
65
+
66
+ Unknown command grammar is rejected. Do not pass a shell string. Use separate
67
+ argv elements: `args: ['keys', 'info', '9a']`, not `args: 'keys info 9a'`.
68
+ Short aliases and `--option=value` are rejected. `--` is available before a
69
+ positional identifier that begins with a dash.
70
+
71
+ ### Confirmation, secrets and deadlines
72
+
73
+ Existing-slot HMAC calculation is the vault unlock path. Omitted `confirm` is
74
+ allowed. If `confirm` is present it must be literal `true`; `false`, `nil` and
75
+ `'true'` are rejected before any open.
76
+
77
+ Mutations, OATH calculation, provisioning and raw APDUs require literal
78
+ `confirm: true` before the device is opened. Confirmation does not guess
79
+ factory PINs, retry a blocked application, or delete a credential. Native OATH
80
+ does not delete or repair a corrupted credential. HOTP still advances its
81
+ counter. A password-protected OATH account needs an in-memory `password:` or
82
+ `access_key:`, or a caller `secret_provider`.
83
+
84
+ PINs, PUKs, management keys, access codes and OATH passwords stay in memory or
85
+ come from that provider. They are not argv, exception text, or logs. For a
86
+ private operator terminal, outside an agent request:
124
87
 
125
88
  ```ruby
126
89
  yk.interact(
127
90
  args: %w[fido access change-pin], serial: '12345678',
128
- confirm: true, timeout: 90
91
+ confirm: true, operator: true, timeout: 90
129
92
  )
130
93
  ```
131
94
 
132
- It bridges a native PTY to the controlling console (or an explicitly supplied
133
- readable **and** writable `terminal:` IO), restores terminal mode and original
134
- file-descriptor status flags on exit, and returns only `:completed`.
135
- There is no retained transcript or returned credential output. The byte limit
136
- counts total terminal traffic, and the timeout includes human input. Timeout or
137
- interruption kills the child process group and reaps the direct child, without
138
- retrying a potentially completed write. Nonterminal and one-way IO callers fail
139
- before spawning, including Ruby read-only/write-only wrappers of duplex descriptors.
140
- Validation does not consume terminal input, and caller-owned IO is never closed.
141
- Run outside active curses screens and recorded tool sessions; this is an
142
- operator workflow, not an automatic agent prompt handler. The bridge does not
143
- mask prompts that upstream itself echoes or guarantee Ruby-memory zeroization.
144
-
145
- The same method accepts `args: %w[otp --access-code - settings 2
146
- --delete-access-code]`: only the literal dash is accepted for that access-code
147
- option, so the actual code is requested at the private prompt. Ordinary `command`
148
- continues to close stdin. Equivalent official command shapes are:
149
-
150
- ```text
151
- ykman --device SERIAL piv access change-pin
152
- ykman --device SERIAL fido access change-pin
153
- ykman --device SERIAL oath accounts add 'Issuer:account'
154
- ykman --device SERIAL otp --access-code - settings 2 --delete-access-code
155
- ```
156
-
157
- Check the installed command's `--help` first. Do not put literal credentials in
158
- shell history or transcripts. Some upstream secret prompts are **not masked**;
159
- turn off terminal recording and screen sharing. Do not guess PINs: failed attempts
160
- can consume retries or block the application. `--remember` writes host keyring
161
- state and requires explicit consent.
162
-
163
- For file-based SCP11, pass `scp: [private_key_path, certificate_path]`,
164
- `scp_ca: ca_path`, and, where needed, `scp_sd: [kid, kvn]` /
165
- `scp_oce: [kid, kvn]`. References are two integer bytes. File order follows Yubico's
166
- private-key/certificate-chain, leaf-last convention. No `scp-password` argument is
167
- accepted; encrypted-key prompting belongs in the operator terminal.
168
-
169
- **SCP03 limitation:** upstream 5.9.1 accepts custom SCP03 key material only inline
170
- for global `--scp` and `sd keys import`; this wrapper refuses that exposure. Import
171
- KID 1 and its alternate spellings are rejected before filename checks, even if a
172
- file has the same name as the inline keys. File imports accept only SCP11 KIDs
173
- `0x10`, `0x11`, `0x13`, `0x15`, `0x20`–`0x2f`, their unsigned decimal spellings,
174
- or `SCP11a`/`SCP11b`/`SCP11c` names. Hex/name case and numeric leading zeros are
175
- preserved; other spellings are rejected, not coerced. Custom SCP03 provisioning is
176
- not provided through this credential-safe wrapper. Do not work around it by
177
- putting keys into APDU argv. Raw APDUs are for nonsecret command bytes only; the
178
- wrapper cannot infer the confidentiality of arbitrary protocol payloads.
95
+ `interact` prompts on the controlling console, or on an explicitly supplied
96
+ readable and writable `terminal:` IO, then runs one native command. It returns
97
+ only `:completed`. It does not start a process, retain a transcript, or close
98
+ caller-owned IO. Run it outside curses and recorded tool sessions. Ruby does
99
+ not guarantee string zeroization.
100
+
101
+ `timeout:` is one finite positive budget in seconds, default **30**. It covers
102
+ open, secure-channel setup, authentication and commands. Remaining time is
103
+ what later exchanges receive; each step does not get a fresh 30 seconds.
104
+ `SCardCancel` does not universally interrupt `SCardTransmit`, and killing a
105
+ Ruby thread does not cancel a native driver call. After a timeout or
106
+ interruption, inspect device state before retrying a mutation.
107
+
108
+ Missing libraries, command failure, timeout, output limits and malformed
109
+ responses raise `DependencyError`, `CommandError`, `TimeoutError`,
110
+ `OutputLimitError` or `ResponseError`. These inherit from `YubiKey::Error`.
111
+ Missing consent raises `ConfirmationRequired`. Invalid caller values raise
112
+ `ArgumentError`. Errors do not include request bytes.
113
+
114
+ ### Secure channel
115
+
116
+ `secure_channel:` is an explicit Hash, not a file of inline CLI keys. It
117
+ requires `scp:` (`:scp03`, `:scp11a`, `:scp11b` or `:scp11c`) and integer
118
+ `kvn:`. SCP03 also requires 16-byte in-memory `key_enc` and `key_mac`. A
119
+ failed secure-channel handshake does not fall back to plaintext. The owned
120
+ handle is still closed.
179
121
 
180
122
  ## Existing-slot HMAC challenge-response
181
123
 
@@ -190,150 +132,87 @@ response = PWN::Plugins::YubiKey.challenge_response(
190
132
  ); nil # Suppress sensitive return-value echo in an interactive console.
191
133
  ```
192
134
 
193
- `challenge:` is a raw Ruby String containing **1–64 bytes**, including NUL or
194
- non-UTF-8 bytes. PWN encodes it once as hex for the official command:
135
+ `challenge:` is a raw Ruby String of **1–64 bytes**, not pre-encoded hex.
136
+ `slot:` is integer **1 or 2**, default **2**. The return value is exactly
137
+ **20 raw bytes**, encoding `ASCII-8BIT`. A touch-enabled slot requires a
138
+ physical touch before timeout. Missing or incompatible slots fail. This method
139
+ never programs, resets or repairs a slot. FIDO-only Security Key models cannot
140
+ provide this OTP HMAC operation.
195
141
 
196
- ```text
197
- ykman --device SERIAL otp calculate SLOT HEX_CHALLENGE
198
- ```
142
+ The challenge is not placed on a child argv, because no child is started. Do
143
+ not use a password as the challenge. The HMAC response is sensitive key
144
+ material in the Ruby process.
199
145
 
200
- `slot:` is integer **1 or 2**, default **2**. The return value is exactly **20 raw
201
- bytes**, encoding `ASCII-8BIT`, not hex and not a time-based OTP. Decorated,
202
- truncated, multiple or malformed responses are rejected. A touch-enabled slot
203
- requires a physical touch before timeout; the wrapper does not print the CLI's
204
- touch prompt. Missing or incompatible slots fail. **This method never programs,
205
- resets, repairs or changes a slot**, and a default of slot 2 does not provision it.
206
- FIDO-only Security Key models cannot provide this OTP HMAC operation.
146
+ ## Explicit HMAC provisioning
207
147
 
208
- The public challenge is visible in child argv. Never use a password or secret
209
- key itself as a challenge. The slot secret is not exported during calculation;
210
- the HMAC response nevertheless is sensitive key material in the Ruby process.
148
+ Provisioning is separate from authentication and vault unlock. It **overwrites
149
+ the chosen slot**. The existing HMAC secret cannot be read back. Keep another
150
+ authentication method, and test recovery before discarding the new key.
211
151
 
212
- ## Explicit HMAC provisioning
152
+ ```ruby
153
+ require 'securerandom'
154
+ secret_key = SecureRandom.random_bytes(20); nil
155
+ PWN::Plugins::YubiKey.provision_challenge_response(
156
+ key: secret_key,
157
+ serial: '12345678',
158
+ slot: 2,
159
+ confirm: true,
160
+ timeout: 30
161
+ ) # => :verified only after a fresh challenge matches the supplied key
162
+ ```
213
163
 
214
- Provisioning is separate from authentication, inventory and vault access. It
215
- **overwrites the chosen slot** and can permanently break existing uses of that
216
- slot. Establish recovery before programming a device; a key's existing HMAC
217
- secret cannot be read back. Preserve another independent authentication method.
218
-
219
- 1. Use `devices`, `info` and `otp(args: ['info'])` with the exact serial. Inspect
220
- the target slot and its current purpose; never infer that slot 2 is unused.
221
- 2. Obtain explicit approval for that serial/slot. Securely generate or recover a
222
- 20-byte key in local memory. Decide whether an independent recovery token must
223
- be provisioned with that same key **before** discarding it.
224
- 3. With **ykman 5.9.1**, run the dedicated method from trusted local Ruby code:
225
-
226
- ```ruby
227
- require 'securerandom'
228
- secret_key = SecureRandom.random_bytes(20); nil
229
- PWN::Plugins::YubiKey.provision_challenge_response(
230
- key: secret_key,
231
- serial: '12345678',
232
- slot: 2,
233
- confirm: true,
234
- timeout: 30
235
- ) # => :verified only after a fresh challenge matches the supplied key
236
- ```
237
-
238
- The method verifies the CLI version, sends the hex key and a fixed affirmative
239
- confirmation **through stdin**, and invokes `otp chalresp --touch 2` without a
240
- key argv or `--force`. It then requests a fresh 32-byte challenge and checks the
241
- response against Ruby OpenSSL HMAC-SHA1. **Touch the device for verification.**
242
- It requires an explicit serial and always enables touch. Protected slots that
243
- need an access code use the terminal workflow instead.
244
- 4. If version validation fails, no provisioning command runs. If provisioning or
245
- readback fails later, the slot may already have changed: inspect it before
246
- retrying. Do not assume rollback. Test a recovery token independently.
247
- 5. Discard the in-memory key only after the chosen recovery procedure is complete.
248
- Ruby does not guarantee zeroization of strings and copies, and the REPL may
249
- retain history/return values; this is why literals and return-value echo are
250
- inappropriate for real keys.
251
-
252
- Other CLI versions require a separately reviewed prompt contract or a private
253
- operator terminal. For the latter, inspect `ykman otp chalresp --help`, then run
254
- `ykman --device SERIAL otp chalresp --touch 2` **without a KEY argument** and supply
255
- the key at the prompt. Upstream may echo it. Its `--generate` option also prints
256
- the generated key, so it is not a secrecy-preserving shortcut.
164
+ `confirm: true` is required before open. An explicit serial, reader,
165
+ device path or caller-owned connection is required. `touch:` defaults to
166
+ `true`. Optional access codes are six raw bytes, supplied in memory, not at a
167
+ CLI prompt. The method checks a fresh challenge with Ruby OpenSSL HMAC-SHA1
168
+ and returns `:verified` only when that check passes. Touch the device when the
169
+ slot requires it. If programming succeeds and readback fails, the slot may
170
+ already have changed: inspect it before retrying. Do not assume rollback.
257
171
 
258
172
  ## Exact OATH retrieval
259
173
 
260
174
  ```ruby
261
175
  code = PWN::Plugins::YubiKey.oath_code(
262
- account: 'Example:alice', # Exact identifier returned by OATH accounts list.
176
+ account: 'Example:alice', # Exact identifier, not a substring.
263
177
  serial: '12345678',
264
178
  confirm: true,
265
179
  timeout: 30
266
180
  ); nil
267
181
  ```
268
182
 
269
- The result contains only **6–8 decimal digits**, preserving leading zeros. Missing,
270
- ambiguous, changed-identity, multiline and nonnumeric results fail closed. Steam
271
- alphanumeric codes are deliberately outside this convenience API; the general
272
- OATH interface still exposes official CLI output.
273
-
274
- There is **no `--exact` option in official 5.9.1**. `--single` ensures one match but
275
- still accepts substrings and removes the account label. Therefore this method:
276
-
277
- 1. Lists accounts with `--show-hidden --oath-type` and requires exactly one
278
- case-sensitive complete identifier match.
279
- 2. Runs `oath accounts code --show-hidden -- ACCOUNT`, keeping the result label.
280
- 3. Checks that exactly one output row has the requested identifier and numeric
281
- code before returning only the digits. It does not use unsupported flags or
282
- falsely describe substring matching as exact matching.
283
-
284
- `confirm: true` covers both inventory and calculation. Even `accounts list` may
285
- authenticate with a remembered credential and delete/persist an incorrect
286
- remembered key, without `--remember`. Consent is required even for TOTP:
287
- upstream 5.9.1 `accounts code` may **automatically delete a corrupted credential**
288
- after a memory error. HOTP also
289
- advances its counter. This is not a read-only operation. Password-protected
290
- applications that cannot unlock through the CLI's existing local state require
291
- the private operator-terminal workflow; do not provide passwords in argv.
292
- Two subprocesses are not an atomic device transaction. Do not concurrently change
293
- accounts or swap devices; output identity is rechecked, but prior side effects
294
- cannot be undone. Pin the serial and complete any required touch within timeout.
183
+ The result is **6–8 decimal digits**, preserving leading zeros. The account
184
+ must be an exact control-free String of 1 to 64 bytes. Steam alphanumeric
185
+ codes are outside this method. `confirm: true` is required before open, including
186
+ for TOTP, because HOTP advances its counter and a protected account may
187
+ authenticate. Consent does not authorize deleting a corrupted credential.
188
+ Device errors do not fall back to a manual token.
295
189
 
296
190
  ## Authentication and interoperability limits
297
191
 
298
- FIDO PIN/resident-credential/fingerprint management is **not website login**.
299
- Real U2F/FIDO2/WebAuthn enrollment and assertions belong to the browser and relying
300
- party, including origin/RP binding, user presence and policy. This module neither
301
- fabricates assertions nor bypasses security-key prompts. OATH codes must only be
302
- submitted to a site's matching authenticator-code challenge, never an SMS-only
303
- or passkey challenge.
192
+ FIDO PIN, resident-credential and fingerprint management is **not website
193
+ login**. U2F/FIDO2/WebAuthn enrollment and assertions belong to the browser and
194
+ relying party. This module does not fabricate assertions. Submit an OATH code
195
+ only to a matching authenticator-code challenge, never an SMS-only or passkey
196
+ challenge. PayPal's SMS adapter rejects hardware OATH before login.
304
197
 
305
- PIV/OpenPGP management does not supply a TLS client, SSH agent or GPG middleware,
306
- and does not export non-exportable private keys. YubiHSM Auth credentials are
307
- not a replacement for a YubiHSM device. Device/firmware/application errors stay
308
- errors rather than triggering reset, provisioning, software-secret fallback or
309
- repeated PIN attempts.
198
+ PIV and OpenPGP management do not supply a TLS client, SSH agent or GPG
199
+ middleware, and do not export non-exportable private keys. YubiHSM Auth
200
+ credentials are not a YubiHSM. Device and firmware errors stay errors. They do
201
+ not trigger reset, provisioning, a software-secret fallback, or repeated PIN
202
+ attempts.
310
203
 
311
- ## Offline verification and sources
204
+ ## Offline verification
312
205
 
313
206
  ```text
314
207
  bundle exec rspec spec/lib/pwn/plugins/yubikey_spec.rb spec/lib/pwn/plugins/yubikey_security_spec.rb spec/lib/pwn/plugins/yubikey_terminal_spec.rb
315
208
  bundle exec rubocop lib/pwn/plugins/yubikey.rb spec/lib/pwn/plugins/yubikey_spec.rb spec/lib/pwn/plugins/yubikey_security_spec.rb spec/lib/pwn/plugins/yubikey_terminal_spec.rb
316
209
  ```
317
210
 
318
- The specs use executable Ruby fakes and synthetic credentials, including an
319
- independent RFC 2202 HMAC-SHA1 vector. They exercise real process execution,
320
- timeout/process-group cleanup, errors, argument guards, binary conversion,
321
- exact OATH parsing, validated provisioning/readback, native PTY prompts and
322
- terminal mode/status-flag restoration on success and failure, and pre-spawn duplex
323
- validation without consuming input. No test opens a physical key,
324
- logs in to a website or uses live credentials.
325
-
326
- An isolated official 5.9.1 CLI was additionally exercised with `--help` at all
327
- **124 recognized management command/group paths**. Its real Click provisioning
328
- callback was exercised with a fake device session to validate the stdin contract.
329
- These establish CLI compatibility, **not physical-device acceptance**. Hardware
330
- USB/NFC, touches, PIN protection, firmware variants and real relying-party login
331
- remain separate opt-in validation.
332
-
333
- - [Yubico YubiKey Manager documentation](https://docs.yubico.com/software/yubikey/tools/ykman/)
334
- - [Official 5.9.1 OTP CLI source](https://github.com/Yubico/yubikey-manager/blob/5.9.1/ykman/_cli/otp.py)
335
- - [Official 5.9.1 OATH CLI source](https://github.com/Yubico/yubikey-manager/blob/5.9.1/ykman/_cli/oath.py)
336
- - [OATH corrupted-credential repair](https://github.com/Yubico/yubikey-manager/blob/5.9.1/ykman/oath.py)
337
- - [CLI piped-input behavior](https://github.com/Yubico/yubikey-manager/blob/5.9.1/ykman/_cli/util.py)
338
- - [Security Domain CLI](https://github.com/Yubico/yubikey-manager/blob/5.9.1/ykman/_cli/securitydomain.py)
211
+ Those specs use synthetic connections and credentials, including an independent
212
+ RFC 2202 HMAC-SHA1 vector. They do not open a physical key, log in to a website,
213
+ or prove every firmware variant. USB/NFC, touch, PIN protection and real
214
+ relying-party login remain separate opt-in checks.
215
+
339
216
  - [RFC 2202 HMAC test vectors](https://www.rfc-editor.org/rfc/rfc2202)
217
+ - [GlobalPlatform Card Specification](https://globalplatform.org/specs-library/card-specification/)
218
+ - [Yubico OTP protocol notes](https://developers.yubico.com/OTP/)
@@ -50,6 +50,6 @@ digraph PWN_Feedback {
50
50
  {rank=same; Retain; Receipt}
51
51
 
52
52
  Trainer [fillcolor="#1e293b", fontcolor="#cbd5e1", style="rounded,dashed,filled",
53
- label="SEPARATE OPTIONAL WEIGHT PATH\nCurriculum export / configured trainer is opt-in\nNot routing-policy promotion or proof of adapter gains\nScheduled curriculum jobs are disabled by default"];
53
+ label="SEPARATE OPTIONAL WEIGHT PATH\nCurriculum export / configured trainer is opt-in\nNot routing-policy promotion or proof of adapter gains\nCurriculum jobs are not seeded"];
54
54
  {rank=same; Reuse; Trainer}
55
55
  }
@@ -27,13 +27,13 @@ digraph "PWN_Reinforcement_Learning" {
27
27
  subgraph cluster_curriculum {
28
28
  label="OPTIONAL CURRICULUM · independent invocation"; fontsize=12;
29
29
  style="rounded,dashed,filled"; color="#94a3b8"; fillcolor="#1e293b";
30
- Schedule [fillcolor="#7dd3fc", label="A OPT IN / INVOKE EXPLICITLY\nCron seeds disabled by default:\npractice 03:00; offline_judge 03:30 daily\ntrain dry_run 04:00 Sunday\nSeeding jobs does not install system crontab"];
30
+ Schedule [fillcolor="#7dd3fc", label="A OPT IN / INVOKE EXPLICITLY\nCurriculum jobs are not seeded\nCreate a CURRICULUM_JOBS template only when wanted\nTemplates stay disabled until explicitly enabled\nSeeding does not install system crontab"];
31
31
  Practice [fillcolor="#6ee7b7", label="B PRACTICE / OFFLINE JUDGING\nPractice: budget-first; real tools may run\nJudge-qualified resolution + winning traces\nOffline judge: backfill, calibrate, warm sentinel\nRecords: curriculum/ + mistakes / preferences"];
32
32
  Export [fillcolor="#fcd34d", label="C EXPORT TRAINING DATA\nLearning.export_finetune + Reward.export_dpo\nEligible sessions / outcomes + preference pairs\nScrub, geometry and source-balance filters\nOutput: finetune/*.jsonl + evaluation set"];
33
33
  Train [fillcolor="#fda4af", label="D train_and_gate\nDry run or no trainer → export_ready only\nOtherwise: configured trainer → LoRA adapter\nollama create → candidate vs baseline gate\nPreference-diet gate also required"];
34
34
  Model [fillcolor="#6ee7b7", label="E RETAIN GATED ADAPTER VERSION\nOn passing gates: update curriculum model state\nOptional local weight path, not routing policy\nExport alone is not training or improvement"];
35
35
  Hygiene [fillcolor="#1e293b", fontcolor="#cbd5e1", style="rounded,dashed,filled",
36
- label="SEPARATE ENABLED HYGIENE SEED\nLearning.consolidate at 05:00 daily\nMemory maintenance, not model training\nESR = verified_exploit_tools / vulnerable_tools\nASR = successful_attacks / total_attack_attempts"];
36
+ label="SEPARATE ENABLED HYGIENE SEED\nsession_cleanup at 03:45 daily\nIncludes memory consolidation; not model training\nESR = verified_exploit_tools / vulnerable_tools\nASR = successful_attacks / total_attack_attempts"];
37
37
  Schedule -> Practice [label="independent jobs; not a job chain", style=dashed];
38
38
  Practice -> Export [label="eligible retained data", style=dashed];
39
39
  Export -> Train -> Model;
@@ -187,7 +187,7 @@
187
187
  <text xml:space="preserve" text-anchor="middle" x="555" y="-71.07" font-family="sans-Serif" font-size="11.00" fill="#cbd5e1">SEPARATE OPTIONAL WEIGHT PATH</text>
188
188
  <text xml:space="preserve" text-anchor="middle" x="555" y="-57.57" font-family="sans-Serif" font-size="11.00" fill="#cbd5e1">Curriculum export / configured trainer is opt&#45;in</text>
189
189
  <text xml:space="preserve" text-anchor="middle" x="555" y="-44.07" font-family="sans-Serif" font-size="11.00" fill="#cbd5e1">Not routing&#45;policy promotion or proof of adapter gains</text>
190
- <text xml:space="preserve" text-anchor="middle" x="555" y="-30.57" font-family="sans-Serif" font-size="11.00" fill="#cbd5e1">Scheduled curriculum jobs are disabled by default</text>
190
+ <text xml:space="preserve" text-anchor="middle" x="555" y="-30.57" font-family="sans-Serif" font-size="11.00" fill="#cbd5e1">Curriculum jobs are not seeded</text>
191
191
  </g>
192
192
  </g>
193
193
  </svg>