MIFE — Multi-Input Functional Encryption

Multi-input functional encryption schemes: the key generator and crypto class of every scheme of the family (the registry: pyfe4ai.core.registry).

The key generator and crypto class of every scheme below are importable from pyfe4ai.schemes.mife under their own names (one name per class, no aliases; the naming rule is in pyfe4ai.core.registry).

DDH

Multi Input Functional Encryption (DDH) | From “Multi-Input Functional Encryption for Inner Products: | Function-Hiding Realizations and Constructions without Pairings” | Published in: CRYPTO 2018 | By: Michel Abdalla, Dario Catalano, Dario Fiore, Romain Gay, and Bogdan Ursu | URL: https://eprint.iacr.org/2017/972.pdf – Fig. 4, instantiated with the | DDH IPFE of Abdalla, Bourse, De Caro, Pointcheval (PKC 2015, Construction 1) | in every slot.

  • type: private-key multi-input encryption

  • setting: Integer based (order-q subgroup of Z_p^*, p = 2q + 1)

Slot i has an independent IPFE key s_i in Z_q^eta (h_i = g^{s_i}) and a one-time pad u_i in Z_q^eta; client i holds (h_i, u_i).

  • Enc: ct_i = (g^r, g^{x_i + u_i} h_i^r) (every element reduced mod p).

  • KeyGen: d_i = <s_i, y_i> mod q per slot and z = sum_i <u_i, y_i> mod q.

  • Dec: prod_i (prod_j c_{i,j}^{y_{i,j}} / t_i^{d_i}) / g^z = g^{<x, y>}, then a discrete log bounded by dlog_bound.

Each slot’s partial result is g^{<x_i + u_i, y_i>}: the pad hides <x_i, y_i>, only the sum over all slots is revealed. As in every MIFE, there are no labels – a key holder may combine any ciphertexts of each slot, so clients must not reuse a key epoch across independent inputs (use MCFE for repeated aggregation). Deviation from ACFGU18, which uses the adaptively secure ALS16 IPFE: ABDP15 is selectively secure. Versions <= 0.2.0 did not reduce ciphertexts modulo p (their factors revealed x + u) nor keys modulo q (a key with a large weight revealed the pads).

class pyfe4ai.schemes.mife.ddh.MIFEDDHKeyGenerator(config, **kwargs)[source]

Bases: DDHKeyGeneratorBase

Key generator for DDH-based multi-input inner-product FE.

Parameters:

config (dict)

__init__(config, **kwargs)[source]

Initialise the MIFE key generator.

Parameters:

config (dict) – Scheme configuration. Recognised keys: sec_param, eta (int or per-client dict), n (number of clients), s, lst_nid (client identifiers).

Return type:

None

setup()[source]

Sample an IPFE key s_i and a pad u_i for every slot.

Return type:

None

get_public_parameters()[source]

Return serialisable public parameters (g, p, q, n, eta, sec_param).

Return type:

dict

get_private_keys(nid)[source]

Return client nid’s encryption key {h, u} (None if unknown).

Parameters:

nid (str)

Return type:

dict | None

get_decryption_keys(sid, **kwargs)[source]

Derive the functional key for weights y covering every slot.

Parameters:
  • sid (str) – Session identifier (unused).

  • **kwargs – credentials with fusion_weight (dict mapping every client ID to eta integer weights).

Return type:

dict | None

Returns:

Dict with d (<s_i, y_i> mod q per slot), z and weights_id.

class pyfe4ai.schemes.mife.ddh.MIFEDDH(config)[source]

Bases: OfflineRandomMixin, IPFEAbsCrypto

Crypto operations for DDH-based multi-input inner-product FE.

Parameters:

config (dict)

__init__(config)[source]

Initialise the MIFE crypto system.

Parameters:

config (dict) – Must contain keys with pp and, for clients, sk; dlog_bound (default 10^(precision + 2)) bounds the inner products decryption recovers.

Return type:

None

encrypt(lst_pt)[source]

Encrypt this client’s eta integers: (g^r, g^{x_j + u_j} h_j^r).

Raises:
Parameters:

lst_pt (list)

Return type:

dict

decrypt(dct_ct, dk, fusion_weight)[source]

Recover sum_i <x_i, y_i> from one ciphertext per slot.

Returns:

The inner product, or None if it is out of the range the discrete-log table covers (at least |<x, y>| <= dlog_bound).

Raises:
  • FEKeyError – If the decryption key is missing.

  • FESchemeError – If ciphertexts, key and weights do not cover the same slots or the weights are not the key’s.

  • FEValidationError – If a ciphertext is malformed.

Parameters:
encrypt_lst_ndarray(lst_ndarray, **kwargs)[source]

Encrypt ndarrays element-wise (eta = 1).

Every element is a separate ciphertext under the same slot key, so a holder of one functional key can combine elements of different positions (MIFE has no labels): use these helpers for one input per key epoch, and MCFE for federated aggregation.

Parameters:

lst_ndarray (list)

Return type:

list | None

decrypt_lst_ndarray_ct(dict_ndarray_ct, dk, fusion_weight)[source]

Decrypt ndarray ciphertexts from all clients element-wise.

Parameters:
Return type:

list | None

compute_lst_ndarray_ct(dict_ndarray_ct, **kwargs)[source]

Aggregate ndarray ciphertexts (delegates to decrypt_lst_ndarray_ct()).

Parameters:

dict_ndarray_ct (dict)

Return type:

list | None

DDH over ristretto255

Multi Input Functional Encryption (DDH) over ristretto255 | From “Multi-Input Functional Encryption for Inner Products: | Function-Hiding Realizations and Constructions without Pairings” | Published in: CRYPTO 2018 | By: Michel Abdalla, Dario Catalano, Dario Fiore, Romain Gay, and Bogdan Ursu | URL: https://eprint.iacr.org/2017/972.pdf – Fig. 4, instantiated with the | DDH IPFE of Abdalla, Bourse, De Caro, Pointcheval (PKC 2015, Construction 1) | in every slot.

  • type: private-key multi-input encryption

  • setting: the prime-order group ristretto255 (RFC 9496), written additively

As pyfe4ai.schemes.mife.ddh in ristretto255. Slot i has an IPFE key s_i in Z_L^eta (H_i = s_i G) and a one-time pad u_i in Z_L^eta; client i holds (H_i, u_i).

  • Enc: ct_i = (r G, (x_i + u_i) G + r H_i).

  • KeyGen: d_i = <s_i, y_i> mod L per slot and z = sum_i <u_i, y_i> mod L.

  • Dec: sum_i (sum_j y_{i,j} c_{i,j} - d_i t_i) - z G = <x, y> G, then a discrete log bounded by dlog_bound.

As in every MIFE there are no labels: clients must not reuse a key epoch across independent inputs (use MCFE for repeated aggregation).

class pyfe4ai.schemes.mife.ddh_ristretto255.MIFEDDHRistretto255KeyGenerator(config, **kwargs)[source]

Bases: ECDDHKeyGeneratorBase

Key generator for DDH-based multi-input inner-product FE over ristretto255.

Parameters:

config (dict)

__init__(config, **kwargs)[source]

Check the configuration.

Parameters:

config (dict) – n (number of clients), lst_nid, eta (int or per-client dict), group (optional, "ristretto255").

Return type:

None

setup()[source]

Sample an IPFE key s_i and a pad u_i for every slot.

Return type:

None

get_public_parameters()[source]

Return serialisable public parameters.

Return type:

dict

Returns:

A dict of public parameters, or None on error.

get_private_keys(nid)[source]

Client nid’s encryption key {h, u} (None if unknown).

Parameters:

nid (str)

Return type:

dict | None

get_decryption_keys(sid, **kwargs)[source]

{d, z, weights_id} for weights covering every slot.

Parameters:

sid (str)

Return type:

dict | None

class pyfe4ai.schemes.mife.ddh_ristretto255.MIFEDDHRistretto255(config, **kwargs)[source]

Bases: ECDDHCryptoBase

