mirror of
				https://source.quilibrium.com/quilibrium/ceremonyclient.git
				synced 2025-11-04 00:27:41 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			272 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			272 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
//
 | 
						|
// Copyright Coinbase, Inc. All Rights Reserved.
 | 
						|
// Copyright Quilibrium, Inc. All Rights Reserved.
 | 
						|
//
 | 
						|
// SPDX-License-Identifier: Apache-2.0
 | 
						|
//
 | 
						|
 | 
						|
package sign
 | 
						|
 | 
						|
import (
 | 
						|
	"crypto/rand"
 | 
						|
	"crypto/subtle"
 | 
						|
	"fmt"
 | 
						|
	"math/big"
 | 
						|
 | 
						|
	"github.com/gtank/merlin"
 | 
						|
	"github.com/pkg/errors"
 | 
						|
	"golang.org/x/crypto/sha3"
 | 
						|
 | 
						|
	"source.quilibrium.com/quilibrium/monorepo/nekryptology/internal"
 | 
						|
	"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves"
 | 
						|
	"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/ot/base/simplest"
 | 
						|
	"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/ot/extension/kos"
 | 
						|
)
 | 
						|
 | 
						|
// This implements the Multiplication protocol of DKLs, protocol 5. https://eprint.iacr.org/2018/499.pdf
 | 
						|
// two parties---the "sender" and "receiver", let's say---each input a scalar modulo q.
 | 
						|
// the functionality multiplies their two scalars modulo q, and then randomly additively shares the product mod q.
 | 
						|
// it then returns the two respective additive shares to the two parties.
 | 
						|
 | 
						|
// MultiplySender is the party that plays the role of Sender in the multiplication protocol (protocol 5 of the paper).
 | 
						|
type MultiplySender struct {
 | 
						|
	cOtSender           *kos.Sender   // underlying cOT sender struct, used by mult.
 | 
						|
	outputAdditiveShare curves.Scalar // ultimate output share of mult.
 | 
						|
	gadget              []curves.Scalar
 | 
						|
	curve               *curves.Curve
 | 
						|
	transcript          *merlin.Transcript
 | 
						|
	uniqueSessionId     [simplest.DigestSize]byte
 | 
						|
}
 | 
						|
 | 
						|
// MultiplyReceiver is the party that plays the role of Sender in the multiplication protocol (protocol 5 of the paper).
 | 
						|
type MultiplyReceiver struct {
 | 
						|
	cOtReceiver         *kos.Receiver // underlying cOT receiver struct, used by mult.
 | 
						|
	outputAdditiveShare curves.Scalar // ultimate output share of mult.
 | 
						|
	omega               []byte        // this is used as an intermediate result during the course of mult.
 | 
						|
	gadget              []curves.Scalar
 | 
						|
	curve               *curves.Curve
 | 
						|
	transcript          *merlin.Transcript
 | 
						|
	uniqueSessionId     [simplest.DigestSize]byte
 | 
						|
}
 | 
						|
 | 
						|
