pwn 0.5.784 → 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.
Files changed (254) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/Gemfile +6 -6
  4. data/README.md +37 -0
  5. data/bin/pwn +2 -1
  6. data/bin/pwn_web_cache_deception +35 -7
  7. data/build_gem.sh +1 -1
  8. data/documentation/AI-Integration.md +7 -5
  9. data/documentation/Agent-Tool-Registry.md +2 -2
  10. data/documentation/Backup-Retention.md +69 -15
  11. data/documentation/Configuration.md +10 -6
  12. data/documentation/Contributing.md +1 -1
  13. data/documentation/Cron.md +2 -2
  14. data/documentation/Diagrams.md +2 -2
  15. data/documentation/General-PWN-Usage.md +4 -3
  16. data/documentation/Harness-Evaluation.md +1 -1
  17. data/documentation/Home.md +6 -5
  18. data/documentation/How-PWN-Works.md +6 -5
  19. data/documentation/Installation.md +12 -9
  20. data/documentation/Mission-Cancellation.md +134 -0
  21. data/documentation/Operability.md +18 -0
  22. data/documentation/Persistence.md +7 -5
  23. data/documentation/Plugin-Degradation.md +14 -0
  24. data/documentation/Plugins.md +3 -2
  25. data/documentation/Policy-Benchmark.md +32 -33
  26. data/documentation/Runtime-Source-Protection.md +162 -0
  27. data/documentation/Transparent-Browser.md +11 -0
  28. data/documentation/Troubleshooting.md +3 -2
  29. data/documentation/What-is-PWN.md +2 -2
  30. data/documentation/YubiKey-Vaults.md +185 -0
  31. data/documentation/YubiKey.md +218 -0
  32. data/documentation/diagrams/cron-scheduling.svg +130 -130
  33. data/documentation/diagrams/dot/cron-scheduling.dot +3 -3
  34. data/documentation/diagrams/dot/persistence-filesystem.dot +5 -3
  35. data/documentation/diagrams/dot/plugin-ecosystem.dot +2 -2
  36. data/documentation/diagrams/dot/pwn-ai-feedback-learning-loop.dot +1 -1
  37. data/documentation/diagrams/dot/reinforcement-learning.dot +2 -2
  38. data/documentation/diagrams/persistence-filesystem.svg +172 -142
  39. data/documentation/diagrams/plugin-ecosystem.svg +64 -64
  40. data/documentation/diagrams/pwn-ai-feedback-learning-loop.svg +1 -1
  41. data/documentation/diagrams/reinforcement-learning.svg +89 -89
  42. data/documentation/pwn-ai-Agent.md +10 -4
  43. data/etc/default_cron/session_cleanup.rb +43 -27
  44. data/etc/default_skills/pwn/ai/agent/mission_namespace/SKILL.md +21 -0
  45. data/etc/default_skills/pwn/ai/agent/tools/browser_act/SKILL.md +45 -0
  46. data/etc/default_skills/pwn/ai/agent/tools/loot_query/SKILL.md +27 -26
  47. data/etc/default_skills/pwn/ai/agent/tools/recon_run/SKILL.md +25 -15
  48. data/etc/default_skills/pwn/ai/agent/tools/workspace_tool/SKILL.md +24 -0
  49. data/etc/default_skills/pwn/ai/server_tools/SKILL.md +25 -0
  50. data/etc/default_skills/pwn/backups/SKILL.md +54 -0
  51. data/etc/default_skills/pwn/plugins/recon/SKILL.md +12 -1
  52. data/etc/default_skills/pwn/plugins/yubikey/SKILL.md +157 -0
  53. data/etc/default_skills/pwn/plugins/yubikey/fido/SKILL.md +189 -0
  54. data/etc/default_skills/pwn/plugins/yubikey/hsmauth/SKILL.md +83 -0
  55. data/etc/default_skills/pwn/plugins/yubikey/management/SKILL.md +80 -0
  56. data/etc/default_skills/pwn/plugins/yubikey/oath/SKILL.md +115 -0
  57. data/etc/default_skills/pwn/plugins/yubikey/openpgp/SKILL.md +144 -0
  58. data/etc/default_skills/pwn/plugins/yubikey/otp/SKILL.md +97 -0
  59. data/etc/default_skills/pwn/plugins/yubikey/piv/SKILL.md +110 -0
  60. data/etc/default_skills/pwn/plugins/yubikey/protocol/SKILL.md +21 -0
  61. data/etc/default_skills/pwn/plugins/yubikey/security_domain/SKILL.md +75 -0
  62. data/etc/default_skills/pwn/plugins/yubikey/security_domain/scp/SKILL.md +46 -0
  63. data/etc/default_skills/pwn/plugins/yubikey/transport/SKILL.md +98 -0
  64. data/etc/default_skills/pwn/redaction/SKILL.md +13 -1
  65. data/lib/pwn/ai/agent/dispatch.rb +26 -15
  66. data/lib/pwn/ai/agent/learning.rb +14 -18
  67. data/lib/pwn/ai/agent/loop.rb +17 -1
  68. data/lib/pwn/ai/agent/mission_namespace.rb +331 -0
  69. data/lib/pwn/ai/agent/policy_evaluation.rb +334 -36
  70. data/lib/pwn/ai/agent/prompt_builder.rb +13 -2
  71. data/lib/pwn/ai/agent/request_runtime.rb +122 -18
  72. data/lib/pwn/ai/agent/reward.rb +5 -3
  73. data/lib/pwn/ai/agent/skill_consolidation.rb +3 -2
  74. data/lib/pwn/ai/agent/skill_review.rb +9 -5
  75. data/lib/pwn/ai/agent/swarm.rb +4 -0
  76. data/lib/pwn/ai/agent/tools/browser_act.rb +32 -0
  77. data/lib/pwn/ai/agent/tools/loot_query.rb +11 -1
  78. data/lib/pwn/ai/agent/tools/recon_run.rb +11 -1
  79. data/lib/pwn/ai/agent/tools/workspace_tool.rb +29 -0
  80. data/lib/pwn/ai/agent/turn_finalizer.rb +6 -2
  81. data/lib/pwn/ai/agent.rb +1 -0
  82. data/lib/pwn/ai/anthropic.rb +8 -33
  83. data/lib/pwn/ai/cli.rb +34 -5
  84. data/lib/pwn/ai/gemini.rb +1 -0
  85. data/lib/pwn/ai/grok.rb +39 -28
  86. data/lib/pwn/ai/ollama.rb +4 -2
  87. data/lib/pwn/ai/open_ai.rb +11 -32
  88. data/lib/pwn/ai/open_web_ui.rb +62 -4
  89. data/lib/pwn/ai/router.rb +8 -1
  90. data/lib/pwn/ai/server_tools.rb +231 -0
  91. data/lib/pwn/ai.rb +1 -0
  92. data/lib/pwn/backups.rb +349 -0
  93. data/lib/pwn/config.rb +70 -39
  94. data/lib/pwn/migrate.rb +85 -280
  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/google_workspace.rb +4 -21
  100. data/lib/pwn/plugins/jobs.rb +1 -1
  101. data/lib/pwn/plugins/log.rb +3 -1
  102. data/lib/pwn/plugins/preflight_checker.rb +34 -9
  103. data/lib/pwn/plugins/recon.rb +4 -1
  104. data/lib/pwn/plugins/repl/ai.rb +293 -26
  105. data/lib/pwn/plugins/repl/ai_console.rb +268 -88
  106. data/lib/pwn/plugins/repl/ai_swarm.rb +177 -58
  107. data/lib/pwn/plugins/repl/mesh.rb +14 -19
  108. data/lib/pwn/plugins/repl/vault.rb +4 -15
  109. data/lib/pwn/plugins/repl.rb +9 -216
  110. data/lib/pwn/plugins/sandbox/driver.rb +4 -0
  111. data/lib/pwn/plugins/transparent_browser.rb +215 -0
  112. data/lib/pwn/plugins/vault.rb +412 -135
  113. data/lib/pwn/plugins/yubikey/fido.rb +1288 -0
  114. data/lib/pwn/plugins/yubikey/hsmauth.rb +439 -0
  115. data/lib/pwn/plugins/yubikey/management.rb +686 -0
  116. data/lib/pwn/plugins/yubikey/oath.rb +766 -0
  117. data/lib/pwn/plugins/yubikey/openpgp.rb +1063 -0
  118. data/lib/pwn/plugins/yubikey/otp.rb +897 -0
  119. data/lib/pwn/plugins/yubikey/piv.rb +1315 -0
  120. data/lib/pwn/plugins/yubikey/protocol.rb +341 -0
  121. data/lib/pwn/plugins/yubikey/security_domain/scp.rb +482 -0
  122. data/lib/pwn/plugins/yubikey/security_domain.rb +464 -0
  123. data/lib/pwn/plugins/yubikey/transport.rb +1017 -0
  124. data/lib/pwn/plugins/yubikey.rb +844 -0
  125. data/lib/pwn/plugins.rb +1 -0
  126. data/lib/pwn/redaction.rb +69 -12
  127. data/lib/pwn/session_trace.rb +4 -0
  128. data/lib/pwn/setup.rb +114 -24
  129. data/lib/pwn/version.rb +1 -1
  130. data/lib/pwn/www/app_cobalt_io.rb +16 -5
  131. data/lib/pwn/www/bug_crowd.rb +16 -5
  132. data/lib/pwn/www/coinbase_pro.rb +16 -5
  133. data/lib/pwn/www/github.rb +30 -14
  134. data/lib/pwn/www/paypal.rb +15 -4
  135. data/lib/pwn/www/synack.rb +16 -5
  136. data/lib/pwn/www/twitter.rb +17 -6
  137. data/lib/pwn.rb +1 -0
  138. data/spec/documentation/installation_md_spec.rb +1 -1
  139. data/spec/fixtures/yubikey/apdu_frames.json +162 -0
  140. data/spec/integration/authentication_helper_oath_spec.rb +117 -0
  141. data/spec/integration/controller_spill_spec.rb +48 -0
  142. data/spec/integration/harness_process_spec.rb +97 -0
  143. data/spec/integration/mission_cancel_spec.rb +79 -0
  144. data/spec/integration/mission_curses_pty_spec.rb +44 -0
  145. data/spec/integration/readme_security_spec.rb +47 -0
  146. data/spec/integration/web_cache_deception_yubikey_spec.rb +159 -0
  147. data/spec/lib/pwn/ai/agent/dispatch_spec.rb +6 -3
  148. data/spec/lib/pwn/ai/agent/dispatch_vault_safety_spec.rb +86 -0
  149. data/spec/lib/pwn/ai/agent/injection_guard_spec.rb +2 -0
  150. data/spec/lib/pwn/ai/agent/learning_spec.rb +4 -4
  151. data/spec/lib/pwn/ai/agent/loop_artifacts_spec.rb +5 -0
  152. data/spec/lib/pwn/ai/agent/loop_spec.rb +104 -29
  153. data/spec/lib/pwn/ai/agent/mission_namespace_spec.rb +138 -0
  154. data/spec/lib/pwn/ai/agent/policy_evaluation_spec.rb +10 -23
  155. data/spec/lib/pwn/ai/agent/request_runtime_spec.rb +48 -0
  156. data/spec/lib/pwn/ai/agent/shell_search_effect_spec.rb +44 -0
  157. data/spec/lib/pwn/ai/agent/solve_spec.rb +6 -4
  158. data/spec/lib/pwn/ai/agent/tools/browser_act_spec.rb +44 -0
  159. data/spec/lib/pwn/ai/agent/tools/loot_query_yubikey_spec.rb +55 -0
  160. data/spec/lib/pwn/ai/agent/tools/recon_run_yubikey_spec.rb +43 -0
  161. data/spec/lib/pwn/ai/agent/tools/workspace_tool_spec.rb +20 -0
  162. data/spec/lib/pwn/ai/agent/turn_finalizer_spec.rb +7 -0
  163. data/spec/lib/pwn/ai/anthropic_vault_enrollment_spec.rb +37 -0
  164. data/spec/lib/pwn/ai/cli_spec.rb +8 -16
  165. data/spec/lib/pwn/ai/oauth_vault_persistence_spec.rb +195 -0
  166. data/spec/lib/pwn/ai/open_ai_spec.rb +2 -2
  167. data/spec/lib/pwn/ai/open_web_ui_workspace_spec.rb +36 -0
  168. data/spec/lib/pwn/ai/router_spec.rb +1 -1
  169. data/spec/lib/pwn/ai/server_tools_spec.rb +39 -0
  170. data/spec/lib/pwn/backups_spec.rb +129 -0
  171. data/spec/lib/pwn/config_redaction_yubikey_spec.rb +28 -0
  172. data/spec/lib/pwn/config_yubikey_spec.rb +92 -0
  173. data/spec/lib/pwn/migrate_spec.rb +29 -81
  174. data/spec/lib/pwn/migrate_yubikey_spec.rb +128 -0
  175. data/spec/lib/pwn/module_skills_spec.rb +8 -0
  176. data/spec/lib/pwn/plugins/ai_sandbox_spec.rb +230 -0
  177. data/spec/lib/pwn/plugins/authentication_helper_native_yubikey_spec.rb +40 -0
  178. data/spec/lib/pwn/plugins/authentication_helper_yubikey_spec.rb +90 -0
  179. data/spec/lib/pwn/plugins/capability_broker/daemon_spec.rb +1 -1
  180. data/spec/lib/pwn/plugins/google_workspace_spec.rb +17 -35
  181. data/spec/lib/pwn/plugins/log_yubikey_spec.rb +47 -0
  182. data/spec/lib/pwn/plugins/preflight_checker_native_yubikey_spec.rb +19 -0
  183. data/spec/lib/pwn/plugins/preflight_checker_yubikey_spec.rb +55 -0
  184. data/spec/lib/pwn/plugins/recon_yubikey_spec.rb +43 -0
  185. data/spec/lib/pwn/plugins/repl/ai_console_spec.rb +110 -75
  186. data/spec/lib/pwn/plugins/repl/ai_legacy_containment_spec.rb +200 -0
  187. data/spec/lib/pwn/plugins/repl/ai_swarm_spec.rb +19 -16
  188. data/spec/lib/pwn/plugins/repl/ai_vault_edit_yubikey_spec.rb +103 -0
  189. data/spec/lib/pwn/plugins/repl/ai_yubikey_spec.rb +48 -0
  190. data/spec/lib/pwn/plugins/repl/mesh_vault_persistence_spec.rb +149 -0
  191. data/spec/lib/pwn/plugins/repl/vault_yaml_serialization_spec.rb +11 -0
  192. data/spec/lib/pwn/plugins/repl/vault_yubikey_spec.rb +39 -0
  193. data/spec/lib/pwn/plugins/repl_mesh_layout_spec.rb +2 -0
  194. data/spec/lib/pwn/plugins/repl_spec.rb +56 -5
  195. data/spec/lib/pwn/plugins/transparent_browser_jev_spec.rb +46 -0
  196. data/spec/lib/pwn/plugins/vault_native_yubikey_spec.rb +29 -0
  197. data/spec/lib/pwn/plugins/vault_publication_safety_spec.rb +258 -0
  198. data/spec/lib/pwn/plugins/vault_store_safety_spec.rb +241 -0
  199. data/spec/lib/pwn/plugins/vault_yubikey_safety_spec.rb +168 -0
  200. data/spec/lib/pwn/plugins/vault_yubikey_spec.rb +128 -0
  201. data/spec/lib/pwn/plugins/vault_yubikey_vector_spec.rb +45 -0
  202. data/spec/lib/pwn/plugins/yubikey/fido_spec.rb +854 -0
  203. data/spec/lib/pwn/plugins/yubikey/hsmauth_spec.rb +235 -0
  204. data/spec/lib/pwn/plugins/yubikey/management_spec.rb +277 -0
  205. data/spec/lib/pwn/plugins/yubikey/oath_spec.rb +349 -0
  206. data/spec/lib/pwn/plugins/yubikey/openpgp_spec.rb +810 -0
  207. data/spec/lib/pwn/plugins/yubikey/otp_spec.rb +337 -0
  208. data/spec/lib/pwn/plugins/yubikey/piv_spec.rb +646 -0
  209. data/spec/lib/pwn/plugins/yubikey/protocol_secure_spec.rb +28 -0
  210. data/spec/lib/pwn/plugins/yubikey/protocol_spec.rb +149 -0
  211. data/spec/lib/pwn/plugins/yubikey/protocol_vectors_spec.rb +18 -0
  212. data/spec/lib/pwn/plugins/yubikey/security_domain/scp_spec.rb +256 -0
  213. data/spec/lib/pwn/plugins/yubikey/security_domain/support.rb +153 -0
  214. data/spec/lib/pwn/plugins/yubikey/security_domain_spec.rb +296 -0
  215. data/spec/lib/pwn/plugins/yubikey/transport_spec.rb +760 -0
  216. data/spec/lib/pwn/plugins/yubikey_security_spec.rb +260 -0
  217. data/spec/lib/pwn/plugins/yubikey_spec.rb +209 -0
  218. data/spec/lib/pwn/plugins/yubikey_terminal_spec.rb +147 -0
  219. data/spec/lib/pwn/redaction_yubikey_spec.rb +61 -0
  220. data/spec/lib/pwn/session_cleanup_default_spec.rb +7 -5
  221. data/spec/lib/pwn/session_cleanup_payload_spec.rb +11 -11
  222. data/spec/lib/pwn/session_cleanup_recovery_spec.rb +34 -20
  223. data/spec/lib/pwn/setup_native_yubikey_spec.rb +66 -0
  224. data/spec/lib/pwn/setup_vault_yubikey_spec.rb +61 -0
  225. data/spec/lib/pwn/setup_yubikey_spec.rb +40 -0
  226. data/spec/lib/pwn/www/login_yubikey_spec.rb +273 -0
  227. data/spec/spec_helper.rb +15 -0
  228. data/spec/support/harness/cli_faults.rb +13 -9
  229. data/spec/support/harness/controller_spill.rb +26 -0
  230. data/spec/support/harness/controller_state.rb +54 -0
  231. data/spec/support/harness/evaluate.rb +12 -1
  232. data/spec/support/harness/experiments.rb +1 -1
  233. data/spec/support/harness/fake_provider.rb +22 -6
  234. data/spec/support/harness/fault_preload.rb +3 -1
  235. data/spec/support/harness/mission_console.rb +54 -0
  236. data/spec/support/harness/mission_curses_pty.rb +41 -0
  237. data/spec/support/harness/mission_dispatch.rb +30 -0
  238. data/spec/support/harness/mission_fixture_preload.rb +24 -0
  239. data/spec/support/harness/mission_job_lifetime.rb +25 -0
  240. data/spec/support/harness/mission_legacy.rb +25 -0
  241. data/spec/support/harness/mission_normal_exit.rb +34 -0
  242. data/spec/support/harness/mission_swarm.rb +23 -0
  243. data/spec/support/harness/mission_unsupported.rb +29 -0
  244. data/spec/support/harness/package_acceptance.rb +33 -5
  245. data/spec/support/harness/session_acceptance.rb +4 -4
  246. data/spec/support/harness/swarm_acceptance.rb +1 -1
  247. data/spec/support/harness/swarm_control.rb +44 -0
  248. data/spec/support/mission_namespace.rb +14 -0
  249. data/spec/support/mission_pipe_queue.rb +33 -0
  250. data/third_party/pwn_rdoc.jsonl +605 -19
  251. metadata +129 -16
  252. data/scripts/benchmark_policy.rb +0 -376
  253. data/scripts/build_gate.rb +0 -58
  254. data/spec/integration/build_gate_spec.rb +0 -73
