使用 Python 加密算法永不忘记您的密码

这种使用 Python 和 Shamir 密钥共享的独特算法可保护您的主密码免受黑客攻击和您自身的健忘。
174 位读者喜欢此文。
Searching for code

Opensource.com

我们中的许多人使用密码管理器来安全地存储我们许多唯一的密码。密码管理器的关键部分是主密码。 此密码保护所有其他密码,因此它存在风险。 拥有它的人可以伪装成你……在任何地方! 自然,您会确保您的主密码难以猜测,牢记在心,并完成所有其他您应该做的事情

但是,如果发生某些事情并且您忘记了它怎么办? 也许您去一个美丽的、遥远的、没有技术的小岛度假一个月。 每天在水中嬉戏并吃菠萝后,您无法完全记住您的密码。 也许是“long legs travel fast”? 还是像“sharp spoons eat quick”这样的东西? 当您想到它时,它绝对很聪明。

当然,您从未向任何人透露过您的密码。 为什么,这实际上是密码管理的第一条规则。 您本来可以做些什么不同的事情?

输入 Shamir 密钥共享,这是一种允许用户将密钥分成若干部分的算法,这些部分只能与其他部分结合使用。

让我们通过古代和现代的故事来看看 Shamir 密钥共享的实际应用。

这个故事确实假设了一些密码学的知识。 您可以通过密码学和公钥基础设施介绍来复习它。

古代的秘密故事

在古代王国,国王有一个秘密。 一个可怕的秘密。

def int_from_bytes(s):
    acc = 0
    for b in s:
        acc = acc * 256
        acc += b
    return acc

secret = int_from_bytes("terrible secret".encode("utf-8"))

太可怕了,国王无法将它托付给他的任何后代。 他有五个孩子,但知道未来的道路上会有危险。 国王知道他的孩子们需要这个秘密来保护王国在他死后,但他无法忍受这个秘密被知道二十年,当他们还在悼念他的时候。

因此,他使用强大的魔法将秘密分成五个碎片。 他知道有可能一个孩子甚至两个孩子不会尊重他的意愿,但他不相信其中三个会这样做。

from mod import Mod
from os import urandom

国王精通有限域随机性的魔法。 作为一位明智的国王,他使用 Python 来分割秘密。

他做的第一件事是选择一个大的质数 - 第 13 个 梅森素数 (2**521 - 1) - 并命令用 10 英尺高的字母写在宫殿上方,用黄金制成。

P = 2**521 - 1

这不是秘密的一部分:它是公共数据

国王知道,如果 P 是一个质数,则模 P 的数字形成一个数学:只要除数不为零,它们就可以被添加、相乘、相减和相除。

作为一个繁忙的国王,他使用了 PyPI mod,它实现了模运算。

他确保他的可怕秘密小于 P

secret < P
TRUE

他将其转换为模数 mod P

secret = mod.Mod(secret, P)

为了让三个后代能够重建秘密,国王必须生成另外两个部分来混合在一起。

polynomial = [secret]
for i in range(2):
    polynomial.append(Mod(int_from_bytes(urandom(16)), P))
len(polynomial)
3

国王接下来需要在随机点评估这个多项式。 评估多项式是计算 polynomial[0] + polynomial[1]*x + polynomial[2]*x**2 ...

虽然有第三方模块可以评估多项式,但它们不适用于有限域。 国王需要自己编写评估代码。

def evaluate(coefficients, x):
    acc = 0
    power = 1
    for c in coefficients:
        acc += c * power
        power *= x
    return acc

接下来,国王在五个不同的点评估多项式,以给每个后代一片。

shards = {}
for i in range(5):
    x = Mod(int_from_bytes(urandom(16)), P)
    y = evaluate(polynomial, x)
    shards[i] = (x, y)

可悲的是,正如国王所担心的那样,并非他所有的后代都是诚实和真实的。 他们中的两个,在他去世后不久,试图从他们拥有的部分中找出可怕的秘密。 尽其所能,他们没有成功。 然而,当其他人得知此事后,他们将他们永远流放出了王国。

del shards[2]
del shards[3]

二十年后,正如国王所 decree 的那样,最年长的兄弟姐妹和两个最小的兄弟姐妹聚在一起,想出他们父亲可怕的秘密。 他们把他们的碎片放在一起。

retrieved = list(shards.values())

40 天 40 夜,他们都在努力寻找国王的秘密。 在他们面前的任务并不容易。 像国王一样,他们懂 Python,但没有人像他那么聪明。

最后,答案来了。

检索代码基于一个名为 拉格朗日插值的概念。 它根据多项式在 n 个其他地方的值评估 0 处的多项式,其中 n 是多项式的次数。 它的工作方式是,您可以明确地找到一个多项式的公式,该公式在 t[0] 处为 1,在 t[i] 处为 0,对于 i0 不同。 由于评估多项式是线性函数,因此您可以评估每个这些多项式,并使用多项式具有的值插值评估结果。

from functools import reduce
from operator import mul

def retrieve_original(secrets):
    x_s = [s[0] for s in secrets]
    acc = Mod(0, P)
    for i in range(len(secrets)):
        others = list(x_s)
        cur = others.pop(i)
        factor = Mod(1, P)
        for el in others:
            factor *= el * (el - cur).inverse()
        acc += factor * secrets[i][1]
    return acc

