SLIP-0039:助记码的Shamir秘密共享

该 SLIP 描述了 Shamir 密钥共享(SSS)的标准和可互操作的实现,以及它在备份分层确定性钱包中的使用规范。SSS 将主密钥分成多个唯一部分,这些部分可以分发给参与者。需要提供指定的最少数量的部分才能重建原始密钥。

SLIP-0039:用于助记码的 Shamir 秘密共享

编号:SLIP-0039
标题:用于助记码的 Shamir 秘密共享
类型:标准
状态:已完成
作者:Pavol Rusnak <stick@satoshilabs.com>
         Andrew Kozlik <andrew.kozlik@satoshilabs.com>
         Ondrej Vejpustek <ondrej.vejpustek@satoshilabs.com>
         Tomas Susanka <tomas.susanka@satoshilabs.com>
         Marek Palatinus <slush@satoshilabs.com>
         Jochen Hoenicke <hoenicke@gmail.com>
创建时间:2017-12-18

目录

摘要

此 SLIP 描述了 Shamir 秘密共享 (SSS) 的标准和可互操作的实现,以及将其用于备份 BIP-0032 中描述的分层确定性钱包的规范。SSS 将主密钥拆分为可在参与者之间分配的唯一部分。需要提供指定的最少数量的部分才能重建原始密钥。少于所需数量的部分的知识不会泄漏有关主密钥的信息。此 SLIP 主要旨在替代 BIP-0039,并且在大多数情况下,两者不兼容

符号

符号 含义
G 组的总数,一个正整数,1 ≤ G ≤ 16
N<sub>i</sub> i 组中的成员总数,一个正整数,1 ≤ N<sub>i</sub> ≤ 16
GT 组阈值,一个正整数,1 ≤ GTG
T<sub>i</sub> i 组的成员阈值,一个正整数,1 ≤ T<sub>i</sub>N<sub>i</sub>
id 随机标识符,一个 15 位无符号整数
ext 可扩展备份标志
e 迭代指数,一个 4 位无符号整数
MS 主密钥,一个八位字节字符串
n 主密钥的长度,以字节为单位
EMS 加密的主密钥,一个八位字节字符串
|| 连接运算符
xor 两个八位字节字符串的按位异或

动机

数字资产的保护通常很重要,在比特币等去中心化支付系统中尤其重要,因为在资产丢失的情况下没有任何追索权。保护数字资产的常用方法是冗余备份,但是当资产本身具有重要的和可变现的价值时,备份持有者携款潜逃的风险很大。Shamir 的秘密共享提供了一种更好的机制来备份秘密,通过在多个受信任方之间分配保管权,即使其中一方或几方受到损害,也可以防止丢失。

但是,迄今为止,缺乏 SSS 标准化会带来将来在工具发生变化时无法执行秘密恢复的风险。因此,我们建议对 SSS 进行标准化,以便 SLIP-0039 兼容的实现可以互操作。

Shamir 的秘密共享

Shamir 的秘密共享 (SSS) 是一种密码学机制,描述了如何将一个秘密分成 N 个唯一的部分,其中任何 T 个部分都需要重建该秘密。首先,构造一个 T − 1 次多项式 f,并为每个参与者提供一个相应的点——多项式的整数输入 x 和相应的输出 f(x)。

当提供任何 T 个点时,它们会精确地定义多项式。通常,多项式 f(0) 的值用作共享密钥。在此规范中,共享密钥存储为 f(255)<sup>3</sup>。有关 SSS 的更多详细信息,请参见 Wikipedia

我们建议给定一个秘密,随机生成 T − 2 个共享,并以 f(255) 对共享秘密进行编码,f(254) 对共享秘密的摘要<sup>4</sup>进行编码的方式计算剩余的共享。对摘要进行编码可以验证共享秘密是否已正确恢复。下图说明了将一个秘密分成五个共享,需要其中任意三个才能恢复共享秘密(N = 5 且 T = 3)。

curve

Shamir 的秘密共享方案分别应用于共享秘密的每个字节,并且 GF(256) 用作底层有限域<sup>1</sup>。字节被解释为 GF(256) 的元素,使用多项式表示,运算模 Rijndael 不可约多项式 x<sup>8</sup> + x<sup>4</sup> + x<sup>3</sup> + x + 1, 请参阅 AES 第 3.2、4.1 和 4.2 节。

两级方案

