mirror of https://github.com/zcash/zips.git
rename to zip-312.rst
This commit is contained in:
parent
bf9edbd0ef
commit
48f8c2c80e
464
zip-0312.rst
464
zip-0312.rst
|
@ -1,7 +1,465 @@
|
|||
::
|
||||
|
||||
ZIP: 312
|
||||
Title: Shielded Multisignatures using FROST
|
||||
Status: Reserved
|
||||
Category: Standards / RPC / Wallet
|
||||
Title: FROST for Spend Authorization Signatures
|
||||
Owners: Conrado Gouvea <conrado@zfnd.org>
|
||||
Chelsea Komlo <ckomlo@uwaterloo.ca>
|
||||
Deirdre Connolly <deirdre@zfnd.org>
|
||||
Status: Draft
|
||||
Category: Wallet
|
||||
Created: 2022-08-dd
|
||||
License: MIT
|
||||
Discussions-To: <https://github.com/zcash/zips/issues/382>
|
||||
Pull-Request: <https://github.com/zcash/zips/pull/TODO>
|
||||
|
||||
|
||||
Terminology
|
||||
===========
|
||||
|
||||
{Edit this to reflect the key words that are actually used.}
|
||||
The key words "MUST", "MUST NOT", "SHOULD", and "MAY" in this document are to
|
||||
be interpreted as described in RFC 2119. [#RFC2119]_
|
||||
|
||||
The terms below are to be interpreted as follows:
|
||||
|
||||
Unlinkability
|
||||
The property of statistical independence of signatures from the signers' long-term keys, ensuring that
|
||||
(for perfectly uniform generation of Randomizers and no leakage of metadata) it is impossible to determine whether two transactions were generated by the same
|
||||
party.
|
||||
|
||||
|
||||
Abstract
|
||||
========
|
||||
|
||||
This proposal adapts FROST [#FROST]_, a threshold signature scheme,
|
||||
to make it unlinkable, which is a requirement for its use in the Zcash protocol.
|
||||
The adapted scheme generates signatures compatible with spend authorization
|
||||
signatures in the Zcash protocol, for the Sapling and Orchard network upgrades.
|
||||
|
||||
|
||||
Motivation
|
||||
==========
|
||||
|
||||
In the Zcash protocol, Spend Authorization Signatures are employed to authorize
|
||||
a transaction. The ability to generate these signatures with the user's
|
||||
private key is what effectively allows the user to spend funds.
|
||||
|
||||
This is a security-critical step, since anyone who obtains access to the private
|
||||
key will be able to spend the user's funds. For this reason, one interesting
|
||||
possibility is to require multiple parties to allow the transaction to go through.
|
||||
This can be accomplished with threshold signatures, where the private key is
|
||||
split between parties in a way that a threshold (e.g. 2 out of 3) of them must
|
||||
sign the transaction in order to create the final signature. This enables scenarios
|
||||
such as users and exchanges sharing custody of a wallet, for example.
|
||||
|
||||
FROST is one of such threshold signature protocols. However, it can't be used as-is
|
||||
since the Zcash protocol also requires re-randomizing public and private keys
|
||||
to ensure unlinkability between transactions. This ZIP specifies a variant of
|
||||
FROST with re-randomization support.
|
||||
|
||||
|
||||
Requirements
|
||||
============
|
||||
|
||||
- All signatures generated by following this ZIP must be verified successfully.
|
||||
as Sapling or Orchard spend authorization signatures.
|
||||
- The signatures generated by following this ZIP should meet the security criteria
|
||||
for Signature with Re-Randomizable Keys as specified in the Zcash protocol [#protocol-concretereddsa]_.
|
||||
- The threat model described below must be taken into account.
|
||||
|
||||
Threat Model
|
||||
------------
|
||||
|
||||
In normal usage, a Zcash user follows multiple steps in order to generate a
|
||||
shielded transaction:
|
||||
|
||||
- The transaction is created.
|
||||
- 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 :math:`\mathsf{ask}` among multiple
|
||||
possible signers. This means that the proof generation will still be generated
|
||||
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 [#frost-protocol]_.
|
||||
The Coordinator is responsible for sending the message to be signed to all participants,
|
||||
and to aggregate the signature shares.
|
||||
|
||||
With those considerations in mind, the threat model considered in this ZIP is:
|
||||
|
||||
- The Coordinator is trusted with the privacy of the transaction (which includes
|
||||
the unlinkability property). A rogue Coordinator will be able to break
|
||||
unlinkability and privacy, but should not be able to create signed transactions
|
||||
without the approval of `MIN_SIGNERS` participants, as specified in FROST.
|
||||
- All key share holders are also trusted with the privacy and of the transaction,
|
||||
thus a rogue key share holder will be able to break its privacy and unlinkability.
|
||||
A future specification may support a scenario where individual key share
|
||||
holders are not trusted with it.
|
||||
|
||||
|
||||
Non-requirements
|
||||
================
|
||||
|
||||
- This ZIP does not support removing the Coordinator role, as described in #[frost-removingcoordinator]_.
|
||||
- This ZIP does not prevent key share holders from linking the signing operation to a transaction in the blockchain.
|
||||
- Like the FROST specification [#FROST], this ZIP does not specify a key generation
|
||||
procedure; but refer to that specification for guidelines.
|
||||
- Network privacy is not in scope for this ZIP, and must be obtained with other
|
||||
tools if desired.
|
||||
|
||||
|
||||
Specification
|
||||
=============
|
||||
|
||||
Algorithms in this section are specified using Python pseudo-code, in the same
|
||||
fashion as the FROST specification [#FROST]_.
|
||||
|
||||
The types Scalar, Element, and G are defined in #[frost-primeordergroup]_, as well
|
||||
as the notation for elliptic-curve arithmetic, which uses the additive notation.
|
||||
|
||||
|
||||
Re-randomizable FROST
|
||||
---------------------
|
||||
|
||||
To add re-randomization to FROST, follow the specification [#FROST]_ with the
|
||||
following modifications.
|
||||
|
||||
|
||||
Key Generation
|
||||
''''''''''''''
|
||||
|
||||
While key generation is out of scope for this ZIP and the FROST spec [#FROST]_,
|
||||
it needs to be consistent with FROST, see [#frost-tdkg]_ for guidance. The
|
||||
spend authorization private key :math:`\mathsf{ask}` [#protocol-spendauthsig]_
|
||||
is the particular key that must be used in the context of this ZIP. Note that
|
||||
while Sapling allows creating it directly, in Orchard it is always derived
|
||||
from the spending key :math:`\mathsf{ask}`. This means that a trusted
|
||||
dealer key generation process might be required as detailed in [#frost-tdkg]_.
|
||||
|
||||
|
||||
Randomizer Generation
|
||||
'''''''''''''''''''''
|
||||
|
||||
A new helper function is defined, which computes :math:`\mathsf{RedDSA.GenRandom}`:
|
||||
|
||||
::
|
||||
|
||||
randomizer_generate():
|
||||
|
||||
Inputs:
|
||||
- None
|
||||
|
||||
Outputs: randomizer, a Scalar
|
||||
|
||||
def randomizer_generate():
|
||||
randomizer_input = random_bytes(64)
|
||||
return H3(randomizer_input)
|
||||
|
||||
|
||||
Binding Factor Computation
|
||||
''''''''''''''''''''''''''
|
||||
|
||||
The `compute_binding_factor` function is changed to receive the `randomizer_point`
|
||||
as follows: ::
|
||||
|
||||
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.
|
||||
- msg, the message to be signed.
|
||||
- randomizer_point, an element in G.
|
||||
|
||||
Outputs:
|
||||
- binding_factor_list, a list of (NonZeroScalar, Scalar) tuples representing the binding factors.
|
||||
|
||||
def compute_binding_factors(commitment_list, msg, randomizer_point):
|
||||
msg_hash = H4(msg)
|
||||
encoded_commitment_hash = H5(encode_group_commitment_list(commitment_list))
|
||||
rho_input_prefix = msg_hash || encoded_commitment_hash
|
||||
|
||||
binding_factor_list = []
|
||||
for (identifier, hiding_nonce_commitment, binding_nonce_commitment) in commitment_list:
|
||||
rho_input = rho_input_prefix || G.SerializeScalar(identifier)
|
||||
binding_factor = H1(rho_input)
|
||||
binding_factor_list.append((identifier, binding_factor))
|
||||
return binding_factor_list
|
||||
|
||||
|
||||
Round One - Commitment
|
||||
''''''''''''''''''''''
|
||||
|
||||
Roune One is exactly the same as specified #[FROST]_. 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.
|
||||
- The commitments are sent to the Coordinator.
|
||||
|
||||
|
||||
Round Two - Signature Share Generation
|
||||
''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
In Round Two, the Coordinator generates a random scalar `randomizer` by calling
|
||||
`randomizer_generate`. Then it computes `randomizer_point = G.ScalarBaseMult(randomizer)`
|
||||
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.)
|
||||
|
||||
The `sign` function is changed to receive `randomizer_point` and incorporate it
|
||||
into the computation of the binding factor. It is specified as the following: ::
|
||||
|
||||
Inputs:
|
||||
- identifier, identifier i of the participant, a NonZeroScalar.
|
||||
- sk_i, Signer secret key share, a Scalar.
|
||||
- group_public_key, public key corresponding to the group signing key,
|
||||
an Element.
|
||||
- nonce_i, pair of Scalar values (hiding_nonce, binding_nonce) generated in
|
||||
round one.
|
||||
- msg, the message to be signed, a byte string.
|
||||
- commitment_list =
|
||||
[(j, hiding_nonce_commitment_j, binding_nonce_commitment_j), ...], a
|
||||
list of commitments issued in Round 1 by each participant and sent by the Coordinator.
|
||||
Each element in the list indicates a NonZeroScalar identifier j and two commitment
|
||||
Element values (hiding_nonce_commitment_j, binding_nonce_commitment_j).
|
||||
This list MUST be sorted in ascending order by identifier.
|
||||
- randomizer_point, an element in G (sent by the Coordinator).
|
||||
|
||||
Outputs:
|
||||
- sig_share, a signature share, a Scalar.
|
||||
|
||||
def sign(identifier, sk_i, group_public_key, nonce_i, msg, commitment_list):
|
||||
# Compute the randomized group public key
|
||||
randomized_group_public_key = group_public_key + randomizer_point
|
||||
|
||||
# Compute the binding factor(s)
|
||||
binding_factor_list = compute_binding_factors(commitment_list, msg, randomizer_point)
|
||||
binding_factor = binding_factor_for_participant(binding_factor_list, identifier)
|
||||
|
||||
# Compute the group commitment
|
||||
group_commitment = compute_group_commitment(commitment_list, binding_factor_list)
|
||||
|
||||
# Compute the interpolating value
|
||||
participant_list = participants_from_commitment_list(commitment_list)
|
||||
lambda_i = derive_interpolating_value(identifier, participant_list)
|
||||
|
||||
# Compute the per-message challenge
|
||||
challenge = compute_challenge(group_commitment, randomized_group_public_key, msg)
|
||||
|
||||
# Compute the signature share
|
||||
(hiding_nonce, binding_nonce) = nonce_i
|
||||
sig_share = hiding_nonce + (binding_nonce * binding_factor) + (lambda_i * sk_i * challenge)
|
||||
|
||||
return sig_share
|
||||
|
||||
|
||||
Signature Share Verification and Aggregation
|
||||
''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The `aggregate` function is changed to incorporate the randomizer as follows: ::
|
||||
|
||||
Inputs:
|
||||
- commitment_list =
|
||||
[(j, hiding_nonce_commitment_j, binding_nonce_commitment_j), ...], a
|
||||
list of commitments issued in Round 1 by each participant, where each element
|
||||
in the list indicates a NonZeroScalar identifier j and two commitment
|
||||
Element values (hiding_nonce_commitment_j, binding_nonce_commitment_j).
|
||||
This list MUST be sorted in ascending order by identifier.
|
||||
- msg, the message to be signed, a byte string.
|
||||
- sig_shares, a set of signature shares z_i, Scalar values, for each participant,
|
||||
of length NUM_PARTICIPANTS, where MIN_PARTICIPANTS <= NUM_PARTICIPANTS <= MAX_PARTICIPANTS.
|
||||
- group_public_key, public key corresponding to the group signing key,
|
||||
- randomizer, the randomizer Scalar.
|
||||
|
||||
Outputs:
|
||||
- (R, z), a Schnorr signature consisting of an Element R and Scalar z.
|
||||
- randomized_group_public_key, the randomized group public key
|
||||
|
||||
def aggregate(commitment_list, msg, sig_shares, group_public_key, randomizer):
|
||||
# Compute the binding factors
|
||||
binding_factor_list = compute_binding_factors(commitment_list, msg)
|
||||
|
||||
# Compute the group commitment
|
||||
group_commitment = compute_group_commitment(commitment_list, binding_factor_list)
|
||||
|
||||
# Compute the challenge
|
||||
randomized_group_public_key = group_public_key + G * randomizer
|
||||
challenge = compute_challenge(group_commitment, randomized_group_public_key, msg)
|
||||
|
||||
# Compute aggregated signature
|
||||
z = Scalar(0)
|
||||
for z_i in sig_shares:
|
||||
z = z + z_i
|
||||
return (group_commitment, z + randomizer * challenge), randomized_group_public_key
|
||||
|
||||
|
||||
The `verify_signature_share` is changed to incorporate the randomizer point,
|
||||
as follows: ::
|
||||
|
||||
Inputs:
|
||||
- identifier, identifier i of the participant, a NonZeroScalar.
|
||||
- PK_i, the public key for the i-th participant, where PK_i = G.ScalarBaseMult(sk_i),
|
||||
an Element.
|
||||
- comm_i, pair of Element values in G (hiding_nonce_commitment, binding_nonce_commitment)
|
||||
generated in round one from the i-th participant.
|
||||
- sig_share_i, a Scalar value indicating the signature share as produced in
|
||||
round two from the i-th participant.
|
||||
- commitment_list =
|
||||
[(j, hiding_nonce_commitment_j, binding_nonce_commitment_j), ...], a
|
||||
list of commitments issued in Round 1 by each participant, where each element
|
||||
in the list indicates a NonZeroScalar identifier j and two commitment
|
||||
Element values (hiding_nonce_commitment_j, binding_nonce_commitment_j).
|
||||
This list MUST be sorted in ascending order by identifier.
|
||||
- group_public_key, public key corresponding to the group signing key,
|
||||
an Element.
|
||||
- msg, the message to be signed, a byte string.
|
||||
- randomizer_point, an element in G.
|
||||
|
||||
Outputs:
|
||||
- True if the signature share is valid, and False otherwise.
|
||||
|
||||
def verify_signature_share(identifier, PK_i, comm_i, sig_share_i, commitment_list,
|
||||
group_public_key, msg, randomizer_point):
|
||||
# Compute the randomized group public key
|
||||
randomized_group_public_key = group_public_key + randomizer_point
|
||||
|
||||
# Compute the binding factors
|
||||
binding_factor_list = compute_binding_factors(commitment_list, msg, randomizer_point)
|
||||
binding_factor = binding_factor_for_participant(binding_factor_list, identifier)
|
||||
|
||||
# Compute the group commitment
|
||||
group_commitment = compute_group_commitment(commitment_list, binding_factor_list)
|
||||
|
||||
# Compute the commitment share
|
||||
(hiding_nonce_commitment, binding_nonce_commitment) = comm_i
|
||||
comm_share = hiding_nonce_commitment + G.ScalarMult(binding_nonce_commitment, binding_factor)
|
||||
|
||||
# Compute the challenge
|
||||
challenge = compute_challenge(group_commitment, randomized_group_public_key, msg)
|
||||
|
||||
# Compute the interpolating value
|
||||
participant_list = participants_from_commitment_list(commitment_list)
|
||||
lambda_i = derive_interpolating_value(identifier, participant_list)
|
||||
|
||||
# Compute relation values
|
||||
l = G.ScalarBaseMult(sig_share_i)
|
||||
r = comm_share + G.ScalarMult(PK_i, challenge * lambda_i)
|
||||
|
||||
return l == r
|
||||
|
||||
|
||||
|
||||
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 [#protocol-concretespendauthsig]_.
|
||||
|
||||
- Group: Jubjub [#protocol-jubjub]_
|
||||
|
||||
- Order: 6554484396890773809930967563523245729705921265872317281365359162392183254199 (see [#protocol-jubjub]_)
|
||||
- Identity: as defined in [#protocol-jubjub]_
|
||||
- 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 :math:`\mathsf{repr}_\mathbb{J}(P)` as defined in [#protocol-jubjub]_
|
||||
- DeserializeElement(P): Implemented as :math:`\mathsf{abst}_\mathbb{J}(P)` as defined in [#protocol-jubjub]_,
|
||||
failing if :math:`\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 [#BLAKE]_ (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 L = 6554484396890773809930967563523245729705921265872317281365359162392183254199.
|
||||
- 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 L = 6554484396890773809930967563523245729705921265872317281365359162392183254199.
|
||||
(This is equivalent to :math:`\mathsf{H}^\circledast(m)`, as defined in
|
||||
[#protocol-concretereddsa]_ parametrized with [#protocol-jubjub]_.)
|
||||
- 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 L = 6554484396890773809930967563523245729705921265872317281365359162392183254199.
|
||||
- H4(m): Implemented by computing BLAKE2b-512("FROST_RedJubjubM", m)
|
||||
- H5(m): Implemented by computing BLAKE2b-512("FROST_RedJubjubC", m)
|
||||
|
||||
|
||||
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 [#protocol-concretespendauthsig]_.
|
||||
|
||||
- Group: Pallas [#protocol-pallasandvesta]_
|
||||
|
||||
- Order: 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001 (see [#protocol-pallasandvesta]_)
|
||||
- Identity: as defined in [#protocol-pallasandvesta]_
|
||||
- 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 :math:`\mathsf{repr}_\mathbb{P}(P)` as defined in [#protocol-pallasandvesta]_
|
||||
- DeserializeElement(P): Implemented as :math:`\mathsf{abst}_\mathbb{P}(P)` as defined in [#protocol-pallasandvesta]_,
|
||||
failing if :math:`\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 [#BLAKE]_ (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 L = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001.
|
||||
- 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 L = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001.
|
||||
(This is equivalent to :math:`\mathsf{H}^\circledast(m)`, as defined in
|
||||
[#protocol-concretereddsa]_ parametrized with [#protocol-pallasandvesta]_.)
|
||||
- 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 L = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001.
|
||||
- H4(m): Implemented by computing BLAKE2b-512("FROST_RedPallasM", m).
|
||||
- H5(m): Implemented by computing BLAKE2b-512("FROST_RedPallasC", m).
|
||||
|
||||
|
||||
Reference implementation
|
||||
========================
|
||||
|
||||
TODO: add links to implementation
|
||||
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [#BLAKE] `BLAKE2: simpler, smaller, fast as MD5 <https://blake2.net/#sp>`_
|
||||
.. [#RFC2119] `RFC 2119: Key words for use in RFCs to Indicate Requirement Levels <https://www.rfc-editor.org/rfc/rfc2119.html>`_
|
||||
.. [#FROST] `Draft RFC: Two-Round Threshold Schnorr Signatures with FROST <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-08.html>`_
|
||||
.. [#frost-protocol] `Draft RFC: Two-Round Threshold Schnorr Signatures with FROST. Section 5: Two-Round FROST Signing Protocol <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-08.html#section-5>`_
|
||||
.. [#frost-removingcoordinator] `Draft RFC: Two-Round Threshold Schnorr Signatures with FROST. Section 7.3: Removing the Coordinator Role <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-08.html#section-7.3>`_
|
||||
.. [#frost-primeordergroup] `Draft RFC: Two-Round Threshold Schnorr Signatures with FROST. Section 3.1: Prime-Order Group <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-08.html#section-3.1>`_
|
||||
.. [#frost-tdkg] `Draft RFC: Two-Round Threshold Schnorr Signatures with FROST. Appendix B: Trusted Dealer Key Generation <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-08.html#appendix-B>`_
|
||||
.. [#frost-randomscalar] `Draft RFC: Two-Round Threshold Schnorr Signatures with FROST. Appendix C: Random Scalar Generation <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-08.html#appendix-C>`_
|
||||
.. [#protocol-concretereddsa] `Zcash Protocol Specification, Version 2022.3.4 [NU5]. Section 5.4.7: RedDSA, RedJubjub, and RedPallas <https://protocol/protocol.pdf#concretereddsa>`_
|
||||
.. [#protocol-concretespendauthsig] `Zcash Protocol Specification, Version 2022.3.4 [NU5]. Section 5.4.7.1: Spend Authorization Signature (Sapling and Orchard) <protocol/protocol.pdf#concretespendauthsig>`_
|
||||
.. [#protocol-spendauthsig] `Zcash Protocol Specification, Version 2022.3.4 [NU5]. Section 4.15: Spend Authorization Signature (Sapling and Orchard) <protocol/protocol.pdf#spendauthsig>`_
|
||||
.. [#protocol-jubjub] `Zcash Protocol Specification, Version 2022.3.4 [NU5]. Section 5.4.9.3: Jubjub <protocol/protocol.pdf#jubjub>`_
|
||||
.. [#protocol-pallasandvesta] `Zcash Protocol Specification, Version 2022.3.4 [NU5]. Section 5.4.9.6: Pallas and Vesta <https://protocol/protocol.pdf#pallasandvesta>`_
|
||||
|
|
465
zip-frost.rst
465
zip-frost.rst
|
@ -1,465 +0,0 @@
|
|||
::
|
||||
|
||||
ZIP: 312
|
||||
Title: FROST for Spend Authorization Signatures
|
||||
Owners: Conrado Gouvea <conrado@zfnd.org>
|
||||
Chelsea Komlo <ckomlo@uwaterloo.ca>
|
||||
Deirdre Connolly <deirdre@zfnd.org>
|
||||
Status: Draft
|
||||
Category: Wallet
|
||||
Created: 2022-08-dd
|
||||
License: MIT
|
||||
Discussions-To: <https://github.com/zcash/zips/issues/382>
|
||||
Pull-Request: <https://github.com/zcash/zips/pull/TODO>
|
||||
|
||||
|
||||
Terminology
|
||||
===========
|
||||
|
||||
{Edit this to reflect the key words that are actually used.}
|
||||
The key words "MUST", "MUST NOT", "SHOULD", and "MAY" in this document are to
|
||||
be interpreted as described in RFC 2119. [#RFC2119]_
|
||||
|
||||
The terms below are to be interpreted as follows:
|
||||
|
||||
Unlinkability
|
||||
The property of statistical independence of signatures from the signers' long-term keys, ensuring that
|
||||
(for perfectly uniform generation of Randomizers and no leakage of metadata) it is impossible to determine whether two transactions were generated by the same
|
||||
party.
|
||||
|
||||
|
||||
Abstract
|
||||
========
|
||||
|
||||
This proposal adapts FROST [#FROST]_, a threshold signature scheme,
|
||||
to make it unlinkable, which is a requirement for its use in the Zcash protocol.
|
||||
The adapted scheme generates signatures compatible with spend authorization
|
||||
signatures in the Zcash protocol, for the Sapling and Orchard network upgrades.
|
||||
|
||||
|
||||
Motivation
|
||||
==========
|
||||
|
||||
In the Zcash protocol, Spend Authorization Signatures are employed to authorize
|
||||
a transaction. The ability to generate these signatures with the user's
|
||||
private key is what effectively allows the user to spend funds.
|
||||
|
||||
This is a security-critical step, since anyone who obtains access to the private
|
||||
key will be able to spend the user's funds. For this reason, one interesting
|
||||
possibility is to require multiple parties to allow the transaction to go through.
|
||||
This can be accomplished with threshold signatures, where the private key is
|
||||
split between parties in a way that a threshold (e.g. 2 out of 3) of them must
|
||||
sign the transaction in order to create the final signature. This enables scenarios
|
||||
such as users and exchanges sharing custody of a wallet, for example.
|
||||
|
||||
FROST is one of such threshold signature protocols. However, it can't be used as-is
|
||||
since the Zcash protocol also requires re-randomizing public and private keys
|
||||
to ensure unlinkability between transactions. This ZIP specifies a variant of
|
||||
FROST with re-randomization support.
|
||||
|
||||
|
||||
Requirements
|
||||
============
|
||||
|
||||
- All signatures generated by following this ZIP must be verified successfully.
|
||||
as Sapling or Orchard spend authorization signatures.
|
||||
- The signatures generated by following this ZIP should meet the security criteria
|
||||
for Signature with Re-Randomizable Keys as specified in the Zcash protocol [#protocol-concretereddsa]_.
|
||||
- The threat model described below must be taken into account.
|
||||
|
||||
Threat Model
|
||||
------------
|
||||
|
||||
In normal usage, a Zcash user follows multiple steps in order to generate a
|
||||
shielded transaction:
|
||||
|
||||
- The transaction is created.
|
||||
- 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 :math:`\mathsf{ask}` among multiple
|
||||
possible signers. This means that the proof generation will still be generated
|
||||
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 [#frost-protocol]_.
|
||||
The Coordinator is responsible for sending the message to be signed to all participants,
|
||||
and to aggregate the signature shares.
|
||||
|
||||
With those considerations in mind, the threat model considered in this ZIP is:
|
||||
|
||||
- The Coordinator is trusted with the privacy of the transaction (which includes
|
||||
the unlinkability property). A rogue Coordinator will be able to break
|
||||
unlinkability and privacy, but should not be able to create signed transactions
|
||||
without the approval of `MIN_SIGNERS` participants, as specified in FROST.
|
||||
- All key share holders are also trusted with the privacy and of the transaction,
|
||||
thus a rogue key share holder will be able to break its privacy and unlinkability.
|
||||
A future specification may support a scenario where individual key share
|
||||
holders are not trusted with it.
|
||||
|
||||
|
||||
Non-requirements
|
||||
================
|
||||
|
||||
- This ZIP does not support removing the Coordinator role, as described in #[frost-removingcoordinator]_.
|
||||
- This ZIP does not prevent key share holders from linking the signing operation to a transaction in the blockchain.
|
||||
- Like the FROST specification [#FROST], this ZIP does not specify a key generation
|
||||
procedure; but refer to that specification for guidelines.
|
||||
- Network privacy is not in scope for this ZIP, and must be obtained with other
|
||||
tools if desired.
|
||||
|
||||
|
||||
Specification
|
||||
=============
|
||||
|
||||
Algorithms in this section are specified using Python pseudo-code, in the same
|
||||
fashion as the FROST specification [#FROST]_.
|
||||
|
||||
The types Scalar, Element, and G are defined in #[frost-primeordergroup]_, as well
|
||||
as the notation for elliptic-curve arithmetic, which uses the additive notation.
|
||||
|
||||
|
||||
Re-randomizable FROST
|
||||
---------------------
|
||||
|
||||
To add re-randomization to FROST, follow the specification [#FROST]_ with the
|
||||
following modifications.
|
||||
|
||||
|
||||
Key Generation
|
||||
''''''''''''''
|
||||
|
||||
While key generation is out of scope for this ZIP and the FROST spec [#FROST]_,
|
||||
it needs to be consistent with FROST, see [#frost-tdkg]_ for guidance. The
|
||||
spend authorization private key :math:`\mathsf{ask}` [#protocol-spendauthsig]_
|
||||
is the particular key that must be used in the context of this ZIP. Note that
|
||||
while Sapling allows creating it directly, in Orchard it is always derived
|
||||
from the spending key :math:`\mathsf{ask}`. This means that a trusted
|
||||
dealer key generation process might be required as detailed in [#frost-tdkg]_.
|
||||
|
||||
|
||||
Randomizer Generation
|
||||
'''''''''''''''''''''
|
||||
|
||||
A new helper function is defined, which computes :math:`\mathsf{RedDSA.GenRandom}`:
|
||||
|
||||
::
|
||||
|
||||
randomizer_generate():
|
||||
|
||||
Inputs:
|
||||
- None
|
||||
|
||||
Outputs: randomizer, a Scalar
|
||||
|
||||
def randomizer_generate():
|
||||
randomizer_input = random_bytes(64)
|
||||
return H3(randomizer_input)
|
||||
|
||||
|
||||
Binding Factor Computation
|
||||
''''''''''''''''''''''''''
|
||||
|
||||
The `compute_binding_factor` function is changed to receive the `randomizer_point`
|
||||
as follows: ::
|
||||
|
||||
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.
|
||||
- msg, the message to be signed.
|
||||
- randomizer_point, an element in G.
|
||||
|
||||
Outputs:
|
||||
- binding_factor_list, a list of (NonZeroScalar, Scalar) tuples representing the binding factors.
|
||||
|
||||
def compute_binding_factors(commitment_list, msg, randomizer_point):
|
||||
msg_hash = H4(msg)
|
||||
encoded_commitment_hash = H5(encode_group_commitment_list(commitment_list))
|
||||
rho_input_prefix = msg_hash || encoded_commitment_hash
|
||||
|
||||
binding_factor_list = []
|
||||
for (identifier, hiding_nonce_commitment, binding_nonce_commitment) in commitment_list:
|
||||
rho_input = rho_input_prefix || G.SerializeScalar(identifier)
|
||||
binding_factor = H1(rho_input)
|
||||
binding_factor_list.append((identifier, binding_factor))
|
||||
return binding_factor_list
|
||||
|
||||
|
||||
Round One - Commitment
|
||||
''''''''''''''''''''''
|
||||
|
||||
Roune One is exactly the same as specified #[FROST]_. 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.
|
||||
- The commitments are sent to the Coordinator.
|
||||
|
||||
|
||||
Round Two - Signature Share Generation
|
||||
''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
In Round Two, the Coordinator generates a random scalar `randomizer` by calling
|
||||
`randomizer_generate`. Then it computes `randomizer_point = G.ScalarBaseMult(randomizer)`
|
||||
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.)
|
||||
|
||||
The `sign` function is changed to receive `randomizer_point` and incorporate it
|
||||
into the computation of the binding factor. It is specified as the following: ::
|
||||
|
||||
Inputs:
|
||||
- identifier, identifier i of the participant, a NonZeroScalar.
|
||||
- sk_i, Signer secret key share, a Scalar.
|
||||
- group_public_key, public key corresponding to the group signing key,
|
||||
an Element.
|
||||
- nonce_i, pair of Scalar values (hiding_nonce, binding_nonce) generated in
|
||||
round one.
|
||||
- msg, the message to be signed, a byte string.
|
||||
- commitment_list =
|
||||
[(j, hiding_nonce_commitment_j, binding_nonce_commitment_j), ...], a
|
||||
list of commitments issued in Round 1 by each participant and sent by the Coordinator.
|
||||
Each element in the list indicates a NonZeroScalar identifier j and two commitment
|
||||
Element values (hiding_nonce_commitment_j, binding_nonce_commitment_j).
|
||||
This list MUST be sorted in ascending order by identifier.
|
||||
- randomizer_point, an element in G (sent by the Coordinator).
|
||||
|
||||
Outputs:
|
||||
- sig_share, a signature share, a Scalar.
|
||||
|
||||
def sign(identifier, sk_i, group_public_key, nonce_i, msg, commitment_list):
|
||||
# Compute the randomized group public key
|
||||
randomized_group_public_key = group_public_key + randomizer_point
|
||||
|
||||
# Compute the binding factor(s)
|
||||
binding_factor_list = compute_binding_factors(commitment_list, msg, randomizer_point)
|
||||
binding_factor = binding_factor_for_participant(binding_factor_list, identifier)
|
||||
|
||||
# Compute the group commitment
|
||||
group_commitment = compute_group_commitment(commitment_list, binding_factor_list)
|
||||
|
||||
# Compute the interpolating value
|
||||
participant_list = participants_from_commitment_list(commitment_list)
|
||||
lambda_i = derive_interpolating_value(identifier, participant_list)
|
||||
|
||||
# Compute the per-message challenge
|
||||
challenge = compute_challenge(group_commitment, randomized_group_public_key, msg)
|
||||
|
||||
# Compute the signature share
|
||||
(hiding_nonce, binding_nonce) = nonce_i
|
||||
sig_share = hiding_nonce + (binding_nonce * binding_factor) + (lambda_i * sk_i * challenge)
|
||||
|
||||
return sig_share
|
||||
|
||||
|
||||
Signature Share Verification and Aggregation
|
||||
''''''''''''''''''''''''''''''''''''''''''''
|
||||
|
||||
The `aggregate` function is changed to incorporate the randomizer as follows: ::
|
||||
|
||||
Inputs:
|
||||
- commitment_list =
|
||||
[(j, hiding_nonce_commitment_j, binding_nonce_commitment_j), ...], a
|
||||
list of commitments issued in Round 1 by each participant, where each element
|
||||
in the list indicates a NonZeroScalar identifier j and two commitment
|
||||
Element values (hiding_nonce_commitment_j, binding_nonce_commitment_j).
|
||||
This list MUST be sorted in ascending order by identifier.
|
||||
- msg, the message to be signed, a byte string.
|
||||
- sig_shares, a set of signature shares z_i, Scalar values, for each participant,
|
||||
of length NUM_PARTICIPANTS, where MIN_PARTICIPANTS <= NUM_PARTICIPANTS <= MAX_PARTICIPANTS.
|
||||
- group_public_key, public key corresponding to the group signing key,
|
||||
- randomizer, the randomizer Scalar.
|
||||
|
||||
Outputs:
|
||||
- (R, z), a Schnorr signature consisting of an Element R and Scalar z.
|
||||
- randomized_group_public_key, the randomized group public key
|
||||
|
||||
def aggregate(commitment_list, msg, sig_shares, group_public_key, randomizer):
|
||||
# Compute the binding factors
|
||||
binding_factor_list = compute_binding_factors(commitment_list, msg)
|
||||
|
||||
# Compute the group commitment
|
||||
group_commitment = compute_group_commitment(commitment_list, binding_factor_list)
|
||||
|
||||
# Compute the challenge
|
||||
randomized_group_public_key = group_public_key + G * randomizer
|
||||
challenge = compute_challenge(group_commitment, randomized_group_public_key, msg)
|
||||
|
||||
# Compute aggregated signature
|
||||
z = Scalar(0)
|
||||
for z_i in sig_shares:
|
||||
z = z + z_i
|
||||
return (group_commitment, z + randomizer * challenge), randomized_group_public_key
|
||||
|
||||
|
||||
The `verify_signature_share` is changed to incorporate the randomizer point,
|
||||
as follows: ::
|
||||
|
||||
Inputs:
|
||||
- identifier, identifier i of the participant, a NonZeroScalar.
|
||||
- PK_i, the public key for the i-th participant, where PK_i = G.ScalarBaseMult(sk_i),
|
||||
an Element.
|
||||
- comm_i, pair of Element values in G (hiding_nonce_commitment, binding_nonce_commitment)
|
||||
generated in round one from the i-th participant.
|
||||
- sig_share_i, a Scalar value indicating the signature share as produced in
|
||||
round two from the i-th participant.
|
||||
- commitment_list =
|
||||
[(j, hiding_nonce_commitment_j, binding_nonce_commitment_j), ...], a
|
||||
list of commitments issued in Round 1 by each participant, where each element
|
||||
in the list indicates a NonZeroScalar identifier j and two commitment
|
||||
Element values (hiding_nonce_commitment_j, binding_nonce_commitment_j).
|
||||
This list MUST be sorted in ascending order by identifier.
|
||||
- group_public_key, public key corresponding to the group signing key,
|
||||
an Element.
|
||||
- msg, the message to be signed, a byte string.
|
||||
- randomizer_point, an element in G.
|
||||
|
||||
Outputs:
|
||||
- True if the signature share is valid, and False otherwise.
|
||||
|
||||
def verify_signature_share(identifier, PK_i, comm_i, sig_share_i, commitment_list,
|
||||
group_public_key, msg, randomizer_point):
|
||||
# Compute the randomized group public key
|
||||
randomized_group_public_key = group_public_key + randomizer_point
|
||||
|
||||
# Compute the binding factors
|
||||
binding_factor_list = compute_binding_factors(commitment_list, msg, randomizer_point)
|
||||
binding_factor = binding_factor_for_participant(binding_factor_list, identifier)
|
||||
|
||||
# Compute the group commitment
|
||||
group_commitment = compute_group_commitment(commitment_list, binding_factor_list)
|
||||
|
||||
# Compute the commitment share
|
||||
(hiding_nonce_commitment, binding_nonce_commitment) = comm_i
|
||||
comm_share = hiding_nonce_commitment + G.ScalarMult(binding_nonce_commitment, binding_factor)
|
||||
|
||||
# Compute the challenge
|
||||
challenge = compute_challenge(group_commitment, randomized_group_public_key, msg)
|
||||
|
||||
# Compute the interpolating value
|
||||
participant_list = participants_from_commitment_list(commitment_list)
|
||||
lambda_i = derive_interpolating_value(identifier, participant_list)
|
||||
|
||||
# Compute relation values
|
||||
l = G.ScalarBaseMult(sig_share_i)
|
||||
r = comm_share + G.ScalarMult(PK_i, challenge * lambda_i)
|
||||
|
||||
return l == r
|
||||
|
||||
|
||||
|
||||
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 [#protocol-concretespendauthsig]_.
|
||||
|
||||
- Group: Jubjub [#protocol-jubjub]_
|
||||
|
||||
- Order: 6554484396890773809930967563523245729705921265872317281365359162392183254199 (see [#protocol-jubjub]_)
|
||||
- Identity: as defined in [#protocol-jubjub]_
|
||||
- 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 :math:`\mathsf{repr}_\mathbb{J}(P)` as defined in [#protocol-jubjub]_
|
||||
- DeserializeElement(P): Implemented as :math:`\mathsf{abst}_\mathbb{J}(P)` as defined in [#protocol-jubjub]_,
|
||||
failing if :math:`\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 [#BLAKE]_ (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 L = 6554484396890773809930967563523245729705921265872317281365359162392183254199.
|
||||
- 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 L = 6554484396890773809930967563523245729705921265872317281365359162392183254199.
|
||||
(This is equivalent to :math:`\mathsf{H}^\circledast(m)`, as defined in
|
||||
[#protocol-concretereddsa]_ parametrized with [#protocol-jubjub]_.)
|
||||
- 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 L = 6554484396890773809930967563523245729705921265872317281365359162392183254199.
|
||||
- H4(m): Implemented by computing BLAKE2b-512("FROST_RedJubjubM", m)
|
||||
- H5(m): Implemented by computing BLAKE2b-512("FROST_RedJubjubC", m)
|
||||
|
||||
|
||||
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 [#protocol-concretespendauthsig]_.
|
||||
|
||||
- Group: Pallas [#protocol-pallasandvesta]_
|
||||
|
||||
- Order: 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001 (see [#protocol-pallasandvesta]_)
|
||||
- Identity: as defined in [#protocol-pallasandvesta]_
|
||||
- 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 :math:`\mathsf{repr}_\mathbb{P}(P)` as defined in [#protocol-pallasandvesta]_
|
||||
- DeserializeElement(P): Implemented as :math:`\mathsf{abst}_\mathbb{P}(P)` as defined in [#protocol-pallasandvesta]_,
|
||||
failing if :math:`\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 [#BLAKE]_ (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 L = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001.
|
||||
- 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 L = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001.
|
||||
(This is equivalent to :math:`\mathsf{H}^\circledast(m)`, as defined in
|
||||
[#protocol-concretereddsa]_ parametrized with [#protocol-pallasandvesta]_.)
|
||||
- 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 L = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001.
|
||||
- H4(m): Implemented by computing BLAKE2b-512("FROST_RedPallasM", m).
|
||||
- H5(m): Implemented by computing BLAKE2b-512("FROST_RedPallasC", m).
|
||||
|
||||
|
||||
Reference implementation
|
||||
========================
|
||||
|
||||
TODO: add links to implementation
|
||||
|
||||
|
||||
References
|
||||
==========
|
||||
|
||||
.. [#BLAKE] `BLAKE2: simpler, smaller, fast as MD5 <https://blake2.net/#sp>`_
|
||||
.. [#RFC2119] `RFC 2119: Key words for use in RFCs to Indicate Requirement Levels <https://www.rfc-editor.org/rfc/rfc2119.html>`_
|
||||
.. [#FROST] `Draft RFC: Two-Round Threshold Schnorr Signatures with FROST <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-08.html>`_
|
||||
.. [#frost-protocol] `Draft RFC: Two-Round Threshold Schnorr Signatures with FROST. Section 5: Two-Round FROST Signing Protocol <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-08.html#section-5>`_
|
||||
.. [#frost-removingcoordinator] `Draft RFC: Two-Round Threshold Schnorr Signatures with FROST. Section 7.3: Removing the Coordinator Role <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-08.html#section-7.3>`_
|
||||
.. [#frost-primeordergroup] `Draft RFC: Two-Round Threshold Schnorr Signatures with FROST. Section 3.1: Prime-Order Group <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-08.html#section-3.1>`_
|
||||
.. [#frost-tdkg] `Draft RFC: Two-Round Threshold Schnorr Signatures with FROST. Appendix B: Trusted Dealer Key Generation <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-08.html#appendix-B>`_
|
||||
.. [#frost-randomscalar] `Draft RFC: Two-Round Threshold Schnorr Signatures with FROST. Appendix C: Random Scalar Generation <https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-08.html#appendix-C>`_
|
||||
.. [#protocol-concretereddsa] `Zcash Protocol Specification, Version 2022.3.4 [NU5]. Section 5.4.7: RedDSA, RedJubjub, and RedPallas <https://protocol/protocol.pdf#concretereddsa>`_
|
||||
.. [#protocol-concretespendauthsig] `Zcash Protocol Specification, Version 2022.3.4 [NU5]. Section 5.4.7.1: Spend Authorization Signature (Sapling and Orchard) <protocol/protocol.pdf#concretespendauthsig>`_
|
||||
.. [#protocol-spendauthsig] `Zcash Protocol Specification, Version 2022.3.4 [NU5]. Section 4.15: Spend Authorization Signature (Sapling and Orchard) <protocol/protocol.pdf#spendauthsig>`_
|
||||
.. [#protocol-jubjub] `Zcash Protocol Specification, Version 2022.3.4 [NU5]. Section 5.4.9.3: Jubjub <protocol/protocol.pdf#jubjub>`_
|
||||
.. [#protocol-pallasandvesta] `Zcash Protocol Specification, Version 2022.3.4 [NU5]. Section 5.4.9.6: Pallas and Vesta <https://protocol/protocol.pdf#pallasandvesta>`_
|
Loading…
Reference in New Issue