Crypto operations for DDH-based multi-input inner-product FE over ristretto255.

Parameters:

config (dict)

encrypt(lst_pt)[source]

{t: r G, c: [(x_j + u_j) G + r H_j]} for this client’s eta integers.

Parameters:

lst_pt (list)

Return type:

dict

decrypt(dct_ct, dk, fusion_weight)[source]

sum_i <x_i, y_i>, or None if beyond dlog_bound.

Raises:
  • FEKeyError – If the decryption key is missing.

  • FESchemeError – If ciphertexts, key and weights do not cover the same slots or the weights are not the key’s.

  • FEValidationError – If a ciphertext is malformed.

Parameters:
encrypt_lst_ndarray(lst_ndarray, **kwargs)[source]

Encrypt ndarrays element-wise (eta = 1); one input per key epoch.

Parameters:

lst_ndarray (list)

Return type:

list | None

decrypt_lst_ndarray_ct(dict_ndarray_ct, dk, fusion_weight)[source]

Decrypt aggregated ndarray ciphertexts.

Parameters:
  • dict_ndarray_ct (dict) – Mapping of client IDs to their ciphertext lists.

  • dk (dict)

  • fusion_weight (dict)

Return type:

list | None

Returns:

One float array of decrypted results per layer.

Raises:

FESchemeError – If an element does not decrypt (see decoded_layer()).

compute_lst_ndarray_ct(dict_ndarray_ct, **kwargs)[source]

Aggregate ciphertexts from multiple clients.

Parameters:

dict_ndarray_ct (dict) – Mapping of client IDs to their ciphertext lists.

Return type:

list | None

Returns:

Aggregated ciphertext list, or None on error.

DDH Threshold

Threshold multi-input inner-product FE (DDH), in the setting of | R. Xu et al., “TAPFed: Threshold Secure Aggregation for Privacy-Preserving | Federated Learning”, IEEE TDSC, doi: 10.1109/TDSC.2024.3350206.

  • type: private-key multi-input encryption with threshold decryption

  • setting: Integer based (order-q subgroup of Z_p^*, p = 2q + 1)

The MIFE of pyfe4ai.schemes.mife.ddh (ACFGU18 Fig. 4 with the ABDP15 DDH IPFE per slot). Its functional key – the per-slot d_i and the pad term z – is Shamir-shared modulo q among s decryption servers; any t of them decrypt by interpolating in the exponent, as the threshold layer of pyfe4ai.schemes.mcfe.ddh_threshold:

  • server k with Lagrange coefficient lambda_k publishes c_y = prod_{i,j} c_{i,j}^{y_{i,j}} (public) and w_k = prod_i t_i^{lambda_k d_i^(k)} g^{lambda_k z^(k)};

  • the combiner computes c_y / prod_k w_k = g^{<x, y>} and a bounded discrete log.

Fewer than t servers learn nothing about the key, and a set of partial decryptions reveals no more than the MIFE key holder learns (the sum). Versions <= 0.2.0 used an own IPFE replicated over eta rows and did not bind key shares to their weights.

class pyfe4ai.schemes.mife.ddh_threshold.ThresholdMIFEDDHKeyGenerator(config, **kwargs)[source]

Bases: MIFEDDHKeyGenerator

MIFE-DDH setup plus Shamir-shared functional keys.

Parameters:

config (dict)

__init__(config, **kwargs)[source]

Initialise the key generator.

Parameters:

config (dict) – As for MIFEDDHKeyGenerator, plus t and lst_sid (s decryption servers).

Return type:

None

property lambd: int

Alias of sec_param (name used by versions <= 0.2.0).

setup()[source]

MIFE setup; forgets previously shared keys.

Return type:

None

get_public_parameters()[source]

MIFE public parameters plus t and lst_sid.

Return type:

dict

get_decryption_keys(sid, **kwargs)[source]

Return server sid’s Shamir share of the functional key for y.

All servers asking for the same weights receive shares of one key.

Return type:

dict | None

Returns:

Dict with d (per slot), z, sid, lst_sid and weights_id, or None if sid is unknown.

Parameters:

sid (str)

class pyfe4ai.schemes.mife.ddh_threshold.ThresholdMIFEDDH(config)[source]

Bases: MIFEDDH

Encryption (with quantisation) and threshold decryption.

Parameters:

config (dict)

encrypt(pt)[source]

Encrypt eta values, quantised with 10^precision.

Parameters:

pt (list)

Return type:

dict

share_decrypt(dict_ct, credentials, dk, lst_sid_enrolled)[source]

Compute this server’s partial decryption for an enrolled set.

Parameters:
  • dict_ct (dict) – One ciphertext per client.

  • credentials (dict) – The fusion weights (client ID -> weights) of the key.

  • dk (dict) – This server’s key share.

  • lst_sid_enrolled (list) – The servers whose partials will be combined.

Return type:

dict

Returns:

Dict with c_y, w, ct_digest, sid and lst_sid_enrolled.

combine_decrypt(partials)[source]

Combine partial decryptions from at least t servers.

Parameters:

partials – {sid: share_decrypt(...)} for one enrolled set, or a list of such mappings for several enrolled sets, whose results must then agree (see combine_consistently(): servers are assumed honest-but-curious, and only this redundancy detects one that returns a wrong partial).

Raises:

FESchemeError – If a group is incomplete or invalid, its partial decryptions disagree on the ciphertexts they decrypted, or the groups disagree.

Return type:

float | None

encrypt_lst_ndarray(lst_ndarray, **kwargs)[source]

Encrypt ndarrays element-wise (eta = 1); see MIFEDDH.encrypt_lst_ndarray.

Parameters:

lst_ndarray (list)

Return type:

list

compute_lst_ndarray_ct(dict_ndarray_ct, credentials, dk, lst_sid_enrolled)[source]

Partially decrypt ndarray ciphertexts element-wise.

Parameters:
Return type:

list

decrypt_lst_ndarray_ct(dict_ndarray_ct, **kwargs)[source]

Combine element-wise partial decryptions from at least t servers.

Raises:

FESchemeError – If the servers’ outputs differ in structure, an element does not decrypt (out of range), or the partial decryptions of an element disagree on the ciphertexts (the error and message of combine_decrypt).

Parameters:

dict_ndarray_ct (dict)

Return type:

list

Damgård DDH

DDH (Damgard-style) multi-input inner-product FE.

Abdalla, Catalano, Fiore, Gay, Ursu:
“Multi-Input Functional Encryption for Inner Products: Function-Hiding
Realizations and Constructions without Pairings”, CRYPTO 2018
(ePrint 2017/972) – Fig. 4 (transformation for bounded-norm inner products)
instantiated with the DDH IPFE of Agrawal, Libert, Stehle (CRYPTO 2016),
recalled as Fig. 7 (D_k-MDDH, here k = 1) with its two-step decryption in
Section 4.1.
  • type: private-key multi-input encryption (MIFE, DDH)

  • setting: Integer based (order-q subgroup of Z_p^*, p = 2q + 1)

Construction (slot i is client i; [a] = g^a, h = g^a):

  • Setup: W_i = (s_i, t_i) <- (Z_q^eta)^2, pk_{i,j} = g^{s_{i,j}} h^{t_{i,j}}, one-time pad u_i <- Z_q^eta; client i keeps (pk_i, u_i).

  • Encrypt: w = x_i + u_i mod q; C = g^r, D = h^r, E_j = g^{w_j} pk_{i,j}^r.

  • KeyGen: per slot (<s_i, y_i>, <t_i, y_i>) and z = sum_i <u_i, y_i> mod q.

  • Decrypt: Dec1 per slot [<w_i, y_i>] = prod_j E_j^{y_j} / (C^{<s,y>} D^{<t,y>}); [<x, y>] = prod_i [<w_i, y_i>] / [z], then a bounded dlog (Dec2).

