ceremonyclient/verenc/verifiable_encryption.go

302 lines
7.7 KiB
Go

package verenc
import (
"encoding/binary"
"slices"
"sync"
lru "github.com/hashicorp/golang-lru/v2"
"source.quilibrium.com/quilibrium/monorepo/types/crypto"
generated "source.quilibrium.com/quilibrium/monorepo/verenc/generated/verenc"
)
var _ crypto.VerifiableEncryptor = (*MPCitHVerifiableEncryptor)(nil)
type MPCitHVerEncProof struct {
generated.VerencProofAndBlindingKey
}
type MPCitHVerEnc struct {
generated.CompressedCiphertext
BlindingPubkey []uint8
Statement []uint8
}
func MPCitHVerEncProofFromBytes(data []byte) MPCitHVerEncProof {
if len(data) != 9268 {
return MPCitHVerEncProof{}
}
polycom := [][]byte{}
for i := 0; i < 23; i++ {
polycom = append(polycom, data[235+(i*57):292+(i*57)])
}
ctexts := []generated.VerencCiphertext{}
srs := []generated.VerencShare{}
for i := 0; i < 42; i++ {
ctexts = append(ctexts, generated.VerencCiphertext{
C1: data[1546+(i*(57+56+8)) : 1603+(i*(57+56+8))],
C2: data[1603+(i*(57+56+8)) : 1659+(i*(57+56+8))],
I: binary.BigEndian.Uint64(data[1659+(i*(57+56+8)) : 1667+(i*(57+56+8))]),
})
}
for i := 0; i < 22; i++ {
srs = append(srs, generated.VerencShare{
S1: data[6628+(i*(56+56+8)) : 6684+(i*(56+56+8))],
S2: data[6684+(i*(56+56+8)) : 6740+(i*(56+56+8))],
I: binary.BigEndian.Uint64(data[6740+(i*(56+56+8)) : 6748+(i*(56+56+8))]),
})
}
return MPCitHVerEncProof{
generated.VerencProofAndBlindingKey{
BlindingPubkey: data[:57],
EncryptionKey: data[57:114],
Statement: data[114:171],
Challenge: data[171:235],
Polycom: polycom,
Ctexts: ctexts,
SharesRands: srs,
},
}
}
func (p MPCitHVerEncProof) ToBytes() []byte {
output := []byte{}
output = append(output, p.BlindingPubkey...)
output = append(output, p.EncryptionKey...)
output = append(output, p.Statement...)
output = append(output, p.Challenge...)
for _, pol := range p.Polycom {
output = append(output, pol...)
}
for _, ct := range p.Ctexts {
output = append(output, ct.C1...)
output = append(output, ct.C2...)
output = binary.BigEndian.AppendUint64(output, ct.I)
}
for _, sr := range p.SharesRands {
output = append(output, sr.S1...)
output = append(output, sr.S2...)
output = binary.BigEndian.AppendUint64(output, sr.I)
}
return output
}
func (p MPCitHVerEncProof) VerifyStatement(input []byte) bool {
return VerencVerifyStatement(input, p.BlindingPubkey, p.Statement)
}
func (p MPCitHVerEncProof) GetStatement() []byte {
return slices.Clone(p.Statement)
}
func (p MPCitHVerEncProof) Compress() crypto.VerEnc {
compressed := VerencCompress(generated.VerencProof{
BlindingPubkey: p.BlindingPubkey,
EncryptionKey: p.EncryptionKey,
Statement: p.Statement,
Challenge: p.Challenge,
Polycom: p.Polycom,
Ctexts: p.Ctexts,
SharesRands: p.SharesRands,
})
return MPCitHVerEnc{
CompressedCiphertext: compressed,
BlindingPubkey: p.BlindingPubkey,
Statement: p.Statement,
}
}
func (p MPCitHVerEncProof) Verify() bool {
return VerencVerify(generated.VerencProof{
BlindingPubkey: p.BlindingPubkey,
EncryptionKey: p.EncryptionKey,
Statement: p.Statement,
Challenge: p.Challenge,
Polycom: p.Polycom,
Ctexts: p.Ctexts,
SharesRands: p.SharesRands,
})
}
type InlineEnc struct {
iv []byte
ciphertext []byte
}
func MPCitHVerEncFromBytes(data []byte) crypto.VerEnc {
if len(data) < 621 {
return MPCitHVerEnc{}
}
ciphertext := generated.CompressedCiphertext{}
for i := 0; i < 3; i++ {
ciphertext.Ctexts = append(ciphertext.Ctexts, generated.VerencCiphertext{
C1: data[0+(i*(57+56)) : 57+(i*(57+56))],
C2: data[57+(i*(57+56)) : 113+(i*(57+56))],
})
ciphertext.Aux = append(ciphertext.Aux, data[339+(i*56):395+(i*56)])
}
return MPCitHVerEnc{
CompressedCiphertext: ciphertext,
BlindingPubkey: data[507:564],
Statement: data[564:621],
}
}
func (e MPCitHVerEnc) ToBytes() []byte {
output := []byte{}
for _, ct := range e.Ctexts {
output = append(output, ct.C1...)
output = append(output, ct.C2...)
}
for _, a := range e.Aux {
output = append(output, a...)
}
output = append(output, e.BlindingPubkey...)
output = append(output, e.Statement...)
return output
}
func (e MPCitHVerEnc) GetStatement() []byte {
return e.Statement
}
func (e MPCitHVerEnc) Verify(proof []byte) bool {
proofData := MPCitHVerEncProofFromBytes(proof)
return proofData.Verify()
}
type MPCitHVerifiableEncryptor struct {
parallelism int
lruCache *lru.Cache[string, crypto.VerEnc]
}
func NewMPCitHVerifiableEncryptor(parallelism int) *MPCitHVerifiableEncryptor {
cache, err := lru.New[string, crypto.VerEnc](10000)
if err != nil {
panic(err)
}
return &MPCitHVerifiableEncryptor{
parallelism: parallelism,
lruCache: cache,
}
}
func (v *MPCitHVerifiableEncryptor) ProofFromBytes(
data []byte,
) crypto.VerEncProof {
return MPCitHVerEncProofFromBytes(data)
}
func (v *MPCitHVerifiableEncryptor) FromBytes(data []byte) crypto.VerEnc {
return MPCitHVerEncFromBytes(data)
}
func (v *MPCitHVerifiableEncryptor) Encrypt(
data []byte,
publicKey []byte,
) []crypto.VerEncProof {
chunks := ChunkDataForVerenc(data)
results := make([]crypto.VerEncProof, len(chunks))
var wg sync.WaitGroup
throttle := make(chan struct{}, v.parallelism)
for i, chunk := range chunks {
throttle <- struct{}{}
wg.Add(1)
go func(chunk []byte, i int) {
defer func() { <-throttle }()
defer wg.Done()
proof := NewVerencProofEncryptOnly(chunk, publicKey)
results[i] = MPCitHVerEncProof{
generated.VerencProofAndBlindingKey{
BlindingKey: proof.BlindingKey,
BlindingPubkey: proof.BlindingPubkey,
EncryptionKey: proof.EncryptionKey,
Statement: proof.Statement,
Challenge: proof.Challenge,
Polycom: proof.Polycom,
Ctexts: proof.Ctexts,
SharesRands: proof.SharesRands,
},
}
}(chunk, i)
}
wg.Wait()
return results
}
func (v *MPCitHVerifiableEncryptor) EncryptAndCompress(
data []byte,
publicKey []byte,
) []crypto.VerEnc {
chunks := ChunkDataForVerenc(data)
results := make([]crypto.VerEnc, len(chunks))
var wg sync.WaitGroup
throttle := make(chan struct{}, v.parallelism)
for i, chunk := range chunks {
throttle <- struct{}{}
wg.Add(1)
go func(chunk []byte, i int) {
defer func() { <-throttle }()
defer wg.Done()
existing, ok := v.lruCache.Get(string(publicKey) + string(chunk))
if ok {
results[i] = existing
} else {
proof := NewVerencProofEncryptOnly(chunk, publicKey)
result := MPCitHVerEncProof{
generated.VerencProofAndBlindingKey{
BlindingKey: proof.BlindingKey,
BlindingPubkey: proof.BlindingPubkey,
EncryptionKey: proof.EncryptionKey,
Statement: proof.Statement,
Challenge: proof.Challenge,
Polycom: proof.Polycom,
Ctexts: proof.Ctexts,
SharesRands: proof.SharesRands,
},
}
results[i] = result.Compress()
v.lruCache.Add(string(publicKey)+string(chunk), results[i])
}
}(chunk, i)
}
wg.Wait()
return results
}
func (v *MPCitHVerifiableEncryptor) Decrypt(
encrypted []crypto.VerEnc,
decyptionKey []byte,
) []byte {
results := make([][]byte, len(encrypted))
var wg sync.WaitGroup
throttle := make(chan struct{}, v.parallelism)
for i, chunk := range encrypted {
throttle <- struct{}{}
wg.Add(1)
go func(chunk crypto.VerEnc, i int) {
defer func() { <-throttle }()
defer wg.Done()
results[i] = VerencRecover(generated.VerencDecrypt{
BlindingPubkey: chunk.(MPCitHVerEnc).BlindingPubkey,
DecryptionKey: decyptionKey,
Statement: chunk.(MPCitHVerEnc).Statement,
Ciphertexts: chunk.(MPCitHVerEnc).CompressedCiphertext,
})
}(chunk, i)
}
wg.Wait()
return CombineChunkedData(results)
}