mirror of
				https://source.quilibrium.com/quilibrium/ceremonyclient.git
				synced 2025-11-04 11:37:27 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			268 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			268 lines
		
	
	
		
			7.6 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"
 | 
						|
 | 
						|
	"source.quilibrium.com/quilibrium/monorepo/nekryptology/internal"
 | 
						|
	mod "source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core"
 | 
						|
)
 | 
						|
 | 
						|
// ProofVerEnc is a proof of verifiable encryption for a discrete log
 | 
						|
type ProofVerEnc struct {
 | 
						|
	challenge, r *big.Int
 | 
						|
	m            []*big.Int
 | 
						|
}
 | 
						|
 | 
						|
type proofMarshal struct {
 | 
						|
	M         [][]byte `bare:"m"`
 | 
						|
	R         []byte   `bare:"r"`
 | 
						|
	Challenge []byte   `bare:"challenge"`
 | 
						|
}
 | 
						|
 | 
						|
func (pf ProofVerEnc) MarshalBinary() ([]byte, error) {
 | 
						|
	tv := new(proofMarshal)
 | 
						|
	tv.R = pf.r.Bytes()
 | 
						|
	tv.Challenge = pf.challenge.Bytes()
 | 
						|
	tv.M = make([][]byte, len(pf.m))
 | 
						|
	for i, m := range pf.m {
 | 
						|
		tv.M[i] = m.Bytes()
 | 
						|
	}
 | 
						|
 | 
						|
	return bare.Marshal(tv)
 | 
						|
}
 | 
						|
 | 
						|