The pad makes each slot’s partial result [<x_i + u_i, y_i>] uniform, so the key holder learns only <x, y>; versions <= 0.2.0 omitted it and revealed every <x_i, y_i>. Deviation: all slots share the group and h (the matrix A of Fig. 7) while W_i and u_i are independent per slot, instead of running Setup independently per slot. A decryption needs ciphertexts of every slot named in the key.

class pyfe4ai.schemes.mife.damgard_ddh.MIFEDamgardKeyGenerator(config, **kwargs)[source]

Bases: DamgardDDHKeyGeneratorBase

Key generator for DDH multi-input inner-product FE (ACFGU18 Fig. 4 + ALS16).

Parameters:

config (dict)

__init__(config, **kwargs)[source]

Initialise the key generator.

Parameters:

config (dict) – Scheme configuration. Recognised keys: n (number of slots), lst_nid, eta (slot dimension), modulus_length (bits of p), bound (on |x| and |y|), sec_param.

Return type:

None

setup()[source]

Sample W_i = (s_i, t_i) and the one-time pad u_i for every slot.

Return type:

None

get_public_parameters()[source]

Return serialisable public parameters.

Return type:

dict

get_private_keys(nid, **kwargs)[source]

Return slot nid’s encryption key {pk, u} (None if unknown).

The key includes the one-time pad and must stay with the client.

Parameters:

nid (str)

Return type:

dict | None

get_decryption_keys(sid, **kwargs)[source]

Derive ({(<s_i, y_i>, <t_i, y_i>)}_i, z = sum <u_i, y_i> mod q).

Parameters:
  • sid (str) – Session identifier (unused).

  • **kwargs – Must include credentials with fusion_weight (dict mapping every client ID to an integer weight list).

Raises:
Return type:

dict | None

class pyfe4ai.schemes.mife.damgard_ddh.MIFEDamgard(config, **kwargs)[source]

Bases: IPFEAbsCrypto

Crypto operations for DDH multi-input inner-product FE (ACFGU18 Fig. 4 + ALS16).

Parameters:

config (dict)

__init__(config, **kwargs)[source]

Initialise with keys.pp and, for clients, keys.sk.

Parameters:

config (dict)

Return type:

None

encrypt(lst_pt)[source]

Encrypt this slot’s vector: ALS16 encryption of x + u mod q.

Return type:

dict

Returns:

Dict with c (g^r), d (h^r) and e (one element per coordinate).

Parameters:

lst_pt (list)

decrypt(dct_ct, dk, fusion_weight)[source]

Recover sum_i <x_i, y_i> from one ciphertext per slot.

Returns:

The inner product, or None if it is out of the dlog bound.

Raises:
Parameters:
encrypt_lst_ndarray(lst_ndarray, **kwargs)[source]

Encrypt ndarrays element-wise (eta = 1).

Parameters:

lst_ndarray (list)

Return type:

list | None

compute_lst_ndarray_ct(dict_ndarray_ct, **kwargs)[source]

Aggregate-decrypt ndarray ciphertexts (needs dk and fusion_weight).

Parameters:

dict_ndarray_ct (dict)

Return type:

list | None

decrypt_lst_ndarray_ct(dict_ndarray_ct, **kwargs)[source]

Decrypt ndarray ciphertexts element-wise.

Parameters:

dict_ndarray_ct (dict)

Return type:

list | None

LWE

LWE multi-input inner-product FE (selective security).

Abdalla, Catalano, Fiore, Gay, Ursu:
“Multi-Input Functional Encryption for Inner Products: Function-Hiding
Realizations and Constructions without Pairings”, CRYPTO 2018
(ePrint 2017/972) – Fig. 4 (transformation for bounded-norm inner
products), instantiated with the LWE IPFE of Abdalla, Bourse, De Caro,
Pointcheval, “Simple Functional Encryption Schemes for Inner Products”,
PKC 2015 (full version: ePrint 2015/017), using the two-step decryption
of ACFGU18 Section 4.2.
  • type: private-key multi-input encryption (MIFE, LWE)

  • setting: Integer based (Z_q, plaintext modulus p, Delta = floor(q / p))

Construction (slot i is client i, L = q):

  • Setup: public A; per slot S_i (lwe_n x eta uniform), pk_i = A S_i + E_i and a one-time pad u_i <- Z_q^eta; client i keeps (pk_i, u_i).

  • Encrypt: w = x_i + u_i mod q; ct0 = A^T r, ct1 = pk_i^T r + Delta * w mod q (r binary).

  • KeyGen: per slot sk_i = S_i y_i mod q, and z = sum_i <u_i, y_i> mod q.

  • Decrypt: Dec1 per slot <y_i, ct1> - <ct0, sk_i> = Delta <w_i, y_i> + noise_i; mu = round(centre(sum_i Dec1_i - Delta z) / Delta) (Dec2).

The pad hides each slot’s partial result; versions <= 0.2.0 had no pad and encoded floor(x q / p), so the key holder decoded every <x_i, y_i>. Deviations: ACFGU18 instantiates Fig. 4 with the adaptively secure ALS16 LWE IPFE (that is pyfe4ai.schemes.mife.fullysec_lwe); this module uses the selectively secure ABDP15 IPFE, whose two-step decryption is the same. All slots share A (secrets and pads are independent per slot). A decryption needs ciphertexts of every slot named in the key.

Security of a slot’s IPFE rests on the LWE proof of the ABDP15 full version, Thm. 4.4 with Thms. 6.6 and 6.7 (see pyfe4ai.schemes.sife.lwe). Here it reduces to LWE with errors of width sigma / (1 + 4 X sqrt(eta)), since the per-slot challenge vectors of the ACFGU18 reduction differ by up to 4 X per coordinate; this composition is the library’s own, and neither paper covers it. The pad makes the slot plaintexts w uniform mod q; the proof only needs their difference to be short. standard reads the 128-bit table at that width.

A is published as a seed and expanded with SHAKE-128; encryption runs in numpy over an A kept in memory or expanded per batch (matrix_mode), see pyfe4ai.schemes.common.abdp15_lwe. Versions <= 0.2.0 published A entry by entry.

class pyfe4ai.schemes.mife.lwe.MIFELWEKeyGenerator(config, **kwargs)[source]

Bases: IPFEAbsKeyGenerator, ABDP15KeyGeneratorMixin

Key generator for LWE-based multi-input inner-product FE.

Parameters:

config (dict)

__init__(config, **kwargs)[source]

Initialise the MIFE LWE key generator.

Parameters:

config (dict) – Scheme configuration. Recognised keys: sec_param, eta, n (number of clients), lwe_n (default 32; "auto" for the smallest dimension meeting the 128-bit table, the standard profile’s choice), bound_x, bound_y and matrix_mode ("memory" or "stream", see pyfe4ai.schemes.common.abdp15_lwe).

Return type:

None

setup()[source]

Generate per-client secret keys and public keys (one pass over A).

Return type:

None

get_public_parameters()[source]

Return serialisable public parameters.

Return type:

dict

Returns:

Dict containing A_seed, m, lwe_n, p, q, eta, n, bound_x, bound_y, sec_param.

get_private_keys(nid, **kwargs)[source]

Return encryption keys for client nid.

Parameters:

nid (str) – Client identifier.

Return type:

dict | None

Returns:

Dict with pk matrix, or None if nid is invalid.

get_decryption_keys(sid, **kwargs)[source]

Derive a functional decryption key for the given fusion weights.

Parameters:
  • sid (str) – Session identifier.

  • **kwargs – Must include credentials with fusion_weight (dict mapping client IDs to weight lists).

Return type:

dict | None

Returns:

Dict with sk_y (per-client projected secret keys).

Raises:
class pyfe4ai.schemes.mife.lwe.MIFELWE(config, **kwargs)[source]

Bases: IPFEAbsCrypto, ABDP15CryptoMixin

Crypto operations for LWE-based multi-input inner-product FE.

Parameters:

config (dict)

__init__(config, **kwargs)[source]

Initialise the MIFE LWE crypto system.

Parameters:

config (dict) – Must contain keys with pp (public parameters) and optionally sk (encryption keys); optional matrix_mode ("memory" or "stream").

