ML-DSA-65 Post-Quantum Transaction Signatures - #645
Conversation
2e339e7 to
5969077
Compare
Draft NEP documenting ML-DSA-65 (FIPS 204) as a third transaction-signature scheme, offered alongside ed25519 and secp256k1 and stabilized at protocol version 85 (neard 2.13). Covers access-key storage by hash, verification gas pricing, transaction-size accounting, security implications, and the out-of-scope follow-ups (validator keys, implicit accounts, host functions).
5969077 to
44d2830
Compare
iso.org returns 403 to the link-checker bot (the link resolves fine in a browser), failing the folder-wide check on a pre-existing link in nep-0177. Mirrors the eprint.iacr.org ignore added in #644.
|
Thank you @Wiezzel for submitting this NEP. As a moderator, I reviewed this NEP and it meets the proposed template guidelines. I am moving this NEP to the REVIEW stage and suggest @gilcu3, @SimonRastikian, and @darioush as SME reviewers to complete a technical review (see expectations). @near/wg-protocol working group members, please let us know if you have better suggestions. Please find some guidelines below for completing the technical review. Technical Review Guidelines
Please tag the @near/nep-moderators once you are done, so we can move this NEP to the voting stage. Thanks again. |
darioush
left a comment
There was a problem hiding this comment.
This is a well written proposal that accurately reflects the implementation in 2.13 release. I recommend the working group consider approving this NEP.
Benefits
- Allows users to authorize transactions and custody assets using quantum resistant keys today, addressing concerns regarding upredictable timing of quantum computing advancement.
- Charging the extra verification compute as burnt gas is a practical way to make post-quantum signers pay for the work they add, leaving classical-scheme users unaffected. The additional witness space/bandwidth is bounded by the size-limit accounting but per-byte pricing is deliberately deferred.
Concerns
- The suggested cryptography scheme has large signatures not amenable to aggregation. Adding a future scheme for use in consensus could require downstream dependencies (wallets, explorers, exchanges) to update again.
- Deferring universal account scheme to a later protocol upgrade means an account must first go through one of the existing access keys to add a quantum-resistant key.
SimonRastikian
left a comment
There was a problem hiding this comment.
Please next time when you write a doc, do add line breaks so the review is easier to make and so we can propose changes per line not per paragraph
|
|
||
| ### ML-DSA-65 parameters and library | ||
|
|
||
| ML-DSA-65 is the middle of the three FIPS 204 variants (44 / 65 / 87). It targets **NIST Security Category 3** (comparable to a brute-force key search on AES-192; a Grover-style quantum search gives only a quadratic speedup and remains far out of practical reach). |
There was a problem hiding this comment.
Instead of the parenthesis say how many bits of security it provides and link the paragraph
| This extra cost is charged as **gas** at transaction conversion, through a new runtime parameter: | ||
|
|
||
| - `ml_dsa_65_verification_cost = 100_000_000_000` (100 Ggas), introduced at protocol version 85. | ||
| - It is wired through `RuntimeFeesConfig::signature_verification_costs`, an `EnumMap<SignatureKind, ParameterCost>` over `SignatureKind { Ed25519, Secp256k1, MlDsa65 }`. Only `MlDsa65` is non-zero; `Ed25519` and `Secp256k1` are zero, so the classical schemes are unaffected and backwards-compatible. |
There was a problem hiding this comment.
Why here it's MlDsa65 not MLDSA65
There was a problem hiding this comment.
That's a code name. I believe we have a convention of not capitalizing all characters in acronyms, only the first one, when it's a part of a camel-case name.
| ### View API and RPC representation | ||
|
|
||
| - **`view_access_key_list`** returns `ml-dsa-65-hash:<bs58>` for ML-DSA-65 entries, because the trie stores only the hash. The full public key is not recoverable from chain state. Wallets, explorers, and indexers MUST know their own public key (or recompute the handle from it) to reconcile a chain entry with a known key. This is the same model already used for ETH-style implicit accounts, whose on-chain identifier is also a hash. | ||
| - **Rosetta-RPC** gains `CurveType::MlDsa65` and `SignatureType::MlDsa65` (serialized as `"ml_dsa_65"`), with full conversions to and from `KeyType::MLDSA65`. The corresponding upstream addition to the Coinbase Mesh specification is [mesh-specifications#129](https://github.com/coinbase/mesh-specifications/pull/129). Mesh clients that have not picked up the new enum value yet may reject it; this is accepted. |
There was a problem hiding this comment.
Please MLDSA65 not MlDsa65. It's called Module-Lattice-Based Digital Signature Standard not like Ed25519 where Ed stands for Edwards.
|
|
||
| - **Pricing witness/wire size.** Charging for the extra bytes the large signature and public key add is deliberately deferred. The team agreed not to implement it for this milestone; whether and how to track it for a future release was raised and not resolved in the design discussion. | ||
| - **External audit.** Whether ML-DSA-65 integration warrants a dedicated external audit beyond the Trail of Bits library recommendation was raised during design and not formally resolved. | ||
| - **Mirror / fork-network behavior** is inconsistent (some paths panic, others silently skip). Unifying this to a visible "log and skip" before any serious mirroring of a post-quantum-bearing chain is an open task. |
There was a problem hiding this comment.
Seems better to resolve this issue soon as well. Maybe the tooling added in offline_test.py can be used for checking the PQ keys work with mirror
- Lead framing with ML-DSA-65; keep PostQuantumSignatures as gate name - Reframe quantum threat as future forgeability - Correct signature-count bound (one DelegateAction per tx max) - Note receipt-carried pubkey, gas-key support, near-sdk UnknownCurve compat - Trim variable-time section; secp256k1 -> ECDSA wording; minor edits
RFC 2119 consistency with the surrounding bullets (review nit).
- Reword Positive backwards-compat bullet to avoid overclaiming (SDK caveat) - Move large-transaction bullet from Neutral to Negative - Drop redundant mirror Negative (covered in Future/Unresolved) - Trim SHA3-384 alternative to cross-reference the Security section
The table column mixed casing (ED25519/SECP256K1 vs MlDsa65) because it mirrored the code enum, which is itself inconsistent. Present scheme names consistently, matching the trie-encoding table (review nit).
Not adding validator keys / implicit accounts / on-chain verification is unchanged behavior, not something made worse by the NEP; it is already listed under Future possibilities (review r3519122004).
SimonRastikian
left a comment
There was a problem hiding this comment.
Thank you, much better. One last comment to fix please.
Drop the absolute 'a misbehaving signer cannot inflate it' claim. State that invalid-signature txs are rejected free (uncharged), the gas charge covers included txs, and the benches (valid/random/tampered) are empirical evidence, not a formal constant-time guarantee (review r3528320712).
rfc-editor.org blocks the link-checker bot (Status 0) for a live RFC link in nep-0488, failing the folder-wide check. Same treatment as the other standards hosts already ignored (w3.org, eprint.iacr.org, iso.org).
|
As the moderator, I would like to thank @Wiezzel for submitting this NEP, and for the SME for your review. Based on the discussion it seems like this proposal is ready to be voted. We have already scheduled a meeting for this Wednesday, and might be able to present the proposal there if you all have time to join. No worries if not, simply react with a thumbs down (👎) and we will try to find a more suitable time. @Wiezzel during this meeting it would be nice to briefly present the NEP, 1 or 2 slides would suffice. Meeting Info |
birchmd
left a comment
There was a problem hiding this comment.
As a protocol working group member, I approve this proposal.
I appreciate that the scope of this feature is clearly defined, and while not "fully complete" in a certain sense (missing host function validation for example), it is a minimum viable feature that brings value to users immediately. Even though the total number of NEPs will be higher, I think having smaller proposals iterate on the protocol in manageable-sized increments will be better for stability in the long term.
Adding the option for post-quantum security using a well-accepted algorithm (and corresponding implementation) is a forward-facing upgrade I am happy to see in Near protocol.
I have one minor technical comment, but otherwise the proposal looks good to me.
PublicKey and PublicKeyHandle share a borsh/Hash discriminant-tag
namespace but use disjoint tag sets: PublicKey owns {0,1,2} (tag 2 =
full 1952-byte ML-DSA-65 key) and PublicKeyHandle owns {0,1,3} (tag 3 =
32-byte SHA3-256 hash), so a full key in the trie and a hash on the wire
stay unrepresentable. The reservation was enforced only by hand-written
magic-number match arms, so a future variant could silently reuse a
reserved tag.
Introduce a shared KeyTag enum as the single source of tag bytes, used
by both enums' Hash/BorshSerialize/BorshDeserialize impls, and match it
exhaustively in both deserializers. Adding a future scheme now fails to
compile in both enums until handled explicitly, and the reserved tags
are explicit named error arms (tag 2 rejected by PublicKeyHandle, tag 3
by PublicKey). Encoding is byte-for-byte unchanged.
Addresses review feedback on near/NEPs#645.
near#16043) `PublicKey` and `PublicKeyHandle` deliberately use disjoint borsh/hash tag spaces: `PublicKey` owns {0, 1, 2} (tag 2 = full 1952-byte ML-DSA-65 key) and `PublicKeyHandle` owns {0, 1, 3} (tag 3 = 32-byte SHA3-256 hash), so "a full key in the trie" and "a hash on the wire" stay unrepresentable. The reservation was enforced only by hand-written magic-number match arms, so a future variant could silently reuse a reserved tag. This introduces a shared `KeyTag` enum as the single source of tag bytes, used by both enums' `Hash`/`BorshSerialize`/`BorshDeserialize` impls, and matches it exhaustively in both deserializers. Adding a future scheme now fails to compile in both enums until it is handled explicitly, and the reserved tags become explicit named error arms (tag 2 rejected by `PublicKeyHandle`, tag 3 by `PublicKey`). Encoding is byte-for-byte unchanged, guarded by the existing round-trip and backwards-compat tests. Addresses review feedback on near/NEPs#645 (comment).
|
Thank you to everyone who attended the Contracts Standard meeting on Wednesday! The working group members reviewed the NEP and reached the following consensus: Status: Approved #645 Meeting Recording: https://youtu.be/zdDVwdG3yzw @Wiezzel thanks for authoring this NEP! Next Steps:
If anyone is interested in revisiting this NEP, please submit a new one. |
|
@gagdiez Can I merge now? |
Summary
This draft NEP adds FIPS 204 ML-DSA-65 as a third transaction-signature scheme for NEAR, offered alongside
ed25519andsecp256k1(which are not deprecated). It documents the feature as stabilized at protocol version 85 (neard 2.13):DelegateActioninner signers.ed25519key despite a 1952-byte public key.The reference implementation lives in nearcore behind
ProtocolFeature::PostQuantumSignatures; the load-bearing design decisions are captured in the in-tree design notedocs/architecture/how/post_quantum_signatures.md.Status
Draft, opened for Subject Matter Expert review. The NEP number and file name are aligned with this PR number.