Spark employs a customized version of the FROST (Flexible Round-Optimized Schnorr Threshold) signing scheme to enable transaction signing by a required participant alongside a threshold group of signers known as Signing Operators (SOs).

Problem Statement

In Spark, transactions require co-signing by a user and a group of Signing Operators (see SOs), together SOs make up that Spark Entity (SE). The signature must meet these requirements:

  • Threshold Signing for SOs: Only tt out of nn total SOs are needed to complete the signing process.
  • Mandatory User Signing: The user’s participation is essential for a valid signature.
  • Single Schnorr Signature: The final signature must be aggregated into a single Schnorr signature, ruling out multi-signature schemes like 2-of-2. The final signature is the aggregate of the user’s signature and the SOs’ signatures, where the user is the required participant.

Additionally, the cryptographic keys must support:

  • Tweakability: Keys can be modified by adding or multiplying a tweak value.
  • Additive Aggregatability: Given all shards of two keys (e.g., key1 and key2), it must be possible to derive all shards for their sum (key1 + key2).

This document presents a modified FROST signing scheme tailored to address these needs in Spark.

Key Generation

User Key

The user independently generates a single key pair, denoted as (skuser,pkuser)(sk_{user}, pk_{user}), where skusersk_{user} is the secret key and pkuserpk_{user} is the corresponding public key.

SO Keys

The Signing Operators (SOs) collaboratively generate a threshold signing key pair, denoted as (skso,pkso)(sk_{so}, pk_{so}), using a Distributed Key Generation (DKG) protocol. Each SO receives a Shamir secret share ssiss_i of sksosk_{so}, configured with a threshold (t,n)(t, n), meaning any tt of the nn SOs can reconstruct the key.

Note: Details of the secure DKG process are beyond the scope of this document. You can find more information in the FROST paper.

Key Aggregation

The user’s key and the SOs’ key are combined into an aggregated key using additive aggregation:

  • The aggregated secret key is computed as skagg=skuser+sksosk_{agg} = sk_{user} + sk_{so}.
  • The corresponding public key is Y=pkuser+pksoY = pk_{user} + pk_{so}.

All participants must know the aggregated public key YY.

Pre-processing

Mirroring FROST’s pre-processing phase, all participants generate and commit to nonces:

  • SO Nonces: Each SO generates nonce pairs (dij,eij)(d_{ij}, e_{ij}) and their commitments (Dij,Eij)(D_{ij}, E_{ij}), where Dij=gdijD_{ij} = g^{d_{ij}} and Eij=geijE_{ij} = g^{e_{ij}}, using a fixed generator gg.
  • User Nonces: The user generates nonce pairs (duseri,euseri)(d_{user_i}, e_{user_i}) and commitments (Duseri,Euseri)(D_{user_i}, E_{user_i}), sharing these commitments with all SOs.

These nonces enhance security during signing by preventing replay attacks.

Signing Flow

The signing process involves the user, a coordinator, and a subset of SOs, proceeding as follows:

  1. Initiation:

    • The user submits a signing request for message mm to a signing operator coordinator.
  2. Participant and Nonce Selection:

    • The coordinator selects a set SS of tt participating SOs.
    • It compiles an unused nonce commitment list B={(Dij,Eij)iS}{(Duserj,Euserj)}B = \{(D_{ij}, E_{ij}) \mid i \in S\} \cup \{(D_{user_j}, E_{user_j})\} and broadcasts BB to all participants.
  3. Signature Share Calculation by SOs:

    • Each SO iSi \in S computes:
      • ρi=H1(i,m,B)\rho_i = H_1(i, m, B), using a hash function H1H_1.
      • Nonce contribution: Ri=DijEijρiR_i = D_{ij} \cdot E_{ij}^{\rho_i}.
      • Challenge: c=H2(R,Y,m)c = H_2(R, Y, m), where R=iSRiR = \prod_{i \in S} R_i.
      • Signature share: zi=dij+eijρi+λissicz_i = d_{ij} + e_{ij} \rho_i + \lambda_i ss_i c, where λi\lambda_i is the Lagrange coefficient for reconstructing sksosk_{so}.
  4. SO Signature Aggregation:

    • The coordinator aggregates the SO shares: zso=iSziz_{so} = \sum_{i \in S} z_i.
    • It computes Rso=iSRiR_{so} = \prod_{i \in S} R_i, validates the partial signature, and sends (Rso,zso)(R_{so}, z_{so}) along with BB to the user.
  5. User Signature Share Calculation:

    • The user computes:
      • ρuser=H1(0,m,B)\rho_{user} = H_1(0, m, B) (assuming user index 0).
      • Nonce contribution: Ruser=DuserjEuserjρuserR_{user} = D_{user_j} \cdot E_{user_j}^{\rho_{user}}.
      • Full nonce: R=RsoRuserR = R_{so} \cdot R_{user}.
      • Challenge: c=H2(R,Y,m)c = H_2(R, Y, m).
      • Signature share: zuser=duserj+euserjρuser+skusercz_{user} = d_{user_j} + e_{user_j} \rho_{user} + sk_{user} c.
  6. Final Signature:

    • The user aggregates the final signature as (R,z)(R, z), where z=zso+zuserz = z_{so} + z_{user}.