Return type:

None

encrypt(lst_pt)[source]

Encrypt a plaintext vector for one client.

Parameters:

lst_pt (list) – Integer plaintext vector of length eta.

Return type:

dict

Returns:

Dict with ct0 and ct1 (serialised LWE ciphertext vectors).

Raises:
decrypt(dct_ct, dk, fusion_weight)[source]

Decrypt aggregated ciphertexts to recover the multi-input inner product.

Parameters:
  • dct_ct (dict) – Mapping of client IDs to their ciphertext dicts.

  • dk (dict) – Decryption key dict with sk_y (per-client).

  • fusion_weight (dict) – Mapping of client IDs to weight lists.

Returns:

The inner product as an integer.

Raises:
encrypt_lst_ndarray(lst_ndarray, **kwargs)[source]

Encrypt a list of ndarrays element-wise (requires eta=1).

Parameters:

lst_ndarray (list) – List of numpy arrays to encrypt.

Return type:

list | None

Returns:

List of object ndarrays containing per-element ciphertexts.

compute_lst_ndarray_ct(dict_ndarray_ct, **kwargs)[source]

Aggregate ndarray ciphertexts (delegates to decrypt_lst_ndarray_ct()).

Parameters:
  • dict_ndarray_ct (dict) – Mapping of client IDs to ciphertext ndarray lists.

  • **kwargs – Must include dk and fusion_weight.

Return type:

list | None

Returns:

Decrypted ndarray list.

decrypt_lst_ndarray_ct(dict_ndarray_ct, **kwargs)[source]

Decrypt ndarray ciphertexts from multiple clients.

Parameters:
  • dict_ndarray_ct (dict) – Mapping of client IDs to ciphertext ndarray lists.

  • **kwargs – Must include dk and fusion_weight.

Return type:

list | None

Returns:

List of float ndarrays with decrypted values.

Fully-Secure LWE

LWE multi-input inner-product FE (adaptive security).

Abdalla, Catalano, Fiore, Gay, Ursu:
“Multi-Input Functional Encryption for Inner Products: Function-Hiding
Realizations and Constructions without Pairings”, CRYPTO 2018
(ePrint 2017/972) – Fig. 4 (transformation for bounded-norm inner
products) instantiated with the LWE IPFE of Agrawal, Libert, Stehle
(CRYPTO 2016, Section 4.1), recalled as Fig. 8 with its two-step decryption
in Section 4.2.
  • type: private-key multi-input encryption (MIFE, LWE)

  • setting: Integer based (Z_q, Delta = floor(q / K))

Construction (slot i is client i, L = q):

  • Setup*: parameters of ALS16 derived as for vectors of dimension n eta and plaintext bound 3X (Section 4.2, footnote 14 and Fig. 4); public A; per slot Z_i <- D (ALS16 key distribution), U_i = Z_i A and a one-time pad u_i <- Z_q^eta; client i keeps (U_i, u_i).

  • Encrypt: w = x_i + u_i mod q; c0 = A s + e0, c1 = U_i s + e1 + Delta * w mod q.

  • KeyGen: per slot Z_i^T y_i mod q, and z = sum_i <u_i, y_i> mod q.

  • Decrypt: Dec1 per slot <y_i, c1> - <Z_i^T y_i, c0> = Delta <w_i, y_i> + noise_i; mu = round(centre(sum_i Dec1_i - Delta z) / Delta) (Dec2).

The pad hides each slot’s partial result; versions <= 0.2.0 had no pad (the key holder decoded every <x_i, y_i>) and derived single-slot parameters. Deviation: all slots share A instead of running Setup independently per slot (Z_i and u_i are independent). A decryption needs ciphertexts of every slot named in the key.

class pyfe4ai.schemes.mife.fullysec_lwe.MIFEFullySecLWEKeyGenerator(config, **kwargs)[source]

Bases: IPFEAbsKeyGenerator, ParameterCacheMixin

Key generator for fully secure LWE-based multi-input inner-product FE.

Parameters:

config (dict)

__init__(config, **kwargs)[source]

Initialise the MIFE fully secure LWE key generator.

Parameters:

config (dict) – Scheme configuration. Recognised keys: n (number of clients, required), lst_nid, eta, lwe_n (default 64), bound_x and bound_y. The standard profile refuses this scheme.

Return type:

None

setup()[source]

Generate master public and secret keys for the scheme.

Return type:

None

get_public_parameters()[source]

Return serialisable public parameters.

Return type:

dict

Returns:

A dict of public parameters, or None on error.

get_private_keys(nid, **kwargs)[source]

Return client nid’s encryption key (pk and its pad u).

Parameters:

nid (str) – Client identifier.

Return type:

dict | None

Returns:

The key dict, or None for an unknown client.

get_decryption_keys(sid, **kwargs)[source]

Derive a functional decryption key for the given fusion weights.

Parameters:
  • sid (str) – Session identifier.

  • **kwargs – Must include credentials with fusion_weight (dict {nid: list of int}).

Return type:

dict | None

class pyfe4ai.schemes.mife.fullysec_lwe.MIFEFullySecLWE(config, **kwargs)[source]

Bases: IPFEAbsCrypto

Crypto operations for fully secure LWE-based multi-input inner-product FE.

Parameters:

config (dict)

__init__(config, **kwargs)[source]

Initialise the MIFE fully secure LWE crypto object.

Parameters:

config (dict) – Must contain keys with pp (public parameters) and optionally sk (encryption keys).

Return type:

None

encrypt(lst_pt)[source]
Parameters:

lst_pt (list)

Return type:

dict

decrypt(dct_ct, dk, fusion_weight)[source]

Decrypt ciphertexts and recover the inner product.

Parameters:
  • dct_ct (dict) – Ciphertext dict (or dict of per-client ciphertexts).

  • dk (dict) – Functional decryption key.

  • fusion_weight (dict) – Fusion weight vector (or dict of per-client weight vectors).

encrypt_lst_ndarray(lst_ndarray, **kwargs)[source]

Encrypt a list of ndarrays element-wise (requires eta = 1).

Parameters:

lst_ndarray (list) – List of numpy arrays, scaled by 10^precision.

Return type:

list | None

Returns:

One object array of ciphertexts per input array.

Raises:

NotImplementedError – If eta != 1.

compute_lst_ndarray_ct(dict_ndarray_ct, **kwargs)[source]

Compute inner products on encrypted ndarray ciphertexts.

Parameters:

dict_ndarray_ct (dict) – Encrypted ndarray ciphertexts (list or dict).

Return type:

list | None

decrypt_lst_ndarray_ct(dict_ndarray_ct, **kwargs)[source]

Decrypt encrypted ndarray ciphertexts element-wise.

Parameters:

dict_ndarray_ct (dict) – Encrypted ndarray ciphertexts (list or dict).

Return type:

list | None

LWE Threshold

Threshold LWE multi-input inner-product FE: ACFGU18 MIFE-LWE with a Shamir-shared functional key and statistically flooded partial decryptions.

MIFE layer: pyfe4ai.schemes.mife.lwe (ACFGU18 Fig. 4 over the
ABDP15 LWE IPFE).
Threshold layer in the setting of R. Xu et al., “TAPFed: Threshold Secure
Aggregation for Privacy-Preserving Federated Learning”, IEEE TDSC 2024:
the functional key is Shamir-shared among s servers, any t decrypt.
  • type: private-key multi-input encryption, threshold functional decryption

  • setting: Integer based (Z_q, q prime, Delta = floor(q / p))

Construction (notation of pyfe4ai.schemes.mife.lwe):

  • KeyGen: the MIFE key ({sk_i = S_i y_i}, z); every coordinate of every sk_i and z is Shamir-shared with threshold t over Z_q at points 1..s.

  • ShareDec: server k of an enrolled set E publishes w_k = lambda_k (sum_i <ct0_i, sk_i^(k)> + Delta z^(k)) + e_k mod q and c_y = sum_i <y_i, ct1_i> mod q for a consistency check.

  • Combine: c_y - sum_{k in E} w_k = Delta <x, y> + noise - sum e_k; decode.

