ceremonyclient/nekryptology/pkg/verenc/camshoup/encryption_key.go
2023-07-05 00:32:28 -05:00

169 lines
4.0 KiB
Go

//
// 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 <https://shoup.net/papers/verenc.pdf>.
// 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 <https://shoup.net/papers/verenc.pdf>
// `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
}
}