@@ -12,7 +12,7 @@ with a **tool-calling AI agent** on top that can run the same methods.
12
12
 
13
13
  | Namespace | Count | What it is |
14
14
  |---|---|---|
15
- | `PWN::Plugins::*` | **107** | Wrappers for external and native tooling (Burp, Nmap, Metasploit, Shodan, browsers, serial, ...) |
15
+ | `PWN::Plugins::*` | **108** | Wrappers for external and native tooling (Burp, Nmap, Metasploit, Shodan, browsers, serial, YubiKey, ...) |
16
16
  | `PWN::SAST::*` | **48** | Static-analysis rules across C/Java/Go/Python/Ruby/Scala/PHP/TS |
17
17
  | `PWN::AWS::*` | **90** | One module per AWS service for cloud enumeration |
18
18
  | `PWN::WWW::*` | **22** | Site-specific browser automations (HackerOne, BugCrowd, GitHub, Google, LinkedIn, ...) |
@@ -20,7 +20,7 @@ with a **tool-calling AI agent** on top that can run the same methods.
20
20
  | `PWN::FFI::*` | **11** | Native bindings (including DSPNative, Capstone and Keystone): Volk · Liquid · FFTW · RTLSdr · HackRF · AdalmPluto · SoapySDR · Stdio |
