Skip to content

DIP: Dash Platform Application Key Exchange and State Transition Signing#181

Open
PastaPastaPasta wants to merge 1 commit intodashpay:masterfrom
PastaPastaPasta:dip-pasta-yappr
Open

DIP: Dash Platform Application Key Exchange and State Transition Signing#181
PastaPastaPasta wants to merge 1 commit intodashpay:masterfrom
PastaPastaPasta:dip-pasta-yappr

Conversation

@PastaPastaPasta
Copy link
Member

@PastaPastaPasta PastaPastaPasta commented Mar 11, 2026

Summary

  • Defines the dash-key: URI protocol for wallet-based QR code login to Dash Platform applications using ECDH key exchange and AES-256-GCM encryption
  • Defines the dash-st: URI protocol for requesting wallet signature and broadcast of unsigned state transitions
  • Specifies the full cryptographic flow: BIP32 derivation (m/9'/coin_type'/21'/account'), HKDF key derivation, ECDH shared secret, and per-contract/per-identity key isolation
  • Documents the loginKeyResponse Platform contract schema, indices, and polling mechanism
  • Covers first-time key registration via IdentityUpdateTransition with ECDSA_HASH160 keys

Test plan

  • Review spec for technical accuracy against existing implementations in dash-evo-tool and yappr
  • Verify all cryptographic parameters (HKDF salts, info fields, key sizes) match implementations
  • Review security considerations section for completeness

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation
    • Added comprehensive specification for URI-based protocols enabling secure wallet-app interaction, covering key exchange and state transition signing workflows, cryptographic processes, and security considerations.

…ansition Signing

Defines the dash-key: and dash-st: URI protocols for wallet-based QR code login to Dash Platform applications. Covers ECDH key exchange, AES-256-GCM encryption, BIP32+HKDF key derivation, the loginKeyResponse contract schema, and first-time key registration flow.
@coderabbitai
Copy link

coderabbitai bot commented Mar 11, 2026

📝 Walkthrough

Walkthrough

Introduces a new DIP document specifying two complementary URI-based protocols (dash-key and dash-st) for secure wallet-app interaction, including protocol architecture, URI formats, cryptographic flows, data contract schemas, security considerations, and validation requirements.

Changes

Cohort / File(s) Summary
DIP Specification Document
dip-pasta-yappr.md
New document describing Key Exchange Protocol (dash-key) and State Transition Signing Protocol (dash-st) for wallet-app interaction, including protocol goals, URI formats, Base58 payload layouts, ECDH-based cryptographic flows, AES-256-GCM encryption, BIP32/HKDF key derivation, Dash Platform data contract schemas, dashboard-st signing workflow, first-time login key registration, security considerations, and validation/UX requirements.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'DIP: Dash Platform Application Key Exchange and State Transition Signing' directly and accurately summarizes the main changes—introduction of two complementary URI protocols (dash-key and dash-st) for secure wallet-app interaction with key exchange and state transition signing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
dip-pasta-yappr.md (2)

262-265: Consider clarifying concurrent request handling.

The keyIndex logic states that if no document exists, keyIndex is 0. If a wallet processes multiple key exchange requests for the same identity and contract concurrently (before the first document is created), both could use keyIndex 0, potentially creating a race condition. Consider adding guidance on how wallets should handle this scenario (e.g., serialize requests per identity+contract or retry on conflict).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dip-pasta-yappr.md` around lines 262 - 265, Clarify concurrent-request
behavior for keyIndex/loginKeyResponse: explain that when no loginKeyResponse
exists wallets must handle races by serializing requests per identity+contract
or using a create-if-not-exists atomic operation and retry-on-conflict;
reference the keyIndex and loginKeyResponse documents and state a recommended
strategy (e.g., queue/serialize per identity+contract, use optimistic
concurrency/version checks on loginKeyResponse, or retry on conflict) so two
concurrent requests cannot both assume keyIndex = 0 and create duplicate keys.

391-401: Consider exponential backoff for revision conflict retries.

The document lifecycle specifies retrying up to 3 times with a fixed 500ms delay on revision conflicts. Consider recommending exponential backoff (e.g., 500ms, 1000ms, 2000ms) to reduce contention under high load while maintaining the same maximum retry count.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dip-pasta-yappr.md` around lines 391 - 401, The retry logic for handling
revision conflicts when updating a loginKeyResponse document should use
exponential backoff instead of a fixed 500ms delay: update the retry loop that
currently retries up to 3 times with a fixed 500ms pause to instead wait 500ms,
then 1000ms, then 2000ms between attempts (keeping the same max retry count),
and continue to re-query the loginKeyResponse document on each retry and
preserve the existing update/create flow and revision bump behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@dip-pasta-yappr.md`:
- Line 36: The Table of Contents link fragment "#supported-encodings" is broken
because the actual header is "## Encoding"; update either the TOC entry or the
heading so they match—e.g., change the TOC item "* [Supported
Encodings](`#supported-encodings`)" to "* [Encoding](`#encoding`)" or rename the
header "## Encoding" to "## Supported Encodings" so the fragment and header text
are consistent.

---

Nitpick comments:
In `@dip-pasta-yappr.md`:
- Around line 262-265: Clarify concurrent-request behavior for
keyIndex/loginKeyResponse: explain that when no loginKeyResponse exists wallets
must handle races by serializing requests per identity+contract or using a
create-if-not-exists atomic operation and retry-on-conflict; reference the
keyIndex and loginKeyResponse documents and state a recommended strategy (e.g.,
queue/serialize per identity+contract, use optimistic concurrency/version checks
on loginKeyResponse, or retry on conflict) so two concurrent requests cannot
both assume keyIndex = 0 and create duplicate keys.
- Around line 391-401: The retry logic for handling revision conflicts when
updating a loginKeyResponse document should use exponential backoff instead of a
fixed 500ms delay: update the retry loop that currently retries up to 3 times
with a fixed 500ms pause to instead wait 500ms, then 1000ms, then 2000ms between
attempts (keeping the same max retry count), and continue to re-query the
loginKeyResponse document on each retry and preserve the existing update/create
flow and revision bump behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6edfd7c4-83bf-45c9-b579-c785373b1631