func (pf *ProofVerEnc) UnmarshalBinary(data []byte) error {
 | 
						|
	tv := new(proofMarshal)
 | 
						|
	err := bare.Unmarshal(data, tv)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	pf.r = new(big.Int).SetBytes(tv.R)
 | 
						|
	pf.challenge = new(big.Int).SetBytes(tv.Challenge)
 | 
						|
	pf.m = make([]*big.Int, len(tv.M))
 | 
						|
	for i, m := range tv.M {
 | 
						|
		pf.m[i] = new(big.Int).SetBytes(m)
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// EncryptAndProve is a NIZK where the ciphertext and commitments are computed (t values).
 | 
						|
// The blindings are generated as part of calling this function
 | 
						|
// Return ciphertext and proof created during encryption.
 | 
						|
// "The protocol" from section 5.2 in <https://shoup.net/papers/verenc.pdf>
 | 
						|
// Not using t = g^m*h^s as the idemix protocol does not use it.
 | 
						|
// Guess is that since the knowledge of m is proved in the credential attribute proving protocol.
 | 
						|
// Use this function if the proof is by itself and not part of a bigger composite proof.
 | 
						|
func (ek EncryptionKey) EncryptAndProve(nonce []byte, msgs []*big.Int) (*CipherText, *ProofVerEnc, error) {
 | 
						|
	var err error
 | 
						|
	blindings := make([]*big.Int, len(msgs))
 | 
						|
	for i := 0; i < len(blindings); i++ {
 | 
						|
		blindings[i], err = ek.group.RandForEncrypt()
 | 
						|
		if err != nil {
 | 
						|
			return nil, nil, err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return ek.EncryptAndProveBlindings(nonce, msgs, blindings)
 | 
						|
}
 | 
						|
 | 
						|
// EncryptAndProveBlindings is a NIZK where the ciphertext and commitments are computed (t values).
 | 
						|
// The blindings are generated prior to calling this function
 | 
						|
// Return ciphertext and proof created during encryption.
 | 
						|
// "The protocol" from section 5.2 in <https://shoup.net/papers/verenc.pdf>
 | 
						|
// Not using t = g^m*h^s as the idemix protocol does not use it.
 | 
						|
// Guess is that since the knowledge of m is proved in the credential attribute proving protocol.
 | 
						|
// Use this function if the proof will be part of more proofs.
 | 
						|
func (ek EncryptionKey) EncryptAndProveBlindings(nonce []byte, msgs []*big.Int, blindings []*big.Int) (*CipherText, *ProofVerEnc, error) {
 | 
						|
	if len(msgs) != len(blindings) {
 | 
						|
		return nil, nil, fmt.Errorf("number of messages %d != number of blindings %d", len(msgs), len(blindings))
 | 
						|
	}
 | 
						|
	if len(msgs) > len(ek.y1) {
 | 
						|
		return nil, nil, fmt.Errorf("number of messages %d is more than supported by this key %d", len(msgs), len(ek.y1))
 | 
						|
	}
 | 
						|
	for i, b := range blindings {
 | 
						|
		if b == nil {
 | 
						|
			return nil, nil, internal.ErrNilArguments
 | 
						|
		}
 | 
						|
		if msgs[i] == nil {
 | 
						|
			return nil, nil, internal.ErrNilArguments
 | 
						|
		}
 | 
						|
		if b.Cmp(mod.Zero) == 0 {
 | 
						|
			return nil, nil, internal.ErrZeroValue
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	r, err := ek.group.RandForEncrypt()
 | 
						|
	if err != nil {
 | 
						|
		return nil, nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	rBlinding, err := ek.group.RandForEncrypt()
 | 
						|
	if err != nil {
 | 
						|
		return nil, nil, err
 | 
						|
	}
 | 
						|
	ciphertext, err := ek.encryptWithR(nonce, msgs, r)
 | 
						|
	if err != nil {
 | 
						|
		return nil, nil, err
 | 
						|
	}
 | 
						|
	hs, err := ek.group.Hash(ciphertext.u, ciphertext.e, nonce)
 | 
						|
	if err != nil {
 | 
						|
		return nil, nil, err
 | 
						|
	}
 | 
						|
	ciphertextTValues, err := ek.ciphertextTestValues(rBlinding, hs, blindings)
 | 
						|
	if err != nil {
 | 
						|
		return nil, nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	challenge, err := ek.fiatShamir(nonce, ciphertext, ciphertextTValues)
 | 
						|
	if err != nil {
 | 
						|
		return nil, nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	// generate the schnorr proofs
 | 
						|
	rHat := ek.schnorr(rBlinding, challenge, r)
 | 
						|
	mHat := make([]*big.Int, len(msgs))
 | 
						|
	for i, m := range msgs {
 | 
						|
		mHat[i] = ek.schnorr(blindings[i], challenge, m)
 | 
						|
	}
 | 
						|
 | 
						|
	return ciphertext, &ProofVerEnc{
 | 
						|
		challenge: challenge,
 | 
						|
		r:         rHat,
 | 
						|
		m:         mHat,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
// ciphertextTestValues computes commitments for the ciphertext when proving encryption is correct
 | 
						|
func (ek EncryptionKey) ciphertextTestValues(r, hash *big.Int, msgs []*big.Int) (*CipherText, error) {
 | 
						|
	twoR := new(big.Int).Lsh(r, 1)
 | 
						|
	twoMsgs := make([]*big.Int, len(msgs))
 | 
						|
	for i, m := range msgs {
 | 
						|
		twoMsgs[i] = new(big.Int).Lsh(m, 1)
 | 
						|
	}
 | 
						|
	u := ek.computeU(twoR)
 | 
						|
	e := ek.computeE(twoMsgs, twoR)
 | 
						|
	v := ek.computeV(twoR, hash, false)
 | 
						|
	return &CipherText{u, v, e}, nil
 | 
						|
}
 | 
						|
 | 
						|
// fiatShamir computes h(n, g, Y2, Y3, Y1, C.U, C.V, C.E, CT.U, CT.V, CT.E)
 | 
						|
func (ek EncryptionKey) fiatShamir(nonce []byte, ciphertext *CipherText, ciphertextTValues *CipherText) (*big.Int, error) {
 | 
						|
	hValues := make([][]byte, len(ciphertext.e)+len(ciphertextTValues.e)+len(ek.y1)+9)
 | 
						|
	hValues[0] = ek.group.n.Bytes()
 | 
						|
	hValues[1] = ek.group.g.Bytes()
 | 
						|
	hValues[2] = ek.y2.Bytes()
 | 
						|
	hValues[3] = ek.y3.Bytes()
 | 
						|
	offset := 4
 | 
						|
	for _, y := range ek.y1 {
 | 
						|
		hValues[offset] = y.Bytes()
 | 
						|
		offset++
 | 
						|
	}
 | 
						|
	hValues[offset] = ciphertext.u.Bytes()
 | 
						|
	offset++
 | 
						|
	for _, n := range ciphertext.e {
 | 
						|
		hValues[offset] = n.Bytes()
 | 
						|
		offset++
 | 
						|
	}
 | 
						|
	hValues[offset] = ciphertext.v.Bytes()
 | 
						|
	offset++
 | 
						|
 | 
						|
	hValues[offset] = ciphertextTValues.u.Bytes()
 | 
						|
	offset++
 | 
						|
	for _, n := range ciphertextTValues.e {
 | 
						|
		hValues[offset] = n.Bytes()
 | 
						|
		offset++
 | 
						|
	}
 | 
						|
	hValues[offset] = ciphertextTValues.v.Bytes()
 | 
						|
 | 
						|
	hValues[len(hValues)-1] = nonce
 | 
						|
	h, err := internal.Hash([]byte("Coinbase Hash 1.0"), hValues...)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return new(big.Int).SetBytes(h), nil
 | 
						|
}
 | 
						|
 | 
						|
// schnorr computes tilde - challenge * value mod n^2
 | 
						|
func (ek EncryptionKey) schnorr(tilde, challenge, value *big.Int) *big.Int {
 | 
						|
	r := ek.group.Mul(challenge, value)
 | 
						|
	t := new(big.Int).Sub(tilde, r)
 | 
						|
	return t
 | 
						|
}
 | 
						|
 | 
						|
// VerifyEncryptProof a Proof of Verifiable Encryption
 | 
						|
// See section 6.2.19 in <https://dominoweb.draco.res.ibm.com/reports/rz3730_revised.pdf>
 | 
						|
func (ek EncryptionKey) VerifyEncryptProof(nonce []byte, ciphertext *CipherText, proof *ProofVerEnc) error {
 | 
						|
	if ciphertext == nil || proof == nil {
 | 
						|
		return internal.ErrNilArguments
 | 
						|
	}
 | 
						|
	if proof.r == nil || proof.challenge == nil || proof.m == nil {
 | 
						|
		return internal.ErrNilArguments
 | 
						|
	}
 | 
						|
	if ciphertext.u == nil || ciphertext.v == nil || ciphertext.e == nil {
 | 
						|
		return internal.ErrNilArguments
 | 
						|
	}
 | 
						|
	if len(proof.m) > len(ek.y1) {
 | 
						|
		return fmt.Errorf("number of messages %d is more than supported by this key %d", len(proof.m), len(ek.y1))
 | 
						|
	}
 | 
						|
 | 
						|
	// Reconstruct u
 | 
						|
	// 2c
 | 
						|
	c2 := new(big.Int).Lsh(proof.challenge, 1)
 | 
						|
	// 2r
 | 
						|
	r2 := new(big.Int).Lsh(proof.r, 1)
 | 
						|
 | 
						|
	// u^{2c} mod n^2
 | 
						|
	uc := ek.group.Exp(ciphertext.u, c2)
 | 
						|
 | 
						|
	// Reconstruct e
 | 
						|
	// g^{2r} mod n^2
 | 
						|
	gr := ek.group.Gexp(r2)
 | 
						|
 | 
						|
	// u^{2c} * g^{2r} mod n^2
 | 
						|
	u := ek.group.Mul(uc, gr)
 | 
						|
 | 
						|
	e := make([]*big.Int, len(proof.m))
 | 
						|
	for i, mm := range proof.m {
 | 
						|
		// e^{2c}
 | 
						|
		ec := ek.group.Exp(ciphertext.e[i], c2)
 | 
						|
		// y1^{2r}
 | 
						|
		yr := ek.group.Exp(ek.y1[i], r2)
 | 
						|
		// h^{2m}
 | 
						|
		hm := ek.group.Hexp(new(big.Int).Lsh(mm, 1))
 | 
						|
		// e = ec * yr * hm mod n^2
 | 
						|
		e[i] = ek.group.Mul(ek.group.Mul(ec, yr), hm)
 | 
						|
	}
 | 
						|
 | 
						|
	// Reconstruct v
 | 
						|
	// v^{2c}
 | 
						|
	hs, err := ek.group.Hash(ciphertext.u, ciphertext.e, nonce)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	vc := ek.group.Exp(ciphertext.v, c2)
 | 
						|
	y3hs := ek.group.Exp(ek.y3, hs)
 | 
						|
	y2y3hs := ek.group.Mul(ek.y2, y3hs)
 | 
						|
	y2y3hsr := ek.group.Exp(y2y3hs, r2)
 | 
						|
	v := ek.group.Mul(vc, y2y3hsr)
 | 
						|
 | 
						|
	ciphertextTestValues := &CipherText{u, v, e}
 | 
						|
	challenge, err := ek.fiatShamir(nonce, ciphertext, ciphertextTestValues)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	if challenge.Cmp(proof.challenge) == 0 {
 | 
						|
		return nil
 | 
						|
	} else {
 | 
						|
		return fmt.Errorf("invalid ciphertext")
 | 
						|
	}
 | 
						|
}
 |