21
21
  | `PWN::AI::*` | **6** engines | OpenAI, Anthropic, Grok (OAuth device-flow), Gemini, Ollama, Open WebUI |
22
22
  | `bin/` (including `pwn-ai` and `pwn-capd`) | **56** | Headless CLI executables for CI/CD |
23
- | Agent toolsets | **16** · **150 tools** | terminal · pwn · memory · skills · sessions · learning · metrics · policy · extrospection · cron · swarm · reward · curriculum · http · mcp · manifest |
23
+ | Agent toolsets | **16** · **152 tools** | terminal · pwn · memory · skills · sessions · learning · metrics · policy · extrospection · cron · swarm · reward · curriculum · http · mcp · manifest |
24
24
 
25
25
  ## Three ways to use it
26
26
 
@@ -0,0 +1,185 @@
1
+ # YubiKey-backed vaults and authentication
2
+
3
+ See [YubiKey device operations](YubiKey.md) for native HID/PC/SC operations,
4
+ provisioning safeguards and the firmware boundary. No `ykman` process is used.
5
+
6
+ ## Encryption model
7
+
8
+ This is **hardware-derived key encryption**, not bulk encryption inside the
9
+ YubiKey. An already provisioned OTP HMAC-SHA1 slot answers a fresh random
10
+ 32-byte challenge. Ruby derives a 32-byte encryption key using HKDF-SHA256 with
11
+ a fresh 16-byte salt and a domain-specific context, then uses AES-256-GCM with
12
+ a fresh nonce and a full 16-byte tag. The HMAC response and derived key exist
13
+ transiently in the Ruby process; the slot secret is not exported. Ruby does not
14
+ promise zeroization of copied strings.
15
+
16
+ The versioned `PWN-YUBIKEY-VAULT:1` envelope authenticates its metadata as AAD.
17
+ The public challenge, salt, slot and optional serial are stored with ciphertext,
18
+ not in a secret sidecar. Executable paths and timeout overrides are supplied
19
+ by the caller, never loaded from ciphertext. PINs, OTP codes and serial numbers
20
+ are not encryption keys. HMAC-SHA1 is used as the device PRF, not as an
21
+ unauthenticated document hash; it provides at most the slot/response security,
22
+ not 256 bits of entropy merely because AES-256 is used.
23
+
24
+ Existing Base64 AES-256-CBC files and software loot remain readable. New
25
+ hardware protection is opt-in; no existing configuration is migrated merely by
26
+ loading PWN. A hardware envelope never falls back to a software master key,
27
+ passphrase or unauthenticated plaintext when a device is missing or a tag fails.
28
+ Do not combine a software key/IV or passphrase/keyring with hardware selection.
29
+
30
+ ## Use an existing HMAC slot
31
+
32
+ Use a key model supporting OTP HMAC challenge-response. FIDO-only Security Keys
33
+ do not support this vault backend. Set up the slot explicitly using the device
34
+ management interface; prefer its physical-touch policy. PWN never provisions,
35
+ replaces, unlocks by guessing, or resets a slot in order to open a vault.
36
+
37
+ ```ruby
38
+ # The named file must already contain the plaintext you intend to encrypt.
39
+ PWN::Plugins::Vault.create(file: '/path/to/private.yaml', yubikey: { slot: 2 })
40
+
41
+ # With multiple devices, explicitly select the intended device serial.
42
+ # Serial is a placeholder; replace locally, never paste secrets into a transcript.
43
+ selector = { slot: 2, serial: '12345678' }
44
+ PWN::Plugins::Vault.encrypt(file: '/path/to/another.yaml', yubikey: selector)
45
+
46
+ # Read-only: decrypted data exists in memory, not an intermediate file.
47
+ config = PWN::Plugins::Vault.dump(file: '/path/to/private.yaml'); nil
48
+
49
+ # Mutate YAML in memory and atomically publish ciphertext, preserving the backend.
50
+ PWN::Plugins::Vault.update(file: '/path/to/private.yaml') do |document|
51
+ document[:setting] = 'replacement'
52
+ end
53
+ ```
54
+
55
+ `yubikey` is a Hash accepting `slot` (integer 1 or 2; default 2), optional
56
+ exact decimal `serial`, optional `reader` or `device_path`, and bounded
57
+ `timeout` seconds. An executable path such as `ykman` is rejected. The
58
+ envelope stores slot and serial only; it never stores a backend path.
59
+ `dump`, `decrypt`, `open_credentials` and hardware loot retrieval otherwise use
60
+ the slot/serial stored in their envelope. Selecting another serial permits an
61
+ explicitly provisioned recovery key with the same slot secret; the authentication
62
+ tag still has to verify. A different slot requires explicit rewrapping.
63
+
64
+ `encrypt_data` / `decrypt_data` are in-memory String APIs, including arbitrary
65
+ binary data and empty plaintext. `seal_credentials` / `open_credentials` work
66
+ on JSON-compatible credential values. Neither API opens the live configuration
67
+ or writes keys. `file_encrypted?` / `hardware_encrypted?` inspect format only;
68
+ they do not prove the device is present or that a ciphertext authenticates.
69
+
70
+ `decrypt(file:)` explicitly replaces ciphertext with plaintext. Use `dump`
71
+ instead when plaintext-on-disk is unnecessary. `edit` explicitly gives a private
72
+ 0600 temporary copy to the chosen editor under `~/.pwn/workspaces/runtime`, then
73
+ removes its private working directory. The original stays encrypted until a
74
+ successful re-encryption and atomic replacement. Editors are trusted programs:
75
+ their external backups, OS swap, crash dumps and an abruptly killed process are
76
+ not a secure-erasure guarantee. The curses `/vault edit` editor works in memory
77
+ and writes only ciphertext. Built-in file writers share its parent-directory
78
+ advisory lock across the final baseline check and atomic rename. A stale edit
79
+ raises rather than overwriting a newer publication. This is not a filesystem
80
+ compare-and-swap guarantee against external writers that ignore the lock.
81
+
82
+ ## Conversion and recovery
83
+
84
+ Before converting a legacy vault, retain its verified encrypted backup and
85
+ legacy decryptor somewhere secure. Conversion is explicit:
86
+
87
+ ```ruby
88
+ PWN::Plugins::Vault.refresh_encryption_secrets(
89
+ file: '/path/to/legacy.yaml',
90
+ key: legacy_key, iv: legacy_iv,
91
+ new_yubikey: { slot: 2 }
92
+ )
93
+ ```
94
+
95
+ No temporary plaintext is published during conversion. On an existing hardware
96
+ vault, `refresh_encryption_secrets(file:)` rewraps with fresh challenge/salt/nonce;
97
+ it does **not** change the device secret. Supply `new_yubikey:` to move to another
98
+ already provisioned slot/device while the old one is available. An explicit
99
+ `new_yubikey:` must be a valid selector Hash; values such as `false` are rejected
100
+ before opening the source vault, not treated as a request for software rotation.
101
+ Hardware access or encryption failure leaves the original ciphertext intact. Old software
102
+ sidecars and backups are not silently deleted; retire them deliberately after
103
+ verifying recovery.
104
+
105
+ Losing or reprogramming the only usable slot loses access. A ciphertext backup
106
+ alone is not an independent recovery key. Establish and test an appropriate
107
+ recovery device/secret before relying on hardware-only storage. Removing a
108
+ YubiKey does not erase an already unlocked `PWN::Env` or revoke remote tokens;
109
+ the application retains its in-memory configuration until exit/refresh.
110
+
111
+ ## Integration coverage
112
+
113
+ | Entry point | Hardware behavior |
114
+ |---|---|
115
+ | `Vault.create/encrypt/decrypt/dump/edit/refresh_encryption_secrets` | Explicit creation/conversion, automatic envelope detection on read, backend-preserving edits |
116
+ | `Vault.seal_credentials/open_credentials` | Authenticated in-memory hardware envelopes alongside existing passphrase/keyring formats |
117
+ | `Vault.store/fetch/offer/ingest/expand/redact` | Optional hardware-protected engagement records; metadata-only `query` does not touch hardware |
118
+ | `BasicAuth.encode(host:)` | Resolves matching protected loot through Vault, not a separate crypto implementation |
119
+ | `Redaction.capture` and `Recon` loot ingestion | Explicit hardware selector for new captures; locked-store failure never creates a software-key fallback |
120
+ | `Config.default_env/refresh_env` | Explicit hardware creation; hardware loading without a legacy decryptor sidecar |
121
+ | `Migrate.vault_drift/backfill_vault` | Read/merge through Vault, preserving hardware format and user settings |
122
+ | `pwn-vault`, `/vault edit`, `/model`, `/system-role`, mesh settings | Backend-aware encrypted persistence; no duplicate CBC-only write path |
123
+ | OpenAI, Anthropic, Grok and Google Workspace OAuth persistence | In-memory configuration merge, ciphertext-only atomic save; hardware unavailability reports persistence failure |
124
+ | `AuthenticationHelper.mfa` and supported WWW login methods | Explicit exact-account OATH code lookup, separate from ordinary manual MFA |
125
+
126
+ Hardware-protected loot reads may require a touch, including explicit expansion
127
+ and secret-value redaction. Do not enable it for unattended workflows expecting
128
+ unbounded automatic access. Background metadata discovery and help do not unlock
129
+ hardware. Existing unrelated records are not implicitly re-encrypted. Missing
130
+ loot stores are empty; an existing unreadable store, malformed JSON, or invalid
131
+ record/envelope shape raises `IOError` on reads and writes, including metadata
132
+ queries. PWN does not infer a software backend from corrupt records, generate a
133
+ replacement master key, or overwrite the damaged store. Restore a verified backup
134
+ before retrying.
135
+
136
+ ## Explicit OATH authentication
137
+
138
+ Select an exact account and authorize its effects explicitly:
139
+
140
+ ```ruby
141
+ code = PWN::Plugins::AuthenticationHelper.mfa(
142
+ yubikey: { account: 'Example:alice', serial: '12345678', confirm: true }
143
+ ); nil
144
+ ```
145
+
146
+ The same `yubikey:` Hash is accepted by GitHub, Synack, Twitter, BugCrowd,
147
+ CoinbasePro and AppCobaltIO login methods. Lookup occurs at the authenticator
148
+ form, not during option validation. Missing/nonliteral consent is rejected before
149
+ password prompts or navigation. Device errors do not fall back to manual MFA;
150
+ explicit manual tokens cannot be combined with the hardware selector. PayPal's
151
+ current SMS-only adapter rejects OATH even with consent, and GitHub's SMS-only
152
+ challenge does not accept an authenticator code.
153
+
154
+ For `pwn_web_cache_deception`, add `--yubikey-account='Example:alice'`, optionally
155
+ `--yubikey-serial=12345678`, and the required `--yubikey-confirm` to the normal
156
+ arguments for a compatible login module. The flag authorizes native OATH
157
+ authentication and HOTP counter advancement. It does not authorize deleting or
158
+ repairing a corrupted credential. It is required for TOTP too; PWN never
159
+ silently supplies consent.
160
+ Confirmation/serial flags without an account are rejected before login.
161
+
162
+ ## Scope boundaries
163
+
164
+ YubiKey FIDO management is not a WebAuthn relying-party implementation. Website
165
+ passkey login still requires the browser's genuine security-key ceremony and
166
+ site enrollment; a generated OATH code must never be submitted to an SMS-only
167
+ challenge. PIV/OpenPGP key management does not export non-exportable keys or
168
+ magically replace TLS, SSH-agent or GPG-provider integrations.
169
+
170
+ Radio protocol keys, packet AES-CCM, TLS inspection certificates, OAuth PKCE,
171
+ artifact hashes, cloud KMS/HSM API clients and Jenkins private-key uploads are
172
+ not rewritten to use hardware-derived bytes. Their stored credentials can be
173
+ protected through Vault where appropriate.
174
+
175
+ Hardware-backed loot does not encrypt Recon's raw evidence/asset files, generic
176
+ tool-result artifacts, exports, or the filesystem as a whole. Those retain their
177
+ existing evidence-storage behavior and need separate access/at-rest protection.
178
+ `recon_run` advertises the same nonsecret hardware selector as `loot_query` for
179
+ harvested loot; metadata-only Vault queries do not unlock a device, but Dispatch's
180
+ separate output-redaction step can still require access to protected secrets.
181
+
182
+ Verification uses synthetic native connections and HMAC responses. Tests
183
+ exercise real Ruby encryption, authentication failures, selector validation
184
+ and persistence entry points. They are not evidence of a physical YubiKey,
185
+ live website authentication or every firmware variant.
@@ -0,0 +1,218 @@
1
+ # YubiKey device operations
2
+
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.
11
+
12
+ For encrypted configuration, credential storage, conversion and recovery, see
13
+ [YubiKey-backed vaults and authentication](YubiKey-Vaults.md).
14
+
15
+ ## Dependency and discovery
16
+
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).
24
+
25
+ ```ruby
26
+ require 'pwn'
27
+ yk = PWN::Plugins::YubiKey
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
32
+ yk.otp(args: ['info'], serial: '12345678')
33
+ yk.help # public methods and option descriptions
34
+ yk.cli_help # local native help metadata, not a CLI process
35
+ yk.cli_help(path: %w[piv keys generate])
36
+ ```
37
+
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.
45
+
46
+ ## Native application coverage
47
+
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.
53
+
54
+ | Method | Application | Covered operations |
55
+ |---|---|---|
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:
87
+
88
+ ```ruby
89
+ yk.interact(
90
+ args: %w[fido access change-pin], serial: '12345678',
91
+ confirm: true, operator: true, timeout: 90
92
+ )
93
+ ```
94
+
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.
121
+
122
+ ## Existing-slot HMAC challenge-response
123
+
124
+ ```ruby
125
+ require 'securerandom'
126
+ challenge = SecureRandom.random_bytes(32) # Public challenge, not a provisioning key.
127
+ response = PWN::Plugins::YubiKey.challenge_response(
128
+ challenge: challenge,
129
+ slot: 2,
130
+ serial: '12345678',
131
+ timeout: 30
132
+ ); nil # Suppress sensitive return-value echo in an interactive console.
133
+ ```
134
+
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.
141
+
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.
145
+
146
+ ## Explicit HMAC provisioning
147
+
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.
151
+
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
+ ```
163
+
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.
171
+
172
+ ## Exact OATH retrieval
173
+
174
+ ```ruby
175
+ code = PWN::Plugins::YubiKey.oath_code(
176
+ account: 'Example:alice', # Exact identifier, not a substring.
177
+ serial: '12345678',
178
+ confirm: true,
179
+ timeout: 30
180
+ ); nil
181
+ ```
182
+
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.
189
+
190
+ ## Authentication and interoperability limits
191
+
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.
197
+
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.
203
+
204
+ ## Offline verification
205
+
206
+ ```text
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
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
209
+ ```
210
+
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
+
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/)