pwn 0.5.783 → 0.5.785

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.
Files changed (264) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/Gemfile +8 -8
  4. data/README.md +37 -0
  5. data/Rakefile +19 -1
  6. data/bin/pwn +2 -1
  7. data/bin/pwn_setup +1 -1
  8. data/bin/pwn_web_cache_deception +35 -7
  9. data/build_gem.sh +1 -1
  10. data/documentation/AI-Integration.md +7 -5
  11. data/documentation/Agent-Tool-Registry.md +2 -2
  12. data/documentation/Backup-Retention.md +82 -0
  13. data/documentation/Configuration.md +10 -6
  14. data/documentation/Contributing.md +1 -1
  15. data/documentation/Cron.md +64 -35
  16. data/documentation/Diagrams.md +2 -2
  17. data/documentation/General-PWN-Usage.md +4 -3
  18. data/documentation/Home.md +6 -5
  19. data/documentation/How-PWN-Works.md +7 -6
  20. data/documentation/Installation.md +254 -10
  21. data/documentation/Mission-Cancellation.md +134 -0
  22. data/documentation/Operability.md +17 -0
  23. data/documentation/Persistence.md +8 -6
  24. data/documentation/Plugin-Degradation.md +12 -0
  25. data/documentation/Plugins.md +3 -2
  26. data/documentation/Reinforcement-Learning.md +5 -6
  27. data/documentation/Runtime-Source-Protection.md +162 -0
  28. data/documentation/Transparent-Browser.md +11 -0
  29. data/documentation/Troubleshooting.md +3 -2
  30. data/documentation/What-is-PWN.md +2 -2
  31. data/documentation/YubiKey-Vaults.md +183 -0
  32. data/documentation/YubiKey.md +339 -0
  33. data/documentation/diagrams/cron-scheduling.svg +130 -128
  34. data/documentation/diagrams/dot/cron-scheduling.dot +5 -5
  35. data/documentation/diagrams/dot/persistence-filesystem.dot +5 -3
  36. data/documentation/diagrams/dot/plugin-ecosystem.dot +2 -2
  37. data/documentation/diagrams/persistence-filesystem.svg +172 -142
  38. data/documentation/diagrams/plugin-ecosystem.svg +64 -64
  39. data/documentation/pwn-ai-Agent.md +10 -4
  40. data/etc/default_cron/session_cleanup.rb +1245 -22
  41. data/etc/default_skills/pwn/ai/agent/mission_namespace/SKILL.md +21 -0
  42. data/etc/default_skills/pwn/ai/agent/tools/browser_act/SKILL.md +45 -0
  43. data/etc/default_skills/pwn/ai/agent/tools/loot_query/SKILL.md +27 -26
  44. data/etc/default_skills/pwn/ai/agent/tools/recon_run/SKILL.md +25 -15
  45. data/etc/default_skills/pwn/ai/agent/tools/workspace_tool/SKILL.md +24 -0
  46. data/etc/default_skills/pwn/ai/server_tools/SKILL.md +25 -0
  47. data/etc/default_skills/pwn/backups/SKILL.md +54 -0
  48. data/etc/default_skills/pwn/plugins/recon/SKILL.md +12 -1
  49. data/etc/default_skills/pwn/plugins/yubikey/SKILL.md +157 -0
  50. data/etc/default_skills/pwn/plugins/yubikey/fido/SKILL.md +189 -0
  51. data/etc/default_skills/pwn/plugins/yubikey/hsmauth/SKILL.md +83 -0
  52. data/etc/default_skills/pwn/plugins/yubikey/management/SKILL.md +80 -0
  53. data/etc/default_skills/pwn/plugins/yubikey/oath/SKILL.md +115 -0
  54. data/etc/default_skills/pwn/plugins/yubikey/openpgp/SKILL.md +144 -0
  55. data/etc/default_skills/pwn/plugins/yubikey/otp/SKILL.md +97 -0
  56. data/etc/default_skills/pwn/plugins/yubikey/piv/SKILL.md +110 -0
  57. data/etc/default_skills/pwn/plugins/yubikey/protocol/SKILL.md +21 -0
  58. data/etc/default_skills/pwn/plugins/yubikey/security_domain/SKILL.md +75 -0
  59. data/etc/default_skills/pwn/plugins/yubikey/security_domain/scp/SKILL.md +46 -0
  60. data/etc/default_skills/pwn/plugins/yubikey/transport/SKILL.md +98 -0
  61. data/etc/default_skills/pwn/redaction/SKILL.md +13 -1
  62. data/lib/pwn/ai/agent/dispatch.rb +26 -15
  63. data/lib/pwn/ai/agent/extrospection.rb +6 -7
  64. data/lib/pwn/ai/agent/learning.rb +62 -25
  65. data/lib/pwn/ai/agent/loop.rb +17 -1
  66. data/lib/pwn/ai/agent/mission_namespace.rb +331 -0
  67. data/lib/pwn/ai/agent/policy.rb +5 -0
  68. data/lib/pwn/ai/agent/policy_evaluation.rb +8 -8
  69. data/lib/pwn/ai/agent/prompt_builder.rb +13 -2
  70. data/lib/pwn/ai/agent/request_runtime.rb +122 -18
  71. data/lib/pwn/ai/agent/reward.rb +5 -3
  72. data/lib/pwn/ai/agent/skill_consolidation.rb +3 -2
  73. data/lib/pwn/ai/agent/skill_review.rb +9 -5
  74. data/lib/pwn/ai/agent/swarm.rb +35 -16
  75. data/lib/pwn/ai/agent/tools/browser_act.rb +32 -0
  76. data/lib/pwn/ai/agent/tools/loot_query.rb +11 -1
  77. data/lib/pwn/ai/agent/tools/recon_run.rb +11 -1
  78. data/lib/pwn/ai/agent/tools/workspace_tool.rb +29 -0
  79. data/lib/pwn/ai/agent/turn_finalizer.rb +6 -2
  80. data/lib/pwn/ai/agent.rb +1 -0
  81. data/lib/pwn/ai/anthropic.rb +8 -33
  82. data/lib/pwn/ai/cli.rb +34 -5
  83. data/lib/pwn/ai/gemini.rb +1 -0
  84. data/lib/pwn/ai/grok.rb +39 -28
  85. data/lib/pwn/ai/ollama.rb +4 -2
  86. data/lib/pwn/ai/open_ai.rb +11 -32
  87. data/lib/pwn/ai/open_web_ui.rb +62 -4
  88. data/lib/pwn/ai/router.rb +53 -3
  89. data/lib/pwn/ai/server_tools.rb +231 -0
  90. data/lib/pwn/ai.rb +1 -0
  91. data/lib/pwn/backups.rb +347 -0
  92. data/lib/pwn/config.rb +70 -39
  93. data/lib/pwn/cron.rb +120 -49
  94. data/lib/pwn/migrate.rb +150 -68
  95. data/lib/pwn/module_skills.rb +7 -0
  96. data/lib/pwn/plugins/ai_sandbox.rb +538 -1
  97. data/lib/pwn/plugins/authentication_helper.rb +74 -4
  98. data/lib/pwn/plugins/basic_auth.rb +2 -1
  99. data/lib/pwn/plugins/binary_parser.rb +5 -4
  100. data/lib/pwn/plugins/ghidra.rb +4 -3
  101. data/lib/pwn/plugins/google_workspace.rb +4 -21
  102. data/lib/pwn/plugins/jobs.rb +1 -1
  103. data/lib/pwn/plugins/log.rb +21 -3
  104. data/lib/pwn/plugins/preflight_checker.rb +34 -9
  105. data/lib/pwn/plugins/recon.rb +4 -1
  106. data/lib/pwn/plugins/repl/ai.rb +305 -27
  107. data/lib/pwn/plugins/repl/ai_console.rb +273 -89
  108. data/lib/pwn/plugins/repl/ai_swarm.rb +181 -62
  109. data/lib/pwn/plugins/repl/mesh.rb +3 -16
  110. data/lib/pwn/plugins/repl/vault.rb +4 -15
  111. data/lib/pwn/plugins/repl.rb +1 -216
  112. data/lib/pwn/plugins/sandbox/driver.rb +4 -0
  113. data/lib/pwn/plugins/transparent_browser.rb +215 -0
  114. data/lib/pwn/plugins/vault.rb +412 -135
  115. data/lib/pwn/plugins/yubikey/fido.rb +1288 -0
  116. data/lib/pwn/plugins/yubikey/hsmauth.rb +439 -0
  117. data/lib/pwn/plugins/yubikey/management.rb +686 -0
  118. data/lib/pwn/plugins/yubikey/oath.rb +766 -0
  119. data/lib/pwn/plugins/yubikey/openpgp.rb +1063 -0
  120. data/lib/pwn/plugins/yubikey/otp.rb +897 -0
  121. data/lib/pwn/plugins/yubikey/piv.rb +1315 -0
  122. data/lib/pwn/plugins/yubikey/protocol.rb +341 -0
  123. data/lib/pwn/plugins/yubikey/security_domain/scp.rb +482 -0
  124. data/lib/pwn/plugins/yubikey/security_domain.rb +464 -0
  125. data/lib/pwn/plugins/yubikey/transport.rb +1017 -0
  126. data/lib/pwn/plugins/yubikey.rb +844 -0
  127. data/lib/pwn/plugins.rb +1 -0
  128. data/lib/pwn/redaction.rb +69 -12
  129. data/lib/pwn/session_trace.rb +4 -0
  130. data/lib/pwn/sessions.rb +58 -16
  131. data/lib/pwn/setup.rb +123 -41
  132. data/lib/pwn/version.rb +1 -1
  133. data/lib/pwn/www/app_cobalt_io.rb +16 -5
  134. data/lib/pwn/www/bug_crowd.rb +16 -5
  135. data/lib/pwn/www/coinbase_pro.rb +16 -5
  136. data/lib/pwn/www/github.rb +30 -14
  137. data/lib/pwn/www/paypal.rb +15 -4
  138. data/lib/pwn/www/synack.rb +16 -5
  139. data/lib/pwn/www/twitter.rb +17 -6
  140. data/lib/pwn.rb +1 -0
  141. data/scripts/build_gate.rb +58 -0
  142. data/spec/documentation/installation_md_spec.rb +1 -1
  143. data/spec/fixtures/yubikey/apdu_frames.json +162 -0
  144. data/spec/integration/authentication_helper_oath_spec.rb +117 -0
  145. data/spec/integration/build_gate_spec.rb +73 -0
  146. data/spec/integration/controller_spill_spec.rb +48 -0
  147. data/spec/integration/harness_process_spec.rb +97 -0
  148. data/spec/integration/mission_cancel_spec.rb +79 -0
  149. data/spec/integration/mission_curses_pty_spec.rb +44 -0
  150. data/spec/integration/persistence_roundtrip_spec.rb +1 -7
  151. data/spec/integration/readme_security_spec.rb +47 -0
  152. data/spec/integration/web_cache_deception_yubikey_spec.rb +159 -0
  153. data/spec/lib/pwn/ai/agent/dispatch_spec.rb +6 -3
  154. data/spec/lib/pwn/ai/agent/dispatch_vault_safety_spec.rb +86 -0
  155. data/spec/lib/pwn/ai/agent/injection_guard_spec.rb +2 -0
  156. data/spec/lib/pwn/ai/agent/learning_spec.rb +4 -4
  157. data/spec/lib/pwn/ai/agent/loop_artifacts_spec.rb +5 -0
  158. data/spec/lib/pwn/ai/agent/loop_spec.rb +104 -29
  159. data/spec/lib/pwn/ai/agent/mission_namespace_spec.rb +138 -0
  160. data/spec/lib/pwn/ai/agent/policy_spec.rb +27 -0
  161. data/spec/lib/pwn/ai/agent/request_runtime_spec.rb +48 -0
  162. data/spec/lib/pwn/ai/agent/shell_search_effect_spec.rb +44 -0
  163. data/spec/lib/pwn/ai/agent/solve_spec.rb +6 -4
  164. data/spec/lib/pwn/ai/agent/tools/browser_act_spec.rb +44 -0
  165. data/spec/lib/pwn/ai/agent/tools/loot_query_yubikey_spec.rb +55 -0
  166. data/spec/lib/pwn/ai/agent/tools/recon_run_yubikey_spec.rb +43 -0
  167. data/spec/lib/pwn/ai/agent/tools/workspace_tool_spec.rb +20 -0
  168. data/spec/lib/pwn/ai/agent/turn_finalizer_spec.rb +7 -0
  169. data/spec/lib/pwn/ai/anthropic_vault_enrollment_spec.rb +37 -0
  170. data/spec/lib/pwn/ai/cli_spec.rb +6 -9
  171. data/spec/lib/pwn/ai/oauth_vault_persistence_spec.rb +195 -0
  172. data/spec/lib/pwn/ai/open_ai_spec.rb +2 -2
  173. data/spec/lib/pwn/ai/open_web_ui_workspace_spec.rb +36 -0
  174. data/spec/lib/pwn/ai/router_spec.rb +31 -3
  175. data/spec/lib/pwn/ai/server_tools_spec.rb +39 -0
  176. data/spec/lib/pwn/backups_spec.rb +114 -0
  177. data/spec/lib/pwn/config_redaction_yubikey_spec.rb +28 -0
  178. data/spec/lib/pwn/config_yubikey_spec.rb +92 -0
  179. data/spec/lib/pwn/cron_spec.rb +113 -6
  180. data/spec/lib/pwn/migrate_spec.rb +108 -5
  181. data/spec/lib/pwn/migrate_yubikey_spec.rb +128 -0
  182. data/spec/lib/pwn/module_skills_spec.rb +8 -0
  183. data/spec/lib/pwn/plugins/ai_sandbox_spec.rb +230 -0
  184. data/spec/lib/pwn/plugins/authentication_helper_native_yubikey_spec.rb +40 -0
  185. data/spec/lib/pwn/plugins/authentication_helper_yubikey_spec.rb +90 -0
  186. data/spec/lib/pwn/plugins/capability_broker/daemon_spec.rb +1 -1
  187. data/spec/lib/pwn/plugins/google_workspace_spec.rb +17 -35
  188. data/spec/lib/pwn/plugins/log_spec.rb +7 -0
  189. data/spec/lib/pwn/plugins/log_yubikey_spec.rb +47 -0
  190. data/spec/lib/pwn/plugins/preflight_checker_native_yubikey_spec.rb +19 -0
  191. data/spec/lib/pwn/plugins/preflight_checker_yubikey_spec.rb +55 -0
  192. data/spec/lib/pwn/plugins/recon_yubikey_spec.rb +43 -0
  193. data/spec/lib/pwn/plugins/repl/ai_console_spec.rb +125 -78
  194. data/spec/lib/pwn/plugins/repl/ai_legacy_containment_spec.rb +200 -0
  195. data/spec/lib/pwn/plugins/repl/ai_swarm_spec.rb +30 -16
  196. data/spec/lib/pwn/plugins/repl/ai_vault_edit_yubikey_spec.rb +103 -0
  197. data/spec/lib/pwn/plugins/repl/ai_yubikey_spec.rb +48 -0
  198. data/spec/lib/pwn/plugins/repl/mesh_vault_persistence_spec.rb +149 -0
  199. data/spec/lib/pwn/plugins/repl/vault_yaml_serialization_spec.rb +11 -0
  200. data/spec/lib/pwn/plugins/repl/vault_yubikey_spec.rb +39 -0
  201. data/spec/lib/pwn/plugins/repl_spec.rb +56 -5
  202. data/spec/lib/pwn/plugins/transparent_browser_jev_spec.rb +46 -0
  203. data/spec/lib/pwn/plugins/vault_native_yubikey_spec.rb +29 -0
  204. data/spec/lib/pwn/plugins/vault_publication_safety_spec.rb +258 -0
  205. data/spec/lib/pwn/plugins/vault_store_safety_spec.rb +241 -0
  206. data/spec/lib/pwn/plugins/vault_yubikey_safety_spec.rb +168 -0
  207. data/spec/lib/pwn/plugins/vault_yubikey_spec.rb +128 -0
  208. data/spec/lib/pwn/plugins/vault_yubikey_vector_spec.rb +45 -0
  209. data/spec/lib/pwn/plugins/yubikey/fido_spec.rb +854 -0
  210. data/spec/lib/pwn/plugins/yubikey/hsmauth_spec.rb +235 -0
  211. data/spec/lib/pwn/plugins/yubikey/management_spec.rb +277 -0
  212. data/spec/lib/pwn/plugins/yubikey/oath_spec.rb +349 -0
  213. data/spec/lib/pwn/plugins/yubikey/openpgp_spec.rb +810 -0
  214. data/spec/lib/pwn/plugins/yubikey/otp_spec.rb +337 -0
  215. data/spec/lib/pwn/plugins/yubikey/piv_spec.rb +646 -0
  216. data/spec/lib/pwn/plugins/yubikey/protocol_secure_spec.rb +28 -0
  217. data/spec/lib/pwn/plugins/yubikey/protocol_spec.rb +149 -0
  218. data/spec/lib/pwn/plugins/yubikey/protocol_vectors_spec.rb +18 -0
  219. data/spec/lib/pwn/plugins/yubikey/security_domain/scp_spec.rb +256 -0
  220. data/spec/lib/pwn/plugins/yubikey/security_domain/support.rb +153 -0
  221. data/spec/lib/pwn/plugins/yubikey/security_domain_spec.rb +296 -0
  222. data/spec/lib/pwn/plugins/yubikey/transport_spec.rb +760 -0
  223. data/spec/lib/pwn/plugins/yubikey_security_spec.rb +260 -0
  224. data/spec/lib/pwn/plugins/yubikey_spec.rb +209 -0
  225. data/spec/lib/pwn/plugins/yubikey_terminal_spec.rb +147 -0
  226. data/spec/lib/pwn/redaction_spec.rb +13 -10
  227. data/spec/lib/pwn/redaction_yubikey_spec.rb +61 -0
  228. data/spec/lib/pwn/session_cleanup_concurrency_spec.rb +201 -0
  229. data/spec/lib/pwn/session_cleanup_default_spec.rb +167 -2
  230. data/spec/lib/pwn/session_cleanup_legacy_spec.rb +101 -0
  231. data/spec/lib/pwn/session_cleanup_payload_spec.rb +335 -2
  232. data/spec/lib/pwn/session_cleanup_recovery_spec.rb +403 -0
  233. data/spec/lib/pwn/sessions_spec.rb +112 -0
  234. data/spec/lib/pwn/setup_native_yubikey_spec.rb +66 -0
  235. data/spec/lib/pwn/setup_spec.rb +3 -1
  236. data/spec/lib/pwn/setup_vault_yubikey_spec.rb +61 -0
  237. data/spec/lib/pwn/setup_yubikey_spec.rb +40 -0
  238. data/spec/lib/pwn/www/login_yubikey_spec.rb +273 -0
  239. data/spec/spec_helper.rb +15 -0
  240. data/spec/support/gate_failure_formatter.rb +14 -0
  241. data/spec/support/harness/cli_faults.rb +13 -9
  242. data/spec/support/harness/controller_spill.rb +26 -0
  243. data/spec/support/harness/controller_state.rb +54 -0
  244. data/spec/support/harness/evaluate.rb +12 -1
  245. data/spec/support/harness/experiments.rb +1 -1
  246. data/spec/support/harness/fake_provider.rb +22 -6
  247. data/spec/support/harness/fault_preload.rb +3 -1
  248. data/spec/support/harness/mission_console.rb +54 -0
  249. data/spec/support/harness/mission_curses_pty.rb +41 -0
  250. data/spec/support/harness/mission_dispatch.rb +30 -0
  251. data/spec/support/harness/mission_fixture_preload.rb +24 -0
  252. data/spec/support/harness/mission_job_lifetime.rb +25 -0
  253. data/spec/support/harness/mission_legacy.rb +25 -0
  254. data/spec/support/harness/mission_normal_exit.rb +34 -0
  255. data/spec/support/harness/mission_swarm.rb +23 -0
  256. data/spec/support/harness/mission_unsupported.rb +29 -0
  257. data/spec/support/harness/package_acceptance.rb +33 -5
  258. data/spec/support/harness/session_acceptance.rb +4 -4
  259. data/spec/support/harness/swarm_acceptance.rb +1 -1
  260. data/spec/support/harness/swarm_control.rb +44 -0
  261. data/spec/support/mission_namespace.rb +14 -0
  262. data/spec/support/mission_pipe_queue.rb +33 -0
  263. data/third_party/pwn_rdoc.jsonl +599 -15
  264. metadata +140 -17
