// // Copyright Coinbase, Inc. All Rights Reserved. // // SPDX-License-Identifier: Apache-2.0 // package camshoup import ( "fmt" "math/big" "git.sr.ht/~sircmpwn/go-bare" mod "source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core" ) type encryptionKeyMarshal struct { Y1 [][]byte `bare:"y1"` Y2 []byte `bare:"y2"` Y3 []byte `bare:"y3"` Group []byte `bare:"group"` } // EncryptionKey encrypts a message to a ciphertext from which // zero-knowledge proofs can be derived // as described in section 3.2 in . // n, g are stored in the `PaillierGroup` struct type EncryptionKey struct { y1 []*big.Int y2, y3 *big.Int group *PaillierGroup } func NewKeys(numMsgs uint, group *PaillierGroup) (*EncryptionKey, *DecryptionKey, error) { if numMsgs < 1 { return nil, nil, fmt.Errorf("number of messages should be greater than 0") } x1 := make([]*big.Int, numMsgs) y1 := make([]*big.Int, numMsgs) for i := range x1 { x, err := mod.Rand(group.n2d4) if err != nil { return nil, nil, err } x1[i] = x y1[i] = group.Gexp(x) } x2, err := mod.Rand(group.n2d4) if err != nil { return nil, nil, err } y2 := group.Gexp(x2) x3, err := mod.Rand(group.n2d4) if err != nil { return nil, nil, err } y3 := group.Gexp(x3) dk := &DecryptionKey{ x1, x2, x3, group, } ek := &EncryptionKey{ y1, y2, y3, group, } return ek, dk, nil } // MarshalBinary serializes a key to bytes func (ek EncryptionKey) MarshalBinary() ([]byte, error) { tv := new(encryptionKeyMarshal) var err error tv.Group, err = ek.group.MarshalBinary() if err != nil { return nil, err } tv.Y3 = ek.y3.Bytes() tv.Y2 = ek.y2.Bytes() tv.Y1 = make([][]byte, len(ek.y1)) for i, y := range ek.y1 { tv.Y1[i] = y.Bytes() } return bare.Marshal(tv) } // UnmarshalBinary deserializes a key from bytes func (ek *EncryptionKey) UnmarshalBinary(data []byte) error { tv := new(encryptionKeyMarshal) err := bare.Unmarshal(data, tv) if err != nil { return err } ek.group = new(PaillierGroup) err = ek.group.UnmarshalBinary(tv.Group) if err != nil { return err } ek.y2 = new(big.Int).SetBytes(tv.Y2) ek.y3 = new(big.Int).SetBytes(tv.Y3) ek.y1 = make([]*big.Int, len(tv.Y1)) for i, b := range tv.Y1 { ek.y1[i] = new(big.Int).SetBytes(b) } return nil } // Encrypt multiple messages as described in // `domain` represents a domain separation tag or nonce func (ek EncryptionKey) Encrypt(domain []byte, msgs []*big.Int) (*CipherText, error) { if len(msgs) > len(ek.y1) { return nil, fmt.Errorf("number of messages %d is more than supported by this key %d", len(msgs), len(ek.y1)) } for i, m := range msgs { if m == nil || m.Cmp(ek.group.n) == 1 { return nil, fmt.Errorf("message %d is not valid", i) } } r, err := ek.group.RandForEncrypt() if err != nil { return nil, err } return ek.encryptWithR(domain, msgs, r) } func (ek EncryptionKey) encryptWithR(domain []byte, msgs []*big.Int, r *big.Int) (*CipherText, error) { u := ek.computeU(r) e := ek.computeE(msgs, r) hs, err := ek.group.Hash(u, e, domain) if err != nil { return nil, err } v := ek.computeV(r, hs, true) return &CipherText{u, v, e}, nil } func (ek EncryptionKey) computeE(msgs []*big.Int, r *big.Int) []*big.Int { e := make([]*big.Int, len(msgs)) for i, m := range msgs { y := ek.group.Exp(ek.y1[i], r) hM := ek.group.Hexp(m) e[i] = ek.group.Mul(y, hM) } return e } func (ek EncryptionKey) computeU(r *big.Int) *big.Int { return ek.group.Gexp(r) } // computeV computes the `v` value during encryption // abs is present for code reuse as during the proof of encryption // in the commitment step absolute value is not taken. func (ek EncryptionKey) computeV(r, hash *big.Int, abs bool) *big.Int { // y3 ^ h(u, e, L) y3hs := ek.group.Exp(ek.y3, hash) // y2 * (y3^h(u, e, L)) y2y3hs := ek.group.Mul(ek.y2, y3hs) // (y2y3^h(u, e, L))^r y2y3hsr := ek.group.Exp(y2y3hs, r) if abs { return ek.group.Abs(y2y3hsr) } else { return y2y3hsr } }