func generateGadgetVector(kappa uint, l uint, curve *curves.Curve) ([]curves.Scalar, error) {
 | 
						|
	var err error
 | 
						|
	gadget := make([]curves.Scalar, l)
 | 
						|
	for i := uint(0); i < kappa; i++ {
 | 
						|
		gadget[i], err = curve.Scalar.SetBigInt(new(big.Int).Lsh(big.NewInt(1), uint(i)))
 | 
						|
		if err != nil {
 | 
						|
			return gadget, errors.Wrap(err, "creating gadget scalar from big int")
 | 
						|
		}
 | 
						|
	}
 | 
						|
	shake := sha3.NewCShake256(nil, []byte("Coinbase DKLs gadget vector"))
 | 
						|
	for i := kappa; i < l; i++ {
 | 
						|
		var err error
 | 
						|
		bytes := [simplest.DigestSize]byte{}
 | 
						|
		if _, err = shake.Read(bytes[:]); err != nil {
 | 
						|
			return gadget, err
 | 
						|
		}
 | 
						|
		gadget[i], err = curve.Scalar.SetBytes(bytes[:])
 | 
						|
		if err != nil {
 | 
						|
			return gadget, errors.Wrap(err, "creating gadget scalar from bytes")
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return gadget, nil
 | 
						|
}
 | 
						|
 | 
						|
// NewMultiplySender generates a `MultiplySender` instance, ready to take part in multiplication as the "sender".
 | 
						|
// You must supply it the _output_ of a seed OT, from the receiver's point of view, as well as params and a unique ID.
 | 
						|
// That is, the mult sender must run the base OT as the receiver; note the (apparent) reversal of roles.
 | 
						|
func NewMultiplySender(kappa uint, s uint, seedOtResults *simplest.ReceiverOutput, curve *curves.Curve, uniqueSessionId [simplest.DigestSize]byte) (*MultiplySender, error) {
 | 
						|
	sender := kos.NewCOtSender(kappa, s, seedOtResults, curve)
 | 
						|
	gadget, err := generateGadgetVector(sender.Kappa, sender.L, curve)
 | 
						|
	if err != nil {
 | 
						|
		return nil, errors.Wrap(err, "error generating gadget vector in new multiply sender")
 | 
						|
	}
 | 
						|
 | 
						|
	transcript := merlin.NewTranscript("Coinbase_DKLs_Multiply")
 | 
						|
	transcript.AppendMessage([]byte("session_id"), uniqueSessionId[:])
 | 
						|
	return &MultiplySender{
 | 
						|
		cOtSender:       sender,
 | 
						|
		curve:           curve,
 | 
						|
		transcript:      transcript,
 | 
						|
		uniqueSessionId: uniqueSessionId,
 | 
						|
		gadget:          gadget,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
// NewMultiplyReceiver generates a `MultiplyReceiver` instance, ready to take part in multiplication as the "receiver".
 | 
						|
// You must supply it the _output_ of a seed OT, from the sender's point of view, as well as params and a unique ID.
 | 
						|
// That is, the mult sender must run the base OT as the sender; note the (apparent) reversal of roles.
 | 
						|
func NewMultiplyReceiver(kappa uint, s uint, seedOtResults *simplest.SenderOutput, curve *curves.Curve, uniqueSessionId [simplest.DigestSize]byte) (*MultiplyReceiver, error) {
 | 
						|
	receiver := kos.NewCOtReceiver(kappa, s, seedOtResults, curve)
 | 
						|
	gadget, err := generateGadgetVector(receiver.Kappa, receiver.L, curve)
 | 
						|
	if err != nil {
 | 
						|
		return nil, errors.Wrap(err, "error generating gadget vector in new multiply receiver")
 | 
						|
	}
 | 
						|
	transcript := merlin.NewTranscript("Coinbase_DKLs_Multiply")
 | 
						|
	transcript.AppendMessage([]byte("session_id"), uniqueSessionId[:])
 | 
						|
	return &MultiplyReceiver{
 | 
						|
		cOtReceiver:     receiver,
 | 
						|
		curve:           curve,
 | 
						|
		transcript:      transcript,
 | 
						|
		uniqueSessionId: uniqueSessionId,
 | 
						|
		gadget:          gadget,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
// MultiplyRound2Output is the output of the second round of the multiplication protocol.
 | 
						|
type MultiplyRound2Output struct {
 | 
						|
	COTRound2Output *kos.Round2Output
 | 
						|
	R               []curves.Scalar
 | 
						|
	U               curves.Scalar
 | 
						|
}
 | 
						|
 | 
						|
// Algorithm 5. in DKLs. "Encodes" Bob's secret input scalars `beta` in the right way, using the opts.
 | 
						|
// The idea is that if Bob were to just put beta's as the choice vector, then Alice could learn a few of Bob's bits.
 | 
						|
// using selective failure attacks. so you subtract random components of a public random vector. see paper for details.
 | 
						|
func (receiver *MultiplyReceiver) encode(beta curves.Scalar) ([]byte, error) {
 | 
						|
	// passing beta by value, so that we can mutate it locally. check that this does what i want.
 | 
						|
	encoding := make([]byte, receiver.cOtReceiver.COtBlockSizeBytes)
 | 
						|
	bytesOfBetaMinusDotProduct := beta.Bytes()
 | 
						|
	if _, err := rand.Read(encoding[receiver.cOtReceiver.KappaBytes:]); err != nil {
 | 
						|
		return encoding, errors.Wrap(err, "sampling `gamma` random bytes in multiply receiver encode")
 | 
						|
	}
 | 
						|
	for j := receiver.cOtReceiver.Kappa; j < receiver.cOtReceiver.L; j++ {
 | 
						|
		jthBitOfGamma := simplest.ExtractBitFromByteVector(encoding[:], int(j))
 | 
						|
		// constant-time computation of the dot product beta - < gR, gamma >.
 | 
						|
		// we can only `ConstantTimeCopy` byte slices (as opposed to big ints). so keep them as bytes.
 | 
						|
		option0, err := receiver.curve.Scalar.SetBytes(bytesOfBetaMinusDotProduct[:])
 | 
						|
		if err != nil {
 | 
						|
			return encoding, errors.Wrap(err, "setting masking bits scalar from bytes")
 | 
						|
		}
 | 
						|
		option0Bytes := option0.Bytes()
 | 
						|
		option1 := option0.Sub(receiver.gadget[j])
 | 
						|
		option1Bytes := option1.Bytes()
 | 
						|
		bytesOfBetaMinusDotProduct = option0Bytes
 | 
						|
		subtle.ConstantTimeCopy(int(jthBitOfGamma), bytesOfBetaMinusDotProduct[:], option1Bytes)
 | 
						|
	}
 | 
						|
	copy(encoding[0:receiver.cOtReceiver.KappaBytes], internal.ReverseScalarBytes(bytesOfBetaMinusDotProduct[:]))
 | 
						|
	return encoding, nil
 | 
						|
}
 | 
						|
 | 
						|
// Round1Initialize Protocol 5., Multiplication, 3). Bob (receiver) encodes beta and initiates the cOT extension
 | 
						|
func (receiver *MultiplyReceiver) Round1Initialize(beta curves.Scalar) (*kos.Round1Output, error) {
 | 
						|
	var err error
 | 
						|
	if receiver.omega, err = receiver.encode(beta); err != nil {
 | 
						|
		return nil, errors.Wrap(err, "encoding input beta in receiver round 1 initialize")
 | 
						|
	}
 | 
						|
	cOtRound1Output, err := receiver.cOtReceiver.Round1Initialize(receiver.uniqueSessionId, receiver.omega)
 | 
						|
	if err != nil {
 | 
						|
		return nil, errors.Wrap(err, "error in cOT round 1 initialize within multiply round 1 initialize")
 | 
						|
	}
 | 
						|
	// write the output of the first round to the transcript
 | 
						|
	for i := uint(0); i < receiver.cOtReceiver.Kappa; i++ {
 | 
						|
		label := []byte(fmt.Sprintf("row %d of U", i))
 | 
						|
		receiver.transcript.AppendMessage(label, cOtRound1Output.U[i][:])
 | 
						|
	}
 | 
						|
	receiver.transcript.AppendMessage([]byte("wPrime"), cOtRound1Output.WPrime[:])
 | 
						|
	receiver.transcript.AppendMessage([]byte("vPrime"), cOtRound1Output.VPrime[:])
 | 
						|
	return cOtRound1Output, nil
 | 
						|
}
 | 
						|
 | 
						|
// Round2Multiply Protocol 5., steps 3) 5), 7). Alice _responds_ to Bob's initial cOT message, using alpha as input.
 | 
						|
// Doesn't actually send the message yet, only stashes it and moves onto the next steps of the multiplication protocol
 | 
						|
// specifically, Alice can then do step 5) (compute the outputs of the multiplication protocol), also stashes this.
 | 
						|
// Finishes by taking care of 7), after that, Alice is totally done with multiplication and has stashed the outputs.
 | 
						|
func (sender *MultiplySender) Round2Multiply(alpha curves.Scalar, round1Output *kos.Round1Output) (*MultiplyRound2Output, error) {
 | 
						|
	var err error
 | 
						|
	alphaHat := sender.curve.Scalar.Random(rand.Reader)
 | 
						|
	input := make([][]curves.Scalar, sender.cOtSender.L) // sender's input, namely integer "sums" in case w_j == 1.
 | 
						|
	for j := uint(0); j < sender.cOtSender.L; j++ {
 | 
						|
		input[j] = make([]curves.Scalar, 2)
 | 
						|
		input[j][0] = alpha
 | 
						|
		input[j][1] = alphaHat
 | 
						|
	}
 | 
						|
	round2Output := &MultiplyRound2Output{}
 | 
						|
	round2Output.R = make([]curves.Scalar, sender.cOtSender.L)
 | 
						|
	round2Output.COTRound2Output, err = sender.cOtSender.Round2Transfer(sender.uniqueSessionId, input, round1Output)
 | 
						|
	if err != nil {
 | 
						|
		return nil, errors.Wrap(err, "error in cOT within round 2 multiply")
 | 
						|
	}
 | 
						|
	// write the output of the first round to the transcript
 | 
						|
	for i := uint(0); i < sender.cOtSender.Kappa; i++ {
 | 
						|
		label := []byte(fmt.Sprintf("row %d of U", i))
 | 
						|
		sender.transcript.AppendMessage(label, round1Output.U[i][:])
 | 
						|
	}
 | 
						|
	sender.transcript.AppendMessage([]byte("wPrime"), round1Output.WPrime[:])
 | 
						|
	sender.transcript.AppendMessage([]byte("vPrime"), round1Output.VPrime[:])
 | 
						|
	// write our own output of the second round to the transcript
 | 
						|
	chiWidth := 2
 | 
						|
	for i := uint(0); i < sender.cOtSender.Kappa; i++ {
 | 
						|
		for k := 0; k < chiWidth; k++ {
 | 
						|
			label := []byte(fmt.Sprintf("row %d of Tau", i))
 | 
						|
			sender.transcript.AppendMessage(label, round2Output.COTRound2Output.Tau[i][k].Bytes())
 | 
						|
		}
 | 
						|
	}
 | 
						|
	chi := make([]curves.Scalar, chiWidth)
 | 
						|
	for k := 0; k < 2; k++ {
 | 
						|
		label := []byte(fmt.Sprintf("draw challenge chi %d", k))
 | 
						|
		randomBytes := sender.transcript.ExtractBytes(label, int(sender.cOtSender.KappaBytes))
 | 
						|
		chi[k], err = sender.curve.Scalar.SetBytes(randomBytes)
 | 
						|
		if err != nil {
 | 
						|
			return nil, errors.Wrap(err, "setting chi scalar from bytes")
 | 
						|
		}
 | 
						|
	}
 | 
						|
	sender.outputAdditiveShare = sender.curve.Scalar.Zero()
 | 
						|
	for j := uint(0); j < sender.cOtSender.L; j++ {
 | 
						|
		round2Output.R[j] = sender.curve.Scalar.Zero()
 | 
						|
		for k := 0; k < chiWidth; k++ {
 | 
						|
			round2Output.R[j] = round2Output.R[j].Add(chi[k].Mul(sender.cOtSender.OutputAdditiveShares[j][k]))
 | 
						|
		}
 | 
						|
		sender.outputAdditiveShare = sender.outputAdditiveShare.Add(sender.gadget[j].Mul(sender.cOtSender.OutputAdditiveShares[j][0]))
 | 
						|
	}
 | 
						|
	round2Output.U = chi[0].Mul(alpha).Add(chi[1].Mul(alphaHat))
 | 
						|
	return round2Output, nil
 | 
						|
}
 | 
						|
 | 
						|
// Round3Multiply Protocol 5., Multiplication, 3) and 6). Bob finalizes the cOT extension.
 | 
						|
// using that and Alice's multiplication message, Bob completes the multiplication protocol, including checks.
 | 
						|
// At the end, Bob's values tB_j are populated.
 | 
						|
func (receiver *MultiplyReceiver) Round3Multiply(round2Output *MultiplyRound2Output) error {
 | 
						|
	chiWidth := 2
 | 
						|
	// write the output of the second round to the transcript
 | 
						|
	for i := uint(0); i < receiver.cOtReceiver.Kappa; i++ {
 | 
						|
		for k := 0; k < chiWidth; k++ {
 | 
						|
			label := []byte(fmt.Sprintf("row %d of Tau", i))
 | 
						|
			receiver.transcript.AppendMessage(label, round2Output.COTRound2Output.Tau[i][k].Bytes())
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if err := receiver.cOtReceiver.Round3Transfer(round2Output.COTRound2Output); err != nil {
 | 
						|
		return errors.Wrap(err, "error within cOT round 3 transfer within round 3 multiply")
 | 
						|
	}
 | 
						|
	var err error
 | 
						|
	chi := make([]curves.Scalar, chiWidth)
 | 
						|
	for k := 0; k < chiWidth; k++ {
 | 
						|
		label := []byte(fmt.Sprintf("draw challenge chi %d", k))
 | 
						|
		randomBytes := receiver.transcript.ExtractBytes(label, int(receiver.cOtReceiver.KappaBytes))
 | 
						|
		chi[k], err = receiver.curve.Scalar.SetBytes(randomBytes)
 | 
						|
		if err != nil {
 | 
						|
			return errors.Wrap(err, "setting chi scalar from bytes")
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	receiver.outputAdditiveShare = receiver.curve.Scalar.Zero()
 | 
						|
	for j := uint(0); j < receiver.cOtReceiver.L; j++ {
 | 
						|
		// compute the LHS of bob's step 6) for j. note that we're "adding r_j" to both sides"; so this LHS includes r_j.
 | 
						|
		// the reason to do this is so that the constant-time (i.e., independent of w_j) calculation of w_j * u can proceed more cleanly.
 | 
						|
		leftHandSideOfCheck := round2Output.R[j]
 | 
						|
		for k := 0; k < chiWidth; k++ {
 | 
						|
			leftHandSideOfCheck = leftHandSideOfCheck.Add(chi[k].Mul(receiver.cOtReceiver.OutputAdditiveShares[j][k]))
 | 
						|
		}
 | 
						|
		uBytes := round2Output.U.Bytes()
 | 
						|
		rightHandSideOfCheck := make([]byte, len(uBytes))
 | 
						|
		jthBitOfOmega := simplest.ExtractBitFromByteVector(receiver.omega[:], int(j))
 | 
						|
		subtle.ConstantTimeCopy(int(jthBitOfOmega), rightHandSideOfCheck[:], uBytes)
 | 
						|
		if subtle.ConstantTimeCompare(rightHandSideOfCheck[:], leftHandSideOfCheck.Bytes()) != 1 {
 | 
						|
			return fmt.Errorf("alice's values R and U failed to check in round 3 multiply")
 | 
						|
		}
 | 
						|
		receiver.outputAdditiveShare = receiver.outputAdditiveShare.Add(receiver.gadget[j].Mul(receiver.cOtReceiver.OutputAdditiveShares[j][0]))
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 |