4. 基于秘密共享的隐私均值计算

基于秘密共享的隐私均值计算 学习卡片

问题定义与协议选择

三个参与方 P₁、P₂、P₃ 各自持有私密数值 x₁、x₂、x₃,希望共同计算均值 μ = (x₁+x₂+x₃)/3,但任何一方都不能向其他方泄露自己的原始数值。Shamir 秘密共享的加法同态特性正好满足这一需求:各方将秘密拆分为份额分发,各方对收到的份额求和,再对份额和进行重构,即可得到秘密和,最后除以参与方数量得到均值。

选择 (t, n) = (2, 3) 门限方案:任意 2 个份额即可重构秘密,单个份额不泄露任何信息。所有运算在有限域 GF(p) 上进行,取素数 p = 2³¹ − 1 = 2147483647,确保中间结果不溢出且除法可逆。

Shamir 秘密共享 · 隐私均值计算流程(p = 2³¹ − 1) ① 份额生成(各方独立完成) P₁ 持有 x₁ f₁(z) = x₁ + a₁z s₁₁=f₁(1), s₁₂=f₁(2), s₁₃=f₁(3) P₂ 持有 x₂ f₂(z) = x₂ + b₁z s₂₁=f₂(1), s₂₂=f₂(2), s₂₃=f₂(3) P₃ 持有 x₃ f₃(z) = x₃ + c₁z s₃₁=f₃(1), s₃₂=f₃(2), s₃₃=f₃(3) ② 份额分发(通过安全信道) P₁ 发送 s₁₂ → P₂ s₁₃ → P₃ P₂ 发送 s₂₁ → P₁ s₂₃ → P₃ P₃ 发送 s₃₁ → P₁ s₃₂ → P₂ ③ 各方本地求和(加法同态) S₁ = s₁₁+s₂₁+s₃₁ S₂ = s₁₂+s₂₂+s₃₂ S₃ = s₁₃+s₂₃+s₃₃ ④ 重构秘密和 → 均值 S = 拉格朗日插值(S₁, S₂) = x₁+x₂+x₃ → μ = S · 3⁻¹ mod p

核心实现:份额生成与重构

以下 Python 代码完整实现了上述协议。所有运算在 GF(p) 上进行,使用 pow(a, -1, p) 求模逆元。随机系数 a₁、b₁、c₁ 由各方独立生成,不对外暴露。

import random

P = 2147483647  # 2^31 - 1,素数

def generate_shares(secret, n=3, t=2):
    """生成 n 个份额,门限为 t"""
    coeffs = [secret] + [random.randint(1, P - 1) for _ in range(t - 1)]
    shares = []
    for i in range(1, n + 1):
        val = 0
        for c in reversed(coeffs):
            val = (val * i + c) % P
        shares.append((i, val))
    return shares

def reconstruct_secret(shares):
    """拉格朗日插值重构秘密(在 x=0 处求值)"""
    total = 0
    for i, (xi, yi) in enumerate(shares):
        num, den = 1, 1
        for j, (xj, _) in enumerate(shares):
            if i != j:
                num = (num * (0 - xj)) % P
                den = (den * (xi - xj)) % P
        lagrange = (num * pow(den, -1, P)) % P
        total = (total + yi * lagrange) % P
    return total

# 各方私密数据
x1, x2, x3 = 100, 200, 300

# ① 各方生成份额
shares1 = generate_shares(x1)
shares2 = generate_shares(x2)
shares3 = generate_shares(x3)

# ② 分发:P_i 保留 shares_i[0],将 shares_i[1] 给 P_{i+1},shares_i[2] 给 P_{i+2}
# P1 收到: shares1[0], shares2[0], shares3[0]
# P2 收到: shares1[1], shares2[1], shares3[1]
# P3 收到: shares1[2], shares2[2], shares3[2]

# ③ 各方本地求和
S1 = (shares1[0][1] + shares2[0][1] + shares3[0][1]) % P
S2 = (shares1[1][1] + shares2[1][1] + shares3[1][1]) % P
S3 = (shares1[2][1] + shares2[2][1] + shares3[2][1]) % P

# ④ 任选 2 个份额和重构秘密和
secret_sum = reconstruct_secret([(1, S1), (2, S2)])
mean = (secret_sum * pow(3, -1, P)) % P

print(f"重构的秘密和: {secret_sum}")   # 应为 600
print(f"隐私均值: {mean}")             # 应为 200

验证与结果判读

运行上述代码,预期输出 重构的秘密和: 600 和 隐私均值: 200,与直接计算 (100+200+300)/3 = 200 一致。验证要点如下:

  • 正确性:重构和等于三方原始数据之和,均值等于算术平均值。
  • 隐私性:任意单方持有的份额和 Sᵢ 是随机值,无法反推任何原始数据。例如 P₁ 只知道 S₁ = s₁₁+s₂₁+s₃₁,其中每个 sᵢ₁ 都是随机多项式在 z=1 处的取值,不泄露 xᵢ。
  • 门限性:任意 2 个份额和即可重构,单个份额和无法重构。可尝试仅用 S₁ 重构,结果将是随机值而非 600。
  • 有限域一致性:所有中间结果均 mod p,最终均值也需 mod p 后映射回整数域。当真实均值小于 p 时,直接取模结果即为正确值。
扩展验证:将 x₁、x₂、x₃ 改为任意整数(如 7、13、25),重复运行,检查均值是否为 (7+13+25)/3 = 15。若结果不符,检查拉格朗日插值中模逆元计算是否正确,以及分发时份额索引是否与接收方编号对应。
注意事项:实际部署中,份额分发必须通过认证加密信道进行,防止中间人窃取份额。此外,各方需承诺使用的多项式阶数不超过 t−1,否则恶意方可通过发送高阶份额破坏协议。本示例未包含恶意行为检测,属于半诚实模型下的实现。