1. 基于Paillier同态加密的隐私求和协议实现

基于Paillier同态加密的隐私求和协议实现 学习卡片

Paillier 同态加密的数学基础

Paillier 加密是一种加法同态公钥密码体制,其安全性基于判定性合数剩余假设(DCR)。协议涉及三个核心操作:密钥生成、加密、同态加法。

  • 密钥生成:选取两个大素数 p、q,令 n = p·q,λ = lcm(p−1, q−1)。公钥为 (n, g),其中 g = n+1;私钥为 λ。
  • 加密:对明文 m ∈ ℤn,选随机数 r ∈ ℤn*,密文 c = gm · rn mod n²。
  • 同态加法:Dec(c₁ · c₂ mod n²) = m₁ + m₂ mod n。这是隐私求和协议的核心性质。
关键性质:密文相乘对应明文相加。因此两方各自加密私有数值后,只需在密文域相乘,即可得到和的密文,无需暴露任何一方明文。
Paillier 两方隐私求和协议数据流 密钥生成 公钥 (n, g=n+1) 私钥 λ 公钥 参与方 A 私有数值 x = 42 加密 → c_A 参与方 B 私有数值 y = 58 加密 → c_B c_A c_B 同态加法 c_sum = c_A · c_B mod n² c_sum 解密 Dec(c_sum, λ) = x + y = 100 验证结果 42 + 58 = 100 ✓ 密文加法正确 安全边界 明文 x, y 不出本地 仅密文参与计算 计算开销 加密:2次模幂 同态乘:1次模乘

协议实现与验证

下面给出一个可直接运行的 Python 实现。为便于教学演示,使用小素数生成密钥;实际部署应使用 2048 位以上安全参数。

import random
from math import gcd

def lcm(a, b):
    return a * b // gcd(a, b)

def gen_keypair(bits=16):
    """生成 Paillier 密钥对(教学用小素数)"""
    p = gen_prime(bits)
    q = gen_prime(bits)
    while p == q:
        q = gen_prime(bits)
    n = p * q
    lam = lcm(p - 1, q - 1)
    g = n + 1
    mu = pow(lam, -1, n)  # λ⁻¹ mod n
    return (n, g), (lam, mu)

def gen_prime(bits):
    """生成指定位数的素数(教学用)"""
    while True:
        num = random.getrandbits(bits) | (1 << (bits - 1)) | 1
        if is_prime(num):
            return num

def is_prime(n, k=20):
    if n < 2: return False
    for p in [2,3,5,7,11,13,17,19,23,29,31,37]:
        if n % p == 0: return n == p
    d, r = n - 1, 0
    while d % 2 == 0:
        d //= 2; r += 1
    for _ in range(k):
        a = random.randrange(2, n - 1)
        x = pow(a, d, n)
        if x == 1 or x == n - 1: continue
        for _ in range(r - 1):
            x = pow(x, 2, n)
            if x == n - 1: break
        else:
            return False
    return True

def encrypt(pub, m):
    """加密:c = g^m · r^n mod n²"""
    n, g = pub
    n2 = n * n
    r = random.randrange(1, n)
    while gcd(r, n) != 1:
        r = random.randrange(1, n)
    return (pow(g, m, n2) * pow(r, n, n2)) % n2

def decrypt(priv, pub, c):
    """解密:m = L(c^λ mod n²) · μ mod n"""
    n, g = pub
    lam, mu = priv
    n2 = n * n
    u = pow(c, lam, n2)
    l = (u - 1) // n
    return (l * mu) % n

def homomorphic_add(pub, c1, c2):
    """同态加法:密文相乘"""
    n, _ = pub
    return (c1 * c2) % (n * n)

# ===== 协议执行 =====
pub, priv = gen_keypair(bits=16)
x, y = 42, 58

c_x = encrypt(pub, x)
c_y = encrypt(pub, y)
c_sum = homomorphic_add(pub, c_x, c_y)
result = decrypt(priv, pub, c_sum)

print(f"参与方A明文: {x}")
print(f"参与方B明文: {y}")
print(f"密文 c_A: {c_x}")
print(f"密文 c_B: {c_y}")
print(f"密文和 c_sum: {c_sum}")
print(f"解密结果: {result}")
print(f"验证: {x} + {y} = {result} -> {'通过' if result == x + y else '失败'}")
运行说明:直接执行上述脚本即可。由于每次加密使用随机数 r,密文 c_A、c_B 每次运行不同,但解密结果恒为 100。这体现了 Paillier 的语义安全性。

计算开销分析

Paillier 的主要开销集中在模幂运算。下表以 1024 位模数为例,给出各阶段的理论复杂度与实测参考量级。

操作数学表达式模幂次数复杂度典型耗时(1024位)
加密gm · rn mod n²2O(log n) 次模乘~2–5 ms
同态加法c₁ · c₂ mod n²0O(1) 次模乘< 0.01 ms
解密L(cλ mod n²) · μ mod n1O(log n) 次模乘~2–5 ms
性能瓶颈:加密与解密各需一次或两次模幂运算,是协议的主要开销来源。同态加法仅需一次模乘,几乎可忽略。若需对大量数据求和,建议批量加密后一次性同态累加,再统一解密,以减少模幂次数。

正确性验证与边界条件

验证协议正确性时,需覆盖以下场景:

  1. 基本求和:x=42, y=58,解密结果应为 100。
  2. 零值参与:x=0, y=77,解密结果应为 77。Paillier 支持明文 0 的加密。
  3. 大数求和:x=9999, y=8888,解密结果应为 18887。需确保 x+y < n,否则会发生模 n 回绕。
  4. 多次累加:对同一密文自乘 k 次,解密结果应为 k·m mod n。
边界提醒:Paillier 的同态加法是在模 n 意义下进行的。若两方数值之和超过 n,解密结果将回绕。实际应用中需根据数值范围选择足够大的 n,或引入范围证明与溢出检测机制。