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

157 lines
4.1 KiB
Go

//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package elgamal
import (
"crypto/aes"
"crypto/cipher"
crand "crypto/rand"
"fmt"
"math/big"
"git.sr.ht/~sircmpwn/go-bare"
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core"
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves"
)
type encryptionKeyMarshal struct {
Value []byte `bare:"value"`
Curve string `bare:"curve"`
}
// EncryptParams is all the options for doing verifiable encryption.
// Message must be supplied and is the value to be encrypted.
// MessageIsHashed defines whether Message represents an unhashed arbitrary
// byte sequence or already represents a scalar in the elliptic curve.
// Domain is an optional domain separation tag and used to generate a separate
// point for the pedersen commitment in El-Gamal ciphertexts.
// Blinding is blinding factor (bf) of the El-Gamal ciphertext. If nil
// a bf is generated at random.
// GenProof indicates whether to return a proof of encryption correctness to the
// corresponding public key.
// ProofNonce is the nonce used to generate the one time proof. This value
// is required if GenProof is true.
type EncryptParams struct {
MessageIsHashed bool
Domain []byte
Blinding curves.Scalar
GenProof bool
ProofNonce []byte
}
// EncryptionKey encrypts a message to a ciphertext from which
// zero-knowledge proofs can be derived
type EncryptionKey struct {
Value curves.Point
}
// NewKeys creates a new key pair for El-Gamal encryption
func NewKeys(curve *curves.Curve) (*EncryptionKey, *DecryptionKey, error) {
if curve == nil {
return nil, nil, fmt.Errorf("invalid curve")
}
x := curve.Scalar.Random(crand.Reader)
if x == nil {
return nil, nil, fmt.Errorf("cannot generate decryption key")
}
value := curve.Point.Generator().Mul(x)
if value == nil {
return nil, nil, fmt.Errorf("cannot generate encryption key")
}
return &EncryptionKey{value}, &DecryptionKey{x}, nil
}
// MarshalBinary serializes a key to bytes
func (ek EncryptionKey) MarshalBinary() ([]byte, error) {
tv := new(encryptionKeyMarshal)
tv.Curve = ek.Value.CurveName()
tv.Value = ek.Value.ToAffineCompressed()
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
}
curve := curves.GetCurveByName(tv.Curve)
if curve == nil {
return fmt.Errorf("unknown curve")
}
value, err := curve.Point.FromAffineCompressed(tv.Value)
if err != nil {
return err
}
ek.Value = value
return nil
}
func (ek EncryptionKey) HomomorphicEncrypt(msg curves.Scalar) (*HomomorphicCipherText, error) {
r := ek.Value.Scalar().Random(crand.Reader)
return &HomomorphicCipherText{
C1: ek.Value.Generator().Mul(r),
C2: ek.Value.Mul(r).Add(ek.Value.Generator().Mul(msg)),
}, nil
}
func (ek EncryptionKey) encryptWithRandNonce(msg []byte, msgIsHashed bool, r curves.Scalar, h curves.Point, nonce []byte) (*CipherText, error) {
// r * Q
t := ek.Value.Mul(r)
// Derive AEAD encryption key
aeadKey, err := core.FiatShamir(new(big.Int).SetBytes(t.ToAffineCompressed()))
if err != nil {
return nil, err
}
block, err := aes.NewCipher(aeadKey)
if err != nil {
return nil, err
}
aesGcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
// C1 = r * G
c1 := ek.Value.Generator().Mul(r)
// C2 = m * H + r * Q
msgScalar := r.New(0)
if msgIsHashed {
msgScalar, err = msgScalar.SetBytes(msg)
if err != nil {
return nil, err
}
} else {
msgScalar = msgScalar.Hash(msg)
}
c2 := h.Mul(msgScalar).Add(t)
aad := c1.ToAffineUncompressed()
aad = append(aad, c2.ToAffineUncompressed()...)
// AAD = C1 || C2
// this protects them from modifications
aead := aesGcm.Seal(nil, nonce, msg, aad)
return &CipherText{
c1, c2, nonce, aead, msgIsHashed,
}, nil
}
func (ek EncryptionKey) genNonce() []byte {
var nonce [12]byte
n, err := crand.Read(nonce[:])
if err != nil {
return nil
}
if n != 12 {
return nil
}
return nonce[:]
}