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,189 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pwn-plugins-yubikey-fido
|
|
3
|
+
description: Use when managing native FIDO and U2F operations.
|
|
4
|
+
license: MIT
|
|
5
|
+
allowed-tools: [pwn, pwn_eval]
|
|
6
|
+
metadata:
|
|
7
|
+
bundled: true
|
|
8
|
+
preserve: true
|
|
9
|
+
module: PWN::Plugins::YubiKey::FIDO
|
|
10
|
+
source: pwn/plugins/yubikey/fido.rb
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Native FIDO/U2F
|
|
14
|
+
|
|
15
|
+
Use `PWN::Plugins::YubiKey::FIDO` for CTAP2 management and caller-controlled
|
|
16
|
+
credential operations. Application logic and CBOR are Ruby; cryptography uses
|
|
17
|
+
Ruby OpenSSL and host entropy. No executable, interpreter worker or CBOR gem is
|
|
18
|
+
required. CTAPHID framing and shared-library device I/O belong to `Transport`.
|
|
19
|
+
|
|
20
|
+
## When to use
|
|
21
|
+
|
|
22
|
+
- Read authenticator capabilities, manage PINs, discoverable credentials,
|
|
23
|
+
fingerprints, always-UV and enterprise-attestation configuration.
|
|
24
|
+
- Run low-level makeCredential/getAssertion or U2F registration/authentication
|
|
25
|
+
with an RP/application and client-data hash supplied by the caller.
|
|
26
|
+
- Do not treat this module as a browser or WebAuthn relying-party server. It does
|
|
27
|
+
not invent origins, approve RP IDs or verify server challenges/signatures.
|
|
28
|
+
|
|
29
|
+
## Prerequisites and safety
|
|
30
|
+
|
|
31
|
+
Obtain an explicitly selected FIDO connection from `YubiKey::Transport.open`
|
|
32
|
+
with `application: :fido`. Serial-less Security Keys are valid; select them by
|
|
33
|
+
exact device path/reader or unambiguous inventory, never an OTP serial guess.
|
|
34
|
+
HID supports CTAP/U2F; APDU supports FIDO AID selection, NFC CTAP, command chaining,
|
|
35
|
+
GET RESPONSE, keepalive and cancellation. Read getInfo to negotiate actual
|
|
36
|
+
options and PIN protocol 2 or 1; do not infer support from the model name.
|
|
37
|
+
|
|
38
|
+
Every authentication, signature/counter operation and mutation requires literal
|
|
39
|
+
`confirm: true`. `--force` is only an accepted compatibility flag; it grants no
|
|
40
|
+
consent. `info`, retry counters and fingerprint sensor information do not require
|
|
41
|
+
consent. Help groups are local and do not need a connection.
|
|
42
|
+
|
|
43
|
+
Supply `pin:` and `new_pin:` as in-memory Strings, or an explicitly provided
|
|
44
|
+
`secret_provider:` callable receiving `:pin`/`:new_pin`. Never put these in args,
|
|
45
|
+
process arguments, environment variables, files or transcripts. There is no
|
|
46
|
+
factory PIN, default prompt, automatic PIN retry or automatic reset retry.
|
|
47
|
+
Sessions erase owned secret copies on completion, but Ruby/OpenSSL cannot promise
|
|
48
|
+
that every temporary allocation is zeroized. Caller-owned Strings are untouched.
|
|
49
|
+
|
|
50
|
+
## Management calls
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
fido = PWN::Plugins::YubiKey::FIDO
|
|
54
|
+
fido.execute(args: []) # local leaf catalog
|
|
55
|
+
fido.execute(args: ['access']) # local group help
|
|
56
|
+
fido.execute(connection: connection, args: ['info'])
|
|
57
|
+
fido.perform(connection: connection, operation: :pin_retries)
|
|
58
|
+
# secret_provider below is an operator-supplied private callable, not a prompt default.
|
|
59
|
+
fido.execute(connection: connection, args: %w[access verify-pin],
|
|
60
|
+
secret_provider: secret_provider, confirm: true)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Implemented leaf paths (args exclude the `fido` prefix):
|
|
64
|
+
|
|
65
|
+
| Path | Additional in-memory inputs / behavior |
|
|
66
|
+
|---|---|
|
|
67
|
+
| `info` | Actual CTAP getInfo, or U2F version/capabilities |
|
|
68
|
+
| `reset` | One reset command; firmware power-up/touch window still applies |
|
|
69
|
+
| `access change-pin` | `new_pin:`, plus `pin:` when already configured; initializes only if getInfo reports no PIN |
|
|
70
|
+
| `access change-pin --u2f` | YubiKey 4 FIPS vendor U2F PIN command, capability-probed |
|
|
71
|
+
| `access verify-pin` | `pin:`; verifies by obtaining and discarding a token, no fabricated RP |
|
|
72
|
+
| `access force-change` | Config permission; requires existing PIN and setMinPINLength |
|
|
73
|
+
| `access set-min-length LENGTH` | Optional repeatable `--rp-id RPID`; minimum cannot decrease |
|
|
74
|
+
| `credentials list` | PIN/UV permission 0x04; exhausts every RP and credential; checks totals and duplicates |
|
|
75
|
+
| `credentials list --csv` | Returns CSV in memory; no output file is opened |
|
|
76
|
+
| `credentials delete HEX_ID` | Complete exact credential ID; no ambiguous prefix matching |
|
|
77
|
+
| `fingerprints list` | Permission 0x08; returns template IDs and names |
|
|
78
|
+
| `fingerprints add [NAME]` | Bounded capture loop and optional friendly name |
|
|
79
|
+
| `fingerprints rename HEX_ID NAME` | Exact template ID and UTF-8 friendly name |
|
|
80
|
+
| `fingerprints delete HEX_ID` | Exact template ID |
|
|
81
|
+
| `config toggle-always-uv` | Permission 0x20 if a PIN/UV token is required |
|
|
82
|
+
| `config enable-ep-attestation` | Requires advertised enterprise-attestation capability |
|
|
83
|
+
|
|
84
|
+
Local groups: root, `access`, `credentials`, `fingerprints`, `config`.
|
|
85
|
+
`operations` lists leaf paths only; variants above are not extra leaf operations.
|
|
86
|
+
PIN verification selects a supported non-RP management permission when scoped
|
|
87
|
+
tokens are advertised, or scopes getAssertion permission to an explicitly supplied
|
|
88
|
+
`rp_id:`. Without either it uses legacy getPINToken, never an invented website.
|
|
89
|
+
An authenticator disabling legacy tokens and exposing no non-RP permission needs
|
|
90
|
+
the caller's `rp_id:`. Built-in UV is not accepted as proof of PIN verification.
|
|
91
|
+
`perform(operation: :update_credential, credential_id: binary_id, user: user_hash,
|
|
92
|
+
...)` implements CTAP2.1 updateUserInformation; `user['id']` must be the existing
|
|
93
|
+
user ID. Preview credential management supports list/delete, not user update.
|
|
94
|
+
|
|
95
|
+
All native IDs/hashes are **binary Strings** (`.b`); text is valid UTF-8.
|
|
96
|
+
Hex CLI selectors are decoded only after strict even-length hex validation.
|
|
97
|
+
PINs are not normalized or silently truncated. Standard CTAP PINs require at
|
|
98
|
+
least the advertised minimum and at most the advertised byte limit (63 by
|
|
99
|
+
default), with no NUL. Old FIPS U2F requires at least 6 characters and permits
|
|
100
|
+
at most 32 encoded bytes. A newer advertised PIN limit does not waive the CTAP
|
|
101
|
+
255-byte bound on the block-padded PIN plaintext.
|
|
102
|
+
|
|
103
|
+
## Credential operations
|
|
104
|
+
|
|
105
|
+
```ruby
|
|
106
|
+
# client_data_hash is a 32-byte SHA-256 digest produced by the caller's ceremony.
|
|
107
|
+
attestation = fido.make_credential(
|
|
108
|
+
connection: connection, confirm: true, client_data_hash: client_data_hash,
|
|
109
|
+
rp: { 'id' => relying_party_id, 'name' => relying_party_name },
|
|
110
|
+
user: { 'id' => user_id.b, 'name' => user_name },
|
|
111
|
+
secret_provider: secret_provider
|
|
112
|
+
)
|
|
113
|
+
assertions = fido.get_assertion(
|
|
114
|
+
connection: connection, confirm: true, rp_id: relying_party_id,
|
|
115
|
+
client_data_hash: client_data_hash,
|
|
116
|
+
allow_list: [{ 'type' => 'public-key', 'id' => credential_id.b }]
|
|
117
|
+
)
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
makeCredential accepts `key_params:` (default ES256), `exclude_list:`,
|
|
121
|
+
`extensions:`, `authenticator_options:` and `enterprise_attestation:` (1 or 2).
|
|
122
|
+
getAssertion accepts `allow_list:`, `extensions:` and `authenticator_options:`;
|
|
123
|
+
it returns an Array containing all advertised assertions, using getNextAssertion.
|
|
124
|
+
Use `pin:`/`secret_provider:` for PIN verification, or literal `uv: true` for
|
|
125
|
+
configured built-in UV and a permission-scoped token. Permission tokens bind
|
|
126
|
+
makeCredential/getAssertion to the exact caller RP ID. No-PIN/non-UV requests
|
|
127
|
+
remain possible where the authenticator permits them; the device enforces policy.
|
|
128
|
+
CTAP2.0 preview commands use the legacy PIN token when scoped tokens are absent.
|
|
129
|
+
|
|
130
|
+
`u2f_register(connection:, confirm: true, client_data_hash:, app_id_hash:)`
|
|
131
|
+
returns the raw public key, key handle, attestation certificate and signature.
|
|
132
|
+
`u2f_authenticate(..., key_handle:)` returns presence, counter and signature.
|
|
133
|
+
Literal `check_only: true` interprets only defined U2F recognition statuses.
|
|
134
|
+
These return cryptographic material, **not a server authentication verdict**.
|
|
135
|
+
The caller must verify challenge, RP/application hash, origin policy, flags,
|
|
136
|
+
algorithm, signature, attestation trust and counter semantics before accepting it.
|
|
137
|
+
|
|
138
|
+
## Bounds, cancellation and recovery
|
|
139
|
+
|
|
140
|
+
- `timeout:` is one finite positive operation budget, default 30 seconds.
|
|
141
|
+
`cancelled:` is a callable, forwarded to Transport as well as checked between
|
|
142
|
+
exchanges; `on_keepalive:` receives status 1 or 2. Callback exceptions propagate.
|
|
143
|
+
- `max_records:` defaults to 1024, maximum 16384. Counts are assertions, not hints:
|
|
144
|
+
excessive, duplicate, inconsistent or prematurely ended lists fail explicitly.
|
|
145
|
+
- Enrollment uses `capture_timeout_ms:` (default 10000), `max_samples:` (default
|
|
146
|
+
128), and optional `on_sample:` callable receiving template ID, status and
|
|
147
|
+
remaining samples. Intermediate bad captures are reported; PIN attempts are not
|
|
148
|
+
repeated. Failure attempts one separately bounded enrollment cancel, without
|
|
149
|
+
deleting any credential. A failed cleanup cannot replace the original error.
|
|
150
|
+
- `perform(operation: :bio_sensor_info)` is unauthenticated. Explicit
|
|
151
|
+
`perform(operation: :cancel_enrollment, confirm: true)` cancels enrollment.
|
|
152
|
+
- If enrollment completes but naming fails, `EnrollmentError#template_id` names
|
|
153
|
+
the already-created template. Rename that template; do not enroll again or
|
|
154
|
+
assume a rollback. A transport timeout can leave device state uncertain.
|
|
155
|
+
- A connection is caller-owned. Use it exclusively for the whole operation; do
|
|
156
|
+
not interleave concurrent enumeration, PIN or enrollment workflows. Close it in
|
|
157
|
+
the caller's ensure block. A closed/error transport cannot promise cleanup.
|
|
158
|
+
- U2F-only authenticators report U2F capabilities. CTAP2-only management fails
|
|
159
|
+
explicitly. FIPS-only PIN/reset commands are implemented, not substituted for
|
|
160
|
+
ordinary U2F devices. Physical touch/reset timing is never simulated.
|
|
161
|
+
|
|
162
|
+
## CBOR and crypto verification
|
|
163
|
+
|
|
164
|
+
`FIDO::CBOR.encode(value:)` / `.decode(data:)` use CTAP canonical major-type
|
|
165
|
+
ordering, minimal integer/length encodings, 32-level depth, 16384-item and 1 MiB
|
|
166
|
+
bounds. Map keys are integer/text; byte strings use ASCII-8BIT encoding. Reject
|
|
167
|
+
indefinite lengths, duplicate keys, noncanonical maps, invalid UTF-8, trailing
|
|
168
|
+
bytes, tags and floating-point values rather than coercing them.
|
|
169
|
+
|
|
170
|
+
Protocol 1 derives SHA256(ECDH-x), zero-IV AES-256-CBC and 16-byte HMAC outputs.
|
|
171
|
+
Protocol 2 derives independent HKDF-SHA256 HMAC/AES keys using the CTAP labels,
|
|
172
|
+
uses a fresh host-random IV prefix and 32-byte HMAC. Validate COSE EC2/P-256/-25
|
|
173
|
+
key agreement points before ECDH. No device OTP output supplies entropy.
|
|
174
|
+
|
|
175
|
+
Run the paired RSpec offline with an isolated HOME and the repository's normal
|
|
176
|
+
Ruby bundle; only Connection or native I/O fixtures may be faked. Keep independent
|
|
177
|
+
RFC8949/NIST/RFC4231/P-256/KDF vectors, both PIN protocols, complete public
|
|
178
|
+
management paths, response errors and cancellation tests. Passing fixtures prove
|
|
179
|
+
host protocol behavior, not physical-device or platform interoperability.
|
|
180
|
+
|
|
181
|
+
References used for the protocol contract:
|
|
182
|
+
|
|
183
|
+
- [FIDO CTAP 2.1 proposed standard, 2021-06-15](https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html)
|
|
184
|
+
- [U2F raw message formats 1.2](https://fidoalliance.org/specs/u2f-specs-1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.html)
|
|
185
|
+
- [RFC 8949](https://www.rfc-editor.org/rfc/rfc8949) and [RFC 4231](https://www.rfc-editor.org/rfc/rfc4231)
|
|
186
|
+
- Official `python-fido2` 2.2.1: `cbor.py`, `ctap1.py`, `pcsc.py`,
|
|
187
|
+
`ctap2/{base,pin,credman,bio,config}.py`; YubiKey Manager 5.9.1 `ykman/fido.py`
|
|
188
|
+
and its FIDO command module. These were read-only interoperability references,
|
|
189
|
+
not runtime dependencies; the Ruby implementation is independently written.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pwn-plugins-yubikey-hsmauth
|
|
3
|
+
description: Manage native HSM Auth credentials and sessions.
|
|
4
|
+
license: MIT
|
|
5
|
+
allowed-tools: [pwn, pwn_eval]
|
|
6
|
+
metadata:
|
|
7
|
+
bundled: true
|
|
8
|
+
preserve: true
|
|
9
|
+
module: PWN::Plugins::YubiKey::HSMAuth
|
|
10
|
+
source: pwn/plugins/yubikey/hsmauth.rb
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Native YubiHSM Auth
|
|
14
|
+
|
|
15
|
+
## When to use
|
|
16
|
+
|
|
17
|
+
Use `PWN::Plugins::YubiKey::HSMAuth` for YubiKey YubiHSM Auth credentials and
|
|
18
|
+
session-key derivation. This is not the YubiHSM2 management protocol or a browser
|
|
19
|
+
WebAuthn ceremony. The root YubiKey router owns transport lifetime.
|
|
20
|
+
|
|
21
|
+
## Public API
|
|
22
|
+
|
|
23
|
+
- `execute(args:, connection:, ...)` handles `info`, `reset`, `credentials list`,
|
|
24
|
+
`generate`, `import`, `export`, `symmetric`, `derive`, `delete`, `change-password`,
|
|
25
|
+
and `access change-management-key` / `change-management-password`.
|
|
26
|
+
- `operations`, `help`, `authors` are local metadata.
|
|
27
|
+
- `open_session(connection:, timeout:)` selects once and returns a session handle.
|
|
28
|
+
- `challenge(session:, label:, algorithm:, credential_password:, confirm: true)`
|
|
29
|
+
returns the host challenge or ephemeral uncompressed P-256 public point.
|
|
30
|
+
- `calculate(session:, label:, algorithm:, context:, credential_password:,
|
|
31
|
+
card_crypto:, public_key:, confirm: true)` returns a Hash containing exactly
|
|
32
|
+
`key_senc`, `key_smac`, `key_srmac`, each sixteen raw bytes.
|
|
33
|
+
|
|
34
|
+
## Procedure
|
|
35
|
+
|
|
36
|
+
1. Select the intended APDU connection; never guess a device or factory key.
|
|
37
|
+
2. Read `.help`. Supply passwords/keys only in memory or through a
|
|
38
|
+
`secret_provider:` receiving a field Symbol after literal `confirm: true`.
|
|
39
|
+
Wrong authentication is attempted once; APDU errors preserve `status` without
|
|
40
|
+
logging payloads. Status 63Cx encodes remaining attempts in its low nibble.
|
|
41
|
+
3. Use exact complete label bytes. Management changes do not substring-match.
|
|
42
|
+
4. Symmetric import uses raw `key_enc:` and `key_mac:` (sixteen bytes each).
|
|
43
|
+
`derive` uses `derivation_password:` with PBKDF2-HMAC-SHA256, salt `Yubico`,
|
|
44
|
+
10,000 iterations. `generate` creates a P-256 private key on device; symmetric
|
|
45
|
+
`--generate` returns the generated keys in memory for explicit caller storage.
|
|
46
|
+
5. Asymmetric import accepts `private_key:` as a raw 32-byte scalar, an in-memory
|
|
47
|
+
PEM/DER String or private OpenSSL EC object on prime256v1. Encrypted PEM/DER
|
|
48
|
+
takes `key_password:`; it never prompts implicitly or retries decryption.
|
|
49
|
+
Export returns public key bytes in `RAW`, `PEM` (default) or `DER` format;
|
|
50
|
+
filenames and secret argv are not accepted by the family API.
|
|
51
|
+
6. For a live HSM challenge exchange, retain the same `open_session` handle for
|
|
52
|
+
`challenge` and `calculate`. Do not select another application between them.
|
|
53
|
+
Symmetric context is host challenge + card challenge (sixteen bytes), optional
|
|
54
|
+
card cryptogram eight bytes. Asymmetric context is two uncompressed P-256
|
|
55
|
+
points (130 bytes), `public_key:` is the HSM long-term public point, and
|
|
56
|
+
`card_crypto:` contains sixteen bytes. The caller manages the HSM side.
|
|
57
|
+
7. Check the returned verification scope. Create/delete/reset verify inventory
|
|
58
|
+
metadata, not private key contents. Password and management-key writes return
|
|
59
|
+
`status: :acknowledged, verification: :not_readable`; no second authentication
|
|
60
|
+
attempt is made merely to simulate readback.
|
|
61
|
+
|
|
62
|
+
## Pitfalls
|
|
63
|
+
|
|
64
|
+
- No shell, executable, network lookup, hidden cache or filesystem credential
|
|
65
|
+
lookup is performed. Raw keys are not auto-decoded from hexadecimal Strings.
|
|
66
|
+
- Credential/management passwords are at most sixteen raw bytes and zero-padded
|
|
67
|
+
per protocol. An explicitly supplied empty String is allowed, but nil never
|
|
68
|
+
means the factory credential. Ruby memory is not guaranteed zeroizable.
|
|
69
|
+
- Asymmetric operations and challenges require application version 5.6.0;
|
|
70
|
+
credential-password changes require 5.8.0. Random management-key generation is
|
|
71
|
+
rejected on 5.8 or later. SELECT failure is not permission to fall back.
|
|
72
|
+
- Device timeout, cancellation or failure must propagate; never replay a mutation.
|
|
73
|
+
- Session keys are secret return values. Do not print or put them in transcripts.
|
|
74
|
+
|
|
75
|
+
## Verification
|
|
76
|
+
|
|
77
|
+
Run `bundle exec rspec spec/lib/pwn/plugins/yubikey/hsmauth_spec.rb` and scoped
|
|
78
|
+
RuboCop. The specs exercise production Protocol APDUs with a fake raw exchange
|
|
79
|
+
boundary and independent PBKDF2 / P-256 wire vectors. No hardware interoperability
|
|
80
|
+
or secure-channel transport is claimed by these offline tests.
|
|
81
|
+
|
|
82
|
+
References: pinned yubikey-manager 5.9.1 `yubikit/hsmauth.py` and
|
|
83
|
+
`ykman/_cli/hsmauth.py`; https://developers.yubico.com/yubihsm-auth/ .
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pwn-plugins-yubikey-management
|
|
3
|
+
description: Configure native device capabilities with exact readback.
|
|
4
|
+
license: MIT
|
|
5
|
+
allowed-tools: [pwn, pwn_eval]
|
|
6
|
+
metadata:
|
|
7
|
+
bundled: true
|
|
8
|
+
preserve: true
|
|
9
|
+
module: PWN::Plugins::YubiKey::Management
|
|
10
|
+
source: pwn/plugins/yubikey/management.rb
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# PWN::Plugins::YubiKey::Management
|
|
14
|
+
|
|
15
|
+
Native device information/configuration over OTP HID, management smartcard or
|
|
16
|
+
CTAP vendor commands. No executable, subprocess, interpreter or network backend.
|
|
17
|
+
|
|
18
|
+
## API and routing
|
|
19
|
+
|
|
20
|
+
`execute(args: [...], connection: ...)` takes argv after `config`.
|
|
21
|
+
`operations` lists five prior leaves: `usb`, `nfc`, `mode`, `set-lock-code`, `reset`.
|
|
22
|
+
Empty args return local metadata without device I/O. `--force` is not consent.
|
|
23
|
+
Typed APIs: `info`, `configure_usb`, `configure_nfc`, `set_mode`, `set_lock_code`,
|
|
24
|
+
`reset`, `reboot`, `help`, `authors`. See runtime help for exact options.
|
|
25
|
+
|
|
26
|
+
## Procedure
|
|
27
|
+
|
|
28
|
+
1. Select the exact device explicitly. `info` reads version, serial, transport
|
|
29
|
+
capability masks, lock state, flags/timeouts, NFC restrictions, FIPS masks and
|
|
30
|
+
reset metadata. Retain unknown raw TLVs; reject malformed/duplicate records and
|
|
31
|
+
bound pagination. Do not infer all apps from a model or firmware version.
|
|
32
|
+
2. On pre-4.1 firmware, report unavailable capability metadata explicitly and
|
|
33
|
+
only expose observed USB product/interface information. On 4.x, do not trust
|
|
34
|
+
the broken USB-enabled tag; 4.2.4 has a documented supported-mask correction.
|
|
35
|
+
3. Application toggles and lock configuration require firmware 5.x plus observed
|
|
36
|
+
capabilities. `--enable`/`--disable` may repeat. Unsupported apps, conflicting
|
|
37
|
+
options, disabling all USB apps and missing lock metadata fail before a write.
|
|
38
|
+
`--list` is read-only and cannot be combined with changes.
|
|
39
|
+
4. Every write/reboot/reset requires literal `confirm: true`. Lock material is
|
|
40
|
+
sixteen raw in-memory bytes via `lock_code:`/`new_lock_code:` or an explicit
|
|
41
|
+
`secret_provider.call(field)`. Never guess a credential. Generation requires
|
|
42
|
+
`secret_sink.call(:new_lock_code, bytes)`; never print the generated value.
|
|
43
|
+
5. Write once, then read actual device information and compare requested fields.
|
|
44
|
+
Any lost acknowledgment, mismatching readback or identity change raises an
|
|
45
|
+
uncertainty error; never automatically resend. A lock flag verifies protection
|
|
46
|
+
state, not export/equality of the lock secret.
|
|
47
|
+
6. USB interface changes normally reboot and need an explicit `reconnect`
|
|
48
|
+
callable before writing. It receives nonsecret prior identity metadata and
|
|
49
|
+
must return a fresh handle to the exact device. The returned handle is closed
|
|
50
|
+
after readback; the original connection remains caller-owned. Never rediscover
|
|
51
|
+
or switch to the first available key. Serial-less reconnection must preserve
|
|
52
|
+
the same observed explicit selector; missing/changed selectors fail uncertain.
|
|
53
|
+
Use `reboot: false` only to intentionally
|
|
54
|
+
defer activation; stored configuration readback is not active-interface proof.
|
|
55
|
+
7. Legacy NEO/4 `mode` emits the four-byte little-endian mode/timeouts record.
|
|
56
|
+
Reconnect after physical reinsertion and verify the observed USB product ID
|
|
57
|
+
maps to exactly the requested interfaces. A CCID reader that provides no USB
|
|
58
|
+
product ID cannot prove full legacy interface mode; fail uncertain rather than
|
|
59
|
+
claim success. Legacy timeout values are not readable and are labeled so.
|
|
60
|
+
8. Full-device reset is gated by Bio multi-protocol/PIV or reset-blocked capability
|
|
61
|
+
metadata and smartcard transport, not assumed available on all YubiKeys. Verify
|
|
62
|
+
the fresh reset-state field is cleared. `data_erasure_verified: false` is
|
|
63
|
+
explicit: configuration metadata does not independently prove forensic erasure
|
|
64
|
+
of every application credential. Use application-specific resets otherwise.
|
|
65
|
+
9. NFC restriction is available from firmware 5.7 and lasts until USB power.
|
|
66
|
+
Disabling the currently used NFC path needs a separately usable readback path;
|
|
67
|
+
absence or lost connectivity is uncertainty, never verified completion.
|
|
68
|
+
|
|
69
|
+
## Evidence boundaries
|
|
70
|
+
|
|
71
|
+
Same-path specs use synthetic HID/APDU/CTAP wire frames and the real shared
|
|
72
|
+
Protocol codec. They cover field packing, pagination, mutations/readback,
|
|
73
|
+
capability/version refusals, secrets, stale responses and legacy modes. No real
|
|
74
|
+
hardware, credential, network service or global reset is exercised by default.
|
|
75
|
+
Physical compatibility across firmware/models still requires explicit operator
|
|
76
|
+
hardware validation.
|
|
77
|
+
|
|
78
|
+
Protocol provenance: official yubikey-manager **5.9.1** source,
|
|
79
|
+
`yubikit/management.py`, `yubikit/core/otp.py`, `ykman/_cli/config.py` and
|
|
80
|
+
<https://docs.yubico.com/yesdk/yubikey-api/Yubico.YubiKey.Management.Commands.html>.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pwn-plugins-yubikey-oath
|
|
3
|
+
description: Use native OATH with exact account identities.
|
|
4
|
+
license: MIT
|
|
5
|
+
allowed-tools: [pwn, pwn_eval]
|
|
6
|
+
metadata:
|
|
7
|
+
bundled: true
|
|
8
|
+
preserve: true
|
|
9
|
+
module: PWN::Plugins::YubiKey::OATH
|
|
10
|
+
source: pwn/plugins/yubikey/oath.rb
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Native OATH
|
|
14
|
+
|
|
15
|
+
## When to use
|
|
16
|
+
|
|
17
|
+
Use `PWN::Plugins::YubiKey::OATH` for OATH application APDUs, not software-only OTP
|
|
18
|
+
or browser WebAuthn. The root YubiKey router owns transport lifetime.
|
|
19
|
+
|
|
20
|
+
## Public API
|
|
21
|
+
|
|
22
|
+
- `execute(args:, connection:, ...)` implements `info`, `reset`, `access change`,
|
|
23
|
+
`remember`, `forget`, and `accounts add`, `uri`, `import`, `list`, `code`,
|
|
24
|
+
`rename`, `delete`. Empty/group args return local metadata.
|
|
25
|
+
- `operations`, `help`, `authors` are local metadata.
|
|
26
|
+
- `code(connection:, account:, confirm: true, ...)` returns one numeric String.
|
|
27
|
+
- `calculate(connection:, account:, challenge:, confirm: true, ...)` returns a
|
|
28
|
+
raw full HMAC, not a formatted OTP.
|
|
29
|
+
- `calculate_all(connection:, timestamp:, confirm: true, ...)` returns credential
|
|
30
|
+
records with `code` value/validity Hashes; HOTP and touch records have nil codes.
|
|
31
|
+
- `parse_uri(uri:)` validates and parses an in-memory otpauth URI locally.
|
|
32
|
+
|
|
33
|
+
## Procedure
|
|
34
|
+
|
|
35
|
+
1. Use a caller-selected APDU connection. Never select the first device implicitly.
|
|
36
|
+
2. Pass the exact complete ID returned by LIST, including period/issuer prefixes,
|
|
37
|
+
whitespace and UTF-8 bytes. No substring, case folding or Unicode normalization
|
|
38
|
+
is performed when identifying an account.
|
|
39
|
+
3. Use `code` for one numeric OTP String; leading zeroes are significant. Optional
|
|
40
|
+
`timestamp:` is nonnegative Integer epoch seconds. TOTP periods come from the
|
|
41
|
+
credential ID, while HOTP uses an empty challenge and increments its counter.
|
|
42
|
+
4. Supply `password:` or sixteen raw bytes in `access_key:` in memory, or an
|
|
43
|
+
explicit `secret_provider:` callback. Authentication is single-attempt with
|
|
44
|
+
mutual HMAC-SHA1 verification after SELECT. Missing or wrong credentials never
|
|
45
|
+
trigger default guesses, repair, cached-key deletion, fallback or retry.
|
|
46
|
+
5. For account creation supply `secret:` (raw bytes) or strict `secret_base32:`;
|
|
47
|
+
`--generate` returns the generated seed in memory. Metadata options include
|
|
48
|
+
oath-type, algorithm (SHA1/SHA256/SHA512), digits (6–8), period, issuer, initial
|
|
49
|
+
HOTP counter and touch. Invalid values fail before authentication. Existing
|
|
50
|
+
IDs require explicit `overwrite: true` or `--force` in addition to consent.
|
|
51
|
+
6. URI input goes in `uri:`, not argv. Conflicting issuer fields, noncanonical
|
|
52
|
+
Base32, duplicate URI parameters and invalid periods are rejected, not repaired.
|
|
53
|
+
7. Bulk import accepts `credentials:` (complete Hash records) or `pskc:` (in-memory
|
|
54
|
+
PSKC 1.0 XML). Validate the complete batch before any authentication/write;
|
|
55
|
+
never silently skip a bad entry. Each successful PUT is checked with LIST.
|
|
56
|
+
Partial device failure is not rolled back or retried.
|
|
57
|
+
8. Use `access change` with `new_password:` / `new_access_key:`, or `--clear`.
|
|
58
|
+
Changes read back protection state only, not secret contents. Reset uses DEAD
|
|
59
|
+
confirmation bytes and verifies regenerated salt, unlocked state and empty
|
|
60
|
+
inventory. Rename/delete verify exact IDs and never delete as a repair step.
|
|
61
|
+
|
|
62
|
+
## Explicit encrypted-cache handoff
|
|
63
|
+
|
|
64
|
+
There is no built-in host cache or implicit lookup. `access remember`, `--remember`
|
|
65
|
+
and `use_cache: true` require an explicit `encrypted_cache:` adapter supplied by
|
|
66
|
+
the caller. It must expose `encrypted? == true` and these methods:
|
|
67
|
+
|
|
68
|
+
```ruby
|
|
69
|
+
cache.store(device_id: id, access_key: raw_key)
|
|
70
|
+
cache.fetch(device_id: id) # raw bytes or nil
|
|
71
|
+
cache.delete(device_id: id)
|
|
72
|
+
# access forget --all additionally requires:
|
|
73
|
+
cache.clear
|
|
74
|
+
cache.empty?
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The adapter owns encryption-at-rest and unlock policy; the assertion is the
|
|
78
|
+
caller's contract, not a cryptographic audit by this module. This is a transfer to
|
|
79
|
+
an explicit encrypted storage API, **not a plaintext file store**. Mutations are
|
|
80
|
+
read back through the adapter. Stored keys are never deleted after failed auth,
|
|
81
|
+
or automatically removed on password change/reset; explicitly forget stale
|
|
82
|
+
records. `use_cache:` never falls back to a password after failure. Do not supply
|
|
83
|
+
a plaintext Hash/file adapter in production.
|
|
84
|
+
|
|
85
|
+
## Pitfalls and limits
|
|
86
|
+
|
|
87
|
+
- `confirm: true` is mandatory for auth, code calculation, mutations and cache
|
|
88
|
+
writes. `--force` does not supply confirmation. Unprotected inventory is read-only.
|
|
89
|
+
- Native PSKC import supports plain values, AES-128/192/256 key-wrap and
|
|
90
|
+
MAC-authenticated AES-CBC, raw `pskc_key:` or embedded PBKDF2 with
|
|
91
|
+
`pskc_password:`. Unsupported algorithms, XML signatures and DTD/entities are
|
|
92
|
+
rejected. PSKC export-on-add, legacy file-path argv, arbitrary XML encryption
|
|
93
|
+
algorithms and Steam-specific five-character output are not implemented.
|
|
94
|
+
- LIST returns all exact IDs, including hidden-named records. Display-only legacy
|
|
95
|
+
flags do not change the structured result. Exact numeric MFA does not silently
|
|
96
|
+
switch to Steam formatting.
|
|
97
|
+
- Touch requires version 4.2.6, SHA512 4.3.1, rename 5.3.1. Known pre-4.3.5 prefix
|
|
98
|
+
overwrite defects fail closed. FIPS policy is ultimately enforced by the
|
|
99
|
+
selected application; no host override or plaintext secure-channel fallback exists.
|
|
100
|
+
- No CLI, subprocess, corrupt-account repair, silent credential deletion or disk
|
|
101
|
+
access exists. Ruby does not guarantee memory zeroization.
|
|
102
|
+
- A successful APDU or inventory readback is not physical interoperability or
|
|
103
|
+
proof of the imported secret. Timeouts leave mutation state uncertain.
|
|
104
|
+
|
|
105
|
+
## Verification
|
|
106
|
+
|
|
107
|
+
Run `bundle exec rspec spec/lib/pwn/plugins/yubikey/oath_spec.rb` and scoped
|
|
108
|
+
RuboCop. Tests exercise production Protocol framing and a raw exchange boundary,
|
|
109
|
+
including application-specific A5 SEND REMAINING, mutual auth, RFC 6238 vectors,
|
|
110
|
+
strict identities, mutation readback, cache handoff, and authenticated PSKC import.
|
|
111
|
+
No real hardware, home credentials or network is accessed.
|
|
112
|
+
|
|
113
|
+
References: Yubico YKOATH protocol and pinned yubikey-manager 5.9.1
|
|
114
|
+
`yubikit/oath.py` / `ykman/_cli/oath.py`; RFC 4226, RFC 6238, RFC 6030 and RFC 3394.
|
|
115
|
+
https://developers.yubico.com/OATH/YKOATH_Protocol.html
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pwn-plugins-yubikey-openpgp
|
|
3
|
+
description: Use native Ruby OpenPGP card operations through YubiKey APDUs.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# PWN::Plugins::YubiKey::OpenPGP
|
|
7
|
+
|
|
8
|
+
Native Ruby application logic using the shared `YubiKey::Protocol` APDU/TLV codec
|
|
9
|
+
and a caller-owned `Connection`. It does not run a CLI, start a subprocess, invoke
|
|
10
|
+
another interpreter, discover hardware at load time, or invent factory credentials.
|
|
11
|
+
Use `PWN::Plugins::YubiKey::OpenPGP.help` for all options.
|
|
12
|
+
|
|
13
|
+
## Safety and connection ownership
|
|
14
|
+
|
|
15
|
+
- Supply `connection:` with `descriptor[:protocol] == :apdu`. The caller owns
|
|
16
|
+
connection selection, exclusive access and closing. Use a secure-connection
|
|
17
|
+
wrapper when the selected device/interface requires SCP; never fall back to
|
|
18
|
+
plaintext automatically.
|
|
19
|
+
- Pass literal `confirm: true` for **every** mutation, authentication attempt and
|
|
20
|
+
private-key operation. A compatibility `--force` argument is not consent.
|
|
21
|
+
- Supply secrets as `pin:`, `admin_pin:`, `new_pin:`, `new_admin_pin:`, `reset_code:`
|
|
22
|
+
or `key_password:`. A `secret_provider:` callable receives only the field Symbol
|
|
23
|
+
for a required missing secret. Never put credentials in argv, logs or examples.
|
|
24
|
+
- Validate input, key type, algorithm, curve, firmware and card-advertised
|
|
25
|
+
capabilities before authentication. Unsupported operations raise explicitly.
|
|
26
|
+
- Do not replay a mutation or crypto request after timeout, failed authentication,
|
|
27
|
+
or a readback mismatch. A timeout can leave uncertain card state. Reset alone
|
|
28
|
+
deliberately exhausts PIN counters, bounded by observed counters or the
|
|
29
|
+
one-byte counter range during an explicitly requested corruption recovery.
|
|
30
|
+
- Replacing keys or changing algorithm attributes may erase existing keys.
|
|
31
|
+
`fixed` and `cached-fixed` touch policy cannot be reversed without replacing
|
|
32
|
+
the key. Factory reset does **not** restore the attestation key/certificate.
|
|
33
|
+
- PIN changes return freshly read retry counters and `credential_verified: false`:
|
|
34
|
+
secret values cannot be read back. Use a separate explicitly confirmed
|
|
35
|
+
`verify_pin` call if verifying the new credential is wanted.
|
|
36
|
+
- Secrets are not persisted by this module. Ruby/OpenSSL allocation and garbage
|
|
37
|
+
collection do not guarantee zeroization of all temporary copies.
|
|
38
|
+
|
|
39
|
+
## Management leaf operations
|
|
40
|
+
|
|
41
|
+
`execute(args: [...], connection: connection, ...)` accepts the words **after**
|
|
42
|
+
`openpgp`. Empty args and the `access`, `keys`, `certificates` groups return local
|
|
43
|
+
help metadata without touching a connection. `operations` lists leaves only.
|
|
44
|
+
|
|
45
|
+
| Leaf | Native operation and evidence |
|
|
46
|
+
|---|---|
|
|
47
|
+
| `info` | SELECT, GET VERSION, application DO 6E, signature-counter DO 7A, KDF DO F9 when advertised |
|
|
48
|
+
| `reset` | Deliberate bounded PIN blocking, TERMINATE E6, ACTIVATE 44, fresh empty-key metadata readback |
|
|
49
|
+
| `access set-retries USER RESET ADMIN` | Admin VERIFY 83, F2, fresh C4 readback; NEO PIN-reset side effect reported |
|
|
50
|
+
| `access change-pin` | CHANGE REFERENCE DATA 24/81, fresh C4; requires `pin`, `new_pin` |
|
|
51
|
+
| `access change-admin-pin` | CHANGE REFERENCE DATA 24/83, fresh C4; requires `admin_pin`, `new_admin_pin` |
|
|
52
|
+
| `access change-reset-code` | Admin VERIFY, PUT DATA D3, fresh C4; requires `reset_code` |
|
|
53
|
+
| `access unblock-pin` | RESET RETRY COUNTER 2C, P1=0 with reset code or P1=2 with admin authorization |
|
|
54
|
+
| `access set-signature-policy always|once` | PUT DATA C4 with readback |
|
|
55
|
+
| `keys info SLOT` | Parsed algorithm, origin, fingerprint, generation time and UIF metadata |
|
|
56
|
+
| `keys set-touch SLOT POLICY` | GET/PUT DATA D6–D9, button byte 20, readback, fixed-policy protection |
|
|
57
|
+
| `keys import SLOT FILE|-` | Native OpenSSL key parsing, import template 4D/7F48/5F48, PUT DATA ODD DB/3FFF, public-key readback |
|
|
58
|
+
| `keys attest SLOT OUTPUT|-` | User VERIFY 81, proprietary 80/FB, certificate readback; SIG/DEC/AUT only |
|
|
59
|
+
| `certificates export SLOT OUTPUT|-` | SELECT DATA A5 and GET DATA 7F21, or attestation DO FC |
|
|
60
|
+
| `certificates import SLOT FILE|-` | Single PEM/DER X.509 parsing, certificate-limit validation, PUT DATA and exact DER readback |
|
|
61
|
+
| `certificates delete SLOT` | Empty PUT DATA, empty-slot readback |
|
|
62
|
+
|
|
63
|
+
Slots: `sig`, `dec`, `aut`, `att`; `enc` is an alias for `dec`. Touch policies:
|
|
64
|
+
`off`, `on`, `fixed`, `cached`, `cached-fixed`. Export/attest accept `--format PEM`
|
|
65
|
+
or `--format DER`; PEM is the default. Input `-` requires `key:` or `certificate:`
|
|
66
|
+
in memory, not stdin. Output `-` returns bytes, not stdout. Named output files are
|
|
67
|
+
created exclusively with mode 0600; existing paths are not overwritten.
|
|
68
|
+
|
|
69
|
+
## Typed cryptographic APIs
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
pgp = PWN::Plugins::YubiKey::OpenPGP
|
|
73
|
+
|
|
74
|
+
# Read-only capability discovery and native OpenSSL public-key export.
|
|
75
|
+
supported = pgp.algorithms(connection: connection)
|
|
76
|
+
public_key = pgp.public_key(connection: connection, slot: :sig)
|
|
77
|
+
|
|
78
|
+
# Explicitly destructive on-card generation; returns an OpenSSL public key.
|
|
79
|
+
public_key = pgp.generate_key(
|
|
80
|
+
connection: connection, slot: :sig, algorithm: :secp256r1,
|
|
81
|
+
admin_pin: admin_pin, confirm: true
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
signature = pgp.sign(
|
|
85
|
+
connection: connection, message: message, hash_algorithm: :sha256,
|
|
86
|
+
pin: user_pin, confirm: true
|
|
87
|
+
)
|
|
88
|
+
authentication_signature = pgp.authenticate(
|
|
89
|
+
connection: connection, message: challenge, pin: user_pin, confirm: true
|
|
90
|
+
)
|
|
91
|
+
plaintext_or_shared_secret = pgp.decipher(
|
|
92
|
+
connection: connection, value: ciphertext_or_peer_public_key,
|
|
93
|
+
pin: user_pin, confirm: true
|
|
94
|
+
)
|
|
95
|
+
# decrypt is an alias to the same decipher implementation.
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
- RSA signing sends DigestInfo through PSO 2A/9E9A; authentication uses INTERNAL
|
|
99
|
+
AUTHENTICATE 88. RSA signatures are raw modulus-length bytes.
|
|
100
|
+
- ECDSA signs a SHA digest; card `r || s` is checked and converted to DER.
|
|
101
|
+
- Ed25519 sends the original message and returns raw 64-byte signatures.
|
|
102
|
+
`prehashed: true` is rejected for Ed25519. Other signing methods can accept
|
|
103
|
+
prehashed SHA-1/224/256/384/512 input of exactly the digest length.
|
|
104
|
+
- RSA decipher sends the zero padding indicator before a modulus-length
|
|
105
|
+
PKCS1-v1_5 ciphertext. EC/X25519 decipher sends the A6/7F49/86 peer template
|
|
106
|
+
and returns the **raw shared secret**, not a decrypted OpenPGP message.
|
|
107
|
+
- `verify_pin(mode: :signature|:extended|:admin, ...)` chooses PW1/81, PW1/82 or
|
|
108
|
+
PW3/83. `unverify_pin` clears that reference on firmware 5.6 or newer.
|
|
109
|
+
- RSA2048/3072/4096, NIST P-256/384/521, secp256k1, Brainpool P256/P384/P512,
|
|
110
|
+
Ed25519 and X25519 are recognized. Actual generation/import support comes
|
|
111
|
+
from firmware restrictions and advertised FA entries, not merely host support.
|
|
112
|
+
- NEO RSA imports use CRT components; 4.4 FIPS firmware is limited to RSA2048.
|
|
113
|
+
RSA generation is disabled on 4.2.0–4.3.4 vulnerable firmware.
|
|
114
|
+
- PKCS8 Curve25519 import extracts the inner 32-byte seed/scalar natively;
|
|
115
|
+
X25519 reverses scalar bytes for the card. P-521 scalars use 66 bytes.
|
|
116
|
+
- PIN KDF supports card-advertised iterated-and-salted S2K with SHA256/SHA512,
|
|
117
|
+
role-specific salts and bounded byte counts. Unknown KDFs never send plaintext.
|
|
118
|
+
|
|
119
|
+
## Verification and boundaries
|
|
120
|
+
|
|
121
|
+
Run the paired offline suite:
|
|
122
|
+
|
|
123
|
+
```text
|
|
124
|
+
bundle exec rspec spec/lib/pwn/plugins/yubikey/openpgp_spec.rb
|
|
125
|
+
bundle exec rubocop lib/pwn/plugins/yubikey/openpgp.rb spec/lib/pwn/plugins/yubikey/openpgp_spec.rb
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Tests use the actual shared Protocol plus a strict synthetic Connection, literal
|
|
129
|
+
APDU fixtures, native OpenSSL keys, RFC seed material, host signature verification,
|
|
130
|
+
certificate parsing, public-key equality and ECDH equality. They cover command and
|
|
131
|
+
response chaining, version quirks, readback mismatch, malformed input, consent,
|
|
132
|
+
missing secrets, incorrect PIN and timeout without replay. They do not establish
|
|
133
|
+
USB/NFC interoperability or on-device behavior on every firmware/FIPS variant.
|
|
134
|
+
|
|
135
|
+
This is the card application layer, not a keyring, OpenPGP packet/ASCII-armor
|
|
136
|
+
encoder or signature/attestation trust-chain validator. RSA OAEP and symmetric
|
|
137
|
+
OpenPGP message decryption are not implied by the card PKCS1/ECDH primitives.
|
|
138
|
+
|
|
139
|
+
Protocol provenance: OpenPGP Card specification 3.4
|
|
140
|
+
(<https://gnupg.org/ftp/specs/OpenPGP-smart-card-application-3.4.pdf>),
|
|
141
|
+
Yubico YubiKey Manager 5.9.1 `yubikit/openpgp.py` and
|
|
142
|
+
`ykman/_cli/openpgp.py` (<https://github.com/Yubico/yubikey-manager>).
|
|
143
|
+
The cached official sources were consulted read-only, never used as runtime
|
|
144
|
+
backends. Source attribution and the reference BSD license are retained in Ruby.
|