Shamir 的秘密共享方案的一个特点是所有共享都是平等的。因此,如果秘密的所有者需要不均匀地在股东之间分配信任,那么需要向某些股东提供多个份额。此外,正如 Allen 和 Friedenbach 所讨论的那样,所有者可能希望限制能够重建秘密的股东组合,因为某些股东组合可能比其他组合更有可能串通反对所有者。为了促进这一点,我们建议首先使用一个 GT-of-G 方案拆分加密的主密钥 (EMS) 以获得一组一级共享,也称为组共享。然后使用一个 T<sub>i</sub>-of-N<sub>i</sub> 方案拆分第 i 个组共享(1 ≤ iG)以获得一组二级共享,也称为成员共享,这些成员共享在股东之间分配。假设两层足以容纳大多数用例,同时保持全面的用户界面。

例如,Alice 希望能够使用她存储在不同位置的 2 个共享来自己重建她的 EMS。如果这些共享被销毁,她还希望与她的朋友和家人进行备份,这样她的 5 个朋友中的 3 个以及她的 6 个家庭成员中的 2 个需要一起重建 EMS。一个两级秘密共享方案可以轻松满足这些要求。在给定的示例中,Alice 首先使用 2-of-4 方案拆分 EMS 以获得组共享 A、B、C 和 D。她自己保留 A 和 B,并使用 3-of-5 方案进一步拆分 C 以获得成员共享 C1、...、C5,并将其中的一个给每个朋友。同样,Alice 使用 2-of-6 方案在她的家人之间拆分 D。因此,家庭成员比朋友获得更多的信任,而无需给一个人多个共享。但是,即使所有六个家庭成员串通反对 Alice,如果没有至少三个 Alice 朋友的帮助,或者没有窃取 Alice 自己的共享之一,他们也无法获得 EMS

根据本规范创建的所有共享都使用两级秘密共享方案。如果共享的创建者希望仅使用基本的单级 T-of-N 方案,则他们应该<sup>2</sup>创建一个组并在第二级进行拆分,即 GT = 1、G = 1、T<sub>1</sub> = TN<sub>1</sub> = N

如果一个组的成员阈值 T<sub>i</sub> 为 1,那么该组的大小 N<sub>i</sub> 应该<sup>2</sup>也等于 1。然后可以将这一个共享给多个成员。

共享助记词的格式

我们建议使用以下共享格式:

标识符 (id) 可扩展 (ext) 迭代指数 (e) 组索引 (GI) 组阈值 (Gt) 组计数 (g) 成员索引 (I) 成员阈值 (t) 填充的共享值 (ps) 校验和 (C)
15 位 1 位 4 位 4 位 4 位 4 位 4 位 4 位 padding + 8n 30 位
  • 标识符 (id) 字段是一个随机的 15 位值,对于所有共享都是相同的,用于验证这些共享是否属于一起。
  • 可扩展备份标志 (ext) 字段<sup>10</sup>指示当 ext = 0 时,id 用作主密钥加密中的 salt。
  • 迭代指数 (e) 字段指示 PBKDF2 中要使用的总迭代次数。迭代次数计算为 10000×2<sup>e</sup>。
  • 组索引 (GI) 字段<sup>3</sup>是组共享的 x 值。
  • 组阈值 (Gt) 字段<sup>3</sup>指示需要多少组共享才能重建主密钥。实际的值编码为 Gt = GT − 1,因此值为 0 表示需要单个组共享 (GT = 1),值为 1 表示需要两个组共享 (GT = 2) 等。
  • 组计数 (g) 指示组的总数。实际的值编码为 g = G − 1。
  • 成员索引 (I) 字段<sup>3</sup>是给定组中成员共享的 x 值。
  • 成员阈值 (t) 字段<sup>3</sup>指示需要多少成员共享才能重建组共享。实际的值编码为 t = T − 1。
  • 填充的共享值 (ps) 字段对应于 SSS 部分的 f<sub>k</sub>(x) 值的列表(参见上图),1 ≤ kn。每个 f<sub>k</sub>(x) 值都编码为大端顺序的八位字符串。这些位字符串的连接是共享值。该值用 "0" 位左填充,以便填充的共享值的长度(以位为单位)变为最接近的 10 的倍数。
  • 校验和 (C) 字段是共享的数据部分(即 id || ext || e || GI || Gt || g || I || t || ps)的 RS1024 校验和(参见下文)。如果 ext = 0,RS1024 的自定义字符串 (cs) 为 "shamir",如果 ext = 1,则为 "shamir_extendable"。

然后,通过将其拆分为 10 位段并将每个段都变成一个包含 1024 个单词的单词列表的索引,从而将此结构转换为助记码(参见下文)。所有转换都使用大端位顺序。主密钥的熵<sup>4</sup>必须至少为 128 位,并且其长度必须为 16 位的倍数。所有实现必须支持长度为 128 位和 256 位的主密钥:

