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