Key Tweaks

The SO key skso\mathit{sk_{so}} is shared via Shamir secret sharing with the polynomial:

f(x)=skso+a1x+a2x2++at1xt1f(x) = \mathit{sk_{so}} + a_1 x + a_2 x^2 + \cdots + a_{t-1} x^{t-1}

Each SO ii holds the share (i,f(i))(i, f(i)).

Additive Tweak

To tweak skso\mathit{sk_{so}} by adding tt (i.e., skso=skso+t\mathit{sk_{so}'} = \mathit{sk_{so}} + t):

  • Define the new polynomial f(x)=f(x)+tf'(x) = f(x) + t
  • Update each share to f(i)=f(i)+tf'(i) = f(i) + t

Multiplicative Tweak

For a multiplicative tweak by tt (i.e., skso=tskso\mathit{sk_{so}'} = t \cdot \mathit{sk_{so}}):

  • Update each share to f(i)=tf(i)f'(i) = t \cdot f(i)

Secure Tweak Distribution

Directly sharing tt with SOs risks key exposure if the sender is an SO or colludes with one. Instead:

  • Construct a polynomial g(x)g(x) of degree t1t-1 where g(0)=tg(0) = t
  • Distribute g(i)g(i) to each SO ii
  • Each SO updates their share: f(i)=f(i)+g(i)f'(i) = f(i) + g(i)

This method applies the tweak securely without revealing tt.

Key Split

When splitting a key into nn child keys (e.g., for transaction splitting), the property holds:

Aliceold+SOold=i=1n(Alicei+SOi)\mathit{Alice_{old}} + \mathit{SO_{old}} = \sum_{i=1}^n (\mathit{Alice_i} + \mathit{SO_i})

Here, Aliceold\mathit{Alice_{old}} and SOold\mathit{SO_{old}} are the original user and SO keys, and Alicei\mathit{Alice_i} and SOi\mathit{SO_i} are the child keys.

Process

  1. User Key Splitting:

    • The user (Alice) generates nn key pairs (skAlicei,pkAlicei)(\mathit{sk_{Alice_i}}, \mathit{pk_{Alice_i}}) for i=1i = 1 to nn
    • Compute sumAlice=i=1nskAlicei\mathit{sum_{Alice}} = \sum_{i=1}^n \mathit{sk_{Alice_i}}
    • Calculate Tweak=skAliceoldsumAlice\mathit{Tweak} = \mathit{sk_{Alice_{old}}} - \mathit{sum_{Alice}}
  2. Tweak Communication:

    • The user sends Tweak\mathit{Tweak} to the SOs.
  3. SO Key Splitting:

    • The SOs use DKG to generate n1n-1 key pairs (skSOi,pkSOi)(\mathit{sk_{SO_i}}, \mathit{pk_{SO_i}}) for i=1i = 1 to n1n-1
    • Set the nn-th key as skSOn=skSOold(i=1n1skSOiTweak)\mathit{sk_{SO_n}} = \mathit{sk_{SO_{old}}} - \left( \sum_{i=1}^{n-1} \mathit{sk_{SO_i}} - \mathit{Tweak} \right)
  4. Verification:

    • The sum of child keys is: i=1nskAlicei+i=1nskSOi=sumAlice+i=1n1skSOi+skSOoldi=1n1skSOi+Tweak\sum_{i=1}^n \mathit{sk_{Alice_i}} + \sum_{i=1}^n \mathit{sk_{SO_i}} = \mathit{sum_{Alice}} + \sum_{i=1}^{n-1} \mathit{sk_{SO_i}} + \mathit{sk_{SO_{old}}} - \sum_{i=1}^{n-1} \mathit{sk_{SO_i}} + \mathit{Tweak}
    • Substituting Tweak=skAliceoldsumAlice\mathit{Tweak} = \mathit{sk_{Alice_{old}}} - \mathit{sum_{Alice}}: =sumAlice+skSOold+(skAliceoldsumAlice)=skAliceold+skSOold= \mathit{sum_{Alice}} + \mathit{sk_{SO_{old}}} + (\mathit{sk_{Alice_{old}}} - \mathit{sum_{Alice}}) = \mathit{sk_{Alice_{old}}} + \mathit{sk_{SO_{old}}}
  5. Shard Adjustment:

    • For SOs’ Shamir shares, the nn-th key’s share for SO jj is adjusted as: fSOn(j)=fSOold(j)(i=1n1fSOi(j)Tweak)f_{SO_n}(j) = f_{SO_{old}}(j) - \left( \sum_{i=1}^{n-1} f_{SO_i}(j) - \mathit{Tweak} \right)