// // Copyright Coinbase, Inc. All Rights Reserved. // // SPDX-License-Identifier: Apache-2.0 // package v0 import ( "crypto/rand" "crypto/subtle" "io" "math/big" "source.quilibrium.com/quilibrium/monorepo/nekryptology/internal" ) // this implements the _coalesced_ multiplication, in which Alice and Bob perform 2 "parallel" multiplication protocols // they wind up with additive sharings of the respective two products. type MultiplySender struct { sender *cOTSender TA []*big.Int multiplicity int } type MultiplyReceiver struct { receiver *cOTReceiver TB []*big.Int omega []byte // stashing this here, though ironically the cOT doesn't need to stash it. multiplicity int } func NewMultiplySender(multiplicity int, receiver *seedOTReceiver) *MultiplySender { sender := newCOTSender(multiplicity, receiver) return &MultiplySender{ sender: sender, TA: make([]*big.Int, multiplicity), multiplicity: multiplicity, } } func NewMultiplyReceiver(multiplicity int, sender *seedOTSender) *MultiplyReceiver { receiver := newCOTReceiver(multiplicity, sender) return &MultiplyReceiver{ receiver: receiver, TB: make([]*big.Int, multiplicity), multiplicity: multiplicity, } } // Algorithm 5. in DKLs. this "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. // note that we're using the optimization now where both multiplications get "coalesced". func (receiver *MultiplyReceiver) encode(beta []*big.Int) ([]byte, error) { // passing beta by value, so that we can mutate it locally. check that this does what i want. bytes := make([][]byte, receiver.multiplicity) params := receiver.receiver.sender.params result := make([]byte, receiver.receiver.l>>3) for i := 0; i < receiver.multiplicity; i++ { bytes[i] = params.Scalar.Bytes(beta[i]) if _, err := rand.Read(result[(1+i*2)*kappa>>3 : (2+i*2)*kappa>>3]); err != nil { return nil, err } } if _, err := rand.Read(result[2*receiver.multiplicity*kappa>>3 : (2*receiver.multiplicity*kappa+s)>>3]); err != nil { return nil, err } for i := 0; i < receiver.multiplicity; i++ { for j := 0; j < kappa; j++ { bit := int(result[((1+2*i)*kappa+j)>>3]) >> (j & 0x07) & 0x01 mask := params.Scalar.Bytes(params.Scalar.Sub(new(big.Int).SetBytes(bytes[i][:]), params.gadget[kappa+j])) subtle.ConstantTimeCopy(bit, bytes[i][:], mask) } // some converting from bytes and back. a bit cumbersome, but in practice this will be negligible // it'd be simpler to just keep running big ints, which we are subtracting from. // but we can only `ConstantTimeCopy` byte slices (as opposed to big ints). so keep them as bytes. for j := 0; j < 2*s; j++ { bit := int(result[(2*receiver.multiplicity*kappa+j)>>3]) >> (j & 0x07) & 0x01 mask := params.Scalar.Bytes(params.Scalar.Sub(new(big.Int).SetBytes(bytes[i][:]), params.gadget[2*kappa+j])) subtle.ConstantTimeCopy(bit, bytes[i][:], mask) } copy(result[2*i*kappa>>3:(2*i+1)*kappa>>3], internal.ReverseScalarBytes(bytes[i][:])) } return result, nil } // MultiplyInit Protocol 5., Multiplication, 3). Bob (receiver) encodes beta and initiates the cOT extension! func (receiver *MultiplyReceiver) MultiplyInit(idExt [32]byte, beta []*big.Int, w io.Writer) error { var err error if receiver.omega, err = receiver.encode(beta); err != nil { return err } return receiver.receiver.init(idExt, receiver.omega, w) } // Multiply Protocol 5., steps 3) 5), 7). Alice _responds_ to Bob's initial cOT message, using a vector of alphas as input. // doesn't actually send that 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 up by taking care of 7), after that, Alice is totally done with multiplication and has stashed the outputs. func (sender *MultiplySender) Multiply(idExt [32]byte, alpha []*big.Int, rw io.ReadWriter) error { inputMain := make([]*big.Int, 2*sender.multiplicity*kappa) inputOT := [2 * s][]*big.Int{} // inputOT := [2 * s][2 * sender.multiplicity]*big.Int{} for i := range inputOT { inputOT[i] = make([]*big.Int, sender.multiplicity) } var err error params := sender.sender.receiver.params for i := 0; i < sender.multiplicity; i++ { for j := 0; j < 2*kappa; j++ { inputMain[kappa*2*i+j] = params.Scalar.Mul(params.gadget[j], alpha[i]) } for j := 0; j < 2*s; j++ { inputOT[j][i] = params.Scalar.Mul(params.gadget[j+2*kappa], alpha[i]) } } if err = sender.sender.transfer(idExt, inputMain, inputOT, rw); err != nil { return err } scalar := sender.sender.receiver.params.Scalar // stash this just to shorten the expressions for i := 0; i < sender.multiplicity; i++ { sender.TA[i] = new(big.Int) for j := 0; j < 2*kappa; j++ { sender.TA[i] = scalar.Add(sender.TA[i], sender.sender.tA[2*kappa*i+j]) } } for i := 0; i < 2*s; i++ { for j := 0; j < sender.multiplicity; j++ { sender.TA[j] = scalar.Add(sender.TA[j], sender.sender.tAOT[i][j]) } } return nil } // MultiplyTransfer 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) MultiplyTransfer(r io.Reader) error { if err := receiver.receiver.transfer(r); err != nil { return err } scalar := receiver.receiver.sender.params.Scalar for i := 0; i < receiver.multiplicity; i++ { receiver.TB[i] = new(big.Int) for j := 0; j < 2*kappa; j++ { receiver.TB[i] = scalar.Add(receiver.TB[i], receiver.receiver.tB[2*kappa*i+j]) } } for i := 0; i < 2*s; i++ { for j := 0; j < receiver.multiplicity; j++ { receiver.TB[j] = scalar.Add(receiver.TB[j], receiver.receiver.tBOT[i][j]) } } return nil } // illustrative helper method which goes through the whole flow for Bob, assuming a channel to pass messages through. func (receiver *MultiplyReceiver) multiply(idExt [32]byte, beta []*big.Int, rw io.ReadWriter) error { if err := receiver.MultiplyInit(idExt, beta, rw); err != nil { return err } return receiver.MultiplyTransfer(rw) } // illustrative helper method which goes through the whole flow for Alice, assuming a channel. func (sender *MultiplySender) multiply(idExt [32]byte, alpha []*big.Int, rw io.ReadWriter) error { return sender.Multiply(idExt, alpha, rw) }