【密码学】Shamir秘密分享python实现

Shamir原理参见

分析

1.随机生成最高m - 1次的多项式
2.获取f(x)的值
3.随机生成n份shares
4.根据m份shares重构出secret(拉格朗日插值)
5.测试函数

source code

import random
from math import ceil
from decimal import Decimal

FIELD_SIZE = 10 ** 5


def coeff(t, secret):
    """
    生成最高次为t - 1次的多项式,其中常数项是secret
    """
    # 保证第一项不为0
    coeff = [random.randrange(1, FIELD_SIZE)]
    # 后面t - 2系数项可为0
    if t > 3:
        coeff += [random.randrange(0, FIELD_SIZE) for _ in range(t - 2)]
    # 加入常数项
    coeff.append(secret)
    return coeff


def polynom(x, coefficients):
    """
    获取f(x)的值
    """
    point = 0
    # coeff从左到右是高次到低次的(使用enumerate表示指数)
    for coefficient_index, coefficient_value in enumerate(coefficients[::-1]):
        point += x ** coefficient_index * coefficient_value
    return point


def generate_shares(n, m, secret):
    """
    将秘密分成n份,只需要m份就可以复原(也就是阈值,函数的最高次数 + 1)
    """
    coefficient = coeff(m, secret)
    shares = []
    
    for i in range(1, n + 1):
        x = random.randrange(1, FIELD_SIZE)
        shares.append((x, polynom(x, coefficient)))
    
    return shares


def reconstruct_secret(shares):
    """
    利用拉格朗日插值法(已知m个秘密)还原并得到secret(f(0))
    """
    sums = 0
    
    for j, share_j in enumerate(shares):
        xj, yj = share_j
        prod = Decimal(1)
        
        for i, share_i in enumerate(shares):
            xi, _ = share_i
            if i != j:
                prod *= Decimal(Decimal(xi) / (xi - xj))
        
        prod *= yj
        sums += Decimal(prod)
    
    return int(round(Decimal(sums), 0))
# Driver code
if __name__ == '__main__':
 
    # (3,5) sharing scheme
    t, n = 3, 5
    secret = 1234
    print(f'Original Secret: {secret}')
 
    # Phase I: Generation of shares
    shares = generate_shares(n, t, secret)
    print(f'Shares: {", ".join(str(share) for share in shares)}')
 
    # Phase II: Secret Reconstruction
    # Picking t shares randomly for
    # reconstruction
    pool = random.sample(shares, t)
    print(f'Combining shares: {", ".join(str(share) for share in pool)}')
    print(f'Reconstructed secret: {reconstruct_secret(pool)}')

最终结果

在这里插入图片描述

总结

这是门限(n,t)Shamir秘密分享
用了多项式理论
在区块链的多方签名有所应用

20221003 bug修复

  • 由于filed_size过大可能存在超大数溢出的可能,后面可以取MOD进行解决,由于涉及除法,因此需考虑到逆元
  • x的取值需要保证不同
  • 下面修改filed_size为100,可以在一定概率上支持t = 50的情况
import random
from math import ceil
from decimal import Decimal

FIELD_SIZE = 10 ** 2
MOD = 9999999987


def coeff(t, secret):
    """
    生成最高次为t - 1次的多项式,其中常数项是secret
    """
    # 保证第一项不为0
    coeff = [random.randrange(1, FIELD_SIZE)]
    # 后面t - 2系数项可为0
    if t > 3:
        coeff += [random.randrange(0, FIELD_SIZE) for _ in range(t - 2)]
    # 加入常数项
    coeff.append(secret)
    return coeff


def polynom(x, coefficients):
    """
    获取f(x)的值
    """
    point = 0
    # coeff从左到右是高次到低次的(使用enumerate表示指数)
    for coefficient_index, coefficient_value in enumerate(coefficients[::-1]):
        point += x ** coefficient_index * coefficient_value
    return point


def generate_shares(n, m, secret):
    """
    将秘密分成n份,只需要m份就可以复原(也就是阈值,函数的最高次数 + 1)
    """
    coefficient = coeff(m, secret)
    shares = []
    xs = random.sample(range(0, FIELD_SIZE), n)

    for i in range(1, n + 1):
        x = xs[i - 1]
        shares.append((x, polynom(x, coefficient)))

    return shares


def reconstruct_secret(shares):
    """
    利用拉格朗日插值法(已知m个秘密)还原并得到secret(f(0))
    """
    sums = 0

    for j, share_j in enumerate(shares):
        xj, yj = share_j
        prod = Decimal(1)

        for i, share_i in enumerate(shares):
            xi, _ = share_i
            if i != j:
                #print(Decimal(Decimal(xi) / (xi - xj)))
                prod *= Decimal(Decimal(xi) / (xi - xj))
        #print(yj)
        prod *= yj
        sums += Decimal(prod)
    print(sums)

    return int(round(Decimal(sums), 0))


