mpc.go raw

   1  // Multi-party homomorphic encryption session.
   2  //
   3  // MPCSession composes key aggregation, BGV HE, and anti-malleability
   4  // into a single cohesive API for multi-party computation on encrypted data.
   5  //
   6  // Workflow:
   7  //
   8  //  1. All participants agree on a common reference string (seed).
   9  //  2. Each generates a key pair with GenerateSharedA + HEKeyGenWithA.
  10  //  3. Public keys are aggregated via AggregateHEKeys.
  11  //  4. MPCSession is created with the aggregate key.
  12  //  5. Encryption, homomorphic operations, and distributed decryption
  13  //     are performed through the session.
  14  //
  15  // Anti-malleability: every evaluation result is rerandomized, noise-flooded,
  16  // tagged with the session ID, and optionally GPV-signed. Results from
  17  // different sessions cannot be spliced together.
  18  
  19  package ring
  20  
  21  import "errors"
  22  
  23  // MPCSession holds state for a multi-party homomorphic computation.
  24  type MPCSession struct {
  25  	// AggPK is the aggregate public key (sum of all participants' B components).
  26  	AggPK *KEMPublicKey
  27  
  28  	// RLK is the relinearization key for multiplicative operations.
  29  	// nil if only additive/XOR operations are needed.
  30  	RLK *RelinearizationKey
  31  
  32  	// session wraps results with anti-malleability protections.
  33  	session *SessionWrapper
  34  }
  35  
  36  // NewMPCSession creates a multi-party computation session from a set of
  37  // public keys. All keys must share the same A element (from GenerateSharedA).
  38  //
  39  // gpvPK/gpvSK are optional GPV signing keys for result authenticity.
  40  // rlk is optional — only needed for AND/multiplication gates.
  41  func NewMPCSession(pks []*KEMPublicKey, rlk *RelinearizationKey, gpvPK *GPVPublicKey, gpvSK *GPVSecretKey) (*MPCSession, error) {
  42  	aggPK, err := AggregateHEKeys(pks)
  43  	if err != nil {
  44  		return nil, err
  45  	}
  46  
  47  	sw := NewSession(aggPK, gpvPK, gpvSK)
  48  
  49  	return &MPCSession{
  50  		AggPK:   aggPK,
  51  		RLK:     rlk,
  52  		session: sw,
  53  	}, nil
  54  }
  55  
  56  // SessionID returns the unique session identifier.
  57  func (s *MPCSession) SessionID() []byte {
  58  	return s.session.SessionID
  59  }
  60  
  61  // Encrypt encrypts a single bit under the aggregate public key.
  62  func (s *MPCSession) Encrypt(bit int) *HECiphertext {
  63  	return HEEncrypt(s.AggPK, bit)
  64  }
  65  
  66  // XOR performs homomorphic XOR and wraps the result.
  67  func (s *MPCSession) XOR(a, b *HECiphertext) *SecureHEResult {
  68  	ct := HEXOR(a, b)
  69  	return s.session.Wrap(ct)
  70  }
  71  
  72  // AND performs homomorphic AND and wraps the result.
  73  // Requires that the session was created with a relinearization key.
  74  func (s *MPCSession) AND(a, b *HECiphertext) (*SecureHEResult, error) {
  75  	if s.RLK == nil {
  76  		return nil, errors.New("mpc: AND requires relinearization key")
  77  	}
  78  	ct := HEAND(a, b, s.RLK)
  79  	return s.session.Wrap(ct), nil
  80  }
  81  
  82  // NOT performs homomorphic NOT and wraps the result.
  83  func (s *MPCSession) NOT(a *HECiphertext) *SecureHEResult {
  84  	ct := HENot(a)
  85  	return s.session.Wrap(ct)
  86  }
  87  
  88  // Add performs homomorphic addition and wraps the result.
  89  func (s *MPCSession) Add(a, b *HECiphertext) *SecureHEResult {
  90  	ct := HEAdd(a, b)
  91  	return s.session.Wrap(ct)
  92  }
  93  
  94  // Verify checks anti-malleability protections on a received result.
  95  func (s *MPCSession) Verify(result *SecureHEResult) bool {
  96  	return s.session.Verify(result)
  97  }
  98  
  99  // Unwrap extracts the ciphertext from a verified result.
 100  // Returns nil if verification fails.
 101  func (s *MPCSession) Unwrap(result *SecureHEResult) *HECiphertext {
 102  	if !s.Verify(result) {
 103  		return nil
 104  	}
 105  	return result.Ciphertext
 106  }
 107  
 108  // DecryptDistributed combines partial decryptions to recover the plaintext bit.
 109  // Each party must call PartialDecrypt with their own secret key and contribute
 110  // the result. The partials slice must contain exactly one partial from each
 111  // participant, in any order.
 112  func DecryptDistributed(ct *HECiphertext, partials []*Poly) int {
 113  	return CombinePartialDecryptions(ct, partials)
 114  }
 115