📥 Commits

Reviewing files that changed from the base of the PR and between 40b74e9 and 9482c77.

📒 Files selected for processing (1)
  • dip-pasta-yappr.md

1. [State Transition Signing Protocol](#state-transition-signing-protocol)
* [dash-st: URI Format](#dash-st-uri-format)
* [Query Parameters](#query-parameters)
* [Supported Encodings](#supported-encodings)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix the broken link fragment in the Table of Contents.

The ToC entry references #supported-encodings but the actual section heading at line 434 is ## Encoding (singular). This causes a broken link and is flagged by markdownlint (MD051).

🔗 Proposed fix
-    * [Supported Encodings](`#supported-encodings`)
+    * [Encoding](`#encoding`)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
* [Supported Encodings](#supported-encodings)
* [Encoding](`#encoding`)
🧰 Tools
🪛 GitHub Actions: .github/workflows/markdownlint.yml

[error] 36-36: markdownlint: MD051 link-fragments should be valid for link Supported Encodings in dip-pasta-yappr.md.

🪛 GitHub Check: lint

[failure] 36-36: Link fragments should be valid
dip-pasta-yappr.md:36:7 MD051/link-fragments Link fragments should be valid [Context: "Supported Encodings"] https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/md051.md

🪛 markdownlint-cli2 (0.21.0)

[warning] 36-36: Link fragments should be valid

(MD051, link-fragments)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@dip-pasta-yappr.md` at line 36, The Table of Contents link fragment
"#supported-encodings" is broken because the actual header is "## Encoding";
update either the TOC entry or the heading so they match—e.g., change the TOC
item "* [Supported Encodings](`#supported-encodings`)" to "*
[Encoding](`#encoding`)" or rename the header "## Encoding" to "## Supported
Encodings" so the fragment and header text are consistent.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9482c77c3f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

| Path Level | Value | Description |
| ---------- | ----- | ----------- |
| 9' | Feature purpose (per [DIP-0009](https://github.com/dashpay/dips/blob/master/dip-0009.md)) | |
| coin_type' | `5` (mainnet) or `1` (testnet/devnet/regtest) | Per [BIP-0044](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) |

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add per-network key separation in login-key derivation

Using the same coin_type' = 1 for testnet/devnet/regtest means the derivation inputs are identical across those networks unless contractId or identity differs, so the same wallet can produce the same login key material in multiple non-mainnet environments; this weakens the stated network isolation and creates cross-environment key reuse risk if one environment is less trusted. To preserve isolation, include an explicit network discriminator in HKDF input (or use distinct derivation branches per non-mainnet network).

Useful? React with 👍 / 👎.

| `d`, `devnet` | Devnet |
| `r`, `regtest` | Regtest (local) |

## Binary Payload Layout
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this is a subsection? If so, I'd also move it above Network identifiers to match the order from the component table.

Suggested change
## Binary Payload Layout
### Binary Payload Layout

Comment on lines +242 to +247
| Path Level | Value | Description |
| ---------- | ----- | ----------- |
| 9' | Feature purpose (per [DIP-0009](https://github.com/dashpay/dips/blob/master/dip-0009.md)) | |
| coin_type' | `5` (mainnet) or `1` (testnet/devnet/regtest) | Per [BIP-0044](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) |
| 21' | Key exchange feature index | |
| account' | Wallet account index for the selected identity (default `0`) | |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This table is weird. Seems unnecessary to have the Description column that only contains values for a single row. You could collapse that into the Value column and drop the extra column or smth

| ---------- | ----- | ----------- |
| 9' | Feature purpose (per [DIP-0009](https://github.com/dashpay/dips/blob/master/dip-0009.md)) | |
| coin_type' | `5` (mainnet) or `1` (testnet/devnet/regtest) | Per [BIP-0044](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) |
| 21' | Key exchange feature index | |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why 21?

| --------- | ----------- |
| `dash-st:` | URI scheme prefix |
| `<encoded_transition>` | Encoded unsigned state transition bytes |
| `n` | Network identifier (required; same values as `dash-key:`) |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Link to that table

| --------- | -------- | ----------- |
| `n` | Yes | Network identifier (`m`, `t`, `d`, or `r`) |
| `v` | Yes | Protocol version; must be `1` |
| `id` | No | Base58-encoded identity identifier hint (32 bytes); wallet should pre-select this identity |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extraneous word?

Suggested change
| `id` | No | Base58-encoded identity identifier hint (32 bytes); wallet should pre-select this identity |
| `id` | No | Base58-encoded identity identifier (32 bytes); wallet should pre-select this identity |

| Security Level | HIGH |
| Key Type | ECDSA_HASH160 |
| Data | Hash160 of the authentication public key (20 bytes) |
| Read Only | false |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this really something that needs to be specified?

| contractId | Identifier | 32 | The target application's data contract identifier |
| appEphemeralPubKeyHash | Bytes | 20 | Hash160 of the application's ephemeral public key |
| walletEphemeralPubKey | Bytes | 33 | Wallet's compressed secp256k1 ephemeral public key |
| encryptedPayload | Bytes | 1–4096 | Encrypted login key (nonce ‖ ciphertext ‖ tag; typically 60 bytes) |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why such a large max if 60 is typical?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants