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.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/Gemfile +8 -8
- data/README.md +37 -0
- data/Rakefile +19 -1
- data/bin/pwn +2 -1
- data/bin/pwn_setup +1 -1
- data/bin/pwn_web_cache_deception +35 -7
- data/build_gem.sh +1 -1
- data/documentation/AI-Integration.md +7 -5
- data/documentation/Agent-Tool-Registry.md +2 -2
- data/documentation/Backup-Retention.md +82 -0
- data/documentation/Configuration.md +10 -6
- data/documentation/Contributing.md +1 -1
- data/documentation/Cron.md +64 -35
- data/documentation/Diagrams.md +2 -2
- data/documentation/General-PWN-Usage.md +4 -3
- data/documentation/Home.md +6 -5
- data/documentation/How-PWN-Works.md +7 -6
- data/documentation/Installation.md +254 -10
- data/documentation/Mission-Cancellation.md +134 -0
- data/documentation/Operability.md +17 -0
- data/documentation/Persistence.md +8 -6
- data/documentation/Plugin-Degradation.md +12 -0
- data/documentation/Plugins.md +3 -2
- data/documentation/Reinforcement-Learning.md +5 -6
- data/documentation/Runtime-Source-Protection.md +162 -0
- data/documentation/Transparent-Browser.md +11 -0
- data/documentation/Troubleshooting.md +3 -2
- data/documentation/What-is-PWN.md +2 -2
- data/documentation/YubiKey-Vaults.md +183 -0
- data/documentation/YubiKey.md +339 -0
- data/documentation/diagrams/cron-scheduling.svg +130 -128
- data/documentation/diagrams/dot/cron-scheduling.dot +5 -5
- data/documentation/diagrams/dot/persistence-filesystem.dot +5 -3
- data/documentation/diagrams/dot/plugin-ecosystem.dot +2 -2
- data/documentation/diagrams/persistence-filesystem.svg +172 -142
- data/documentation/diagrams/plugin-ecosystem.svg +64 -64
- data/documentation/pwn-ai-Agent.md +10 -4
- data/etc/default_cron/session_cleanup.rb +1245 -22
- data/etc/default_skills/pwn/ai/agent/mission_namespace/SKILL.md +21 -0
- data/etc/default_skills/pwn/ai/agent/tools/browser_act/SKILL.md +45 -0
- data/etc/default_skills/pwn/ai/agent/tools/loot_query/SKILL.md +27 -26
- data/etc/default_skills/pwn/ai/agent/tools/recon_run/SKILL.md +25 -15
- data/etc/default_skills/pwn/ai/agent/tools/workspace_tool/SKILL.md +24 -0
- data/etc/default_skills/pwn/ai/server_tools/SKILL.md +25 -0
- data/etc/default_skills/pwn/backups/SKILL.md +54 -0
- data/etc/default_skills/pwn/plugins/recon/SKILL.md +12 -1
- data/etc/default_skills/pwn/plugins/yubikey/SKILL.md +157 -0
- data/etc/default_skills/pwn/plugins/yubikey/fido/SKILL.md +189 -0
- data/etc/default_skills/pwn/plugins/yubikey/hsmauth/SKILL.md +83 -0
- data/etc/default_skills/pwn/plugins/yubikey/management/SKILL.md +80 -0
- data/etc/default_skills/pwn/plugins/yubikey/oath/SKILL.md +115 -0
- data/etc/default_skills/pwn/plugins/yubikey/openpgp/SKILL.md +144 -0
- data/etc/default_skills/pwn/plugins/yubikey/otp/SKILL.md +97 -0
- data/etc/default_skills/pwn/plugins/yubikey/piv/SKILL.md +110 -0
- data/etc/default_skills/pwn/plugins/yubikey/protocol/SKILL.md +21 -0
- data/etc/default_skills/pwn/plugins/yubikey/security_domain/SKILL.md +75 -0
- data/etc/default_skills/pwn/plugins/yubikey/security_domain/scp/SKILL.md +46 -0
- data/etc/default_skills/pwn/plugins/yubikey/transport/SKILL.md +98 -0
- data/etc/default_skills/pwn/redaction/SKILL.md +13 -1
- data/lib/pwn/ai/agent/dispatch.rb +26 -15
- data/lib/pwn/ai/agent/extrospection.rb +6 -7
- data/lib/pwn/ai/agent/learning.rb +62 -25
- data/lib/pwn/ai/agent/loop.rb +17 -1
- data/lib/pwn/ai/agent/mission_namespace.rb +331 -0
- data/lib/pwn/ai/agent/policy.rb +5 -0
- data/lib/pwn/ai/agent/policy_evaluation.rb +8 -8
- data/lib/pwn/ai/agent/prompt_builder.rb +13 -2
- data/lib/pwn/ai/agent/request_runtime.rb +122 -18
- data/lib/pwn/ai/agent/reward.rb +5 -3
- data/lib/pwn/ai/agent/skill_consolidation.rb +3 -2
- data/lib/pwn/ai/agent/skill_review.rb +9 -5
- data/lib/pwn/ai/agent/swarm.rb +35 -16
- data/lib/pwn/ai/agent/tools/browser_act.rb +32 -0
- data/lib/pwn/ai/agent/tools/loot_query.rb +11 -1
- data/lib/pwn/ai/agent/tools/recon_run.rb +11 -1
- data/lib/pwn/ai/agent/tools/workspace_tool.rb +29 -0
- data/lib/pwn/ai/agent/turn_finalizer.rb +6 -2
- data/lib/pwn/ai/agent.rb +1 -0
- data/lib/pwn/ai/anthropic.rb +8 -33
- data/lib/pwn/ai/cli.rb +34 -5
- data/lib/pwn/ai/gemini.rb +1 -0
- data/lib/pwn/ai/grok.rb +39 -28
- data/lib/pwn/ai/ollama.rb +4 -2
- data/lib/pwn/ai/open_ai.rb +11 -32
- data/lib/pwn/ai/open_web_ui.rb +62 -4
- data/lib/pwn/ai/router.rb +53 -3
- data/lib/pwn/ai/server_tools.rb +231 -0
- data/lib/pwn/ai.rb +1 -0
- data/lib/pwn/backups.rb +347 -0
- data/lib/pwn/config.rb +70 -39
- data/lib/pwn/cron.rb +120 -49
- data/lib/pwn/migrate.rb +150 -68
- data/lib/pwn/module_skills.rb +7 -0
- data/lib/pwn/plugins/ai_sandbox.rb +538 -1
- data/lib/pwn/plugins/authentication_helper.rb +74 -4
- data/lib/pwn/plugins/basic_auth.rb +2 -1
- data/lib/pwn/plugins/binary_parser.rb +5 -4
- data/lib/pwn/plugins/ghidra.rb +4 -3
- data/lib/pwn/plugins/google_workspace.rb +4 -21
- data/lib/pwn/plugins/jobs.rb +1 -1
- data/lib/pwn/plugins/log.rb +21 -3
- data/lib/pwn/plugins/preflight_checker.rb +34 -9
- data/lib/pwn/plugins/recon.rb +4 -1
- data/lib/pwn/plugins/repl/ai.rb +305 -27
- data/lib/pwn/plugins/repl/ai_console.rb +273 -89
- data/lib/pwn/plugins/repl/ai_swarm.rb +181 -62
- data/lib/pwn/plugins/repl/mesh.rb +3 -16
- data/lib/pwn/plugins/repl/vault.rb +4 -15
- data/lib/pwn/plugins/repl.rb +1 -216
- data/lib/pwn/plugins/sandbox/driver.rb +4 -0
- data/lib/pwn/plugins/transparent_browser.rb +215 -0
- data/lib/pwn/plugins/vault.rb +412 -135
- data/lib/pwn/plugins/yubikey/fido.rb +1288 -0
- data/lib/pwn/plugins/yubikey/hsmauth.rb +439 -0
- data/lib/pwn/plugins/yubikey/management.rb +686 -0
- data/lib/pwn/plugins/yubikey/oath.rb +766 -0
- data/lib/pwn/plugins/yubikey/openpgp.rb +1063 -0
- data/lib/pwn/plugins/yubikey/otp.rb +897 -0
- data/lib/pwn/plugins/yubikey/piv.rb +1315 -0
- data/lib/pwn/plugins/yubikey/protocol.rb +341 -0
- data/lib/pwn/plugins/yubikey/security_domain/scp.rb +482 -0
- data/lib/pwn/plugins/yubikey/security_domain.rb +464 -0
- data/lib/pwn/plugins/yubikey/transport.rb +1017 -0
- data/lib/pwn/plugins/yubikey.rb +844 -0
- data/lib/pwn/plugins.rb +1 -0
- data/lib/pwn/redaction.rb +69 -12
- data/lib/pwn/session_trace.rb +4 -0
- data/lib/pwn/sessions.rb +58 -16
- data/lib/pwn/setup.rb +123 -41
- data/lib/pwn/version.rb +1 -1
- data/lib/pwn/www/app_cobalt_io.rb +16 -5
- data/lib/pwn/www/bug_crowd.rb +16 -5
- data/lib/pwn/www/coinbase_pro.rb +16 -5
- data/lib/pwn/www/github.rb +30 -14
- data/lib/pwn/www/paypal.rb +15 -4
- data/lib/pwn/www/synack.rb +16 -5
- data/lib/pwn/www/twitter.rb +17 -6
- data/lib/pwn.rb +1 -0
- data/scripts/build_gate.rb +58 -0
- data/spec/documentation/installation_md_spec.rb +1 -1
- data/spec/fixtures/yubikey/apdu_frames.json +162 -0
- data/spec/integration/authentication_helper_oath_spec.rb +117 -0
- data/spec/integration/build_gate_spec.rb +73 -0
- data/spec/integration/controller_spill_spec.rb +48 -0
- data/spec/integration/harness_process_spec.rb +97 -0
- data/spec/integration/mission_cancel_spec.rb +79 -0
- data/spec/integration/mission_curses_pty_spec.rb +44 -0
- data/spec/integration/persistence_roundtrip_spec.rb +1 -7
- data/spec/integration/readme_security_spec.rb +47 -0
- data/spec/integration/web_cache_deception_yubikey_spec.rb +159 -0
- data/spec/lib/pwn/ai/agent/dispatch_spec.rb +6 -3
- data/spec/lib/pwn/ai/agent/dispatch_vault_safety_spec.rb +86 -0
- data/spec/lib/pwn/ai/agent/injection_guard_spec.rb +2 -0
- data/spec/lib/pwn/ai/agent/learning_spec.rb +4 -4
- data/spec/lib/pwn/ai/agent/loop_artifacts_spec.rb +5 -0
- data/spec/lib/pwn/ai/agent/loop_spec.rb +104 -29
- data/spec/lib/pwn/ai/agent/mission_namespace_spec.rb +138 -0
- data/spec/lib/pwn/ai/agent/policy_spec.rb +27 -0
- data/spec/lib/pwn/ai/agent/request_runtime_spec.rb +48 -0
- data/spec/lib/pwn/ai/agent/shell_search_effect_spec.rb +44 -0
- data/spec/lib/pwn/ai/agent/solve_spec.rb +6 -4
- data/spec/lib/pwn/ai/agent/tools/browser_act_spec.rb +44 -0
- data/spec/lib/pwn/ai/agent/tools/loot_query_yubikey_spec.rb +55 -0
- data/spec/lib/pwn/ai/agent/tools/recon_run_yubikey_spec.rb +43 -0
- data/spec/lib/pwn/ai/agent/tools/workspace_tool_spec.rb +20 -0
- data/spec/lib/pwn/ai/agent/turn_finalizer_spec.rb +7 -0
- data/spec/lib/pwn/ai/anthropic_vault_enrollment_spec.rb +37 -0
- data/spec/lib/pwn/ai/cli_spec.rb +6 -9
- data/spec/lib/pwn/ai/oauth_vault_persistence_spec.rb +195 -0
- data/spec/lib/pwn/ai/open_ai_spec.rb +2 -2
- data/spec/lib/pwn/ai/open_web_ui_workspace_spec.rb +36 -0
- data/spec/lib/pwn/ai/router_spec.rb +31 -3
- data/spec/lib/pwn/ai/server_tools_spec.rb +39 -0
- data/spec/lib/pwn/backups_spec.rb +114 -0
- data/spec/lib/pwn/config_redaction_yubikey_spec.rb +28 -0
- data/spec/lib/pwn/config_yubikey_spec.rb +92 -0
- data/spec/lib/pwn/cron_spec.rb +113 -6
- data/spec/lib/pwn/migrate_spec.rb +108 -5
- data/spec/lib/pwn/migrate_yubikey_spec.rb +128 -0
- data/spec/lib/pwn/module_skills_spec.rb +8 -0
- data/spec/lib/pwn/plugins/ai_sandbox_spec.rb +230 -0
- data/spec/lib/pwn/plugins/authentication_helper_native_yubikey_spec.rb +40 -0
- data/spec/lib/pwn/plugins/authentication_helper_yubikey_spec.rb +90 -0
- data/spec/lib/pwn/plugins/capability_broker/daemon_spec.rb +1 -1
- data/spec/lib/pwn/plugins/google_workspace_spec.rb +17 -35
- data/spec/lib/pwn/plugins/log_spec.rb +7 -0
- data/spec/lib/pwn/plugins/log_yubikey_spec.rb +47 -0
- data/spec/lib/pwn/plugins/preflight_checker_native_yubikey_spec.rb +19 -0
- data/spec/lib/pwn/plugins/preflight_checker_yubikey_spec.rb +55 -0
- data/spec/lib/pwn/plugins/recon_yubikey_spec.rb +43 -0
- data/spec/lib/pwn/plugins/repl/ai_console_spec.rb +125 -78
- data/spec/lib/pwn/plugins/repl/ai_legacy_containment_spec.rb +200 -0
- data/spec/lib/pwn/plugins/repl/ai_swarm_spec.rb +30 -16
- data/spec/lib/pwn/plugins/repl/ai_vault_edit_yubikey_spec.rb +103 -0
- data/spec/lib/pwn/plugins/repl/ai_yubikey_spec.rb +48 -0
- data/spec/lib/pwn/plugins/repl/mesh_vault_persistence_spec.rb +149 -0
- data/spec/lib/pwn/plugins/repl/vault_yaml_serialization_spec.rb +11 -0
- data/spec/lib/pwn/plugins/repl/vault_yubikey_spec.rb +39 -0
- data/spec/lib/pwn/plugins/repl_spec.rb +56 -5
- data/spec/lib/pwn/plugins/transparent_browser_jev_spec.rb +46 -0
- data/spec/lib/pwn/plugins/vault_native_yubikey_spec.rb +29 -0
- data/spec/lib/pwn/plugins/vault_publication_safety_spec.rb +258 -0
- data/spec/lib/pwn/plugins/vault_store_safety_spec.rb +241 -0
- data/spec/lib/pwn/plugins/vault_yubikey_safety_spec.rb +168 -0
- data/spec/lib/pwn/plugins/vault_yubikey_spec.rb +128 -0
- data/spec/lib/pwn/plugins/vault_yubikey_vector_spec.rb +45 -0
- data/spec/lib/pwn/plugins/yubikey/fido_spec.rb +854 -0
- data/spec/lib/pwn/plugins/yubikey/hsmauth_spec.rb +235 -0
- data/spec/lib/pwn/plugins/yubikey/management_spec.rb +277 -0
- data/spec/lib/pwn/plugins/yubikey/oath_spec.rb +349 -0
- data/spec/lib/pwn/plugins/yubikey/openpgp_spec.rb +810 -0
- data/spec/lib/pwn/plugins/yubikey/otp_spec.rb +337 -0
- data/spec/lib/pwn/plugins/yubikey/piv_spec.rb +646 -0
- data/spec/lib/pwn/plugins/yubikey/protocol_secure_spec.rb +28 -0
- data/spec/lib/pwn/plugins/yubikey/protocol_spec.rb +149 -0
- data/spec/lib/pwn/plugins/yubikey/protocol_vectors_spec.rb +18 -0
- data/spec/lib/pwn/plugins/yubikey/security_domain/scp_spec.rb +256 -0
- data/spec/lib/pwn/plugins/yubikey/security_domain/support.rb +153 -0
- data/spec/lib/pwn/plugins/yubikey/security_domain_spec.rb +296 -0
- data/spec/lib/pwn/plugins/yubikey/transport_spec.rb +760 -0
- data/spec/lib/pwn/plugins/yubikey_security_spec.rb +260 -0
- data/spec/lib/pwn/plugins/yubikey_spec.rb +209 -0
- data/spec/lib/pwn/plugins/yubikey_terminal_spec.rb +147 -0
- data/spec/lib/pwn/redaction_spec.rb +13 -10
- data/spec/lib/pwn/redaction_yubikey_spec.rb +61 -0
- data/spec/lib/pwn/session_cleanup_concurrency_spec.rb +201 -0
- data/spec/lib/pwn/session_cleanup_default_spec.rb +167 -2
- data/spec/lib/pwn/session_cleanup_legacy_spec.rb +101 -0
- data/spec/lib/pwn/session_cleanup_payload_spec.rb +335 -2
- data/spec/lib/pwn/session_cleanup_recovery_spec.rb +403 -0
- data/spec/lib/pwn/sessions_spec.rb +112 -0
- data/spec/lib/pwn/setup_native_yubikey_spec.rb +66 -0
- data/spec/lib/pwn/setup_spec.rb +3 -1
- data/spec/lib/pwn/setup_vault_yubikey_spec.rb +61 -0
- data/spec/lib/pwn/setup_yubikey_spec.rb +40 -0
- data/spec/lib/pwn/www/login_yubikey_spec.rb +273 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/support/gate_failure_formatter.rb +14 -0
- data/spec/support/harness/cli_faults.rb +13 -9
- data/spec/support/harness/controller_spill.rb +26 -0
- data/spec/support/harness/controller_state.rb +54 -0
- data/spec/support/harness/evaluate.rb +12 -1
- data/spec/support/harness/experiments.rb +1 -1
- data/spec/support/harness/fake_provider.rb +22 -6
- data/spec/support/harness/fault_preload.rb +3 -1
- data/spec/support/harness/mission_console.rb +54 -0
- data/spec/support/harness/mission_curses_pty.rb +41 -0
- data/spec/support/harness/mission_dispatch.rb +30 -0
- data/spec/support/harness/mission_fixture_preload.rb +24 -0
- data/spec/support/harness/mission_job_lifetime.rb +25 -0
- data/spec/support/harness/mission_legacy.rb +25 -0
- data/spec/support/harness/mission_normal_exit.rb +34 -0
- data/spec/support/harness/mission_swarm.rb +23 -0
- data/spec/support/harness/mission_unsupported.rb +29 -0
- data/spec/support/harness/package_acceptance.rb +33 -5
- data/spec/support/harness/session_acceptance.rb +4 -4
- data/spec/support/harness/swarm_acceptance.rb +1 -1
- data/spec/support/harness/swarm_control.rb +44 -0
- data/spec/support/mission_namespace.rb +14 -0
- data/spec/support/mission_pipe_queue.rb +33 -0
- data/third_party/pwn_rdoc.jsonl +599 -15
- metadata +140 -17
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pwn-plugins-yubikey-otp
|
|
3
|
+
description: Use native OTP slots with explicit consent and readback.
|
|
4
|
+
license: MIT
|
|
5
|
+
allowed-tools: [pwn, pwn_eval]
|
|
6
|
+
metadata:
|
|
7
|
+
bundled: true
|
|
8
|
+
preserve: true
|
|
9
|
+
module: PWN::Plugins::YubiKey::OTP
|
|
10
|
+
source: pwn/plugins/yubikey/otp.rb
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# PWN::Plugins::YubiKey::OTP
|
|
14
|
+
|
|
15
|
+
Native Ruby OTP application commands over the selected `Connection.exchange`
|
|
16
|
+
boundary. No executable, subprocess, interpreter or network backend. HID framing
|
|
17
|
+
and CRC validation belong to `Transport`; APDU status handling belongs to
|
|
18
|
+
`Protocol`. The caller owns its transport connection.
|
|
19
|
+
|
|
20
|
+
## Operations
|
|
21
|
+
|
|
22
|
+
`execute(args: [...], connection: ...)` accepts argv **after** `otp`.
|
|
23
|
+
`operations` lists ten leaves: `info`, `swap`, `ndef`, `delete`, `yubiotp`,
|
|
24
|
+
`static`, `chalresp`, `calculate`, `hotp`, `settings`. Empty args return local
|
|
25
|
+
metadata, not a device query. Unknown flags/commands and credential argv raise.
|
|
26
|
+
|
|
27
|
+
Typed methods: `info`, `swap`, `delete_slot`, `configure_ndef`, `settings`,
|
|
28
|
+
`provision_yubiotp`, `provision_static`, `provision_hotp`,
|
|
29
|
+
`provision_challenge_response`, `challenge_response`, `calculate`, `help`,
|
|
30
|
+
`authors`. Read runtime `help` for the full option contract.
|
|
31
|
+
|
|
32
|
+
## Safe workflow
|
|
33
|
+
|
|
34
|
+
1. Select the exact intended device using root transport selectors. Do not scan
|
|
35
|
+
or open real hardware in default tests. A model name does not establish its
|
|
36
|
+
supported/enabled applications.
|
|
37
|
+
2. Read `info` without authentication. Firmware/status observations, not guessed
|
|
38
|
+
credentials, gate slot operations. NDEF additionally checks actual NFC support.
|
|
39
|
+
3. Require literal `confirm: true` for every write or calculation. `--force` is
|
|
40
|
+
syntactic compatibility, not consent. Never provision to repair a vault-open
|
|
41
|
+
failure, stale response, wrong slot or missing device.
|
|
42
|
+
4. Supply secrets as raw in-memory `key:`, `password:`, `private_id:`,
|
|
43
|
+
`access_code:` and `new_access_code:`. An explicit `secret_provider` receives
|
|
44
|
+
a missing secret's Symbol. No implicit terminal reads or factory secrets.
|
|
45
|
+
Generated material requires `secret_sink.call(field, raw_secret)`. The sink is
|
|
46
|
+
caller-controlled and must not log or publish secrets.
|
|
47
|
+
5. `challenge_response(connection:, challenge:, slot: 2, confirm: true)` accepts
|
|
48
|
+
**1–64 raw bytes** and returns **20 raw HMAC-SHA1 bytes**. It never hex-encodes,
|
|
49
|
+
truncates to a TOTP or caches a response. Trailing zero challenges use distinct
|
|
50
|
+
padding. USB calculation requires OTP HID; smartcard calculation is NFC-only.
|
|
51
|
+
6. `provision_challenge_response` defaults to touch-required and performs a fresh
|
|
52
|
+
native OpenSSL HMAC comparison against device output after programming and
|
|
53
|
+
separate status readback. Its default return is `:verified`; `details: true`
|
|
54
|
+
returns scoped verification metadata. `execute ['chalresp', '2']` returns the
|
|
55
|
+
metadata and follows prior CLI default
|
|
56
|
+
of no touch unless `--touch`. `--totp` explicitly decodes supplied Base32 key
|
|
57
|
+
text in memory; otherwise `key_encoding` defaults to raw. Keys 21–64 bytes
|
|
58
|
+
cannot fit this application; longer HMAC keys are SHA1-shortened per HMAC.
|
|
59
|
+
7. Check `verification`, not only `verified`. Other OTP credentials/settings have
|
|
60
|
+
no secret/config export command: their verification is programming sequence
|
|
61
|
+
and slot-state readback, **not proof of keyboard output or secret equality**.
|
|
62
|
+
Known 5.0–5.4.2
|
|
63
|
+
smartcard zero-sequence responses are labeled `firmware_quirk_slot_state`;
|
|
64
|
+
HMAC provisioning must still pass the cryptographic challenge comparison.
|
|
65
|
+
Never claim that state metadata proves an NFC URI's emitted value. Failed or
|
|
66
|
+
stale readback raises an uncertainty error; no write is automatically replayed.
|
|
67
|
+
|
|
68
|
+
## Formats and device restrictions
|
|
69
|
+
|
|
70
|
+
- YubiOTP: public ID is lowercase modhex, private ID six raw bytes, AES key sixteen
|
|
71
|
+
raw bytes. `--serial-public-id` reads the actual API serial.
|
|
72
|
+
- Static passwords: 1–38 characters; MODHEX, US, UK, DE, FR, IT, BEPO and NORMAN
|
|
73
|
+
scan-code maps. Unsupported characters fail before programming. The map data
|
|
74
|
+
retains Yubico's BSD license in source.
|
|
75
|
+
- HOTP: raw HMAC key, 6/8 digits, initial counter divisible by 16 and at most
|
|
76
|
+
1048560; optional modhex/BCD token identifier.
|
|
77
|
+
- NDEF: URI prefix compression or UTF8 English text, encoded prefix ≤54 bytes;
|
|
78
|
+
requires a configured slot on an NFC-capable key.
|
|
79
|
+
- Settings replace omitted keyboard options with defaults, as the prior command
|
|
80
|
+
did; they do not infer hidden previous settings. Firmware 4.3.2–4.3.5 access-code
|
|
81
|
+
updates are refused without any delete/reprogram fallback.
|
|
82
|
+
- Explicit YubiOTP `config_output` appends Yubico CSV only after status readback to
|
|
83
|
+
an owned, mode-0600 regular file with no symlinks/hardlinks and an exclusive
|
|
84
|
+
lock. CSV contains secrets. `--upload` is retired upstream and fails without
|
|
85
|
+
any network call. Generated secrets still require an explicit in-memory sink.
|
|
86
|
+
|
|
87
|
+
## Evidence and references
|
|
88
|
+
|
|
89
|
+
Offline specs use fake **connection wire responses**, real shared APDU codecs,
|
|
90
|
+
RFC 2202 HMAC and RFC 6238 TOTP vectors, and an independently computed fixed
|
|
91
|
+
52-byte configuration/CRC vector. These are not hardware interoperability tests.
|
|
92
|
+
|
|
93
|
+
Protocol provenance: official yubikey-manager **5.9.1** source (`yubikit/yubiotp.py`,
|
|
94
|
+
`yubikit/core/otp.py`, `ykman/scancodes`) and
|
|
95
|
+
<https://developers.yubico.com/yubikey-personalization/Manuals/ykpersonalize.1.html>.
|
|
96
|
+
Native reference header:
|
|
97
|
+
<https://github.com/Yubico/yubikey-personalization/blob/master/ykcore/ykdef.h>.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pwn-plugins-yubikey-piv
|
|
3
|
+
description: Native PIV APDU keys, certificates, objects and crypto.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# PWN::Plugins::YubiKey::PIV
|
|
7
|
+
|
|
8
|
+
Use native Ruby/OpenSSL PIV operations through the parent YubiKey connection and
|
|
9
|
+
Protocol codec. No command-line helper, Python runtime, device discovery during
|
|
10
|
+
load, or private-key export is used.
|
|
11
|
+
|
|
12
|
+
## Entry points
|
|
13
|
+
|
|
14
|
+
- `execute(connection:, args:, ...)`: family-relative commands (`keys generate`,
|
|
15
|
+
`certificates request`, `objects export`, `access change-pin`, and so on).
|
|
16
|
+
Empty args or an access/keys/certificates/objects group return local help.
|
|
17
|
+
- `operations`: implemented leaf catalog, separate from group/help paths.
|
|
18
|
+
- `verify_pin(connection:, pin:, confirm: true)` and
|
|
19
|
+
`authenticate(connection:, management_key:, confirm: true)`.
|
|
20
|
+
- `sign(connection:, slot:, algorithm:, message:, pin:, confirm: true)` supports
|
|
21
|
+
RSA PKCS1/PSS, P-256/P-384 ECDSA, and Ed25519. `hash_algorithm:` defaults SHA256;
|
|
22
|
+
`prehashed: true` accepts a correctly sized digest except Ed25519 always uses
|
|
23
|
+
the complete message. RSA `padding:` is `:pkcs1` or `:pss`.
|
|
24
|
+
- `decrypt(connection:, slot:, ciphertext:, padding: :oaep, pin:, confirm: true)`
|
|
25
|
+
supports RSA OAEP (hash, MGF1 hash, label), PKCS1 and explicit raw `:none`.
|
|
26
|
+
- `calculate_secret(connection:, slot:, peer_public_key:, pin:, confirm: true)`
|
|
27
|
+
supports P-256/P-384 and X25519. Treat its output as raw KDF input, not an
|
|
28
|
+
application encryption key.
|
|
29
|
+
|
|
30
|
+
## Consent, secrets, and output
|
|
31
|
+
|
|
32
|
+
Every mutation/authentication/private-key operation requires literal
|
|
33
|
+
`confirm: true`. `--force` never grants consent. PINs, PUKs, management keys,
|
|
34
|
+
passwords and replacements belong in options, never argv. Management keys are
|
|
35
|
+
raw binary bytes, not hex strings. A `secret_provider` callable can supply a
|
|
36
|
+
missing field by Symbol, after consent. Factory credentials are never guessed.
|
|
37
|
+
|
|
38
|
+
`-` as an input filename requires binary `input:`; no implicit terminal read.
|
|
39
|
+
`-` as output returns raw bytes. Named output requires confirmation and is
|
|
40
|
+
created exclusively with mode 0600; existing files are never overwritten.
|
|
41
|
+
`keys export` only returns the public key. `public_key_source:` explicitly
|
|
42
|
+
selects `:metadata` (default, firmware 5.3+), `:attestation` (4.3+) or
|
|
43
|
+
`:certificate`. There is no fallback after a failed key source.
|
|
44
|
+
|
|
45
|
+
Firmware gates include ROCA-vulnerable RSA generation, FIPS restrictions,
|
|
46
|
+
PIN/touch policies and biometric MATCH policy detection. RSA3072/RSA4096,
|
|
47
|
+
Ed25519/X25519 and move/delete require firmware 5.7+. AES management keys require
|
|
48
|
+
5.4+. Supply `fips_restrictions: true` when device capability metadata identifies
|
|
49
|
+
a newer FIPS device.
|
|
50
|
+
|
|
51
|
+
## Management and lifecycle
|
|
52
|
+
|
|
53
|
+
`access change-management-key` accepts `new_management_key:` and `--algorithm`
|
|
54
|
+
TDES/AES128/AES192/AES256. `--protect` stores the new key in PIN-protected PIVMAN
|
|
55
|
+
data. Explicit `use_protected_key: true` enables PIN-protected or legacy
|
|
56
|
+
PIN-derived management authentication; corrupt metadata is never repaired
|
|
57
|
+
implicitly. Unprotected `--generate` requires `secret_sink.call(field, bytes)`
|
|
58
|
+
to securely retain the generated key before installing it. Returned status
|
|
59
|
+
never includes the management key.
|
|
60
|
+
|
|
61
|
+
Key generation/import accepts PIN and touch policies. Import supports native
|
|
62
|
+
OpenSSL PEM/DER private keys, including encrypted inputs through `password:`.
|
|
63
|
+
Certificates support PEM/DER and PKCS12 import, compressed storage, device-signed
|
|
64
|
+
self-signed certificates and PKCS10 requests. The host builds ASN.1, but only
|
|
65
|
+
the device private-key operation signs the TBS/request bytes. The result is
|
|
66
|
+
verified with OpenSSL before storage/return. Signed or corrupt CHUID objects
|
|
67
|
+
are left intact. Certificate changes update unsigned CHUID GUIDs unless
|
|
68
|
+
`--no-update-chuid` is passed. `objects generate` accepts CHUID and CCC only.
|
|
69
|
+
Object writes are read back and compared before reporting success. Multi-APDU
|
|
70
|
+
operations are not transactions: failures must be reconciled, not auto-retried.
|
|
71
|
+
|
|
72
|
+
## Reset
|
|
73
|
+
|
|
74
|
+
`execute(args: ['reset'], confirm: true)` only sends reset after checking for
|
|
75
|
+
enrolled biometrics; it never guesses or blocks PINs/PUKs. Firmware requires
|
|
76
|
+
both retry counters to be blocked. To explicitly authorize the destructive
|
|
77
|
+
blocking process, also supply `block_retries: true`. This sends deliberately
|
|
78
|
+
invalid empty padded PIN and PUK references until their counters reach zero,
|
|
79
|
+
with bounded attempts and decreasing-counter checks, then resets PIV. This
|
|
80
|
+
wipes application data and restores factory references; biometric/full-device
|
|
81
|
+
reset restrictions are not bypassed. Never enable blocking as an automatic
|
|
82
|
+
recovery action.
|
|
83
|
+
|
|
84
|
+
## Implementation verification rules
|
|
85
|
+
|
|
86
|
+
- Encode RSA public material as SubjectPublicKeyInfo with rsaEncryption and NULL
|
|
87
|
+
parameters. Generic OpenSSL loading can interpret a bare two-INTEGER RSA
|
|
88
|
+
public sequence as DH parameters; the RSA generation/import regressions catch
|
|
89
|
+
this ambiguity.
|
|
90
|
+
- For RSA PKCS1 decryption on OpenSSL 3.2+, explicitly disable implicit rejection
|
|
91
|
+
for the ephemeral padding key. Otherwise an invalid block can return synthetic
|
|
92
|
+
plaintext instead of an exception. The paired negative test requires failure.
|
|
93
|
+
- Keep temporary biometric PINs within one selected session: retain a
|
|
94
|
+
`PIV::Session.new(connection: connection, confirm: true)` and call its
|
|
95
|
+
`verify_uv(request_pin: true)` and `verify_temporary_pin(bytes)` methods on
|
|
96
|
+
that same instance. Do not assume reselection preserves a temporary PIN.
|
|
97
|
+
- Separate the transport `serial:` selector from `certificate_serial:` when
|
|
98
|
+
constructing certificates. Tests preserve a zero-prefixed transport selector.
|
|
99
|
+
|
|
100
|
+
## Offline verification and protocol references
|
|
101
|
+
|
|
102
|
+
The paired RSpec uses literal APDU fixtures and a synthetic card boundary,
|
|
103
|
+
independently reconstructs imported private keys, and validates actual RSA,
|
|
104
|
+
ECDSA, Ed25519, ECDH, X25519, certificate and CSR cryptography with OpenSSL.
|
|
105
|
+
It never accesses a physical reader, network, credentials or live home state.
|
|
106
|
+
These fixtures demonstrate host protocol behavior, not physical interoperability.
|
|
107
|
+
|
|
108
|
+
Wire specifications: NIST SP 800-73; Yubico yubikey-manager 5.9.1 source files
|
|
109
|
+
`yubikit/piv.py`, `ykman/piv.py`, `ykman/_cli/piv.py`. Sources were consulted as
|
|
110
|
+
specifications only, not imported or executed by the implementation.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pwn-plugins-yubikey-protocol
|
|
3
|
+
description: Native binary framing for YubiKey device protocols.
|
|
4
|
+
license: MIT
|
|
5
|
+
allowed-tools: [pwn, pwn_eval]
|
|
6
|
+
metadata:
|
|
7
|
+
bundled: true
|
|
8
|
+
preserve: true
|
|
9
|
+
module: PWN::Plugins::YubiKey::Protocol
|
|
10
|
+
source: pwn/plugins/yubikey/protocol.rb
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Native YubiKey protocol framing
|
|
14
|
+
|
|
15
|
+
Use the shared native Ruby codec rather than reconstructing binary headers in
|
|
16
|
+
callers. Preserve raw bytes, repeated TLV tags and exact identifiers. Reject
|
|
17
|
+
truncated/oversized frames before issuing device operations. This module does not
|
|
18
|
+
run an executable or discover hardware merely when loaded.
|
|
19
|
+
|
|
20
|
+
See `PWN::Plugins::YubiKey::Protocol.help` for the current public codec surface.
|
|
21
|
+
Offline framing regressions are in `spec/lib/pwn/plugins/yubikey/protocol_spec.rb`.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pwn-plugins-yubikey-security-domain
|
|
3
|
+
description: Manage native YubiKey Security Domain keys and channels.
|
|
4
|
+
license: MIT
|
|
5
|
+
allowed-tools: [pwn, pwn_eval]
|
|
6
|
+
metadata:
|
|
7
|
+
bundled: true
|
|
8
|
+
preserve: true
|
|
9
|
+
module: PWN::Plugins::YubiKey::SecurityDomain
|
|
10
|
+
source: pwn/plugins/yubikey/security_domain.rb
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Native Security Domain
|
|
14
|
+
|
|
15
|
+
Use `operations`, `help`, `execute(args: ..., connection: ...)` and
|
|
16
|
+
`secure_connection(connection: ..., scp: ..., kvn: ..., confirm: true)`.
|
|
17
|
+
Key and certificate values stay in Ruby/OpenSSL memory, never command-line
|
|
18
|
+
arguments, environment variables, logs or subprocesses. No factory keys are
|
|
19
|
+
assumed. Never print key objects, raw APDUs, or key-bearing options.
|
|
20
|
+
|
|
21
|
+
## Secure connection
|
|
22
|
+
|
|
23
|
+
- SCP03: explicit 16-byte `key_enc`, `key_mac`, optional `key_dek` binary Strings.
|
|
24
|
+
- SCP11a/b/c: explicit `card_public_key` EC pin, or trusted `ca_certificates`
|
|
25
|
+
Array of X509 objects/PEM/DER; `card_certificates` is optional leaf-last chain.
|
|
26
|
+
With only CA anchors, fetch the card bundle and verify it before authentication.
|
|
27
|
+
- SCP11a/c additionally require `oce_private_key` and leaf-last `oce_certificates`;
|
|
28
|
+
optional `oce_kid` and `oce_kvn` identify the card's OCE CA key.
|
|
29
|
+
- Every handshake requires literal `confirm: true`. The returned connection
|
|
30
|
+
provides `exchange(protocol: :apdu, data:, timeout:)`, `descriptor`, `close`.
|
|
31
|
+
- Receipt/card cryptogram failure, malformed response, bad R-MAC, uncertain I/O,
|
|
32
|
+
or exhausted counter closes the channel. No plaintext retry or fallback.
|
|
33
|
+
- SCP11b authenticates the card only. SCP11c does not grant SD administrative
|
|
34
|
+
privilege. Use SCP03 or SCP11a for SD writes.
|
|
35
|
+
|
|
36
|
+
## Administrative operations
|
|
37
|
+
|
|
38
|
+
`execute` implements `info`, `reset`, `keys generate`, `keys import`,
|
|
39
|
+
`keys export`, `keys delete`, `keys set-allowlist`, `keys set-certificate`,
|
|
40
|
+
and `keys set-ca-issuer`. Group paths only return local help metadata.
|
|
41
|
+
|
|
42
|
+
Pass KID/KVN as exact unsigned decimal or `0x` hexadecimal argv tokens. Imports
|
|
43
|
+
and generation require KVN 1..127. KID 1 imports the complete SCP03 keyset from
|
|
44
|
+
`key_enc`, `key_mac`, `key_dek`; KID 0x11/0x13/0x15 imports an EC `key`;
|
|
45
|
+
KID 0x10/0x20..0x2f imports an EC public `key` or CA `certificate`.
|
|
46
|
+
All key wrapping uses the authenticated session DEK. KCV verification is mandatory.
|
|
47
|
+
Private-key imports may include a matching leaf-last `certificates` bundle;
|
|
48
|
+
`ski` sets an explicit CA issuer identifier, and CA certificate SKI is extracted
|
|
49
|
+
when available. These are multiple card writes, not an atomic transaction;
|
|
50
|
+
do not retry automatically after any failure.
|
|
51
|
+
|
|
52
|
+
`keys generate` and `keys export` return PEM bytes rather than writing files.
|
|
53
|
+
An optional trailing `-` is only a compatibility marker. Export never extracts
|
|
54
|
+
private keys. Use `certificates:` and `ski:` for certificate/issuer storage;
|
|
55
|
+
allowlist serials may be argv integers or the `serials:` Array. Mutations require
|
|
56
|
+
`confirm: true` and a genuine SCP03/SCP11a wrapper. `--force` is not consent.
|
|
57
|
+
Reset is deliberately plaintext-only, follows the documented bounded key-blocking
|
|
58
|
+
sequence, and reads back the default-key state before reporting success.
|
|
59
|
+
|
|
60
|
+
Curves are prime256v1, secp384r1, secp521r1, brainpoolP256r1,
|
|
61
|
+
brainpoolP384r1 and brainpoolP512r1. P-521 private-key import is explicitly
|
|
62
|
+
unsupported because its 66-byte scalar is not block-aligned for the pinned SDK's
|
|
63
|
+
un-padded CBC PUT KEY encoding; key generation and public-key handling work.
|
|
64
|
+
All actual card curve/capacity support remains firmware-dependent.
|
|
65
|
+
|
|
66
|
+
## Evidence boundary
|
|
67
|
+
|
|
68
|
+
Wire behavior is based on GlobalPlatform Card Specification 2.3 Amendments D
|
|
69
|
+
(SCP03 S8/AES128) and F v1.4 (SCP11), and Yubico's official yubikit 5.9.1
|
|
70
|
+
`securitydomain.py` and `core/smartcard/scp.py`. These are references, not runtime
|
|
71
|
+
dependencies. NIST SP 800-38B/RFC 4493 vectors and independent synthetic APDU
|
|
72
|
+
peers validate the host cryptography offline; no physical device compatibility
|
|
73
|
+
is claimed. No SCP03 S16, key diversification, CRL/OCSP retrieval, or guaranteed
|
|
74
|
+
Ruby/OpenSSL memory zeroization is provided. Unauthenticated post-handshake
|
|
75
|
+
status-only replies fail closed rather than releasing an unverified status.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pwn-plugins-yubikey-security-domain-scp
|
|
3
|
+
description: Use the native SCP03 and SCP11 APDU channel wrapper.
|
|
4
|
+
license: MIT
|
|
5
|
+
allowed-tools: [pwn, pwn_eval]
|
|
6
|
+
metadata:
|
|
7
|
+
bundled: true
|
|
8
|
+
preserve: true
|
|
9
|
+
module: PWN::Plugins::YubiKey::SecurityDomain::SCP
|
|
10
|
+
source: pwn/plugins/yubikey/security_domain/scp.rb
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# SCP channel implementation
|
|
14
|
+
|
|
15
|
+
Prefer `SecurityDomain.secure_connection` to direct `SCP.open`. Both require
|
|
16
|
+
explicit keys and literal confirmation; see the parent SecurityDomain skill
|
|
17
|
+
for in-memory key and certificate options. Public-key pins and certificates are
|
|
18
|
+
explicit trust decisions: do not discover a card key and silently pin it. Pass
|
|
19
|
+
unencrypted PEM/DER or already-decrypted OpenSSL key objects; this module never
|
|
20
|
+
prompts a terminal for encrypted-PEM passwords. This module never discovers devices
|
|
21
|
+
or spawns executables. Returned APDU wrappers retain verified status words,
|
|
22
|
+
serialize counter/MAC updates and invalidate themselves after any uncertain
|
|
23
|
+
exchange or authentication failure. Do not reuse an underlying connection after
|
|
24
|
+
handshake failure, and never replace a failed channel with plaintext I/O.
|
|
25
|
+
|
|
26
|
+
SCP03 supports YubiKey S8/AES128: derives S-ENC/S-MAC/S-RMAC, verifies the full
|
|
27
|
+
card cryptogram, authenticates the host at security level 0x33, uses CBC with
|
|
28
|
+
counter-derived IVs and ISO7816 padding, and verifies R-MAC before decryption.
|
|
29
|
+
SCP11a/b/c use native OpenSSL EC agreement, SHA256 X9.63 KDF and receipt CMAC;
|
|
30
|
+
certificate trust is explicit, not silently bypassed. CA validation is offline
|
|
31
|
+
and does not retrieve CRLs/OCSP. No guaranteed memory erasure is claimed.
|
|
32
|
+
|
|
33
|
+
The descriptor advertises `extended_apdu: true`. The shared Protocol layer must
|
|
34
|
+
honor this before command chaining: upstream SCP wraps the whole command before
|
|
35
|
+
transport fragmentation, not each plaintext chunk. The wrapper accepts short and
|
|
36
|
+
extended input and upgrades encrypted payloads over 255 bytes to extended frames.
|
|
37
|
+
Transport `61xx` fragments are collected with raw GET RESPONSE before one R-MAC
|
|
38
|
+
verification, preserving the counter/MAC chain. This is continuation retrieval,
|
|
39
|
+
never authentication failure fallback. Fragment count, total response size and
|
|
40
|
+
elapsed time are bounded. Noncanonical receipt encodings and padding longer than
|
|
41
|
+
one AES block fail closed.
|
|
42
|
+
|
|
43
|
+
References: GlobalPlatform Card Specification 2.3 Amendments D/F; official
|
|
44
|
+
Yubico yubikit 5.9.1 core/smartcard/scp.py and smartcard/__init__.py. Tests use
|
|
45
|
+
RFC 4493 known vectors and independent synthetic card-side cryptographic
|
|
46
|
+
transcripts, not implementation-produced fixture outputs or real secrets.
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pwn-plugins-yubikey-transport
|
|
3
|
+
description: Use native HIDAPI and PC/SC YubiKey transports.
|
|
4
|
+
license: MIT
|
|
5
|
+
allowed-tools: [pwn, pwn_eval]
|
|
6
|
+
metadata:
|
|
7
|
+
bundled: true
|
|
8
|
+
preserve: true
|
|
9
|
+
module: PWN::Plugins::YubiKey::Transport
|
|
10
|
+
source: pwn/plugins/yubikey/transport.rb
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Native YubiKey transport
|
|
14
|
+
|
|
15
|
+
## Procedure
|
|
16
|
+
|
|
17
|
+
1. `Transport.available?(application: :piv)` probes **host shared libraries only**.
|
|
18
|
+
Optional `backend: :hid` or `:pcsc` narrows the probe. Requiring the module and
|
|
19
|
+
checking availability never initialize HIDAPI, enumerate devices, establish a
|
|
20
|
+
PC/SC context, or open hardware. Availability does not prove permission,
|
|
21
|
+
service health, card presence, or application support.
|
|
22
|
+
2. `Transport.devices(timeout: 5)` explicitly enumerates HID interfaces and
|
|
23
|
+
PC/SC readers. Reader descriptors do not prove that a card is inserted.
|
|
24
|
+
`backend:`, exact `serial:`, `reader:` and `device_path:` filter inventory.
|
|
25
|
+
This inventory call does not connect to cards to obtain their serials.
|
|
26
|
+
3. `Transport.open(application: :otp, serial: '0007', timeout: 30)` selects one
|
|
27
|
+
endpoint or raises on absent/ambiguous selection. Preserve the operator's
|
|
28
|
+
exact serial String; never normalize, trim or parse it as an Integer.
|
|
29
|
+
Use `device_path:` or `reader:` for serial-less Security Key models. A reader
|
|
30
|
+
explicitly selects PC/SC, including OTP/management smartcard variants. Generic
|
|
31
|
+
NFC readers require explicit selection. Default OTP/FIDO prefers matching HID;
|
|
32
|
+
other applications prefer PC/SC. PC/SC ownership is exclusive, with no shared
|
|
33
|
+
fallback, process killing, automatic authentication or command replay.
|
|
34
|
+
4. Call `connection.exchange(opts)` and always close in `ensure`. The descriptor's
|
|
35
|
+
`:protocol` is authoritative:
|
|
36
|
+
- `:otp`: `command:` byte, binary `data:` up to 64 bytes, optional
|
|
37
|
+
`response_length:`. CRC is checked and removed; status writes require a
|
|
38
|
+
programming-sequence acknowledgment. Command `0` with empty data reads six
|
|
39
|
+
status bytes without writing a command. Supply expected data lengths whenever
|
|
40
|
+
known; variable-length replies require an unambiguous CRC/padding boundary.
|
|
41
|
+
- `:ctap`: `command:` in 1..127, excluding internal INIT/CANCEL/KEEPALIVE/ERROR,
|
|
42
|
+
binary `data:` up to 7609 bytes. `open` performs nonce-checked CTAPHID INIT and
|
|
43
|
+
exposes `:version` and `:capabilities`. Responses retain the CTAP status byte.
|
|
44
|
+
- `:apdu`: binary `data:` containing the complete C-APDU. The result is the
|
|
45
|
+
complete R-APDU **including SW1/SW2**; shared `Protocol.apdu` owns chaining,
|
|
46
|
+
GET RESPONSE and application-status validation.
|
|
47
|
+
5. `timeout:` is a finite positive monotonic budget. Optional `cancelled:` is a
|
|
48
|
+
callable returning truthy to stop; `on_keepalive:` receives 1 (processing) or
|
|
49
|
+
2 (touch/user presence). Callbacks must be bounded and thread-safe. They run
|
|
50
|
+
on the connection's owner worker. Never log packet bodies or secret replies.
|
|
51
|
+
|
|
52
|
+
## Lifecycle and limits
|
|
53
|
+
|
|
54
|
+
- A transport error, interrupted exchange or deadline retires the connection.
|
|
55
|
+
CTAPHID CANCEL / OTP reset is sent serially after the current native call.
|
|
56
|
+
Application-error status bytes are returned unchanged and do not retire it.
|
|
57
|
+
Do not expect subsequent application cleanup on a retired connection to work.
|
|
58
|
+
- `close` is idempotent. `:closed` means native cleanup completed; `:closing`
|
|
59
|
+
means it is deferred. Timed-out C calls retain their owned thread, buffers and
|
|
60
|
+
handle until the native call returns. Never kill that Ruby thread or close its
|
|
61
|
+
in-flight handle: this would risk a use-after-free and is not native cancellation.
|
|
62
|
+
- HID interrupt reads use `hid_read_timeout`. HID feature/write calls and PC/SC
|
|
63
|
+
`SCardTransmit` have no portable hard deadline. `SCardCancel` does not universally
|
|
64
|
+
cancel transmit. A permanently stuck driver may retain a thread and native
|
|
65
|
+
resources indefinitely; a bounded Ruby waiter is not driver-independent abort.
|
|
66
|
+
- Explicit serial selection may perform read-only OTP identity APDUs, with a
|
|
67
|
+
management-information fallback using shared Protocol codecs when OTP is
|
|
68
|
+
disabled. Unobservable identities fail closed; no serial is invented.
|
|
69
|
+
- PC/SC `:transport` derives from the Yubico ATR USB/NFC distinction; it is not
|
|
70
|
+
cryptographic device/vendor attestation. HID transport is USB. NEO's historic
|
|
71
|
+
cached-programming-sequence workaround is not issued automatically; uncertain
|
|
72
|
+
acknowledgments fail rather than trigger an implicit write or retry.
|
|
73
|
+
|
|
74
|
+
## Offline verification
|
|
75
|
+
|
|
76
|
+
Run `bundle exec rspec spec/lib/pwn/plugins/yubikey/transport_spec.rb` and focused
|
|
77
|
+
RuboCop on the paired source/spec. Default tests use fake native C-library/IO
|
|
78
|
+
boundaries with independent fixed CTAPHID and OTP CRC/framing fixtures. They
|
|
79
|
+
exercise ordering, bounds, exact selectors, platform ABI widths/wide strings,
|
|
80
|
+
blocked-call ownership, cancellation, and native cleanup without real hardware.
|
|
81
|
+
Linux library-symbol load checks are separate from physical interoperability;
|
|
82
|
+
Windows/macOS ABI simulations are not tests executed on those operating systems.
|
|
83
|
+
|
|
84
|
+
## Protocol provenance
|
|
85
|
+
|
|
86
|
+
This is a native Ruby implementation, not a launcher or a vendored SDK runtime.
|
|
87
|
+
Protocol references:
|
|
88
|
+
|
|
89
|
+
- YubiKey Manager 5.9.1 `yubikit/core/otp.py`, `yubikit/yubiotp.py`, and
|
|
90
|
+
`ykman/pcsc/__init__.py` (read-only reference).
|
|
91
|
+
- python-fido2 2.2.1 `fido2/hid/__init__.py` (read-only reference), and the
|
|
92
|
+
[FIDO HID protocol](https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#usb-hid).
|
|
93
|
+
- [HIDAPI 0.14.0 C header](https://github.com/libusb/hidapi/blob/hidapi-0.14.0/hidapi/hidapi.h).
|
|
94
|
+
- [PC/SC ABI definitions](https://github.com/LudovicRousseau/PCSC/blob/master/src/PCSC/wintypes.h):
|
|
95
|
+
Unix `DWORD=unsigned long`, Darwin `DWORD=uint32`, Windows handles pointer-sized;
|
|
96
|
+
exported PCI symbols avoid guessed structure packing.
|
|
97
|
+
- [Microsoft SCardCancel](https://learn.microsoft.com/en-us/windows/win32/api/winscard/nf-winscard-scardcancel)
|
|
98
|
+
and [SCardTransmit](https://learn.microsoft.com/en-us/windows/win32/api/winscard/nf-winscard-scardtransmit).
|
|
@@ -6,13 +6,14 @@ allowed-tools: [pwn, pwn_eval]
|
|
|
6
6
|
metadata:
|
|
7
7
|
bundled: true
|
|
8
8
|
generated: true
|
|
9
|
+
preserve: true
|
|
9
10
|
module: PWN::Redaction
|
|
10
11
|
source: pwn/redaction.rb
|
|
11
12
|
---
|
|
12
13
|
|
|
13
14
|
# PWN::Redaction
|
|
14
15
|
|
|
15
|
-
Write-boundary redaction; reject cyclic or over-100-level containers before inspecting secrets.
|
|
16
|
+
Write-boundary redaction; reject cyclic or over-100-level containers before inspecting secrets. `redact` never opens a credential store; `capture` can explicitly store ordinary secret matches.
|
|
16
17
|
|
|
17
18
|
## When to use
|
|
18
19
|
|
|
@@ -26,6 +27,17 @@ Class methods take `(opts = {})` and read `opts`.
|
|
|
26
27
|
|
|
27
28
|
## How to call
|
|
28
29
|
|
|
30
|
+
PINs, PUKs, MFA/OTP values, access codes, management keys, OATH secrets, static
|
|
31
|
+
passwords and SCP key fields use the constant `[REDACTED]` mask in hashes and
|
|
32
|
+
key-value text. Do not replace those masks with hash markers: low-entropy values
|
|
33
|
+
can be brute-forced from unsalted digests. `capture` masks these fields without
|
|
34
|
+
storing them. Ordinary API/password tokens retain the existing digest behavior.
|
|
35
|
+
|
|
36
|
+
For other captured loot, pass `yubikey: {slot: 2, serial: '12345678', timeout: 30}`
|
|
37
|
+
to select hardware encryption. Hardware capture failure must leave the returned
|
|
38
|
+
text redacted and must never trigger software fallback. A returned capture
|
|
39
|
+
reference is not proof of successful persistence; verify metadata separately.
|
|
40
|
+
|
|
29
41
|
```ruby
|
|
30
42
|
PWN::Redaction.help
|
|
31
43
|
PWN::Redaction.redact(opts)
|
|
@@ -72,11 +72,14 @@ module PWN
|
|
|
72
72
|
violation = JSONSchemer.schema(JSON.parse(JSON.generate(type_schema))).validate(JSON.parse(JSON.generate(args))).first
|
|
73
73
|
return schema_denial(violation.transform_keys(&:to_sym)) if violation
|
|
74
74
|
|
|
75
|
-
blob = args.inspect
|
|
76
75
|
if defined?(PWN::Plugins::Vault)
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
begin
|
|
77
|
+
args = expand_vault_args(args: args)
|
|
78
|
+
rescue StandardError
|
|
79
|
+
return JSON.generate(success: false, code: 'VAULT_EXPANSION_FAILED', error: 'Vault access failed; check the selected device and retry. The tool was not executed.')
|
|
80
|
+
end
|
|
79
81
|
end
|
|
82
|
+
blob = args.inspect
|
|
80
83
|
denied = Manifest.check(opts.merge(name: entry.name, args: args))
|
|
81
84
|
return JSON.generate(denied) if denied
|
|
82
85
|
|
|
@@ -129,7 +132,11 @@ module PWN
|
|
|
129
132
|
begin
|
|
130
133
|
runtime = Thread.current[:pwn_request_runtime]
|
|
131
134
|
result = if runtime
|
|
132
|
-
runtime.execute_tool(name: entry.name, args: args, effect: effect(name: entry.name, args: args), action_id: tool_call[:id] || tool_call['id'])
|
|
135
|
+
runtime.execute_tool(name: entry.name, args: args, effect: effect(name: entry.name, args: args), action_id: tool_call[:id] || tool_call['id']) do
|
|
136
|
+
PWN::Plugins::AISandbox.protect_call(handler: { name: entry.name, args: args }, runtime: runtime, workspace: runtime.contract[:cwd], write_roots: runtime.contract[:required_artifacts]) do
|
|
137
|
+
entry.handler.call(args)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
133
140
|
else
|
|
134
141
|
entry.handler.call(args)
|
|
135
142
|
end
|
|
@@ -141,13 +148,18 @@ module PWN
|
|
|
141
148
|
result = Result.page(value: result, entry: entry, session_id: opts[:session_id])
|
|
142
149
|
result = ToolGuard.quarantine_output(text: result) if defined?(ToolGuard) && result.is_a?(String) && ToolGuard.respond_to?(:quarantine_output)
|
|
143
150
|
note_taint(text: result)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
151
|
+
begin
|
|
152
|
+
if defined?(PWN::Plugins::Vault) && result.is_a?(String)
|
|
153
|
+
result = PWN::Plugins::Vault.redact(text: result)
|
|
154
|
+
elsif defined?(PWN::Plugins::Vault) && (result.is_a?(Hash) || result.is_a?(Array))
|
|
155
|
+
result = JSON.parse(PWN::Plugins::Vault.redact(text: JSON.generate(Result.utf8_value(value: result))))
|
|
156
|
+
end
|
|
157
|
+
rescue StandardError
|
|
158
|
+
return JSON.generate(success: false, code: 'VAULT_REDACTION_FAILED', handler_executed: true,
|
|
159
|
+
error: 'Vault redaction failed; result withheld. The tool already executed; verify its effects before retrying.')
|
|
148
160
|
end
|
|
149
161
|
response = { success: true, result: result, effect: effect(name: entry.name, args: args) }
|
|
150
|
-
response[:success] = false if result.is_a?(Hash) && (result[:error] || result['error'] || result[:success] == false || result['success'] == false)
|
|
162
|
+
response[:success] = false if result.is_a?(Hash) && (result[:error] || result['error'] || result[:success] == false || result['success'] == false || result[:ok] == false || result['ok'] == false || (result.key?(:exit) && result[:exit] != 0) || (result.key?('exit') && result['exit'] != 0))
|
|
151
163
|
response[:budget] = telemetry if telemetry
|
|
152
164
|
if telemetry && telemetry[:remaining_s].zero?
|
|
153
165
|
response[:success] = false
|
|
@@ -591,13 +603,12 @@ module PWN
|
|
|
591
603
|
|
|
592
604
|
private_class_method def self.expand_vault_args(opts = {})
|
|
593
605
|
args = opts[:args]
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
args.
|
|
597
|
-
|
|
606
|
+
case args
|
|
607
|
+
when Hash then args.transform_values { |value| expand_vault_args(args: value) }
|
|
608
|
+
when Array then args.map { |value| expand_vault_args(args: value) }
|
|
609
|
+
when String then PWN::Plugins::Vault.expand(text: args)
|
|
610
|
+
else args
|
|
598
611
|
end
|
|
599
|
-
rescue StandardError
|
|
600
|
-
opts[:args]
|
|
601
612
|
end
|
|
602
613
|
|
|
603
614
|
private_class_method def self.note_taint(opts = {})
|
|
@@ -3572,16 +3572,15 @@ module PWN
|
|
|
3572
3572
|
[true, log]
|
|
3573
3573
|
when :festival
|
|
3574
3574
|
# Use plugin if text file available
|
|
3575
|
-
|
|
3576
|
-
File.write(tmp, text)
|
|
3575
|
+
require_relative '../../../../etc/default_cron/session_cleanup' unless defined?(PWN::SessionCleanup)
|
|
3577
3576
|
begin
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3577
|
+
PWN::SessionCleanup.temporary(bytes: text) do |tmp|
|
|
3578
|
+
require 'pwn/plugins/voice' unless defined?(PWN::Plugins::Voice)
|
|
3579
|
+
PWN::Plugins::Voice.text_to_speech(text_path: tmp)
|
|
3580
|
+
[true, 'festival via PWN::Plugins::Voice']
|
|
3581
|
+
end
|
|
3581
3582
|
rescue StandardError => e
|
|
3582
3583
|
[false, "#{e.class}: #{e.message}"]
|
|
3583
|
-
ensure
|
|
3584
|
-
FileUtils.rm_f(tmp)
|
|
3585
3584
|
end
|
|
3586
3585
|
else
|
|
3587
3586
|
[false, "unsupported TTS engine: #{engine}"]
|