Conversation
Add/update LICENSE, NOTICE, TRADEMARK.md, LICENSE-zh.md, TRADEMARK-zh.md per MushroomDAO ecosystem standard.
Three-layer security for AI Agent programmatic signing:
Layer 1 (TEE): agentKey secp256k1 BIP44 m/44'/60'/0'/1/<idx>, kms_secret HMAC — never leave TEE
Layer 2 (JWT): Bearer HS256, 3-day TTL, single-visible, SHA-256(jwt) stored in DB
Layer 3 (chain): callTargets/spendCap/expiry locked in AgentSessionKeyValidator on-chain
New TA commands (11-15): CreateAgentKey, SignAgentUserOp, JwtHmacSign, JwtHmacVerify, JwtRotateSecret
New HTTP endpoints:
POST /kms/create-agent-key — WebAuthn-gated, returns JWT (single-visible)
POST /kms/sign-agent — Bearer JWT, signs userOpHash with EIP-191 inside TEE
POST /kms/refresh-agent-credential — Bearer + WebAuthn, rotates JWT, supersedes old
POST /kms/revoke-agent-credential — WebAuthn, marks agent key revoked in DB
New DB tables: agent_keys (wallet_id:agent_index PK), jwt_secret_meta (kid rotation tracking)
New CLI binary: kms-admin (rotate-jwt-secret, jwt-secret-status, list-agent-keys, revoke-agent-key)
Auto JWT rotation background task every 24h with 7-day verify-only overlap window
keyId format: "{wallet_uuid}:{agent_index}" — parseable, no separate UUID column needed
Critical 1 — Mnemonic TEE boundary leak: TA now only populates CreateWalletOutput.mnemonic when feature = "export-secrets". In production builds mnemonic = "" — no seed material crosses TEE boundary. kms/ta/src/main.rs: #[cfg(feature = "export-secrets")] on get_mnemonic() call. Critical 2 — ExportPrivateKey passkey-less admin bypass: Production builds always require passkey assertion; the None = skip path is now gated behind #[cfg(feature = "export-secrets")]. The export_key binary itself requires the feature via required-features in host/Cargo.toml. kms/ta/Cargo.toml and kms/host/Cargo.toml: export-secrets feature added. Critical 3 — Agent signing with no TA-side authorization: TA's sign_agent_user_op now verifies the JWT HMAC credential before signing. Host extracts (kid, signing_input, hmac) from the Bearer JWT via agent_jwt::extract_signing_proof() and passes them to the TA as new SignAgentUserOpInput fields. The TA reuses jwt_hmac_verify() with the stored kms_secret — a compromised CA can no longer bypass host-side JWT checks by calling the TA command directly. Bonus — Legacy passkey replay (agent ops): create-agent-key / refresh-agent-credential / revoke-agent-credential now REQUIRE WebAuthn ceremony (BeginAuthentication challenge flow). Passing only a raw passkeyAssertion is rejected with a clear error message. This prevents captured assertions from being replayed against agent operations.
H2 webauthn.rs: enforce UV flag on authentication assertions.
We request userVerification="required"; now we verify the UV bit
is actually set in authenticatorData flags.
H3 api_server.rs: add KMS_REQUIRE_API_KEY=1 env var for fail-closed
mode when no API keys are provisioned yet. Improves startup warning
message.
H4 ta/src/main.rs: force JWT rotation now purges old kid entries.
Normal rotation keeps one retired entry for 7-day overlap window;
force rotation removes all retired entries immediately so old JWTs
become unverifiable. Caps retired entries at 1 to bound memory.
H1 api_server.rs: log a deprecation warning on every legacy passkey
path use (no challenge/origin binding → replay risk).
M2 api_server.rs: reject request bodies > 256 KB in aws_kms_body().
M3 api_server.rs: validate Transaction.to is exactly 20 bytes before
copy_from_slice to prevent panic on short hex input.
M4 api_server.rs: add full bounds checks in parse_der_signature() to
prevent panic on malformed DER input with bad length fields.
M5 db.rs: replace count_agent_keys_for_human()+index with atomic
next_agent_index_for_wallet() that uses MAX(agent_index)+1 in a
single mutex-held query, eliminating the count→insert TOCTOU gap.
H4 (MUST FIX — David's review): JwtSecretStore::rotate() with purge_retired=true was calling `self.entries.retain(|e| !e.current)` which kept retired entries and only removed the current one. Force rotation after this bug left old retired kids still valid for JWT verification. Fixed to `self.entries.clear()` so all old kids are immediately invalidated on force rotation. M2 (double TA call race): issue_credential() made two TA calls — a probe call to get the current kid, then a sign call — with a possible kid rotation between them. The retry mitigation (lines 60-68) reduced but did not eliminate the race. Fixed by adding a new JwtSignPayload TA command that atomically picks the current kid, constructs the JWT header JSON, computes the signing_input, and signs it in a single TA invocation. CA now makes one call and gets (kid, header_b64, hmac) back atomically. Changes: - kms/proto/src/in_out.rs: add JwtSignPayloadInput / JwtSignPayloadOutput - kms/proto/src/lib.rs: add Command::JwtSignPayload = 16, fix stale test - kms/ta/src/main.rs: fix entries.clear(), add jwt_sign_payload handler - kms/host/src/ta_client.rs: add jwt_sign_payload() async method - kms/host/src/agent_jwt.rs: rewrite issue_credential to single TA call
Low-1 credential_jwt column: update_agent_credential() no longer accepts or writes the credential_jwt column. The column exists in the schema for backwards compatibility but the single-visibility guarantee is already enforced by storing SHA-256(jwt) only. Removed the dead parameter and the corresponding UPDATE SET clause so the column remains NULL always (no empty-string writes). Low-2 per-credential rate limit: add agent_rate_limiter field to KmsApiServer (default 20/min, configurable via KMS_AGENT_RATE_LIMIT). sign_agent() now checks this limiter keyed by wallet_id/agent_index after JWT verification but before the TEE signing call, so a compromised or leaked credential cannot flood the TEE at more than 20 req/min regardless of the global API key limit.
… rate limit) feat: v0.17.0 — Agent Key subsystem (secp256k1 + JWT) + security hardening
Contract v0.17.2 unifies AgentSessionKeyValidator into SessionKeyValidator and changes the session-key signature wire format: Old (v0.17.0): [0x08][ECDSA(65)] = 66 bytes (SDK assembled) New (v0.17.2): [0x08][account(20)][key(20)][ECDSA(65)] = 106 bytes (KMS assembled) The embedded account address lets the contract prevent cross-account session-key abuse: sig[1:21] must equal userOp.sender. Changes: - proto: SignAgentUserOpInput gets account_address: [u8; 20] - ta: sign_agent_user_op derives agent key address inside TEE, assembles full 106-byte output [0x08][account][key][r][s][v] - ta_client: sign_agent_user_op() adds account_address parameter - api_server: SignAgentRequest gets accountAddress field; sign_agent() parses and forwards to TA; add parse_address_hex() - docs: update agent-key-design.md with v0.17.2 layout table
fanhousanbu
approved these changes
May 30, 2026
fanhousanbu
left a comment
There was a problem hiding this comment.
✅ APPROVE
All 4 issues from my previous review have been addressed:
- H4 force-rotation purge:
entries.retain(|e| !e.current)logic was inverted →self.entries.clear()now correctly invalidates all old kids - M2 atomic JWT signing: probe+sign two-TA-call race eliminated via new
JwtSignPayloadcommand (single atomic TA call) - L1
credential_jwtcolumn stays NULL permanently; dead UPDATE clause removed - L2 per-credential rate limit 20 req/min added via
agent_rate_limiter
3 Critical + 7 High/Medium security fixes are solid. Ready to merge.
feat: v0.17.2 — session-key 106-byte signature format (issue #7)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
将 KMS 合并到 main。PR #4 已由 fanhousanbu Approved。包含 Agent Key 子系统、3 Critical + 7 High/Medium 安全修复、David review 全部 4 条修复。