BIP Draft: FROST Signing Protocol for BIP340 Signatures #2070

pull siv2r wants to merge 2 commits into bitcoin:master from siv2r:bip-frost-signing changing 39 files +6675 −0
  1. siv2r commented at 6:00 pm on January 3, 2026: contributor

    This PR adds a BIP for the FROST (Flexible Round-Optimized Schnorr Threshold) signing protocol. The development repository is at https://github.com/siv2r/bip-frost-signing.

    There already exists RFC 9591, which standardizes the two-round FROST signing protocol, but it is incompatible with Bitcoin’s BIP340 X-only public keys. This BIP bridges that gap by providing a BIP340-compatible variant of FROST.

    This BIP standardizes the FROST3 variant (Section 2.3 of the ROAST paper). This variant shares significant similarities with the MuSig2 signing protocol (BIP327). Accordingly, this BIP follows the core design principles of BIP327, and many sections have been directly adapted from it.

    FROST key generation is out of scope for this BIP. There are sister BIPs such as ChillDKG and Trusted Dealer Generation that specify key generation mechanisms. This BIP must be used in conjunction with either of those for the full workflow from key generation to signature creation. Careful consideration has been taken to ensure the terminology in this BIP matches that of ChillDKG.

    There are multiple (experimental) implementations of this specification:

    Disclosure: AI has been used to rephrase paragraphs for clarity, refactor certain sections of the reference code, and review pull requests made to the development repository.

    Feedback is appreciated! Please comment on this pull request or open an issue at https://github.com/siv2r/bip-frost-signing for any feedback. Thank you!

    cc @jonasnick @real-or-random @jesseposner

  2. Add BIP FROST Signing for Schnorr threshold signatures b7e42f93b5
  3. siv2r commented at 6:12 pm on January 3, 2026: contributor
    I’ll fix the typos check soon
  4. siv2r commented at 6:49 pm on January 3, 2026: contributor

    I can see that GitHub’s file changes view shows only one file at a time due to the large number of changes. This is because the reference implementation includes dependencies and auxiliary materials:

    • The reference code uses secp256k1lab python library (vendored as a git subtree, ~20 files) for scalar and group arithmetic. I can remove this from the PR when the library is integrated into this repository (#1855).
    • Auxiliary files include docs/partialsig_forgery.md (which I can move to a gist if preferred) and a test vector generation script (~1400 lines). I can exclude these if necessary.
  5. murchandamus added the label New BIP on Jan 6, 2026
  6. murchandamus renamed this:
    Add BIP: FROST Signing for BIP340-compatible Threshold Signatures
    BIP Draft: FROST Signing Protocol for BIP340 Schnorr Signatures
    on Jan 6, 2026
  7. in bip-frost-signing.md:1 in b7e42f93b5
    0@@ -0,0 +1,862 @@
    1+```yaml
    


    murchandamus commented at 0:58 am on January 6, 2026:
    The “yaml” here is superfluous and will likely break CI checks.

    siv2r commented at 7:13 am on January 8, 2026:
    I added it because the syntax highlighting looked nice. Removed it.
  8. in bip-frost-signing.md:52 in b7e42f93b5
    47+  - [Negation of the Secret Share when Signing](#negation-of-the-secret-share-when-signing)
    48+    - [Negation of the Pubshare when Partially Verifying](#negation-of-the-pubshare-when-partially-verifying)
    49+  - [Dealing with Infinity in Nonce Aggregation](#dealing-with-infinity-in-nonce-aggregation)
    50+- [Backwards Compatibility](#backwards-compatibility)
    51+- [Changelog](#changelog)
    52+- [Acknowledgments](#acknowledgments)
    


    murchandamus commented at 1:01 am on January 6, 2026:
    Thanks, but it is not necessary to manually include a table of contents. Mediawiki files are automatically rendered with one on GitHub, and you can find one for markdown files here:

    siv2r commented at 7:13 am on January 8, 2026:
    Ah, I was not aware of this. Thanks!
  9. in bip-frost-signing.md:8 in b7e42f93b5
    0@@ -0,0 +1,862 @@
    1+```yaml
    2+BIP:
    3+Title: FROST Signing Protocol for BIP340 Schnorr Signatures
    4+Author: Sivaram Dhakshinamoorthy <siv2ram@gmail.com>
    5+Status: Draft
    6+License: CC0-1.0
    7+License-Code: MIT
    8+Type: Informational
    


    murchandamus commented at 1:02 am on January 6, 2026:
    This should be labeled as a Standards Track BIP

    siv2r commented at 7:14 am on January 8, 2026:
    Fixed
  10. in bip-frost-signing.md:3 in b7e42f93b5
    0@@ -0,0 +1,862 @@
    1+```yaml
    2+BIP:
    3+Title: FROST Signing Protocol for BIP340 Schnorr Signatures
    


    murchandamus commented at 1:07 am on January 6, 2026:
    Title exceeds 44 characters

    real-or-random commented at 7:37 am on January 6, 2026:
    suggestion: “FROST Signing Protocol for BIP340 Signatures” (exactly 44 characters)

    siv2r commented at 7:14 am on January 8, 2026:
    I like the suggestion. Updated the title.

    murchandamus commented at 8:12 pm on January 8, 2026:
    I also adopted it for the title of this PR. :)
  11. in bip-frost-signing.md:11 in b7e42f93b5
     6+License: CC0-1.0
     7+License-Code: MIT
     8+Type: Informational
     9+Created:
    10+Post-History: https://groups.google.com/g/bitcoindev/c/PeMp2HQl-H4/m/AcJtK0aKAwAJ
    11+Comments-URI:
    


    murchandamus commented at 1:10 am on January 6, 2026:

    The headers here are a bit out of order. They must be in a specific order (see https://bips.xyz/2#bip-header-preamble), and CI checks will fail if they aren’t.

    The order should be:

    0Comments-URI:
    1Status: Draft
    2Type: Informational
    3Created:
    4License: CC0-1.0
    5License-Code: MIT
    6Post-History: https://groups.google.com/g/bitcoindev/c/PeMp2HQl-H4/m/AcJtK0aKAwAJ
    

    siv2r commented at 7:14 am on January 8, 2026:
    Rearragned
  12. murchandamus commented at 1:14 am on January 6, 2026: contributor
    This is just a first glance, but I noticed a few issues:
  13. bip-frost-signing: fix typos, preamble, and title ec46a20323
  14. murchandamus renamed this:
    BIP Draft: FROST Signing Protocol for BIP340 Schnorr Signatures
    BIP Draft: FROST Signing Protocol for BIP340 Signatures
    on Jan 8, 2026
  15. in bip-frost-signing.md:8 in ec46a20323
    0@@ -0,0 +1,822 @@
    1+```
    2+BIP: ?
    3+Title: FROST Signing Protocol for BIP340 Signatures
    4+Author: Sivaram Dhakshinamoorthy <siv2ram@gmail.com>
    5+Comments-URI:
    6+Status: Draft
    7+Type: Standards Track
    8+Assigned: ?
    


    murchandamus commented at 8:14 pm on January 8, 2026:

    As currently BIP 2 still regulates the BIP Process, this should still be Created. (Looking forward to BIP 3 activating any year now. ;))

    0Created: ?
    

    siv2r commented at 1:27 pm on January 9, 2026:
    Okay, I’ll revert it back.
  16. murchandamus commented at 8:17 pm on January 8, 2026: contributor
    Thanks for the quick turn-around. It’s on my todo list to give this a more thorough look, but it might take a bit. If you can motivate some other reviewers meanwhile, that would also be welcome.
  17. siv2r commented at 1:30 pm on January 9, 2026: contributor

    If you can motivate some other reviewers meanwhile, that would also be welcome.

    I’ve shared it with most of the Bitcoin cryptographers I know and will post it on Twitter and the Bitcoin dev groups I’m part of. Hopefully that will bring in more reviewers!

  18. in bip-frost-signing.md:23 in ec46a20323
    18+## Copyright
    19+
    20+This document is made available under [CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/).
    21+The accompanying source code is licensed under the [MIT license](https://opensource.org/license/mit).
    22+
    23+## Motivation
    


    DarkWindman commented at 10:36 am on January 14, 2026:
    The first sections of the BIP use code-style formatting for variables, which is convenient in Markdown and improves readability. However, the following sections, starting with General Signing Flow, do not follow this convention, resulting in an inconsistent style. We suggest adopting a single formatting style throughout the document.
  19. in bip-frost-signing.md:99 in ec46a20323
    94+
    95+The output of the FROST signing protocol is a BIP340 Schnorr signature that verifies under the *threshold public key* as if it were produced by a single signer using the *threshold secret key*.
    96+
    97+### General Signing Flow
    98+
    99+We assume that the coordinator and the signing participants (in the algorithms specified below, is stored in a data structure called [Signers Context](#signers-context)) are selected externally to the signing protocol before it is initiated. They could also optionally tweak the *threshold public key* now, by initializing [Tweak Context](#tweak-context) with it.
    


    DarkWindman commented at 10:42 am on January 14, 2026:
    The first two paragraphs of the General Signing Flow section are repetitive. We suggest keeping the second paragraph, as it is easier to follow.
  20. in bip-frost-signing.md:261 in ec46a20323
    256+| *cbytes(P)* | *P.to_bytes_compressed()* | Returns the 33-byte compressed serialization of a non-infinity point *P* |
    257+| *cbytes_ext(P)* | *P.to_bytes_compressed<br>_with_infinity()* | Returns the 33-byte compressed serialization of a point *P*. If *P* is the point at infinity, it is encoded as a 33-byte array of zeros. |
    258+| *lift_x(x)*[^liftx-soln] | *GE.lift_x(x)* | Decodes a 32-byte x-only serialization *x* into a non-infinity point P. The resulting point always has an even y-coordinate. |
    259+| *cpoint(b)* | *GE.from_bytes_compressed(b)* | Decodes a 33-byte compressed serialization *b* into a non-infinity point |
    260+| *cpoint_ext(b)* | *GE.from_bytes_compressed<br>_with_infinity(b)* | Decodes a 33-byte compressed serialization *b* into a point. If *b* is a 33-byte array of zeros, it returns the point at infinity |
    261+| *scalar_from_bytes_checked(b)* | *Scalar.from_bytes_checked(b)* | Deserializes a 32-byte array *b* to a scalar, fails if the value is ≥ *order* |
    


    DarkWindman commented at 10:44 am on January 14, 2026:
    It is better to reorder the table to place the scalar to_bytes functions before the from_bytes functions, in order to be consistent with the order of the point transformation functions.
  21. in bip-frost-signing.md:289 in ec46a20323
    284+| *has_duplicates(lst)* | Returns *True* if any element in *lst* appears more than once, *False* otherwise |
    285+| *sorted(lst)* | Returns a new list containing the elements of *lst* arranged in ascending order |
    286+| *(a, b, ...)* | Refers to a tuple containing the listed elements |
    287+
    288+> [!NOTE]
    289+> In the following algorithms, all scalar arithmetic is understood to be modulo the group order. For example, *a &middot; b* implicitly means *a &middot; b mod order*
    


    DarkWindman commented at 10:50 am on January 14, 2026:
    This note duplicates the footnote 8 and used twice in the text. We think, it would be simpler to just add modulo n = order wherever scalar arithmetic is used.
  22. in bip-frost-signing.md:449 in ec46a20323
    444+  - The number *u* of signing participants: an integer with *t ≤ u ≤ n*
    445+  - The list of participant public nonces *pubnonce<sub>1..u</sub>*: *u* 66-byte array, each an output of *NonceGen*
    446+  - The list of participant identifiers *id<sub>1..u</sub>*: *u* integers, each with 0 ≤ *id<sub>i</sub>* < *n*
    447+- For *j = 1 .. 2*:
    448+  - For *i = 1 .. u*:
    449+    - Let *R<sub>i,j</sub> = cpoint(pubnonce<sub>i</sub>[(j-1)*33:j*33])*; fail if that fails and blame signer *id<sub>i</sub>* for invalid *pubnonce*
    


    DarkWindman commented at 11:02 am on January 14, 2026:
    The multiplication operator * is not displayed in the compiled version when calculating pubnonce array indices.
  23. in bip-frost-signing.md:509 in ec46a20323
    504+- Let *(Q, gacc, _, id<sub>1..u</sub>, pubshare<sub>1..u</sub>, b, R, e) = GetSessionValues(session_ctx)*; fail if that fails
    505+- Let *k<sub>1</sub>' = scalar_from_bytes_nonzero_checked(secnonce[0:32])*; fail if that fails
    506+- Let *k<sub>2</sub>' = scalar_from_bytes_nonzero_checked(secnonce[32:64])*; fail if that fails
    507+- Let *k<sub>1</sub> = k<sub>1</sub>', k<sub>2</sub> = k<sub>2</sub>'* if *has_even_y(R)*, otherwise let *k<sub>1</sub> = -k<sub>1</sub>', k<sub>2</sub> = -k<sub>2</sub>'*
    508+- Let *d' = scalar_from_bytes_nonzero_checked(secshare)*; fail if that fails
    509+- Let *P = d' &middot; G*
    


    DarkWindman commented at 11:14 am on January 14, 2026:
    The computed value P is only used in the subsequent line. It may be clearer to just write pubshare = cbytes(d′·G), similar to the DeterministicSign algorithm.
  24. in bip-frost-signing.md:526 in ec46a20323
    521+
    522+[^why-verify-partialsig]: Verifying the signature before leaving the signer prevents random or adversarially provoked computation errors. This prevents publishing invalid signatures which may leak information about the secret share. It is recommended but can be omitted if the computation cost is prohibitive.
    523+
    524+### Partial Signature Verification
    525+
    526+Algorithm *PartialSigVerify(psig, pubnonce<sub>1..u</sub>, signers_ctx, tweak<sub>1..v</sub>, is_xonly_t<sub>1..v</sub>, m, i)*:
    


    DarkWindman commented at 11:16 am on January 14, 2026:
    Missing the v argument, which is listed as an input parameter below.
  25. in bip-frost-signing.md:537 in ec46a20323
    532+  - The number *v* of tweaks with *0 ≤ v < 2^32*
    533+  - The list of tweaks *tweak<sub>1..v</sub>*: *v* 32-byte arrays, each a serialized scalar
    534+  - The list of tweak modes *is_xonly_t<sub>1..v</sub>* : *v* booleans
    535+  - The message *m*: a byte array[^max-msg-len]
    536+  - The index *i* of the signer in the list of public nonces where *0 < i ≤ u*
    537+- Let *(_, _, u, id<sub>1..u</sub>, pubshare<sub>1..u</sub>, thresh_pk) = signers_ctx*
    


    DarkWindman commented at 11:19 am on January 14, 2026:
    thresh_pk is never used and should be replaced with a wildcard.
  26. in bip-frost-signing.md:577 in ec46a20323
    572+  - Let *s<sub>i</sub> = scalar_from_bytes_nonzero_checked(psig<sub>i</sub>)*; fail if that fails and blame signer *id<sub>i</sub>* for invalid partial signature.
    573+- Let *g = Scalar(1)* if *has_even_y(Q)*, otherwise let *g = Scalar(-1)*
    574+- Let *s = s<sub>1</sub> + ... + s<sub>u</sub> + e &middot; g &middot; tacc*
    575+- Return *sig = xbytes(R) || scalar_to_bytes(s)*
    576+
    577+### Test Vectors & Reference Code
    


    DarkWindman commented at 11:24 am on January 14, 2026:
    Links to the code and test vectors do not work.
  27. in bip-frost-signing.md:774 in ec46a20323
    769+Therefore, signing continues so that the culprit is revealed when collecting and verifying partial signatures.
    770+
    771+However, the final nonce *R* of a BIP340 Schnorr signature cannot be the point at infinity.
    772+If we would nonetheless allow the final nonce to be the point at infinity, then the scheme would lose the following property:
    773+if *PartialSigVerify* succeeds for all partial signatures, then *PartialSigAgg* will return a valid Schnorr signature.
    774+Since this is a valuable feature, we modify [FROST3 signing][roast] to avoid producing an invalid Schnorr signature while still allowing detection of the dishonest signer: In *GetSessionValues*, if the final nonce *R* would be the point at infinity, set it to the generator instead (an arbitrary choice).
    


    DarkWindman commented at 11:29 am on January 14, 2026:
    It might be better to also mention ROAST in the Motivation section when discussing FROST3.
  28. in bip-frost-signing.md:80 in ec46a20323
    75+
    76+If there is no dedicated coordinator, one of the participants can act as the coordinator.
    77+
    78+#### Signing Inputs and Outputs
    79+
    80+Each signing session requires two inputs: a participant's long-term *secret share* `secshare_i` (individual to each participant, not shared with the coordinator) and a [*Signers Context*](#signers-context)[^signers-ctx-struct] data structure (common to all participants and the coordinator).
    


    DarkWindman commented at 11:55 am on January 14, 2026:
    The Signers Context link is formatted in italics, deviating from the style in the rest of the specification. To maintain a consistent style, italics formatting should be removed from links that do not represent specific functions. Similar issues appear in other sections: Key Material and Setup: Signers Context (line:80) General Signing Flow: Signers Context, Tweak Context (line:102) Signing: See Negation of Secret Share When Signing (line:515) Partial Signature Verification: See Negation of Pubshare When Partially Verifying (line:555)
  29. in bip-frost-signing.md:66 in ec46a20323
    61+This ensures that any subset of at least `t` participants can jointly run the FROST signing protocol to produce a signature under the *threshold secret key*.
    62+
    63+Key generation for FROST signing is out of scope for this document. Implementations can use either a trusted dealer setup, as specified in [Appendix C of RFC 9591](https://www.rfc-editor.org/rfc/rfc9591.html#name-trusted-dealer-key-generati), or a distributed key generation (DKG) protocol such as [ChillDKG](https://github.com/BlockstreamResearch/bip-frost-dkg). The appropriate choice depends on the implementations's trust model and operational requirements.
    64+
    65+This protocol distinguishes between two public key formats: *plain public keys* are 33-byte compressed public keys traditionally used in Bitcoin, while *X-only public keys* are 32-byte keys defined in [BIP340][bip340].
    66+Key generation protocols produce *public shares* and *threshold public keys* in the plain format. During signing, we conditionally negates *secret shares* to ensure the resulting threshold-signature verifies under the corresponding *X-only threshold public key*.
    


    DarkWindman commented at 11:59 am on January 14, 2026:
    Minor typo: negates → negate
  30. in bip-frost-signing.md:63 in ec46a20323
    58+<!-- REVIEW: should we use "identifiers `i`", secret share `secshare_i` style here? -->
    59+A FROST key generation protocol configures a group of `n` participants with a *threshold public key* (representing a `t`-of-`n` threshold policy).
    60+The corresponding *threshold secret key* is Shamir secret-shared among all `n` participants, where each participant holds a distinct long-term *secret share*.
    61+This ensures that any subset of at least `t` participants can jointly run the FROST signing protocol to produce a signature under the *threshold secret key*.
    62+
    63+Key generation for FROST signing is out of scope for this document. Implementations can use either a trusted dealer setup, as specified in [Appendix C of RFC 9591](https://www.rfc-editor.org/rfc/rfc9591.html#name-trusted-dealer-key-generati), or a distributed key generation (DKG) protocol such as [ChillDKG](https://github.com/BlockstreamResearch/bip-frost-dkg). The appropriate choice depends on the implementations's trust model and operational requirements.
    


    DarkWindman commented at 12:00 pm on January 14, 2026:
    Minor typo: implementations’s → implementation’s
  31. DarkWindman commented at 1:05 pm on January 14, 2026: none
    Hi! Quite a remarkable job! We found a few minor issues, and correcting them would improve the overall specification of the BIP.

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bips. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-01-16 16:10 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me