Flooding: c_y - (sum_i <ct0_i, sk_i> + Delta z) = Delta <x, y> + nu where the ciphertext noise nu = sum_i y_i^T E_i^T r_i is bounded by B = n eta Y m 12 sigma. From the result and the ciphertexts one can compute the exact key part only up to nu; the flooding noise e_k <- D_{Z, 2^kappa B} makes the partial decryptions statistically indistinguishable from ones simulated from the result (the standard smudging argument for threshold LWE decryption). q is enlarged so that s flooding terms still decode: Delta / 2 > B + s * 12 * 2^kappa B; at a fixed lwe_n this lowers the noise-to-modulus ratio of the underlying LWE instance, and m grows with log2 q so that A^T r stays close to uniform (versions <= 0.2.0 kept the m of the smaller base modulus: about half the length the leftover hash lemma needs). e_k is derived from a per-server secret key and the request (see below), so repeating a request cannot be used to average the noise away. The threshold layer is our own design.

Ciphertext authentication: w_k is linear in ct0, so whoever chooses the ciphertexts a server decrypts could submit ct0 = c e_j for chosen c and read the functional key off the partials coordinate by coordinate (the flooding hides a fixed noise term, not a chosen multiple of the key). Client i and server k therefore share a MAC key; every ciphertext carries one HMAC-SHA256 tag per server over its serialised (ct0, ct1), and a server only decrypts ciphertexts whose tags for it verify. A corrupted client can still put any ciphertext in its own slot, which reveals that slot’s key part S_i y_i and nothing about the other slots; mixing authentic ciphertexts of different rounds is the usual multi-input functionality. The noise is seeded with the server’s key, lambda_k, the ct0 values and the key share’s pp_binding (the public parameters it is bound to), so a request for the same key part draws the same noise; a corrupted client varying its own ct0 changes the key part and so draws fresh noise each time. The flooding argument is statistical per answer (a statistical distance of about 2^-stat_param per coefficient, adding up over the answers); it does not bound what many answers to varied ct0 reveal together about the other slots’ part: averaging them would take about 2^(2 stat_param) partial decryptions, but other ways of combining these noisy linear equations are not ruled out.

Sharing (config entry sharing; default "replicated" for t < s and "shamir" for t = s): with "shamir" and t < s – the sharing of versions <= 0.3.0 – the argument above does not hold. With t - 1 servers C corrupted, an honest partial is computable from C’s shares and the result up to e_k - M nu, M = lambda_k^E prod_{j in C} (k - j) / (0 - j), and M is a non-integer rational for some (C, E, k) (6 of 18 for s = 3, t = 2): for M = r / d, d times the difference is the small integer d e_k - r nu, which gives nu mod d whatever sigma_f. Fewer corrupted servers, or none, can suffice (the aggregator alone reads nu modulo (s - 1)! for t = 1), and combining the partials of several enrolled sets raises d (144 for s = 5, t = 3); where M is an integer it reaches 4 (45 for s = 5, t = 3), and the statistical distance of the flooding grows |M|-fold. "replicated" shares the key additively over the (t - 1)-subsets of the servers (see share_replicated()): each server of E adds up the pieces assigned to it, M is 0 or 1 and the smudging argument holds as stated, with q unchanged; a server holds C(s - 1, t - 1) pieces instead of one share. With t = s both are sound (M = 1). An explicit "shamir" with t < s draws an FESecurityWarning under toy (the standard profile refuses the scheme altogether, see below); the two sharings have their own parameter records (construction acfgu18-abdp15-replicated-smudging-seeded-a/v1 for "replicated"), so a record cached by an earlier version for t < s is not reused by the default. Servers refuse Shamir key shares with t < s unless created with legacy_shamir = "warn" (never with public parameters that record the standard profile); key shares of versions <= 0.3.0 are refused in any case (another flooding derivation, see check_flooding_version()); public parameters record threshold_construction and security_profile, and servers refuse constructions they do not know. The opt-in "replicated-seeded" (see share_replicated_seeded()) deals the same kind of pieces, all but one expanded from 32-byte seeds: a server stores seeds and at most one full piece, and its partial decryptions are those of the replicated key share of the expanded pieces; the key shares hide the key computationally (SHAKE-256 keyed by the seeds as a PRF) rather than perfectly. Its record is acfgu18-abdp15-replicated-seeded-smudging-seeded-a/v1.

The standard security profile refuses this scheme. lwe_n = "auto" reads pyfe4ai.primitives.lattice.lwe_utils.WIDE_LWE_128_LOG_RATIO at the width sigma / (1 + 4 X sqrt(eta)) of the MIFE layer’s security reduction (see pyfe4ai.schemes.common.abdp15_lwe). At the flooding-enlarged q of stat_param = 128 (two clients) that takes the very end of the table (5888 to 6144, A of 6.5 to 7.8 * 10^9 entries) for a few small bounds, e.g. X = Y in {1, 2, 3, 8, 10} at eta = 1, and more than the table’s 6144 for most others (X = Y = 4 to 6, X = Y >= 12, most eta >= 2); X = Y = 50 would need about 6500. Read at sigma itself, the earlier rule took 5952 for two clients (q of 217 bits, m of 1.3 * 10^6, A of 7.8 * 10^9 entries, so A had to be streamed): key generation took 38 minutes, one encryption 572 s, a batch of 64 encryptions 679 s and a partial decryption 13 ms.

flooding_accounting = "kl" with max_partial_decryptions = Q (opt-in, t = s with the Shamir sharing only, see pyfe4ai.schemes.threshold.kl) sizes the noise for a run in which every server produces at most Q partial decryptions: KL <= 2^-(stat_param + 1) for the N = s Q flooded values, i.e. sigma_f = B sqrt(N 2^stat_param), with a tail cut derived from N (16 sigma_f at N = 2^40), instead of a statistical distance 2^-stat_param per value. It is a different, weaker guarantee (stat_param - 6.85 bits in the bit-security sense of Micciancio and Walter, about 2^-65 of total statistical distance at stat_param = 128), valid only for the first Q partial decryptions of every server, which each server counts in the partial_budget its deployment gives it. At stat_param = 128 with X = Y = 50, two clients, s = t = 3 and N = 2^40 it would take lwe_n about 4864 at sigma' (q of 173 bits, A of 4.1 * 10^9 entries, so "stream" mode), but the standard profile refuses this scheme with either accounting (a maintainer decision of October 2026); "kl" is available under toy. These parameters have their own record (construction acfgu18-abdp15-shamir-smudging-kl/v2).

class pyfe4ai.schemes.mife.lwe_threshold.ThresholdMIFELWEKeyGenerator(config, **kwargs)[source]

Bases: MIFELWEKeyGenerator

Key generator for threshold MIFE-LWE (ACFGU18 + Shamir + smudging).

Parameters:

config (dict)

__init__(config, **kwargs)[source]

Initialise the key generator.

Parameters:

config (dict) – As for MIFELWEKeyGenerator, plus s (number of servers), t (threshold), lst_sid, stat_param (statistical parameter kappa of the flooding), sharing ("replicated", the default for t < s, "shamir", the default for t = s, or the opt-in "replicated-seeded"; see resolve_sharing()) and, opt-in, flooding_accounting = "kl" with max_partial_decryptions (t = s, Shamir sharing; see the module docstring).

Return type:

None

setup()[source]

Sample the MIFE keys and the client-server MAC keys; forget any previously shared keys.

Return type:

None

get_private_keys(nid, **kwargs)[source]

Return client nid’s MIFE encryption key plus mac_keys ({sid: hex}, one key per server for the ciphertext tags).

Parameters:

nid (str)

Return type:

dict | None

get_public_parameters()[source]

Return the MIFE public parameters plus s, t, lst_sid, sigma_f, sharing, threshold_construction, security_profile and, under "kl", flooding_accounting, max_partial_decryptions, stat_param and flood_tail_cut.

Return type:

dict

