diff --git a/rendered/zip-0312.html b/rendered/zip-0312.html index 6471e4b61..c91146c34 100644 --- a/rendered/zip-0312.html +++ b/rendered/zip-0312.html @@ -12,11 +12,11 @@
ZIP: 312
 Title: FROST for Spend Authorization Multisignatures
 Owners: Conrado Gouvea <conrado@zfnd.org>
-        Chelsea Komlo <ckomlo@uwaterloo.ca>
-        Deirdre Connolly <deirdre@zfnd.org>
+Original-Authors: Chelsea Komlo
+                  Deirdre Connolly
 Status: Draft
 Category: Wallet
-Created: 2022-08-dd
+Created: 2023-01-12
 License: MIT
 Discussions-To: <https://github.com/zcash/zips/issues/382>
 Pull-Request: <https://github.com/zcash/zips/pull/662>
@@ -39,7 +39,7 @@

Requirements

Threat Model

@@ -49,7 +49,7 @@
  • The transaction is signed with a re-randomized version of the user's spend authorization private key.
  • The zero-knowledge proof for the transaction is created with the randomizer as an auxiliary (secret) input, among others.
  • -

    When employing re-randomizable FROST as specified in this ZIP, the goal is to split the spend authorization private key +

    When employing re-randomized FROST as specified in this ZIP, the goal is to split the spend authorization private key \(\mathsf{ask}\) among multiple possible signers. This means that the proof generation will still be performed by a single participant, likely the one that created the transaction in the first place. Note that this user already controls the privacy of the transaction since they are responsible for creating the proof.

    This fits well into the "Coordinator" role from the FROST specification 5. The Coordinator is responsible for sending the message to be signed to all participants, and to aggregate the signature shares.

    @@ -75,11 +75,10 @@ with the group implied by \(P\!\) .

    -

    An additional per-ciphersuite hash function is used, denote HR(m), which receives an arbitrary-sized byte string and returns a Scalar. It is defined concretely in the Ciphersuites section.

    Key Generation

    While key generation is out of scope for this ZIP and the FROST spec 3, it needs to be consistent with FROST, see 9 for guidance. The spend authorization private key \(\mathsf{ask}\) - 14 is the particular key that must be used in the context of this ZIP. Note that the + 17 is the particular key that must be used in the context of this ZIP. Note that the \(\mathsf{ask}\) is usually derived from the spending key \(\mathsf{sk}\!\) @@ -89,15 +88,18 @@ \(\mathsf{sk}\) prevents using seed phrases to recover the original secret (which may be something desirable in the context of FROST).

    -

    Re-randomizable FROST

    -

    To add re-randomization to FROST, follow the specification 3 with the following modifications.

    -

    Randomizer Generation

    -

    A new helper function is defined, which generates a randomizer. The encode_signing_package is defined as the byte serialization of the msg, commitment_list values as described in 11. Implementations MAY choose another encoding as long as all values (the message, and the identifier, binding nonce and hiding nonce for each participant) are unambiguously encoded.

    -

    The function random_bytes(n) is defined in 3 and it returns a buffer with n bytes sampled uniformly at random. The constant Ns is also specified in 3 and is the size of a serialized scalar.

    -
    randomizer_generate():
    +            

    Randomizer Generation

    +

    Re-Randomized FROST uses randomizers. This section specifies how they are generated; this will be required for the Signature Share Generation specification below.

    +

    Two functions are provided to generate randomizers: randomizer_generate() and randomizer_regenerate(). Both use helper functions and a constant which are defined as follows:

    +
      +
    • The encode_group_commitment_list() function is defined in 12. It returns a byte serialization of a commitment_list value.
    • +
    • The random_bytes(n) function is defined in 13 and it returns a buffer with n bytes sampled uniformly at random.
    • +
    • The Ns constant is define in 7 and is the size of a serialized scalar.
    • +
    • The H2(m) function is a ciphersuite-generic function defined in 14 but it is instantiated in the Ciphersuites section.
    • +
    +
    randomizer_generate():
     
     Inputs:
    -- msg, the message being signed in the current FROST signing run
     - commitment_list = [(i, hiding_nonce_commitment_i,
       binding_nonce_commitment_i), ...], a list of commitments issued by
       each participant, where each element in the list indicates a
    @@ -105,17 +107,36 @@
       (hiding_nonce_commitment_i, binding_nonce_commitment_i). This list
       MUST be sorted in ascending order by identifier.
     
    -Outputs: randomizer, a Scalar
    +Outputs: (randomizer_seed, randomizer), a byte buffer and a Scalar
     
    -def randomizer_generate(msg, commitment_list):
    +def randomizer_generate(commitment_list):
       # Generate a random byte buffer with the size of a serialized scalar
    -  rng_randomizer = random_bytes(Ns)
    -  signing_package_enc = encode_signing_package(commitment_list, msg)
    -  randomizer_input = rng_randomizer || signing_package_enc
    -  return HR(randomizer_input)
    -
    + randomizer_seed = random_bytes(Ns) + signing_commitments_enc = encode_group_commitment_list(commitment_list) + randomizer_input = randomizer_seed || signing_commitments_enc + return (randomizer_seed, H2(randomizer_input))
    +
    randomizer_regenerate():
    +
    +Inputs:
    +- randomizer_seed = a byte buffer with Ns bytes
    +- commitment_list = [(i, hiding_nonce_commitment_i,
    +  binding_nonce_commitment_i), ...], a list of commitments issued by
    +  each participant, where each element in the list indicates a
    +  NonZeroScalar identifier i and two commitment Element values
    +  (hiding_nonce_commitment_i, binding_nonce_commitment_i). This list
    +  MUST be sorted in ascending order by identifier.
    +
    +Outputs: randomizer, a Scalar
    +
    +def randomizer_regenerate(randomizer_seed, commitment_list):
    +  signing_commitments_enc = encode_group_commitment_list(commitment_list)
    +  randomizer_input = randomizer_seed || signing_commitments_enc
    +  return H2(randomizer_input)
    +
    +

    Re-Randomized FROST

    +

    To add re-randomization to FROST, follow the specification 3 with the following modifications.

    Round One - Commitment

    -

    Roune One is exactly the same as specified 3. But for context, it involves these steps:

    +

    Round One is exactly the same as specified 3. But for context, it involves these steps:

    • Each signer generates nonces and their corresponding public commitments. A nonce is a pair of Scalar values, and a commitment is a pair of Element values.
    • The nonces are stored locally by the signer and kept private for use in the second round.
    • @@ -123,20 +144,20 @@

    Round Two - Signature Share Generation

    -

    In Round Two, the Coordinator generates a random scalar randomizer by calling randomizer_generate and sends it to each signer, over a confidential and authenticated channel, along with the message and the set of signing commitments. (Note that this differs from regular FROST which just requires an authenticated channel.)

    +

    In Round Two of regular FROST, the Coordinator picks a message and sends it to each signer along with the commitments received in Round One. In Re-Randomized FROST, the Coordinator MUST additionally call randomizer_generate() and send the randomizer_seed along with the message and the commitments. Each participant, upon receiving those values, MUST compute randomizer by calling randomizer_regenerate() with the received randomizer_seed and commitments.

    In Zcash, the message that needs to be signed is actually the SIGHASH transaction hash, which does not convey enough information for the signers to decide if they want to authorize the transaction or not. Therefore, in practice, more data is needed to be sent (over the same encrypted, authenticated channel) from the Coordinator to the signers, possibly the transaction itself, openings of value commitments, decryption of note ciphertexts, etc.; and the signers MUST check that the given SIGHASH matches the data sent from the Coordinator, or compute the SIGHASH themselves from that data. However, the specific mechanism for that process is outside the scope of this ZIP.

    -

    The randomized sign function is defined as the regular FROST sign function, but with its inputs modified relative to the randomizer as following:

    +

    The randomized sign() function is defined as the regular FROST sign() function, but its inputs modified relative to the randomizer (which is computed with randomizer_regenerate()).

    • sk_i = sk_i + randomizer
    • group_public_key = group_public_key + G.ScalarBaseMult(randomizer)

    Signature Share Verification and Aggregation

    -

    The randomized aggregate function is defined as the regular FROST aggregate function, but with its inputs modified relative to the randomizer as following:

    +

    The randomized aggregate() function is defined as the regular FROST aggregate() function, but with its inputs modified relative to the randomizer as following:

    • group_public_key = group_public_key + G.ScalarBaseMult(randomizer)
    -

    The randomized verify_signature_share function is defined as the regular FROST verify_signature_share function, but with its inputs modified relative to the randomizer as following:

    +

    The randomized verify_signature_share() function is defined as the regular FROST verify_signature_share() function, but with its inputs modified relative to the randomizer as following:

    • PK_i = PK_i + G.ScalarBaseMult(randomizer)
    • group_public_key = group_public_key + G.ScalarBaseMult(randomizer)
    • @@ -145,37 +166,37 @@

    Ciphersuites

    FROST(Jubjub, BLAKE2b-512)

    -

    This ciphersuite uses Jubjub for the Group and BLAKE2b-512 for the Hash function H meant to produce signatures indistinguishable from RedJubjub Sapling Spend Authorization Signatures as specified in 13.

    +

    This ciphersuite uses Jubjub for the Group and BLAKE2b-512 for the Hash function H meant to produce signatures indistinguishable from RedJubjub Sapling Spend Authorization Signatures as specified in 16.

      -
    • Group: Jubjub 15 with base point +
    • Group: Jubjub 18 with base point \(\mathcal{G}^{\mathsf{Sapling}}\) - as defined in 13. + as defined in 16.
      • Order: \(r_\mathbb{J}\) - as defined in 15.
      • -
      • Identity: as defined in 15.
      • + as defined in 18. +
      • Identity: as defined in 18.
      • RandomScalar(): Implemented by returning a uniformly random Scalar in the range [0, G.Order() - 1]. Refer to {{frost-randomscalar}} for implementation guidance.
      • SerializeElement(P): Implemented as \(\mathsf{repr}_\mathbb{J}(P)\) - as defined in 15
      • + as defined in 18
      • DeserializeElement(P): Implemented as \(\mathsf{abst}_\mathbb{J}(P)\) - as defined in 15, returning an error if + as defined in 18, returning an error if \(\bot\) is returned. Additionally, this function validates that the resulting element is not the group identity element, returning an error if the check fails.
      • SerializeScalar: Implemented by outputting the little-endian 32-byte encoding of the Scalar value.
      • DeserializeScalar: Implemented by attempting to deserialize a Scalar from a little-endian 32-byte string. This function can fail if the input does not represent a Scalar in the range [0, G.Order() - 1].
    • -
    • Hash (H): BLAKE2b-512 1 (BLAKE2b with 512-bit output and 16-byte personalization string), and Nh = 64. +
    • Hash (H): BLAKE2b-512 1 (BLAKE2b with 512-bit output and 16-byte personalization string), and Nh = 64.
      • H1(m): Implemented by computing BLAKE2b-512("FROST_RedJubjubR", m), interpreting the 64 bytes as a little-endian integer, and reducing the resulting integer modulo G.Order().
      • H2(m): Implemented by computing BLAKE2b-512("Zcash_RedJubjubH", m), interpreting the 64 bytes as a little-endian integer, and reducing the resulting integer modulo G.Order(). (This is equivalent to \(\mathsf{H}^\circledast(m)\!\) , as defined by the \(\mathsf{RedJubjub}\) - scheme instantiated in 12.)
      • + scheme instantiated in 15.)
      • H3(m): Implemented by computing BLAKE2b-512("FROST_RedJubjubN", m), interpreting the 64 bytes as a little-endian integer, and reducing the resulting integer modulo G.Order().
      • H4(m): Implemented by computing BLAKE2b-512("FROST_RedJubjubM", m).
      • H5(m): Implemented by computing BLAKE2b-512("FROST_RedJubjubC", m).
      • @@ -183,40 +204,40 @@
    -

    Signature verification is as specified in 13 for RedJubjub.

    +

    Signature verification is as specified in 16 for RedJubjub.

    FROST(Pallas, BLAKE2b-512)

    -

    This ciphersuite uses Pallas for the Group and BLAKE2b-512 for the Hash function H meant to produce signatures indistinguishable from RedPallas Orchard Spend Authorization Signatures as specified in 13.

    +

    This ciphersuite uses Pallas for the Group and BLAKE2b-512 for the Hash function H meant to produce signatures indistinguishable from RedPallas Orchard Spend Authorization Signatures as specified in 16.

      -
    • Group: Pallas 16 with base point +
    • Group: Pallas 19 with base point \(\mathcal{G}^{\mathsf{Orchard}}\) - as defined in 13. + as defined in 16.
      • Order: \(r_\mathbb{P}\) - as defined in 16.
      • -
      • Identity: as defined in 16.
      • + as defined in 19. +
      • Identity: as defined in 19.
      • RandomScalar(): Implemented by returning a uniformly random Scalar in the range [0, G.Order() - 1]. Refer to {{frost-randomscalar}} for implementation guidance.
      • SerializeElement(P): Implemented as \(\mathsf{repr}_\mathbb{P}(P)\) - as defined in 16.
      • + as defined in 19.
      • DeserializeElement(P): Implemented as \(\mathsf{abst}_\mathbb{P}(P)\) - as defined in 16, failing if + as defined in 19, failing if \(\bot\) is returned. Additionally, this function validates that the resulting element is not the group identity element, returning an error if the check fails.
      • SerializeScalar: Implemented by outputting the little-endian 32-byte encoding of the Scalar value.
      • DeserializeScalar: Implemented by attempting to deserialize a Scalar from a little-endian 32-byte string. This function can fail if the input does not represent a Scalar in the range [0, G.Order() - 1].
    • -
    • Hash (H): BLAKE2b-512 1 (BLAKE2b with 512-bit output and 16-byte personalization string), and Nh = 64. +
    • Hash (H): BLAKE2b-512 1 (BLAKE2b with 512-bit output and 16-byte personalization string), and Nh = 64.
      • H1(m): Implemented by computing BLAKE2b-512("FROST_RedPallasR", m), interpreting the 64 bytes as a little-endian integer, and reducing the resulting integer modulo G.Order().
      • H2(m): Implemented by computing BLAKE2b-512("Zcash_RedPallasH", m), interpreting the 64 bytes as a little-endian integer, and reducing the resulting integer modulo G.Order(). (This is equivalent to \(\mathsf{H}^\circledast(m)\!\) , as defined by the \(\mathsf{RedPallas}\) - scheme instantiated in 12.)
      • + scheme instantiated in 15.)
      • H3(m): Implemented by computing BLAKE2b-512("FROST_RedPallasN", m), interpreting the 64 bytes as a little-endian integer, and reducing the resulting integer modulo G.Order().
      • H4(m): Implemented by computing BLAKE2b-512("FROST_RedPallasM", m).
      • H5(m): Implemented by computing BLAKE2b-512("FROST_RedPallasC", m).
      • @@ -224,14 +245,14 @@
    -

    Signature verification is as specified in 13 for RedPallas.

    +

    Signature verification is as specified in 16 for RedPallas.

    Rationale

    FROST is a threshold Schnorr signature scheme, and Zcash Spend Authorization are also Schnorr signatures, which allows the usage of FROST with Zcash. However, since there is no widespread standard for Schnorr signatures, it must be ensured that the signatures generated by the FROST variant specified in this ZIP can be verified successfully by a Zcash implementation following its specification. In practice this entails making sure that the generated signature can be verified by the \(\mathsf{RedDSA.Validate}\) - function specified in 12:

    + function specified in 15:

    • The FROST signature, when split into R and S in the first step of \(\mathsf{RedDSA.Validate}\!\) @@ -243,23 +264,16 @@ Zcash function in the ciphersuites, and making sure its input will be the same. Fortunately FROST and Zcash use the same input order (R, public key, message) so we just need to make sure that SerializeElement (used to compute the encoded public key before passing to the hash function) matches what \(\mathsf{RedDSA.Validate}\) expects; which is possible since both R and vk (the public key) are encoded in the same way as in Zcash.
    • -
    • Note that r (and thus R) will not be generated as specified in RedDSA.Sign. This is not an issue however, since with Schnorr signatures it does not matter for the verifier how the r value was chosen, it just needs to be generated uniformly at random, which is true for FROST.
    • +
    • Note that r (and thus R) will not be generated as specified in RedDSA.Sign. This is not an issue however, since with Schnorr signatures it does not matter for the verifier how the r value was chosen, it just needs to be a uniformly distributed random element, which is true for FROST.
    • The above will ensure that the verification equation in \(\mathsf{RedDSA.Validate}\) - will pass, since FROST ensures the exact same equation will be valid as described in 8.
    • + will pass, since FROST ensures the exact same equation will be valid as described in 8.

    The second step is adding the re-randomization functionality so that each FROST signing generates a re-randomized signature:

    • Anywhere the public key is used, the randomized public key must be used instead. This is exactly what is done in the functions defined above.
    • -
    • The re-randomization must be done in each signature share generation, such that the aggregated signature must be valid under verification with the randomized public key. The R value from the signature is not influenced by the randomizer so we just need to focus on the z value (using FROST notation). Recall that z must equal to r + (c * sk), and that each signature share is z_i = (hiding_nonce + (binding_nonce * binding_factor)) + -(lambda_i * c * sk_i). The first terms are not influenced by the randomizer so we can only look into the second term of each top-level addition, i.e. c -* sk must be equal to sum(lambda_i * c * sk_i) for each participant i. Under re-randomization these become c * (sk + randomizer) (see - \(\mathsf{RedDSA.RandomizedPrivate}\!\) - , which refers to the randomizer as - \(\alpha\!\) - ) and sum(lambda_i * c * (sk_i + randomizer)). The latter can be rewritten as c * (sum(lambda_i * sk_i) + randomizer * -sum(lambda_i). Since sum(lambda_i * sk_i) == sk per the Shamir secret sharing mechanism used by FROST, and since sum(lambda_i) == 1 18, we arrive at c * (sk + randomizer) as required.
    • -
    • The re-randomization procedure must be exactly the same as in 12 to ensure that re-randomized keys are uniformly distributed and signatures are unlinkable. This is also true; observe that randomizer_generate generates randomizer uniformly at random as required by +
    • The re-randomization must be done in each signature share generation, such that the aggregated signature must be valid under verification with the randomized public key. This is shown to be true in 4.
    • +
    • The re-randomization procedure must be exactly the same as in 15 to ensure that re-randomized keys are uniformly distributed and signatures are unlinkable. This is also true; observe that randomizer_generate generates a uniformly distributed random scalar as required by \(\mathsf{RedDSA.GenRandom}\!\) ; and signature generation is compatible with \(\mathsf{RedDSA.RandomizedPrivate}\!\) @@ -271,10 +285,19 @@ \(\mathsf{RedDSA.Validate}\) as explained in the previous item.
    -

    The security of Re-Randomized FROST with respect to the security assumptions of regular FROST is shown in 4.

    +

    The security of Re-Randomized FROST with respect to the security assumptions of regular FROST is shown in 4.

    +

    Regarding randomizer handling, in Zcash, the randomizer is called + \(\mathsf{alpha}\) + and is usually generated using the RedDSA.GenRandom function as defined in the Zcash specification 17. Note that the choice of + \(\mathsf{alpha}\) + influences the SIGHASH computation, so it is impossible to compute the randomizer based on the message (SIGHASH), as suggested in 4. This is not an issue as long the randomizer is generated with the same security properties as RedDSA.GenRandom. We ensure that by using a very similar approach; while the original RedDSA.GenRandom uses + \(\mathsf{H}^\circledast(T)\) + where T is a random byte buffer with a certain size, in this ZIP we effectively use + \(\mathsf{H}^\circledast(T || \mathsf{signing\_commitments\_enc})\) + , i.e. we concatenate the random bytes with the encoding of the signing commitments. This preserves the security assumptions and also hedges against issues in the Coordinator random byte generator and prevents the Coordinator from fully influencing the randomizer, reducing its trust assumptions.

    Reference implementation

    -

    The reddsa crate 17 contains a re-randomized FROST implementation of both ciphersuites.

    +

    The reddsa crate 20 contains a re-randomized FROST implementation of both ciphersuites.

    References

    @@ -305,7 +328,7 @@ - +
    4Re-Randomized FROSTRe-Randomized FROST (ePrint 2024/436)
    @@ -365,10 +388,34 @@ - +
    + + + +
    12RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Section 4.3: List Operations
    + + + + + + + +
    13RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Section 2: Conventions and Definitions
    + + + + + + + +
    14RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Section 3.2: Cryptographic Hash Function
    + + + + @@ -376,7 +423,7 @@
    15 Zcash Protocol Specification, Version 2022.3.4 [NU5]. Section 5.4.7: RedDSA, RedJubjub, and RedPallas
    - + @@ -384,7 +431,7 @@
    1316 Zcash Protocol Specification, Version 2022.3.4 [NU5]. Section 5.4.7.1: Spend Authorization Signature (Sapling and Orchard)
    - + @@ -392,7 +439,7 @@
    1417 Zcash Protocol Specification, Version 2022.3.4 [NU5]. Section 4.15: Spend Authorization Signature (Sapling and Orchard)
    - + @@ -400,7 +447,7 @@
    1518 Zcash Protocol Specification, Version 2022.3.4 [NU5]. Section 5.4.9.3: Jubjub
    - + @@ -408,7 +455,7 @@
    1619 Zcash Protocol Specification, Version 2022.3.4 [NU5]. Section 5.4.9.6: Pallas and Vesta
    - + @@ -416,7 +463,7 @@
    1720 reddsa
    - + diff --git a/zips/zip-0312.rst b/zips/zip-0312.rst index 05c4619f5..886572513 100644 --- a/zips/zip-0312.rst +++ b/zips/zip-0312.rst @@ -3,11 +3,11 @@ ZIP: 312 Title: FROST for Spend Authorization Multisignatures Owners: Conrado Gouvea - Chelsea Komlo - Deirdre Connolly + Original-Authors: Chelsea Komlo + Deirdre Connolly Status: Draft Category: Wallet - Created: 2022-08-dd + Created: 2023-01-12 License: MIT Discussions-To: Pull-Request: @@ -86,7 +86,7 @@ shielded transaction: - The zero-knowledge proof for the transaction is created with the randomizer as an auxiliary (secret) input, among others. -When employing re-randomizable FROST as specified in this ZIP, the goal is to +When employing re-randomized FROST as specified in this ZIP, the goal is to split the spend authorization private key $\mathsf{ask}$ among multiple possible signers. This means that the proof generation will still be performed by a single participant, likely the one that created the transaction in the first @@ -133,10 +133,6 @@ Specification. For example, ``G.ScalarMult(P, k)`` is used for scalar multiplication, where the protocol spec would use $[k] P$ with the group implied by $P$. -An additional per-ciphersuite hash function is used, denote ``HR(m)``, which -receives an arbitrary-sized byte string and returns a Scalar. It is defined -concretely in the Ciphersuites section. - Key Generation -------------- @@ -153,33 +149,54 @@ using seed phrases to recover the original secret (which may be something desirable in the context of FROST). -Re-randomizable FROST +Randomizer Generation --------------------- -To add re-randomization to FROST, follow the specification [#FROST]_ with the -following modifications. +Re-Randomized FROST uses randomizers. This section specifies how they are +generated; this will be required for the Signature Share Generation +specification below. +Two functions are provided to generate randomizers: `randomizer_generate()` and +`randomizer_regenerate()`. Both use helper functions and a constant which are +defined as follows: -Randomizer Generation -''''''''''''''''''''' +- The `encode_group_commitment_list()` function is defined in + [#frost-listoperations]_. It returns a byte serialization of a + `commitment_list` value. +- The `random_bytes(n)` function is defined in [#frost-conventions]_ and it + returns a buffer with `n` bytes sampled uniformly at random. +- The `Ns` constant is define in [#frost-primeordergroup]_ and is the size of a + serialized scalar. +- The `H2(m)` function is a ciphersuite-generic function defined in + [#frost-hash]_ but it is instantiated in the Ciphersuites section. -A new helper function is defined, which generates a randomizer. The -`encode_signing_package` is defined as the byte serialization of the `msg`, -`commitment_list` values as described in [#frost-serialization]_. -Implementations MAY choose another encoding as long as all values (the message, -and the identifier, binding nonce and hiding nonce for each participant) are -unambiguously encoded. +:: -The function `random_bytes(n)` is defined in [#FROST]_ and it returns a buffer -with `n` bytes sampled uniformly at random. The constant `Ns` is also specified -in [#FROST]_ and is the size of a serialized scalar. + randomizer_generate(): + + Inputs: + - commitment_list = [(i, hiding_nonce_commitment_i, + binding_nonce_commitment_i), ...], a list of commitments issued by + each participant, where each element in the list indicates a + NonZeroScalar identifier i and two commitment Element values + (hiding_nonce_commitment_i, binding_nonce_commitment_i). This list + MUST be sorted in ascending order by identifier. + + Outputs: (randomizer_seed, randomizer), a byte buffer and a Scalar + + def randomizer_generate(commitment_list): + # Generate a random byte buffer with the size of a serialized scalar + randomizer_seed = random_bytes(Ns) + signing_commitments_enc = encode_group_commitment_list(commitment_list) + randomizer_input = randomizer_seed || signing_commitments_enc + return (randomizer_seed, H2(randomizer_input)) :: - randomizer_generate(): + randomizer_regenerate(): Inputs: - - msg, the message being signed in the current FROST signing run + - randomizer_seed = a byte buffer with Ns bytes - commitment_list = [(i, hiding_nonce_commitment_i, binding_nonce_commitment_i), ...], a list of commitments issued by each participant, where each element in the list indicates a @@ -189,18 +206,23 @@ in [#FROST]_ and is the size of a serialized scalar. Outputs: randomizer, a Scalar - def randomizer_generate(msg, commitment_list): - # Generate a random byte buffer with the size of a serialized scalar - rng_randomizer = random_bytes(Ns) - signing_package_enc = encode_signing_package(commitment_list, msg) - randomizer_input = rng_randomizer || signing_package_enc - return HR(randomizer_input) + def randomizer_regenerate(randomizer_seed, commitment_list): + signing_commitments_enc = encode_group_commitment_list(commitment_list) + randomizer_input = randomizer_seed || signing_commitments_enc + return H2(randomizer_input) + + +Re-Randomized FROST +------------------- + +To add re-randomization to FROST, follow the specification [#FROST]_ with the +following modifications. Round One - Commitment '''''''''''''''''''''' -Roune One is exactly the same as specified [#FROST]_. But for context, it +Round One is exactly the same as specified [#FROST]_. But for context, it involves these steps: - Each signer generates nonces and their corresponding public commitments. @@ -212,11 +234,12 @@ involves these steps: Round Two - Signature Share Generation '''''''''''''''''''''''''''''''''''''' -In Round Two, the Coordinator generates a random scalar ``randomizer`` by calling -``randomizer_generate`` and sends it to each signer, over a confidential and -authenticated channel, along with the message and the set of signing -commitments. (Note that this differs from regular FROST which just requires an -authenticated channel.) +In Round Two of regular FROST, the Coordinator picks a message and sends it to +each signer along with the commitments received in Round One. In Re-Randomized +FROST, the Coordinator MUST additionally call `randomizer_generate()` and send +the `randomizer_seed` along with the message and the commitments. Each +participant, upon receiving those values, MUST compute `randomizer` by calling +`randomizer_regenerate()` with the received `randomizer_seed` and commitments. In Zcash, the message that needs to be signed is actually the SIGHASH transaction hash, which does not convey enough information for the signers to @@ -228,9 +251,9 @@ that the given SIGHASH matches the data sent from the Coordinator, or compute th SIGHASH themselves from that data. However, the specific mechanism for that process is outside the scope of this ZIP. -The randomized ``sign`` function is defined as the regular FROST ``sign`` -function, but with its inputs modified relative to the ``randomizer`` as -following: +The randomized `sign()` function is defined as the regular FROST `sign()` function, +but its inputs modified relative to the `randomizer` (which is computed with +`randomizer_regenerate()`). - ``sk_i = sk_i + randomizer`` - ``group_public_key = group_public_key + G.ScalarBaseMult(randomizer)`` @@ -239,14 +262,14 @@ following: Signature Share Verification and Aggregation '''''''''''''''''''''''''''''''''''''''''''' -The randomized ``aggregate`` function is defined as the regular FROST -``aggregate`` function, but with its inputs modified relative to the +The randomized ``aggregate()`` function is defined as the regular FROST +``aggregate()`` function, but with its inputs modified relative to the ``randomizer`` as following: - ``group_public_key = group_public_key + G.ScalarBaseMult(randomizer)`` -The randomized ``verify_signature_share`` function is defined as the regular -FROST ``verify_signature_share`` function, but with its inputs modified relative +The randomized ``verify_signature_share()`` function is defined as the regular +FROST ``verify_signature_share()`` function, but with its inputs modified relative to the ``randomizer`` as following: - ``PK_i = PK_i + G.ScalarBaseMult(randomizer)`` @@ -379,10 +402,10 @@ by the $\mathsf{RedDSA.Validate}$ function specified in $\mathsf{RedDSA.Validate}$ expects; which is possible since both `R` and `vk` (the public key) are encoded in the same way as in Zcash. -- Note that ``r`` (and thus ``R``) will not be generated as specified in RedDSA.Sign. - This is not an issue however, since with Schnorr signatures it does not matter - for the verifier how the ``r`` value was chosen, it just needs to be generated - uniformly at random, which is true for FROST. +- Note that ``r`` (and thus ``R``) will not be generated as specified in + RedDSA.Sign. This is not an issue however, since with Schnorr signatures it + does not matter for the verifier how the ``r`` value was chosen, it just needs + to be a uniformly distributed random element, which is true for FROST. - The above will ensure that the verification equation in $\mathsf{RedDSA.Validate}$ will pass, since FROST ensures the exact same @@ -395,25 +418,11 @@ signing generates a re-randomized signature: This is exactly what is done in the functions defined above. - The re-randomization must be done in each signature share generation, such that the aggregated signature must be valid under verification with the - randomized public key. The ``R`` value from the signature is not influenced by - the randomizer so we just need to focus on the ``z`` value (using FROST - notation). Recall that ``z`` must equal to ``r + (c * sk)``, and that each - signature share is ``z_i = (hiding_nonce + (binding_nonce * binding_factor)) + - (lambda_i * c * sk_i)``. The first terms are not influenced by the randomizer - so we can only look into the second term of each top-level addition, i.e. ``c - * sk`` must be equal to ``sum(lambda_i * c * sk_i)`` for each participant - ``i``. Under re-randomization these become ``c * (sk + randomizer)`` (see - $\mathsf{RedDSA.RandomizedPrivate}$, which refers to the randomizer as - $\alpha$) and ``sum(lambda_i * c * (sk_i + randomizer))``. The latter - can be rewritten as ``c * (sum(lambda_i * sk_i) + randomizer * - sum(lambda_i)``. Since ``sum(lambda_i * sk_i) == sk`` per the Shamir secret - sharing mechanism used by FROST, and since ``sum(lambda_i) == 1`` - [#sum-lambda-proof]_, we arrive at ``c * (sk + randomizer)`` as required. - + randomized public key. This is shown to be true in [#frost-rerandomized]_. - The re-randomization procedure must be exactly the same as in [#protocol-concretereddsa]_ to ensure that re-randomized keys are uniformly distributed and signatures are unlinkable. This is also true; observe that - ``randomizer_generate`` generates randomizer uniformly at random as required + ``randomizer_generate`` generates a uniformly distributed random scalar as required by $\mathsf{RedDSA.GenRandom}$; and signature generation is compatible with $\mathsf{RedDSA.RandomizedPrivate}$, $\mathsf{RedDSA.RandomizedPublic}$, $\mathsf{RedDSA.Sign}$ and @@ -422,6 +431,22 @@ signing generates a re-randomized signature: The security of Re-Randomized FROST with respect to the security assumptions of regular FROST is shown in [#frost-rerandomized]_. +Regarding randomizer handling, in Zcash, the `randomizer` is called +:math:`\mathsf{alpha}` and is usually generated using the `RedDSA.GenRandom` +function as defined in the Zcash specification [#protocol-spendauthsig]_. Note +that the choice of :math:`\mathsf{alpha}` influences the SIGHASH computation, so +it is impossible to compute the `randomizer` based on the message (SIGHASH), as +suggested in [#frost-rerandomized]_. This is not an issue as long the +`randomizer` is generated with the same security properties as +`RedDSA.GenRandom`. We ensure that by using a very similar approach; while the +original `RedDSA.GenRandom` uses :math:`\mathsf{H}^\circledast(T)` where T is a +random byte buffer with a certain size, in this ZIP we effectively use +:math:`\mathsf{H}^\circledast(T || \mathsf{signing\_commitments\_enc})`, i.e. we +concatenate the random bytes with the encoding of the signing commitments. This +preserves the security assumptions and also hedges against issues in the +Coordinator random byte generator and prevents the Coordinator from fully +influencing the randomizer, reducing its trust assumptions. + Reference implementation ======================== @@ -436,7 +461,7 @@ References .. [#BLAKE] `BLAKE2: simpler, smaller, fast as MD5 `_ .. [#RFC2119] `RFC 2119: Key words for use in RFCs to Indicate Requirement Levels `_ .. [#FROST] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures `_ -.. [#frost-rerandomized] `Re-Randomized FROST `_ +.. [#frost-rerandomized] `Re-Randomized FROST (ePrint 2024/436) `_ .. [#frost-protocol] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Section 5: Two-Round FROST Signing Protocol `_ .. [#frost-removingcoordinator] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Section 7.3: Removing the Coordinator Role `_ .. [#frost-primeordergroup] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Section 3.1: Prime-Order Group `_ @@ -444,6 +469,9 @@ References .. [#frost-tdkg] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Appendix B: Trusted Dealer Key Generation `_ .. [#frost-randomscalar] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Appendix C: Random Scalar Generation `_ .. [#frost-serialization] `The ZF FROST Book, Serialization Format `_ +.. [#frost-listoperations] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Section 4.3: List Operations `_ +.. [#frost-conventions] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Section 2: Conventions and Definitions `_ +.. [#frost-hash] `RFC 9591: The Flexible Round-Optimized Schnorr Threshold (FROST) Protocol for Two-Round Schnorr Signatures. Section 3.2: Cryptographic Hash Function `_ .. [#protocol-concretereddsa] `Zcash Protocol Specification, Version 2022.3.4 [NU5]. Section 5.4.7: RedDSA, RedJubjub, and RedPallas `_ .. [#protocol-concretespendauthsig] `Zcash Protocol Specification, Version 2022.3.4 [NU5]. Section 5.4.7.1: Spend Authorization Signature (Sapling and Orchard) `_ .. [#protocol-spendauthsig] `Zcash Protocol Specification, Version 2022.3.4 [NU5]. Section 4.15: Spend Authorization Signature (Sapling and Orchard) `_
    1821 Prove that the sum of the Lagrange (interpolation) coefficients is equal to 1