taurushq-io / multi-party-sig

Implementation of protocols for threshold signatures
Apache License 2.0
311 stars 120 forks source link

Make doerner18 configs serializable #116

Open Konubinix opened 3 months ago

Konubinix commented 3 months ago

In my company, we needed to persist the result of the doerner18 dkg so that we can sign later on.

To do so, we needed to be able to serialize the Setup generated during the dkg.

This pull requests makes this possible.

I assume that this pull request is far from being ready for merge, but I'm starting the conversation and hoping that you will point me to the changes to be made to eventually be able to merge this upstream.

veorq commented 1 week ago

Thank you, sorry for the delayed response. Any additional changes you'd like to make? Why moving the ot stuff from internal/ to pkg/?

Konubinix commented 6 days ago

Thank you for your reply,

We use the doerner implementation that defines the structures ConfigReceiver and ConfigSender

At the end of the key generation, both parties need to keep the generated ConfigReceiver (for one party) and the ConfigSender (for the other party) so that they can be used to create signatures in the future. Therefore, we need to serialize them.

In multi-party-sig, we can create a ConfigReceiver if we have instances of Setup, SecretShare, Public and ChainKey, therefore we focused on serializing those types. Among them, only Setup did not expose any way to marshall/unmarshall it.

For that reason, we exposed the ot module, so that we could have access to this Setup structure and implement the Marshall/Unmarshall function. This is what this pull request does.

Our marshalling/unmarshalling code looks like this:

type ConfigReceiverBytes struct {
    Setup       []byte
    SecretShare []byte
    Public      []byte
    ChainKey    []byte
}

func MarshallConfigReceiver(configReceiver *doerner.ConfigReceiver) ([]byte, error) {
    secretShare, err := configReceiver.SecretShare.MarshalBinary()
    if err != nil {
        return nil, fmt.Errorf("could not serialize configReceiver.SecretShare: %v", err)
    }
    public, err := configReceiver.Public.MarshalBinary()
    if err != nil {
        return nil, fmt.Errorf("could not serialize configReceiver.Public: %v", err)
    }
    setup, err := configReceiver.Setup.MarshalJSON()
    if err != nil {
        return nil, fmt.Errorf("could not serialize configReceiver.Setup: %v", err)
    }

    output := ConfigReceiverBytes{
        Setup:       setup,
        SecretShare: secretShare,
        Public:      public,
        ChainKey:    configReceiver.ChainKey,
    }

    res, err := json.Marshal(output)
    if err != nil {
        return nil, fmt.Errorf("could not marshall keygen output: %v", err)
    }

    return res, nil
}

func UnmarshallConfigReceiver(configReceiverRaw []byte) (*doerner.ConfigReceiver, error) {
    configReceiverBytes := ConfigReceiverBytes{}
    err := json.Unmarshal(configReceiverRaw, &configReceiverBytes)
    if err != nil {
        return nil, fmt.Errorf("could not unmarshall keygen output: %v", err)
    }

    secretShare := new(curve.Secp256k1Scalar)
    err = secretShare.UnmarshalBinary(configReceiverBytes.SecretShare)
    if err != nil {
        return nil, fmt.Errorf("could not unmarshal configReceiverBytes.secretShare: %v", err)
    }
    public := new(curve.Secp256k1Point)
    err = public.UnmarshalBinary(configReceiverBytes.Public)
    if err != nil {
        return nil, fmt.Errorf("could not unmarshal configReceiverBytes.public: %v", err)
    }
    setup := ot.CorreOTReceiveSetup{}

    err = setup.UnmarshalJSON(configReceiverBytes.Setup)
    if err != nil {
        return nil, fmt.Errorf("could not unmarshal configReceiverBytes.setup: %v", err)
    }

    configReceiver := doerner.ConfigReceiver{
        Setup:       &setup,
        SecretShare: secretShare,
        Public:      public,
        ChainKey:    configReceiverBytes.ChainKey,
    }
    return &configReceiver, nil
}

We have not gone to production yet, but run a lot keygen and signatures as part of our tests and so far, we have not had any issue yet. Thus, we don't need to add more code.