get_decryption_keys(sid, **kwargs)[source]

Return server sid’s share of the MIFE functional key.

Parameters:
  • sid (str) – Decryption-server identifier.

  • **kwargs – Must include credentials with fusion_weight.

Return type:

dict | None

Returns:

Dict with sk_y (per-slot share vectors), z (share of the pad term), sid, lst_sid, weights_id, noise_key and mac_keys ({nid: hex}, to verify the clients’ ciphertext tags), or None if sid is unknown. With replicated sharing sk_y and z are replaced by sharing and pieces ([{"set": T, "share": {"sk_y": ..., "z": ...}}]); with "replicated-seeded" pieces holds the correction piece (or nothing), and seeds and layout give the others.

class pyfe4ai.schemes.mife.lwe_threshold.ThresholdMIFELWE(config, **kwargs)[source]

Bases: MIFELWE

Crypto operations for threshold MIFE-LWE (ACFGU18 + Shamir + smudging).

A server refuses Shamir key shares with t < s (versions <= 0.3.0) unless its config has legacy_shamir = "warn" (see LEGACY_SHAMIR_POLICIES).

Parameters:

config (dict)

__init__(config, **kwargs)[source]

As MIFELWE; under flooding_accounting = "kl" a server needs partial_budget, the PartialDecryptionBudget that its deployment keeps for it; legacy_shamir as in the class docstring.

Parameters:

config (dict)

Return type:

None

share_decrypt(dict_ct, credentials, dk, lst_sid_enrolled, _share=None)[source]

Compute this server’s flooded partial decryption.

Parameters:
  • dict_ct (dict) – One ciphertext per slot of the key.

  • credentials (dict) – Mapping of client IDs to the fusion weights the key share was derived for.

  • dk (dict) – This server’s key share.

  • lst_sid_enrolled (list) – The servers whose partial decryptions will be combined (at least t, including this one).

Return type:

dict

Returns:

Dict with w, c_y, ct_digest, sid and lst_sid_enrolled.

Raises:

FESchemeError – If the enrolled set is invalid, the weights are not those of the key share, a ciphertext’s tag for this server does not verify (it was not produced by that slot’s client), or, under flooding_accounting = "kl", the server has no partial_budget or its budget is spent (refused requests consume nothing).

combine_decrypt(partials)[source]

Combine partial decryptions from at least t servers.

Parameters:

partials – {sid: share_decrypt(...)} for one enrolled set, or a list of such mappings for several enrolled sets, whose results must then agree (see combine_consistently(): servers are assumed honest-but-curious, and only this redundancy detects one that returns a wrong partial).

Raises:

FESchemeError – If a group is incomplete or invalid, its partial decryptions disagree on the ciphertexts they decrypted, or the groups disagree.

Return type:

float | None

compute_lst_ndarray_ct(dict_ndarray_ct, credentials, dk, lst_sid_enrolled)[source]

Partially decrypt every element of the slots’ ndarray ciphertexts (under "kl" refused whole unless the budget covers all of them; each partial produced then counts).

Parameters:
Return type:

list

decrypt_lst_ndarray_ct(dict_ndarray_ct, **kwargs)[source]

Combine element-wise partial decryptions from at least t servers.

Raises:

FESchemeError – If the servers’ outputs differ in structure, an element does not decrypt (out of range), or the partial decryptions of an element disagree on the ciphertexts (the error and message of combine_decrypt).

Parameters:

dict_ndarray_ct (dict)

Return type:

list

Ring-LWE

Ring-LWE multi-input inner-product FE.

Bermudo Mera, Karmakar, Marc, Soleimanian:
“Efficient Lattice-Based Inner-Product Functional Encryption”, PKC 2022
(ePrint 2021/046) – Section 6, “MIFE-Compiler” (the transformation of
Abdalla, Catalano, Fiore, Gay, Ursu, CRYPTO 2018) applied to their
Ring-LWE IPFE, which has 2-step decryption with a linear encoding
(Proposition 1).
  • type: private-key multi-input encryption (MIFE, Ring-LWE)

  • setting: R_q = Z_q[x] / (x^ring_n + 1), Delta = floor(q / p)

Construction (slot i is client i; every row of the plaintext matrix is one coordinate, its coefficients a batch of independent values):

  • Setup: public a; per slot s_{i,row} (Gaussian), pk_{i,row} = a s_{i,row} + e, and a one-time pad u_{i,row} <- R_q (uniform coefficients); client i keeps (pk_i, u_i).

  • Encrypt: w = x_i + u_i (coefficient-wise mod q); ct0_row = pk_row r + e_row + Delta w_row, ct1 = a r + e.

  • KeyGen: per slot sk_i = sum_row y_{i,row} s_{i,row} and z = sum_i sum_row y_{i,row} u_{i,row} (coefficient-wise mod q).

  • Decrypt: Dec1 per slot sum_row y_row ct0_row - ct1 sk_i; Dec2 decodes sum_i Dec1_i - Delta z coefficient-wise.

