The current implementation of the secp256k1_frost_share_gen
function combines the generation of Verifiable Secret Sharing (VSS) coefficient commitments and the generation of shares. This proposal aims to split these two operations into separate functions, which will provide a safer and more flexible API.
Motivations for this refactor include:
- The
session_id
parameter was confusing and easy to misuse. - The FROST paper requires coefficient commitments and a proof-of-knowledge for the first commitment be distributed prior to shares. This change updates the APIs to conform to the paper specification, which it did not before.
- The updated share generation function now requires the VSS, including the proof-of-knowledge, to ensure the share is not generated until those items have been received, commited to, and the proof-of-knowledge has been verified.
- Using a dedicated authentication key for the
recipient_pk
instead of using the first coefficient commitment is safer; otherwise, the authentication key's private key is split and distributed in the process, which might not be desired or expected.
- Introduce a new data structure,
secp256k1_frost_vss
, to store the VSS commitments and proof-of-knowledge. - Introduce a new function,
secp256k1_frost_vss_gen
, to generate asecp256k1_frost_vss
struct. - Modify the existing
secp256k1_frost_share_gen
function to generate a share for a participant if theirsecp256k1_frost_vss
struct verifies.
typedef struct {
secp256k1_pubkey *vss_commitments;
size_t num_commitments;
const unsigned char *sig64;
} secp256k1_frost_vss;
SECP256K1_API int secp256k1_frost_vss_gen(
const secp256k1_context *ctx,
secp256k1_frost_vss *vss,
const unsigned char *seed,
size_t threshold
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
Generates the VSS coefficient commitments and a proof-of-knowledge.
ctx
: Pointer to a context object initialized for verification.vss
: A VSS struct.seed
: A random seed.threshold
: The minimum number of signers required to produce a signature.
SECP256K1_API int secp256k1_frost_share_gen(
const secp256k1_context *ctx,
secp256k1_pubkey *vss_commitment,
secp256k1_frost_share *share,
const unsigned char *session_id32,
const secp256k1_keypair *keypair,
const secp256k1_xonly_pubkey *recipient_pk,
size_t threshold
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6);
SECP256K1_API int secp256k1_frost_share_gen(
const secp256k1_context *ctx,
secp256k1_frost_share *share,
const secp256k1_frost_vss *vss,
const secp256k1_xonly_pubkey *recipient_pk,
const unsigned char *seed,
size_t threshold
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
This original function combined the generation of VSS coefficients and the generation of shares into a single operation. The proposed refactor aims to separate these two operations into different functions, resulting in a safer and more flexible API that conforms more closely to the FROST protocol as described in the paper.
This function is also updated to verify the VSS proof-of-knowledge prior to generating shares for a participant. In addition, a seed
is used to generate coefficients, like in secp256k1_frost_vss_gen
, which removes the need for a keypair
that is overextended in the existing APIs where it serves as an authentication key, Shamir secret, and a component of the coefficient generation seed.
ctx
: Pointer to a context object initialized for verification.share
: Pointer to the key generation share.vss
: The VSS struct for the recipient.recipient_pk
: Pointer to the public key of the share recipient.seed
: The random seed of the participiant generating the share. This must be the same one used by the participant insecp256k1_frost_vss_gen
.threshold
: The minimum number of signers required to produce a signature.
To further enhance the security of the share generation and distribution process, the authentication key is used to encrypt the shares with the ChaCha20-Poly1305 encryption scheme. This symmetric encryption provides confidentiality and integrity, helping ensure that only the intended recipient can decrypt and access the share.
Yes, we could do the PoK verification without splitting the function.
I had originally thought that a single
keypair
could be reused across many sessions, which is why there's both asession_id
and akeypair
, but I now think that is confusing and easily misused. With the current API, there's 2 sources of entropy: thesession_id
and the private key of thekeypair
. With the new API it's consolidated to theseed
, although now that I think about itsession_id
might actually be a better name thanseed
. But the main idea is that there be a single source of entropy instead of 2, and to no longer use akeypair
for the first coefficient/coefficient commitment of the polynomial that is provided as an argument, but to instead derive the entire polynomial from a seed.In the current API, shares and coefficient commitments are sent together in the same round, however, the paper specifies that commitments be sent prior to the shares:
Current API
(1) send shares + VSS
(2) send PoK/broadcast-proof/sign(vss_hash)
Paper
(1) send PoK/sign(context_string) + VSS
(2) send shares (+ broadcast proof/sign(vss_hash))*
*broadcast proof is not specified by the paper but it could be sent here by signing a VSS hash
It's more flexible because with the proposed API the VSS can be generated prior to knowing the public keys of any participants, whereas with the current API, to generate the VSS, it always requires a
recipient_pk
.secp256k1_frost_vss
would represent a single participant's commitments, not the entire set, andsig64
would be that participant's signature over the context string (i.e. the PoK).