@@ -0,0 +1,339 @@
1
+ # YubiKey device operations
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.
7
+
8
+ For encrypted configuration, credential storage, conversion and recovery, see
9
+ [YubiKey-backed vaults and authentication](YubiKey-Vaults.md).
10
+
11
+ ## Dependency and discovery
12
+
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.
19
+
20
+ ```ruby
21
+ require 'pwn'
22
+ 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
27
+ 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
30
+ yk.cli_help(path: %w[piv keys generate])
31
+ ```
32
+
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.
39
+
40
+ ## Management coverage
41
+
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.
46
+
47
+ | Method | Official family | Covered management operations |
48
+ |---|---|---|
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:
124
+
125
+ ```ruby
126
+ yk.interact(
127
+ args: %w[fido access change-pin], serial: '12345678',
128
+ confirm: true, timeout: 90
129
+ )
130
+ ```
131
+
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.
179
+
180
+ ## Existing-slot HMAC challenge-response
181
+
182
+ ```ruby
183
+ require 'securerandom'
184
+ challenge = SecureRandom.random_bytes(32) # Public challenge, not a provisioning key.
185
+ response = PWN::Plugins::YubiKey.challenge_response(
186
+ challenge: challenge,
187
+ slot: 2,
188
+ serial: '12345678',
189
+ timeout: 30
190
+ ); nil # Suppress sensitive return-value echo in an interactive console.
191
+ ```
192
+
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:
195
+
196
+ ```text
197
+ ykman --device SERIAL otp calculate SLOT HEX_CHALLENGE
198
+ ```
199
+
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.
207
+
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.
211
+
212
+ ## Explicit HMAC provisioning
213
+
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.
257
+
258
+ ## Exact OATH retrieval
259
+
260
+ ```ruby
261
+ code = PWN::Plugins::YubiKey.oath_code(
262
+ account: 'Example:alice', # Exact identifier returned by OATH accounts list.
263
+ serial: '12345678',
264
+ confirm: true,
265
+ timeout: 30
266
+ ); nil
267
+ ```
268
+
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.
295
+
296
+ ## Authentication and interoperability limits
297
+
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.
304
+
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.
310
+
311
+ ## Offline verification and sources
312
+
313
+ ```text
314
+ 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
+ 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
+ ```
317
+
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)
339
+ - [RFC 2202 HMAC test vectors](https://www.rfc-editor.org/rfc/rfc2202)