毫不奇怪,这花了他们 40 天 40 夜 - 这段代码非常复杂! 但他们在幸存的碎片上运行它,屏住呼吸等待着。

retrieved_secret = retrieve_original(retrieved)

孩子们是否得到了正确的秘密?

retrieved_secret == secret
TRUE

数学的魔力在于它每次都能可靠地工作! 孩子们现在长大了,能够理解他们父亲的选择,用可怕的秘密来捍卫王国。 王国繁荣昌盛。

Shamir 密钥共享的现代故事

在现代,我们中的许多人也背负着一个可怕的秘密:密码管理器的密码。 虽然很少有人可以完全信任一个人他们最深、最黑暗的秘密,但许多人可以找到五个人的团体,其中不太可能有三个人一起打破他们的信任。

幸运的是,在这些现代时代,我们不需要像国王那样自己分割我们的秘密。 通过开源的现代技术,我们可以使用现有的软件。

假设你有五个你信任的人 - 不是绝对的,而是一些:你最好的朋友,你的配偶,你的妈妈,一位亲密的同事和你的律师。

您可以安装并运行程序 ssss 来分割密钥

$ echo 'long legs travel fast' | ssss-split -t 3 -n 5
Generating shares using a (3,5) scheme with dynamic security level.
Enter the secret, at most 128 ASCII characters: Using a 168 bit security level.
1-797842b76d80771f04972feb31c66f3927e7183609
2-947925f2fbc23dc9bca950ef613da7a4e42dc1c296
3-14647bdfc4e6596e0dbb0aa6ab839b195c9d15906d
4-97c77a805cd3d3a30bff7841f3158ea841cd41a611
5-17da24ad63f7b704baed220839abb215f97d95f4f8

啊,一个强大而有力的主密码:long legs travel fast。 永远不能委托给一个人,但您可以将五个碎片发送给您的五个监护人。

  • 您将 1 发送给您最好的朋友 F。
  • 您将 2 发送给您的配偶 S。
  • 您将 3 发送给您的妈妈 M。
  • 您将 4 发送给您的同事 C。
  • 您将 5 发送给您的律师 L。

现在,假设您去家庭度假。 一个月来,您在温暖的沙滩上嬉戏。 当您嬉戏时,您不会触摸任何电子设备。 不久,您强大的主密码就被遗忘了。

您心爱的配偶和亲爱的母亲和您一起度假。 他们将碎片安全地保存在密码管理器中 - 他们忘记了他们的密码

没关系。

您联系了您最好的朋友 F,他给了您 1-797842b76d80771f04972feb31c66f3927e7183609。 您的同事,他承担了你所有的轮班,很高兴你回来,并给了你 4-97c77a805cd3d3a30bff7841f3158ea841cd41a611。 您的律师每小时向您收取 150 美元,进入他们的密码管理器,并挖出 5-17da24ad63f7b704baed220839abb215f97d95f4f8

有了这三个碎片,您就可以运行

$ ssss-combine -t 3
Enter 3 shares separated by newlines:
Share [1/3]: 1-797842b76d80771f04972feb31c66f3927e7183609
Share [2/3]: 4-97c77a805cd3d3a30bff7841f3158ea841cd41a611
Share [3/3]: 5-17da24ad63f7b704baed220839abb215f97d95f4f8 
Resulting secret: long legs travel fast

因此,借助开源技术,您也可以像国王一样生活!

为了您的安全,安全共享

密码管理是当今在线生活的一项基本技能。 当然,创建一个复杂的密码,但不要止步于此。 使用方便的 Shamir 密钥共享算法安全地与他人共享它。

接下来要阅读的内容
Moshe sitting down, head slightly to the side. His t-shirt has Guardians of the Galaxy silhoutes against a background of sound visualization bars.
自 1998 年以来,Moshe 一直参与 Linux 社区,帮助举办 Linux“安装派对”。 自 1999 年以来,他一直在编写 Python 程序,并为核心 Python 解释器做出了贡献。 自这些术语存在之前,Moshe 一直是一名 DevOps/SRE,他非常关心软件的可靠性、构建可重复性以及其他此类事情。

11 条评论

不要认为这种事情不会发生在你身上。我曾经从攀岩时摔下来,之后有一段时间什么都记不起来了!

如果我没有律师可以按150美元/小时收费,这个方法还管用吗?

很棒的文章。我正在学习Python,这些正是我感到兴奋的东西 :)

我希望律师费只需要150美元/小时。很棒的帖子!

太棒了。你拥有天赋... 侃侃而谈的天赋!我真的很喜欢。

抱歉,这在Python 3.7中不起作用,因为mod库和inverse()函数已被弃用。

也许这是一个显而易见的问题。但是,我如何从`secret`整数返回到文本。

写得太精彩了。你将故事和代码融合的方式让我找不到密钥。

代码行:secret = mod.Mod(secret, P) 需要改为: secret = Mod(secret, P)

哇,这篇文章写得真好,而且很有趣!我喜欢它 :3

Creative Commons License本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。
© 2025 open-source.net.cn. All rights reserved.