安全性 填充的共享值长度 共享总长度
128 位 130 位 200 位 = 20 个单词
256 位 260 位 330 位 = 33 个单词

这种构造产生了一个有益的属性,即随机标识符和迭代指数会转换为助记码的前两个单词,因此用户可以立即知道是否正在组合正确的共享,即,它们必须具有相同的前两个单词。此外,第三个单词编码组索引、组阈值和部分组计数。由于组阈值和组计数是常量,因此属于同一组的所有共享都以相同的三个单词开头。

生成和组合共享

多项式插值

给定一组 m 个点 (x<sub>i</sub>, y<sub>i</sub>),1 ≤ im,使得没有两个 x<sub>i</sub> 值相等,则存在一个在每个点 x<sub>i</sub> 处采用值 y<sub>i</sub> 的多项式。唯一确定满足这些条件的最低次数的多项式,并且可以使用下面给出的拉格朗日插值公式获得。

由于 Shamir 的秘密共享方案分别应用于共享秘密的每个 n 字节,因此我们将 y<sub>i</sub> 用作 n 个值的向量,其中 y<sub>i</sub>[k] = f<sub>k</sub>(x<sub>i</sub>),1 ≤ kn,并且 f<sub>k</sub> 是方案的第 k 个实例中的多项式。

Interpolate(x, {(x<sub>i</sub>, y<sub>i</sub>), 1 ≤ im})

输入: 所需的索引 x,一组索引/值向量对 {(x<sub>i</sub>, y<sub>i</sub>), 1 ≤ im} ⊆ GF(256) × GF(256)<sup>n</sup>

输出: 值向量 (f<sub>1</sub>(x), ... , f<sub>n</sub>(x))

f_k(x) = \sum_{i=1}^m y_i[k] \prod_{\underset{j \neq i}{j=1}}^m \frac{x - x_j}{x_i - x_j}

共享一个秘密

SplitSecret(T, N, S)

输入: 阈值 T,共享数 N,秘密 S

输出: 共享的索引为 0, ... , N − 1 的共享 y<sub>1</sub>, ... , y<sub>N</sub>

  1. 检查以下条件:

    • 0 < TN ≤ 16
    • S 的长度(以位为单位)至少为 128,并且是 16 的倍数。

    如果未满足任何这些条件,则中止。

  2. 如果 T 为 1,则对于所有 i(1 ≤ iN),令 y<sub>i</sub> = S 并返回。
  3. nS 的长度(以字节为单位)。随机生成具有均匀分布的 R ∈ GF(256)<sup>n−4</sup>,并让 D 为 HMAC-SHA256(key=R, msg=S) 的前 4 个字节与 Rn − 4 个字节的连接。
  4. y<sub>1</sub>, ... , y<sub>T−2</sub> ∈ GF(256)<sup>n</sup> 随机生成, 独立且均匀分布。
  5. 对于满足 T − 2 < iNi,计算 y<sub>i</sub> = Interpolation(i − 1, {(0, y<sub>1</sub>), ... , (T − 3, y<sub>T−2</sub>), (254, D), (255, S)}).

用于生成上述步骤 3 和 4 中的值的随机数源必须适合生成密码密钥。

RecoverSecret(T, [(x<sub>1</sub>, y<sub>1</sub>), ... , (x<sub>m</sub>, y<sub>m</sub>)])

输入: 阈值 T,一个共享索引/共享值对的列表 m [(x<sub>1</sub>, y<sub>1</sub>), ... , (x<sub>m</sub>, y<sub>m</sub>)]

输出: 共享秘密 S

  1. 如果 T 为 1,则令 S = y<sub>1</sub> 并返回。
  2. 计算 S = Interpolation(255, [(x<sub>1</sub>, y<sub>1</sub>), ... , (x<sub>m</sub>, y<sub>m</sub>)]).
  3. 计算 D = Interpolation(254, [(x<sub>1</sub>, y<sub>1</sub>), ... , (x<sub>m</sub>, y<sub>m</sub>)]).
  4. RD 的最后 n − 4 个字节。如果 HMAC-SHA256(key=R, msg=S) 的前 4 个字节等于 D 的前 4 个字节,则返回 S,否则中止。

生成共享

GenerateShares(GT, [(T<sub>1</sub>,N<sub>1</sub>), ... , (T<sub>G</sub>,N<sub>G</sub>)], MS, P, e)

输入: 组阈值 GT,成员阈值的列表 T<sub>1</sub>, ... , T<sub>G</sub> 和组大小 N<sub>1</sub>, ... , N<sub>G</sub>,主密钥 MS,密码 P,迭代指数 e

输出: 共享列表

  1. 如果对于任何 iT<sub>i</sub> = 1 且 N<sub>i</sub> > 1,则中止。
  2. 生成一个随机的 15 位值 id
  3. ext = 1。
  4. 计算加密的主密钥 EMS = Encrypt(MS, P, e, id, ext)。
  5. 计算组共享 s<sub>1</sub>, ... , s<sub>G</sub> = SplitSecret(GT, G, EMS)。
  6. 对于每个组共享 s<sub>i</sub>(1 ≤ iG),计算成员共享 s<sub>i,1</sub>, ... , s<sub>i,N<sub>i</sub></sub> = SplitSecret(T<sub>i</sub>, N<sub>i</sub>, s<sub>i</sub>)。
  7. 对于每个 i 和每个 j(1 ≤ iG,1 ≤ jN<sub>i</sub>),返回 (id, ext, e, i − 1, GT − 1, j − 1, T<sub>i</sub> − 1, s<sub>i,j</sub>)。

组合共享

输入: 共享列表,密码 P

输出: 主密钥 MS

  1. 检查以下条件:

    • 每个共享的校验和必须有效。实现不应实现超出可能向用户建议在助记词中可能发现错误的位置的更正,而不建议进行更正<sup>5</sup>。
    • 所有共享必须具有相同的标识符 id、可扩展备份标志 ext、迭代指数 e、组阈值 GT、组计数 G 和长度。G 的值必须大于或等于 GT
    • GM 为给定共享中成对不同的组索引的数量。那么 GM 必须等于 GT
    • 具有给定组索引 GI<sub>i</sub>(1 ≤ iGM)的所有共享必须具有相同的成员阈值 T<sub>i</sub>,它们的成员索引必须成对不同,并且它们的计数 M<sub>i</sub> 必须等于 T<sub>i</sub>
    • 共享值中填充的长度(以位为单位),该长度等于填充的共享值长度(以位为单位)模 16,不得超过 8 位。
    • 所有填充位必须为 "0"。
    • 每个共享值的长度必须至少为 128 位。

    如果任何检查失败,则中止。

  2. s<sub>i</sub> = RecoverSecret([(I<sub>i,1</sub>, s<sub>i,1</sub>), ... , (I<sub>i,M<sub>i</sub></sub>, s<sub>i,M<sub>i</sub></sub>)]), 其中 I<sub>i,j</sub>s<sub>i,j</sub> 是具有组索引 GI<sub>i</sub> 的共享的成员索引/共享值对。

  3. EMS = RecoverSecret([(GI<sub>1</sub>, s<sub>1</sub>), ... , (GI<sub>GM</sub>, s<sub>GM</sub>)])

  4. 返回 MS = Decrypt(EMS, P, e, id, ext)。

校验和

助记词的最后三个单词形成校验和,不包含任何信息。有效的助记词必须通过以下 Python3 代码段指定的有效性标准。当以下参数为真时,函数 rs1024_verify_checksum 必须返回 true:

  • cs: 自定义字符串
  • data: 数据部分,作为 10 位整数的列表,每个整数对应于助记词的一个单词
def rs1024_polymod(values):
  GEN = [0xe0e040, 0x1c1c080, 0x3838100, 0x7070200, 0xe0e0009, 0x1c0c2412, 0x38086c24, 0x3090fc48, 0x21b1f890, 0x3f3f120]
  chk = 1
  for v in values:
    b = (chk >> 20)
    chk = (chk & 0xfffff) &lt;&lt; 10 ^ v
    for i in range(10):
      chk ^= GEN[i] if ((b >> i) & 1) else 0
  return chk

def rs1024_verify_checksum(cs, data):
  return rs1024_polymod([ord(x) for x in cs] + data) == 1

这实现了 GF(1024) 上的 Reed-Solomon 代码,该代码保证检测到最多影响 3 个单词的任何错误,并且检测到更多错误的概率低于 1/10<sup>9</sup>。有关属性的更多详细信息,请参见校验和设计附录<sup>5</sup>。自定义字符串通过在数据之前将每个字符的 US-ASCII 值馈入校验和计算来处理。

要根据自定义字符串和数据部分单词的值构造有效的校验和,可以使用以下代码:

def rs1024_create_checksum(cs, data):
  values = [ord(x) for x in cs] + data
  polymod = rs1024_polymod(values + [0,0,0]) ^ 1
  return [(polymod >> 10 * (2 - i)) & 1023 for i in range(3)]