# Driver code
if __name__ == '__main__':
    # (3,5) sharing scheme
    t, n = 50, 100
    secret = 1234
    print(f'Original Secret: {secret}')

    # Phase I: Generation of shares
    shares = generate_shares(n, t, secret)
    print(f'Shares: {", ".join(str(share) for share in shares)}')

    # Phase II: Secret Reconstruction
    # Picking t shares randomly for
    # reconstruction
    pool = random.sample(shares, t)
    print(f'Combining shares: {", ".join(str(share) for share in pool)}')
    print(f'Reconstructed secret: {reconstruct_secret(pool)}')

门限密码是指将一个秘密分成多个部分,只有当这些部分汇聚在一起时,才能重构出原来的秘密。在密码学中,门限密码被广泛应用于多方安全计算,数据解密和认证等领域。下面我们将简要介绍如何使用Python实现门限密码。 首先,我们需要安装必要的库。可以使用pip命令安装pycryptodome,它提供了实现门限密码所需的密钥生成和数据加密等函数。 ``` pip3 install pycryptodome ``` 之后,我们要设置门限,假设我们需要将秘密分成5份,每份至少需要3份才能还原出秘密。我们可以使用Shamir的门限密码方案来生成密钥,Shamir方案的实现可以使用pycryptodome库中的函数。 ```python from Crypto.Util.number import getRandomInteger, GCD, inverse from Crypto.Util import number # 参数k表示有k个份秘密,n表示至少需要n份秘密才能还原原始秘密 def generate_shamir_key(k, n): p = number.getPrime(512) a = getRandomInteger(512) b = getRandomInteger(512) % p # 检查p,b是否互质,如果不是,重新生成随机数 while GCD(a, p) != 1 or GCD(b, p) != 1: p = number.getPrime(512) a = getRandomInteger(512) b = getRandomInteger(512) % p shares = [] for i in range(1, k + 1): x = i y = (a * x + b) % p shares.append((x, y)) return shares ``` 在生成密钥的过程中,我们首先生成一个512位的素数p,随机选择两个512位的整数a和b。然后检查a和p,b和p是否互质,确保生成的密钥是安全的。最后我们用x在1到k之间的数值求出对应的y值,并将x和y保存在一个元组中加入密钥列表中。 当我们需要还原秘密时,可以使用Lagrange插值法计算密钥: ```python from collections import Iterable # 参数shares是一个元组列表,表示秘密分成了多少份,以及每份的x和y值 # 参数x是需要还原秘密的x值 def lagrange_interpolation(shares, x): def product(nums): # 计算序列中所有元素的乘积 p = 1 for num in nums: p *= num return p # 检查shares是否为一个可迭代的序列 if isinstance(shares, Iterable): sss = 0 for j, share_j in enumerate(shares): x_j, y_j = share_j p = [(x - x_j) / (j - x_j) for j, share_j in enumerate(shares) if x_j != j] # p 的极限为 -x / j,分母为0时,不做计算 sss += (y_j * product(p)) sss % p[0] return sss ``` Lagrange插值法的实现较为简单,我们首先检查输入的shares参数是否为可迭代序列,然后依次根据每个元组计算Lagrange插值多项式的乘积,最后得出密钥。 使用门限密码对数据进行加密时,我们首先将原始数据切分成n份,并将每份数据分别使用不同的密钥进行加密。当需要解密时,至少需要收集到n份密文才能还原出原始数据。 ```python from Crypto.Cipher import PKCS1_OAEP from Crypto.PublicKey import RSA def encrypt_data(data, keys): encrypted_data = [] for i in range(len(keys)): key = keys[i] message = data[i].to_bytes(16, byteorder="big") rsa_key = RSA.construct(key) cipher = PKCS1_OAEP.new(rsa_key) encrypted_data.append(cipher.encrypt(message)) return encrypted_data def decrypt_data(data, keys): decrypted_data = [] for i in range(len(keys)): key = keys[i] rsa_key = RSA.construct(key) cipher = PKCS1_OAEP.new(rsa_key) decrypted_data.append(int.from_bytes(cipher.decrypt(data[i]), byteorder="big")) return decrypted_data ``` 在进行加密时,我们首先将原始数据分成n份,并对每份数据使用一个不同的密钥进行加密。在进行解密时,我们需要指定解密的密钥,将收到的密文使用相应的密钥进行解密,并将解密后的数据保存在一个列表中。 综上所述,我们可以使用Python编写一个完整的门限密码程序。该程序首先生成密钥,然后将原始数据进行切分并使用不同的密钥进行加密。当需要解密数据时,至少收集到n份密文即可还原出原始数据。
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

白速龙王的回眸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值