原创 首页 算法

Shamir 秘密共享

LinJHS 2

前言

秘密共享:

  • 将秘密 S 派生 n 个碎片,并设置阈值 m,集齐不少于 m 个碎片,即可恢复出秘密 S,少于 m 个碎片则无法获得关于 S 的任何信息

常见的秘密共享方法有

  • Shamir 秘密共享

  • Blakley 秘密共享

  • CRT 秘密共享

准备工作

  • 秘密:S

  • 参与方个数:n

  • 阈值:m

  • 有限域:Fp\mathbb{F}_p

秘密分发

  • 生成随机数:{a0=S,a1,…,am−1}\{a_0=S,a_1,\ldots,a_{m-1}\}

  • 构造多项式 f(x)=a0+a1×x+a2×x2+…+am−1×xm−1f(x)=a_0+a_1\times x+a_2\times x^2+\ldots +a_{m-1}\times x^{m-1}

  • 任取 n 个在 Fp\mathbb{F}_p 中的数 {x1,x2,…,xn}\{x_1,x_2,\ldots,x_n\}

  • 在 Fp\mathbb{F}_p 中计算 {f(x1),f(x2),…,f(xn)}\{f(x_1),f(x_2),\ldots,f(x_n)\}

  • 将 {xi,yi=f(xi)}\{x_i,y_i=f(x_i)\} 发送给 n 个参与方

秘密恢复

基于拉格朗日插值公式,f(x)f(x) 的另一个表示方法如下:

f(x)=∑i=1n(yi∏1≤j≤n,j≠ix−xjxi−xj) mod pf(x)=\sum_{i=1}^{n}\left(y_i\prod_{1\leq j\leq n,j\neq i}\frac{x-x_j}{x_i-x_j} \right) \bmod p

取 x=0x=0 的值,即为秘密 S

一个 (3,5) 的算例

取 p=17p=17、S=13S=13,a1=10a_1=10、a2=2a_2=2,即 f(x)=13+10x+2x2f(x)=13+10x+2x^2。

五个份额(xi=1,…,5x_i=1,\dots,5)依次是 (1,8)(1,8)、(2,7)(2,7)、(3,10)(3,10)、(4,0)(4,0)、(5,11)(5,11)。

取 (1,8)(1,8)、(3,10)(3,10)、(5,11)(5,11),对应 L1(0)=4L_1(0)=4、L3(0)=3L_3(0)=3、L5(0)=11L_5(0)=11:

8×4+10×3+11×11=32+30+121=183≡13(mod17)8\times 4+10\times 3+11\times 11=32+30+121=183\equiv 13 \pmod{17}

换 (2,7)(2,7)、(4,0)(4,0)、(5,11)(5,11),对应 L2(0)=9L_2(0)=9、L4(0)=12L_4(0)=12、L5(0)=14L_5(0)=14:

7×9+0×12+11×14=63+0+154=217≡13(mod17)7\times 9+0\times 12+11\times 14=63+0+154=217\equiv 13 \pmod{17}

两组都还原出 S=13S=13。

Python 实现

只依赖标准库:

import secrets

P = 2**127 - 1  # 梅森素数,须为素数

def split(secret, m, n, p=P):
    """把 secret 拆成 n 份,任意 m 份可还原。"""
    if not 1 <= m <= n < p:
        raise ValueError("需要 1 <= m <= n < p")
    if not 0 <= secret < p:
        raise ValueError("secret 必须落在 [0, p) 内")
    poly = [secret] + [secrets.randbelow(p - 1) + 1 for _ in range(m - 1)]
    shares = []
    for x in range(1, n + 1):
        y = 0
        for c in reversed(poly):  # Horner 求值
            y = (y * x + c) % p
        shares.append((x, y))
    return shares

def combine(shares, p=P):
    """拉格朗日插值求 f(0)。"""
    secret = 0
    for i, (xi, yi) in enumerate(shares):
        num = den = 1
        for j, (xj, _) in enumerate(shares):
            if i == j:
                continue
            num = num * (-xj) % p
            den = den * (xi - xj) % p
        secret = (secret + yi * num * pow(den, -1, p)) % p  # pow(a, -1, p) 需 Python 3.8+
    return secret

参考资料:

  1. 密码学系列-Shamir秘密共享 https://zhuanlan.zhihu.com/p/577211618

分享
如果这篇文章帮到你,可以请我喝一杯茶。

微信扫码打开

请我喝杯茶

如果这篇文章帮到你,可以请我喝一杯茶。

微信收款码
在商城请我一杯