The pad hides each slot’s partial result; versions <= 0.2.0 had no pad and decoded every slot separately. Parameters are derived as for a single-input scheme of dimension n eta (ACFGU18 Setup*), since only the sum over all slots is decoded, and with the Gaussian widths of BMMS21 Sec. 4.2 for messages of norm 3X: ACFGU18 Fig. 4 needs an IPFE for the class F_{m,3X,Y} (footnote 12; the hybrids of Lemma 3 encrypt x^j - x^1 + x'^1), while p stays sized for X. Versions <= 0.3.0 used X, which meets that condition only for nine or more clients. Deviation: all slots share a (secrets and pads are independent per slot). A decryption needs ciphertexts of every slot named in the key.

class pyfe4ai.schemes.mife.ring_lwe.MIFERingLWEKeyGenerator(config, **kwargs)[source]

Bases: IPFEAbsKeyGenerator, ParameterCacheMixin

Key generator for Ring-LWE-based multi-input inner-product FE.

Parameters:

config (dict)

__init__(config, **kwargs)[source]

Initialise the MIFE Ring-LWE key generator.

Parameters:

config (dict) – Scheme configuration. Recognised keys: n (number of clients, required), lst_nid, sec_param (kappa of the noise bounds), eta, bound_x, bound_y and ring_n (default DEFAULT_RING_N; "auto" for the smallest dimension meeting the 128-bit table, the standard profile’s choice); ring_modulus: "prime" (default) or "rns", q the product of two NTT primes (opt-in; see RNS modulus for the Ring-LWE schemes: security review).

Return type:

None

setup()[source]

Generate master public and secret keys for the scheme.

Return type:

None

get_public_parameters()[source]

Return serialisable public parameters.

Return type:

dict

Returns:

A dict of public parameters, or None on error.

get_private_keys(nid, **kwargs)[source]

Return client nid’s encryption key (pk and its pad u).

Parameters:

nid (str) – Client identifier.

Return type:

dict | None

Returns:

The key dict, or None for an unknown client.

get_decryption_keys(sid, **kwargs)[source]

Derive a functional decryption key for the given fusion weights.

Parameters:
  • sid (str) – Session identifier.

  • **kwargs – Must include credentials with fusion_weight (dict {nid: list of int}).

Return type:

dict | None

class pyfe4ai.schemes.mife.ring_lwe.MIFERingLWE(config, **kwargs)[source]

Bases: IPFEAbsCrypto

Crypto operations for Ring-LWE-based multi-input inner-product FE.

Parameters:

config (dict)

__init__(config, **kwargs)[source]

Initialise the MIFE Ring-LWE crypto object.

Parameters:

config (dict) – Must contain keys with pp (public parameters) and optionally sk (encryption keys). ct_format: "json" (default: ct0 and ct1 as decimal strings) or "binary" (ct: bytes, see encode_binary_ciphertext(); about a third of the size, and faster to produce and read). Decryption reads both formats. gaussian_sampler: that of the encryption noise (default: that of pp, else "facct"; see scheme_sampler()).

Return type:

None

property A: list

The public polynomial a, converted from pp["A"] on first use (decryption never needs it).

encrypt(matrix_pt)[source]

Encrypt this slot’s matrix: Ring-LWE encryption of x + u mod q.

Return type:

dict

Returns:

Dict with ct0 (one polynomial per row), ct1 and k (number of used coefficients).

Parameters:

matrix_pt (list[list[int]])

decrypt(dct_ct, dk, fusion_weight)[source]

Recover the batch of inner products sum_i <x_i[:, k], y_i>.

Returns:

List with one inner product per batch position (None entries if a decoded value exceeds n eta X Y).

Raises:
  • FEKeyError – If the key is missing.

  • FESchemeError – If ciphertexts do not cover exactly the key’s slots or have different batch lengths.

Parameters:
encrypt_lst_ndarray(lst_ndarray, **kwargs)[source]

Not implemented for this scheme; raises NotImplementedError.

Parameters:

lst_ndarray (list)

Return type:

list | None

compute_lst_ndarray_ct(dict_ndarray_ct, **kwargs)[source]

Compute inner products on encrypted ndarray ciphertexts.

Parameters:

dict_ndarray_ct (dict) – Encrypted ndarray ciphertexts (list or dict).

Return type:

list | None

decrypt_lst_ndarray_ct(dict_ndarray_ct, **kwargs)[source]

Decrypt encrypted ndarray ciphertexts element-wise.

Parameters:

dict_ndarray_ct (dict) – Encrypted ndarray ciphertexts (list or dict).

Return type:

list | None

Paillier

DCR (Paillier) multi-input inner-product FE.

Abdalla, Catalano, Fiore, Gay, Ursu:
“Multi-Input Functional Encryption for Inner Products: Function-Hiding
Realizations and Constructions without Pairings”, CRYPTO 2018
(ePrint 2017/972) – Fig. 4 (transformation for bounded-norm inner products)
instantiated with the Paillier IPFE of Agrawal, Libert, Stehle (CRYPTO 2016),
recalled as Fig. 9 with its two-step decryption in Section 4.3.
  • type: private-key multi-input encryption (MIFE, DCR)

  • setting: Integer based (Z_{N^2}^*, N = p q with safe primes)

Construction (slot i is client i, L = N):

  • Setup: g = g'^{2N} mod N^2; per slot s_i <- D_{Z^eta, sigma} with sigma > sqrt(kappa) N^{5/2}, h_{i,j} = g^{s_{i,j}} and a one-time pad u_i <- Z_N^eta; client i keeps (h_i, u_i).

  • Encrypt: w = x_i + u_i mod N; C_0 = g^r (r <- {0..N/4}), C_j = (1 + w_j N) h_{i,j}^r.

  • KeyGen: per slot d_i = <s_i, y_i> over Z, and z = sum_i <u_i, y_i> mod N.

  • Decrypt: Dec1 per slot E(<w_i, y_i>) = prod_j C_j^{y_j} C_0^{-d_i}; (prod_i E(<w_i, y_i>) * (1 + N)^{-z} - 1 mod N^2) / N, centred mod N.

The pad hides each slot’s partial result; versions <= 0.2.0 had no pad (the key holder learned every <x_i, y_i>) and drew keys from a range small enough to brute-force from h. Deviation: all slots share N and g instead of running Setup independently per slot (s_i and u_i are independent); the factorization of N is never stored. A decryption needs ciphertexts of every slot named in the key. Correctness needs 2 n eta X Y < N.

class pyfe4ai.schemes.mife.paillier.MIFEPaillierKeyGenerator(config, **kwargs)[source]

Bases: IPFEAbsKeyGenerator, ParameterCacheMixin

Key generator for Paillier-based multi-input inner-product FE.

Parameters:

config (dict)

__init__(config, **kwargs)[source]

Initialise the MIFE Paillier key generator.

Parameters:

config (dict) – Scheme configuration. Recognised keys: n (number of clients, required), lst_nid, eta, bit_length (bits of each prime of N, default 128), bound_x, bound_y and stat_param (default 64).

Return type:

None

setup()[source]

Sample per slot s_i <- D_{Z^eta, sigma} (ALS16) and a pad u_i <- Z_N^eta.

Return type:

None

get_public_parameters()[source]

Return serialisable public parameters.

Return type:

dict

Returns:

A dict of public parameters, or None on error.

get_private_keys(nid, **kwargs)[source]

Return client nid’s encryption key (pk and its pad u).

Parameters:

nid (str) – Client identifier.

Return type:

dict | None

Returns:

The key dict, or None for an unknown client.

get_decryption_keys(sid, **kwargs)[source]

Derive a functional decryption key for the given fusion weights.

Parameters:
  • sid (str) – Session identifier.

  • **kwargs – Must include credentials with fusion_weight (dict {nid: list of int}).

Return type:

dict | None

class pyfe4ai.schemes.mife.paillier.MIFEPaillier(config, **kwargs)[source]

Bases: OfflineRandomMixin, IPFEAbsCrypto

Crypto operations for Paillier-based multi-input inner-product FE.

Parameters:

config (dict)

__init__(config, **kwargs)[source]

Initialise the MIFE Paillier crypto object.

Parameters:

config (dict) – Must contain keys with pp (public parameters) and optionally sk (encryption keys).

Return type:

None

encrypt(lst_pt)[source]
Parameters:

lst_pt (list)

Return type:

dict

decrypt(dct_ct, dk, fusion_weight)[source]

Decrypt ciphertexts and recover the inner product.

Parameters:
  • dct_ct (dict) – Ciphertext dict (or dict of per-client ciphertexts).

  • dk (dict) – Functional decryption key.

  • fusion_weight (dict) – Fusion weight vector (or dict of per-client weight vectors).

encrypt_lst_ndarray(lst_ndarray, **kwargs)[source]

Encrypt a list of ndarrays element-wise (requires eta = 1).

Parameters:

lst_ndarray (list) – List of numpy arrays, scaled by 10^precision.

Return type:

list | None

Returns:

One object array of ciphertexts per input array.

Raises:

NotImplementedError – If eta != 1.

compute_lst_ndarray_ct(dict_ndarray_ct, **kwargs)[source]

Compute inner products on encrypted ndarray ciphertexts.

Parameters:

dict_ndarray_ct (dict) – Encrypted ndarray ciphertexts (list or dict).

Return type:

list | None

decrypt_lst_ndarray_ct(dict_ndarray_ct, **kwargs)[source]

Decrypt encrypted ndarray ciphertexts element-wise.

Parameters:

dict_ndarray_ct (dict) – Encrypted ndarray ciphertexts (list or dict).

Return type:

list | None

FH-IPE (Pairing)

Function-hiding multi-input inner-product FE over pairings.

Abdalla, Catalano, Fiore, Gay, Ursu, “Multi-Input Functional Encryption for
Inner Products: Function-Hiding Realizations and Constructions without
Pairings”, CRYPTO 2018 (ePrint 2017/972) – Fig. 12 over the AGRW17 MIFE
of Fig. 11, with k = 1 (SXDH) and the LV16 padding of Appendix B.
  • type: private-key multi-input encryption with function-hiding keys

  • setting: asymmetric pairing group (default BLS12-381)

Slot i has the master key msk_i of a function-hiding IPFE (pyfe4ai.schemes.mcfe.fh_ipe.FHIPE, which contains the inner AGRW17 secret z_i):

  • Enc: ct_i = FHIPE.Enc(msk_i, [x_i]_1).

  • KeyGen: one r for all slots; dk_i = FHIPE.KeyGen(msk_i, [y_i]_2; r) and [z]_T = [r sum_i z_i]_T.

  • Dec: prod_i FHIPE.Dec(ct_i, dk_i) / [z]_T = [sum_i <x_i, y_i>]_T, then one bounded discrete log.

A slot’s partial result is masked by r z_i, so the key holder learns the sum only, and slot keys of different functional keys (different r) do not combine. Keys carry no digest of y and decryption does not need it. A slot’s client holds that slot’s outer key W_i (needed to encrypt) and could strip the outer layer of its own slot of a functional key – outside the model, where clients are not corrupted. As in every MIFE there are no labels: a key holder may combine any ciphertexts of each slot. Versions <= 0.2.0 ran independent KLMMRW instances per slot and decoded every slot separately.

class pyfe4ai.schemes.mife.fh_ipe_pairing.MIFEFHIPEPairingKeyGenerator(config, **kwargs)[source]

Bases: IPFEAbsKeyGenerator

Key generator for the ACFGU18 function-hiding MIFE.

Parameters:

config (dict)

__init__(config, **kwargs)[source]

Initialise the key generator.

Parameters:

config (dict) – n, lst_nid, eta, bound_x, bound_y and pairing_group_param (asymmetric, default BLS12-381).

Return type:

None

setup()[source]

Sample the generators and one FH-IPFE master key per slot.

Return type:

None

get_public_parameters()[source]

Group description, generators, dimensions and bounds.

Return type:

dict

get_private_keys(nid, **kwargs)[source]

Return slot nid’s FH-IPFE master key {imsk} (None if unknown).

Parameters:

nid (str)

Return type:

dict | None

get_decryption_keys(sid, **kwargs)[source]

Key for weights y covering every slot (one r for all slots).

Return type:

dict | None

Returns:

Dict with dk (per slot the FH-IPFE key), z ([r sum z_i]_T) and a random key_id (nothing derived from y).

Parameters:

sid (str)

class pyfe4ai.schemes.mife.fh_ipe_pairing.MIFEFHIPEPairing(config, **kwargs)[source]

Bases: IPFEAbsCrypto

Crypto operations for the ACFGU18 function-hiding MIFE.

Parameters:

config (dict)

__init__(config, **kwargs)[source]

Initialise with keys.pp and, for clients, keys.sk.

Parameters:

config (dict)

Return type:

None

encrypt(lst_pt)[source]

FHIPE.Enc(msk_i, [x_i]_1) for this slot’s eta integers.

Parameters:

lst_pt (list)

Return type:

dict

decrypt(dct_ct, dk, fusion_weight=None)[source]

log prod_i FHIPE.Dec(ct_i, dk_i) / [z]_T; None if out of bound.

fusion_weight is accepted for API compatibility and ignored: the key encodes y (function hiding).

Raises:

FESchemeError – If the ciphertexts do not cover every slot of the key.

Parameters:
encrypt_lst_ndarray(lst_ndarray, **kwargs)[source]

Not provided: MIFE is not used for element-wise aggregation (no labels).

Parameters:

lst_ndarray (list)

Return type:

list | None

compute_lst_ndarray_ct(dict_ndarray_ct, **kwargs)[source]

Not provided (see encrypt_lst_ndarray()).

Parameters:

dict_ndarray_ct (dict)

Return type:

list | None

decrypt_lst_ndarray_ct(dict_ndarray_ct, **kwargs)[source]

Not provided (see encrypt_lst_ndarray()).

Parameters:

dict_ndarray_ct (dict)

Return type:

list | None

FH-Multi-IPE (Pairing)

Full-hiding multi-input inner-product FE over pairings (bounded slots) | “Full-Hiding (Unbounded) Multi-Input Inner Product Functional Encryption | from the k-Linear Assumption” | By Pratish Datta, Tatsuaki Okamoto, Junichi Tomida, PKC 2018 (ePrint | 2018/061) – Section 3.1, the bounded scheme from k-LIN with dual pairing | vector spaces; structure as in GoFE’s innerprod/fullysec/fh_multi_ipe.go.

  • type: private-key multi-input encryption with function-hiding keys

  • setting: asymmetric pairing group (default BLS12-381), k = sec_level

Per slot dual orthogonal bases B_i, B*_i of dimension 2 eta + 2k + 1 (B_i B*_i^T = mu I), g_T = e(g1, g2)^mu public.

  • Enc: c_i = [(x_i, 0^eta, 1, 0^(k-1), phi, 0) B_i]_1 (phi <- Z_p^k).

  • KeyGen: k_i = [(y_i, 0^eta, r_i, gamma_i, 0^k, 0) B*_i]_2 with fresh sum_i r_i = 0 and gamma_i <- Z_p^(k-1).

  • Dec: prod_i e(c_i, k_i) = g_T^{sum <x_i, y_i>} and one discrete log.

Each slot’s pairing is masked by r_i; slot keys of different functional keys do not combine; keys hold group elements only and decryption does not need y. Deviations: no SKE layer (Remark B.1), so hiding assumes every slot contributes a ciphertext; slot i’s client holds B^_i in Z_p and could decode its own slot of a key (outside the model). n >= 2 is required (the proof needs the zero sharing r_i over at least two slots; with n = 1 and k = 1 keys were deterministic). As in every MIFE there are no labels.

class pyfe4ai.schemes.mife.fh_multi_ipe_pairing.MIFEFHMultiIPEPairingKeyGenerator(config, **kwargs)[source]

Bases: IPFEAbsKeyGenerator, ParameterCacheMixin

Key generator for function-hiding multi-slot multi-input IPE over pairings.

Parameters:

config (dict)

__init__(config, **kwargs)[source]

Initialise the MIFE FH-Multi-IPE key generator.

Parameters:

config (dict) – Scheme configuration. Recognised keys: n (number of clients, at least 2), lst_nid, eta, sec_level (k of k-LIN, default 2), bound_x, bound_y and pairing_group_param (asymmetric group, default "BLS12-381").

Return type:

None

setup()[source]

Generate master public and secret keys for the scheme.

Return type:

None

get_public_parameters()[source]

Return serialisable public parameters.

Return type:

dict

Returns:

A dict of public parameters, or None on error.

get_private_keys(nid, **kwargs)[source]

Return client nid’s encryption key (its basis bhat).

Parameters:

nid (str) – Client identifier.

Return type:

dict | None

Returns:

The key dict, or None for an unknown client.

get_decryption_keys(sid, **kwargs)[source]

Derive a functional decryption key for the given fusion weights.

Parameters:
  • sid (str) – Session identifier.

  • **kwargs – Must include credentials with fusion_weight (dict {nid: list of int} covering every client).

Return type:

dict | None

class pyfe4ai.schemes.mife.fh_multi_ipe_pairing.MIFEFHMultiIPEPairing(config, **kwargs)[source]

Bases: IPFEAbsCrypto

Crypto operations for function-hiding multi-slot multi-input IPE over pairings.

Parameters:

config (dict)

__init__(config, **kwargs)[source]

Initialise the MIFE FH-Multi-IPE crypto object.

Parameters:

config (dict) – Must contain keys with pp (public parameters) and optionally sk (encryption keys).

Return type:

None

encrypt(lst_pt)[source]
Parameters:

lst_pt (list)

Return type:

dict

decrypt(dct_ct, dk, fusion_weight=None)[source]

log_{g_T} prod_i e(c_i, k_i); None if out of bound.

fusion_weight is accepted for API compatibility and ignored (the key encodes y).

Raises:

FESchemeError – Unless there is one ciphertext and one key part per slot.

Parameters:
encrypt_lst_ndarray(lst_ndarray, **kwargs)[source]

Encrypt ndarrays element-wise (eta = 1).

Every element is a separate ciphertext of the slot, and MIFE has no labels: the holder of one key can combine elements of different positions. Use for one input per key epoch, not for aggregation.

Parameters:

lst_ndarray (list)

Return type:

list | None

compute_lst_ndarray_ct(dict_ndarray_ct, **kwargs)[source]

Compute inner products on encrypted ndarray ciphertexts.

Parameters:

dict_ndarray_ct (dict) – Encrypted ndarray ciphertexts (list or dict).

Return type:

list | None

decrypt_lst_ndarray_ct(dict_ndarray_ct, **kwargs)[source]

Decrypt encrypted ndarray ciphertexts element-wise.

Parameters:

dict_ndarray_ct (dict) – Encrypted ndarray ciphertexts (list or dict).

Return type:

list | None