密码

为了允许额外的保护,主密钥使用下面描述的加密函数通过密码加密。没有办法验证是否使用了正确的密码来解密加密的主密钥。这允许用户通过使用不同的密码从单个加密的主密钥获得多个主密钥<sup>8</sup>。

为了在各种操作系统和钱包实现之间实现最佳的互操作性,密码必须是一个仅包含可打印 ASCII 字符(代码点 32-126)的字符串。如果未提供密码,则应将空字符串用作密码。

主密钥的加密

主密钥使用基于 Luby-Rackoff 结构的宽块大小伪随机置换<sup>7</sup>进行加密。它由一个四轮 Feistel 网络组成,密钥派生函数 PBKDF2<sup>6</sup> 作为轮函数。此方案是可逆的,这意味着共享的创建者可以选择主密钥,从而可以将 BIP-32 钱包从 BIP-39 助记词迁移到新的秘密共享方案。首先将主密钥分成两个等长的部分,其中 L 是主密钥的前 n/2 个字节,R 是主密钥的后 n/2 个字节,并按如下方式处理:

L = MS[:len(S)/2]
R = MS[len(S)/2:]
for i in [0,1,2,3]:
    (L, R) = (R, L xor F(i, R))

然后加密的主密钥为 EMS = R || L

i 轮函数 F(i, R) 定义如下:

F(i, R) = PBKDF2(PRF = HMAC-SHA256, Password = (i || passphrase), Salt = (salt_prefix || R), iterations = 2500 &lt;&lt; e, dkLen = n/2 bytes)

i 的值编码为一个字节。

如果 ext = 1,则 salt_prefix 是一个空字符串。 如果 ext = 0,则 salt_prefix = "shamir" || id,其中随机标识符值 id 编码为大端字节顺序的两个字节。

主密钥的解密

加密和解密之间唯一的区别是 i 的值的顺序颠倒:

L = EMS[:len(EMS)/2]
R = EMS[len(EMS)/2:]
for i in [3,2,1,0]:
    (L, R) = (R, L xor F(i, R))
MS = R || L

版本控制

我们的方案不支持版本控制。这是故意的,以避免不明确的声明,例如 SLIP-0039 兼容性,而没有清楚地了解实际意味着该方案的哪个版本。对此规范的任何未来增强都应标准化为新的 BIP 或 SLIP,并且应使用其自己唯一的自定义字符串,以便无法将根据新规范创建的共享误认为是 SLIP-0039 共享。

本地化

不支持本地化。此标准仅处理一组英文单词。先前使用任意单词列表的尝试导致用户之间产生很多困惑,并降低了各种实现之间的互操作性。

词表

此 SLIP 要求的单词列表在此处提供。在创建列表时应用了以下几个标准:

  • 词表按字母顺序排序。
  • 没有一个字短于 4 个字母。
  • 没有一个字长于 8 个字母。
  • 所有单词都以唯一的 4 字母前缀开头。
  • 词表仅包含常见的英语单词(+ 单词 "satoshi")。
  • 任何两个单词之间的最小 Damerau-Levenshtein 距离至少为 2。
  • 任何两个单词的发音之间的相似性已最小化。

(请参阅测试,该测试检查是否满足这些标准)。

备份 BIP-0032 分层确定性钱包的规范

SLIP-0039 可用于备份满足上述长度约束的任何主密钥 S。但是,任何实现 SLIP-0039 以备份 BIP-0032 分层确定性钱包的应用程序都必须使用 BIP-0032 主种子 作为 SLIP-0039 主密钥 S。为了清楚起见,这是最初生成的 128-512 位的种子字节序列,该序列用作 deriving BIP-0032 主节点的 HMAC-SHA512 的输入。

需要此规范以确保在一个钱包中创建的 SLIP-0039 备份可以在任何其他实现 SLIP-0039 的钱包中恢复。

测试向量

测试向量以四元组列表的形式给出。四元组的第一个要素是测试向量的描述,第二个是助记词列表,第三个是组合助记词产生主密钥,第四个是从主密钥派生的 BIP32 主扩展私钥。主密钥编码为一个字符串,每个字节包含两个十六进制数字。如果字符串为空,则尝试组合给定的助记词集应导致错误。所有有效的助记词集都使用密码 "TREZOR"。

<http://github.com/trezor/python-shamir-mnemonic/blob/master/vectors.json>

参考实现

参考实现可从以下网址获得 <http://github.com/trezor/python-shamir-mnemonic/>。

其他实现

C#:

Dart:

Go:

JavaScript:

Rust:

支持 SLIP39 的钱包:

设计原理

  1. <a name="FiniteField"></a>有限域的选择

    对于该方案,考虑了 GF(2<sup>m</sup>) 和 GF(p) 形式的有限域,其中 p 是素数。选择 GF(256) 域是因为该域的算术运算很容易在任何编程语言中实现,并且由于它在 AES 密码中使用,因此许多实现已经可用。它是面向字节的事实使它易于使用。

    使用素数阶 GF(p) 的域,其中 log<sub>2</sub> p 近似于主密钥的位长度,将需要支持多精度算术。许多编程语言,如 C/C++,不支持开箱即用的多精度算术。实现还需要存储关于应该用于主密钥的每个允许长度的素数的信息,或者他们需要在运行时计算素数。

    选择 GF(2<sup>m</sup>),其中 m 是主密钥的位长度,将需要比 GF(256) 更复杂的实现。这部分是由于算术的多精度性质,部分是由于实现需要为 m 的每个允许值存储一个(例如,字典序最小的)m 次不可约多项式,或者他们需要能够在运行时确定这个多项式。

  2. <a name="GroupPolicies"></a>组策略

    建议当需要单层 T-of-N 方案时,应该创建一个组共享并将其拆分为 N 个成员共享。另一种方法是创建 N 个组,每个组使用 1-of-1 成员方案。两种方法在安全性方面没有区别。使用推荐方法的好处是,在恢复密钥时,可以从任何共享中确定使用了单层方案。这使得提供更全面的用户体验成为可能。

    建议如果一个组的成员阈值 T<sub>i</sub> 为 1,那么组的大小 N<sub>i</sub> 也应该为 1。对于 N > 1,使用 1-of-N 方案拆分组共享不会比 1-of-1 方案提供额外的安全性,因为阈值为 1 的组中的共享只会成员索引(助记词的第四个单词)和助记词末尾的三个校验和单词不同。如果用户尝试生成多个阈值为 1 的成员共享,那么这很可能是一个错误或未能理解其后果。

  3. <a name="IndexEncoding"></a>索引编码

    预计每个组中有 16 个组和 16 个成员共享对于 Shamir 密钥共享方案在 BIP-32 主种子上的任何应用来说都绰绰有余。因此,为了减少助记词的长度,索引和阈值被限制为每个 4 位。

    在本规范中,共享密钥存储在索引 255 下,而不是通常的索引 0。使用索引 0 作为共享密钥的缺点是 0 无法用作共享的索引值,因此任何索引值为 0 的共享都必须被视为无效。但是,某些实现可能无法检查这一点,这将为以下攻击打开大门:假设一个实现不检查提供的 x 值是否为非零。有权写入其中一个共享的攻击者可以将存储的点从 (x,y) 更改为 (0,y)。如果该实现在拉格朗日插值公式中使用该值,则无论其他共享的值如何,生成的共享密钥将始终等于 y。如果该值受到弱密码保护并用作 BIP-32 钱包的主种子,那么攻击者将能够窃取转移到该钱包的任何资金,因为他知道 y

  4. <a name="Digest"></a>摘要

    如果阈值 T 至少为 2,那么共享索引 254 用于编码共享密钥 S 的摘要。对应于索引 254 的共享值 D 由两部分组成。D 的前 4 个字节编码实际摘要,剩余的 n − 4 个字节 R 是随机生成的。摘要计算为 HMAC-SHA256(key=R, msg=S) 的前四个字节。编码摘要使得检测无效的共享集成为可能,其随机失败的概率为 2<sup>−32</sup>。由于每个助记词都有一个标识符和一个 RS1024 校验和,因此不太可能随机出现无效的共享集。因此,无效摘要通常表明一个或多个提供的共享已被攻击者恶意伪造。

    m 表示共享密钥的熵(以位为单位)。编码共享密钥的摘要的一个缺点是,了解 T − 1 个共享值的攻击者可以通过对 2<sup>m</sup> 个可能的共享密钥值执行暴力搜索并消除那些给出无效摘要的值,从而将共享密钥的熵降低到 m − 32 位。共享密钥的熵必须足够大才能使此类攻击变得不切实际,这就是为什么本规范要求 m ≥ 128。

    使用 HMAC-SHA256(key=R, msg=S) 而不是 SHA-256(S) 来计算摘要的优点是,它可以更好地防止攻击者仅了解 T − 1 个共享的部分知识或共享密钥的部分知识的攻击。例如,如果摘要仅依赖于 S 而不依赖于 R,那么仅了解 T − 1 个共享值的前 4 个字节就可能执行上述攻击。

  5. <a name="ChecksumDesign"></a>校验和设计

    校验和设计在很大程度上受到 BIP-0173 中定义的 Bech32 的启发。RS1024 校验和使用 GF(1024) 上的 Reed-Solomon 码,因此代码字母表与 10 位单词列表匹配。GF(1024) 上的 Reed-Solomon 码允许创建长度最多为一千个单词的助记词,这已经足够了。需要如此长度的共享密钥对于人工输入来说是不切实际的,应该以二进制形式存储而不是助记词形式存储。我们选择了 3 个校验和单词作为助记词的长度和错误检测能力之间的折衷,因为 3 个校验和单词是随机失败概率低于十亿分之一所需的最低数量。RS1024 是一种 MDS 码,这意味着它保证可以检测到任何 3 个或更少的错误。对于任何长度为 3 的校验和来说,这是可能的最大值。Reed-Solomon 码可以看作是 BCH 码的一个特例。在 Python3 代码片段中,我们使用了 Reed-Solomon 码的 BCH 视图,因为它允许更有效地实现算法。该代码的生成多项式为 (xa)(xa<sup>2</sup>)(xa<sup>3</sup>),其中 a 是 GF(2) 上本原多项式 x<sup>10</sup> + x<sup>3</sup> + 1 的根。GF(1024) 的元素表示为多项式,其运算以该本原多项式为模。

    实现不应实现超出可能向用户建议助记词中可能发现错误的位置的纠正,而不建议进行纠正。BIP-0173 (Bech32) 中也提出了相同的建议,它使用类似的校验和方案。原因是自动纠错会将无效的助记词更改为有效的助记词。问题在于,如果出现多个错误,则自动更正的助记词将是有效的,但与原始助记词不同。使用这样的助记词可能会导致资金无法挽回地丢失(最值得注意的是,如果阈值为 1)。这就是为什么更正应该只由用户进行,用户可以更仔细地检查手写的助记词,因此更有资格确定到底在哪里发生了错误。

  6. <a name="KDFParam"></a>KDF 函数和参数的选择

    PBKDF2 是一种广泛使用的基于密码的标准密钥派生函数。考虑了诸如 scrypt 或 Argon2 之类的新密钥派生函数,但这些函数需要大量内存,这是硬件钱包中的一个限制因素。

    SHA-256 算法对 32 位字进行运算,而 SHA-512 算法对 64 位字进行运算。因此,SHA-512 在 64 位平台上比在 32 位平台上快得多,但 SHA-256 在两个平台上的性能几乎相同。使用 HMAC-SHA512 会使可能在 32 位平台上运行的用户处于与在 64 位平台上对密码进行暴力破解的攻击者相比的明显劣势。这就是为什么选择 HMAC-SHA256 作为 PBKDF2 的伪随机函数。

    PBKDF2 中的总迭代次数被选择为至少 10000 次,即 Feistel 加密函数的四个轮次中的每一个轮次中进行 2500 次迭代。PBKDF2 中更大的迭代次数目前会影响硬件钱包中的用户体验。共享的创建者可以自由选择更大的迭代次数,理论上高达 3.27 亿次,这使得该格式更具有面向未来的特性,更适用于更广泛的环境。

  7. <a name="Encryption"></a>加密

    与简单的加密方案相比,宽块大小伪随机排列的优点在于它可以阻止攻击者获得例如 T 个不同共享的最初几个字节的攻击。如果主密钥没有受到强大的伪随机排列的保护,攻击者就可以计算出部分主密钥。如果主密钥是例如私钥,这是一个严重的问题。在任何常见块密码模式下使用 AES 保护主密钥都不能解决这个问题。

    可能看起来如果使用了更大的有限域,例如 GF(2<sup>m</sup>) 或 GF(p),其中 m ≈ log<sub>2</sub> pm 是主密钥的位长,这种攻击是不可能的。但是,我们不知道有任何证据表明 Shamir 的密钥共享方案在泄露关于共享的部分信息的场景中是安全的。事实上,我们的初步调查表明,如果可以获得 T 个共享的部分知识,则关于共享密钥的信息可能会泄露。因此,无论选择哪个字段,都建议使用强大的伪随机排列。

    密钥派生函数在基于 Feistel 的加密函数中的作用是双重的。首先,它可以保护密码免受暴力破解和字典攻击。其次,如果攻击者如上所述获得部分加密的主密钥,则慢速密钥派生函数可以防止尝试泄露未知部分加密的主密钥的暴力破解攻击。

  8. <a name="PassphraseVerification"></a>密码验证

    所提出的设计没有提供验证是否使用了正确的密码来解密加密的主密钥的方法。这是一个有意为之的功能,允许用户通过使用不同的密码从单个加密的主密钥中获得多个主密钥。当主密钥用作分层确定性钱包的主种子时,此设计允许合理的推诿(参见 BIP-32)。每个密码都会生成一个有效的种子,但只有正确的密码才能使所需的钱包可用。因此,所有者可以使用一个密码来访问他们的真实钱包,而使用另一个密码来访问诱饵钱包。如果所有者后来受到法律或武力胁迫而泄露他们的密码,那么他们可以泄露访问诱饵钱包的密码并合理地否认他们的真实钱包的存在,因为胁迫者无法证明诱饵钱包不是真实的。

  9. <a name="Bip39Compatibility"></a>与 BIP-0039 的兼容性

    将现有的 BIP-0039 助记词转换为 SLIP-0039 共享

    这是可能的,但代价是所有 SLIP-0039 共享的长度都是 59 个单词,而不管原始的 BIP-0039 助记词的长度如何。这是因为在 BIP-0039 中,助记词和密码由 PBKDF2-SHA-512 处理以生成一个 512 位的种子,这需要使用 SLIP-0039 进行拆分。此外,任何将多个不同的密码与一个 BIP-0039 助记词一起使用以拥有多个钱包的用户只能将其中一个钱包转换为 SLIP-0039 共享。

    希望利用 Shamir 密钥共享的用户应将其资金从旧的 BIP-0039 钱包转移到使用 SLIP-0039 备份的新钱包。这样做的好处是完全消除了使用旧的 BIP-0039 助记词进行盗窃的可能性,如果用户在不知不觉中未能销毁其所有副本,则可能会发生盗窃。

    将现有的 SLIP-0039 共享转换为 BIP-0039 助记词

    由于 BIP-0039 的过度耦合设计及其对单向派生函数的使用,这是不可能的。BIP-0039 的工作方式是首先生成一个高熵密钥,然后将其转换为助记词,最后使用助记词本身作为 PBKDF2 的输入来派生种子。这意味着任何与 BIP-0039 兼容的新方案都必须构建在 BIP-0039 之上,并包含其所有现在已过时的方面。这包括使用旧的单词列表将高熵密钥转换为助记词,必须将其包含在实现中,从而不合理地膨胀其大小。SLIP-0039 而是引入了一种新的解耦设计,该设计具有更丰富的功能,并为未来的升级提供了最大的灵活性。

    有些人表示担心无法将 SLIP-0039 共享转换为 BIP-0039 可能会导致供应商锁定,因为硬件钱包供应商采用 SLIP-0039 的速度很慢。这种担忧是没有根据的,因为即使可以转换为 BIP-0039 并且用户需要在不支持 SLIP-0039 的设备上恢复他们的种子,那么他们也需要使用在其计算机上运行的某种转换工具。在这种情况下,他们不妨简单地在计算机上运行的软件钱包中恢复他们的 SLIP-0039 共享,并将他们所有的资金发送到他们新设备上的新种子。因此,将共享转换为 BIP-0039 助记词的能力在这方面没有任何区别。

  10. <a name="ExtendableBackup"></a>可扩展备份标志

    当设置此标志时,它表示 id 不用作加密主密钥中的 salt,从而可以创建多组共享,以便每组共享使用不同的 id 并且每组共享都为每个密码带来相同的主密钥。这是一个理想的属性,因为它允许用户通过创建单共享 (1-of-1) 方案开始使用他们的钱包,并稍后升级到多共享方案,同时保持相同的 EMS 和密码。如果用户两次或多次升级到多共享方案,那么很可能每组共享都有一个不同的 id。因此,可以将这些组区分开来,并避免它们的意外混合,这可能会导致无法访问资金,因为即使它们使用相同的方案,来自不同组的共享也是不兼容的。

    ext 标志是在此规范的更高版本中添加的。以前,ext 是 5 位迭代指数的最高位。在新创建的共享中,ext 应该设置为 1。但是,由于某些用户已经拥有使用 ext = 0 的共享,因此实现必须支持使用两个 ext 值的主密钥解密。如果 ext = 0,那么实现不应该允许有意创建具有相同 idEMS 的另一组共享。这样做会冒着用户尝试从不兼容的组中恢复共享的风险,这是实现不需要处理的。

参考文献

  • 原文链接: github.com/satoshilabs/s...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
satoshilabs
satoshilabs
江湖只有他的大名,没有他的介绍。