密码学/RadioGatún
本文介绍了一种名为**RadioGatún**的加密算法。为了充分理解本文,读者需要理解以下概念
- 二进制数为了最大限度地提高性能,RadioGatún直接使用二进制数
- 十六进制数用于说明算法的某些部分
- 按位逻辑运算 RadioGatún使用按位取反、按位或和按位异或
- 位旋转 RadioGatún使用循环旋转
- 密码哈希 RadioGatún是一种(据我们所知)安全的密码哈希
- 伪随机数生成器 RadioGatún也作为一个安全的随机数生成器
- 读者应该熟悉至少一门编程语言,该语言可以执行 RadioGatún 执行的按位操作。
RadioGatún 是 Keccak 的直接前身,Keccak 是 SHA-3 哈希标准。它是现存速度最快的哈希之一:64 位版本的 RadioGatún 与 Blake2b 的速度大致相同,而 32 位版本的 RadioGatún 的速度与 Blake2s 的速度大致相同。虽然 Blake2 的可变长度输出被添加为附加功能,但 RadioGatún 具有固有的可变长度支持,既可以作为密码哈希,也可以作为流密码。
RadioGatún 是现存最古老的安全可变长度哈希;它自 2006 年起就存在,截至 2020 年,尚无已知的攻击削弱其安全声明。MD5 在发布后 12 年内就被破解;RadioGatún 存在的时间更长,但看起来仍然安全。
32 位版本似乎提供了 304 位的安全保障;64 位版本看起来提供了 608 位的安全保障。换句话说,32 位版本对于 256 位哈希来说足够安全,而 64 位版本可以生成 512 位哈希。
下面的代码使用多个数组来更改 RadioGatún 的状态。数组从零开始索引;这意味着数组的第一个元素的索引为 0 而不是 1。我们不会使用 i[1]、i[2] 和 i[3](从一开始索引的数组)来表示包含 3 个字的数组,而是使用 i[0]、i[1] 和 i[2] 来表示。
由于 RadioGatún 直接操作二进制数,我们有时会将数字以十六进制格式表示。在这种情况下,我们在数字的开头加上“0x”。例如:0x07 是 7;0x10 是 16。
在本页中,字节始终为 8 位。字可以是 32 位或 64 位,具体取决于使用的 RadioGatún 版本。
RadioGatún 是一种在 21 世纪初 20 年代中期设计的密码,当时服务器运行着 32 位和 64 位操作系统的混合体,大多数台式机仍然运行着 32 位系统,并且在任何手持电话运行 64 位代码之前很久。考虑到这一点,该密码具有不同的模式来支持 32 位和 64 位操作。还有一些非常小的版本可以简化密码分析:研究 RadioGatún 安全性的研究人员可以分析 1 位或 2 位版本(截至 2018 年,已被破解的最大 RadioGatún 变体是 2 位变体)。
虽然 RadioGatún 支持 8 位和 16 位,但 8 位版本可能太小而无法保证安全,而 16 位版本可能太小。考虑到这一点,我们只关注 RadioGatún 的 32 位和 64 位变体。
RadioGatún 使用 58 个字(32 位或 64 位,具体取决于我们正在查看的 RadioGatún 变体)来存储其状态。在本文档的其余部分,“字”是指 32 位或 64 位数字,具体取决于正在使用的 RadioGatún 变体。
要初始化 RadioGatún 的状态,所有 58 个字都将重置为值 0。
RadioGatún 将其状态存储在两个部分中:39 个字存储在传送带上,传送带可以可视化为三行,每行 13 个字。磨坊有 19 个字宽。RadioGatún 的某些实现使用一个额外的字来存储传送带,这有利于代码大小优化。
虽然RadioGatún 规范将“传送带”和“磨坊”操作视为独立的,但它们同时完成,因此,在本文件中,我们将考虑一个组合的传送带磨坊操作。这是 RadioGatún 算法的核心,RadioGatún 的所有部分(输入映射、输入结束后的 RadioGatún 混合以及伪随机输出位的生成)都使用相同的传送带磨坊函数。
RadioGatún 规范将磨坊视为三个数组,每个数组包含 13 个元素;在这里,我们将磨坊视为一个包含 40 个字的数组。此外,本书中操作的顺序与规范中描述的顺序不同。
此操作类似于搅拌机,以一种既可预测又据信具有密码安全性的方式混合 RadioGatún 状态的位。
在传送带到磨坊的向前馈送中,我们用传送带中的 12 个字异或磨坊中的 12 个字,如下所示
- 磨坊的第一个词(在数组索引从零开始的编程语言中,元素 0)与磨坊的第二个词(元素 1)进行异或运算;结果词存储为皮带中的元素 0。
- 皮带的元素 14 与磨坊的元素 2 进行异或运算。
- 皮带的元素 28 与磨坊的元素 3 进行异或运算。
- 皮带的元素 3 与磨坊的元素 4 进行异或运算。
- 皮带的元素 17 与磨坊的元素 5 进行异或运算。
- 皮带的元素 31 与磨坊的元素 6 进行异或运算。
- 皮带的元素 6 与磨坊的元素 7 进行异或运算。
- 皮带的元素 20 与磨坊的元素 8 进行异或运算。
- 皮带的元素 34 与磨坊的元素 9 进行异或运算。
- 皮带的元素 9 与磨坊的元素 10 进行异或运算。
- 皮带的元素 23 与磨坊的元素 11 进行异或运算。
- 皮带的元素 37 与磨坊的元素 12 进行异或运算。
这可以用以下伪代码来表示:
For a = 0 to 11 { # "a" has the values (0,1,2,3,4,5,6,7,8,9,10,11) in this loop belt[a + ((a % 3) * 13)] = belt[a + ((a % 3) * 13)] ^ mill[a + 1] }
这里,% 是模运算:除法后的余数(例如:5 模 3 是 2;11 模 4 是 3)
^ 是异或运算
* 是乘法
+ 是加法
在主要的磨坊操作中,我们对磨坊中的词执行一系列操作,并将结果放入一个名为“t”的包含 19 个词的临时数组中。
我们将旋转常数“r”设置为 0。
我们对“a”从 0 到 18(包括 18)进行 19 次以下操作:
- 我们将“a”加到“r”上。
- 我们将“r”对正在使用的 RadioGatún 变体的词大小进行模运算(对于 RadioGatún[32],19 个旋转值是 [0, 1, 3, 6, 10, 15, 21, 28, 4, 13, 23, 2, 14, 27, 9, 24, 8, 25, 11];对于 RadioGatún[64],是 [0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43])。
- 我们将索引“i”设置为“a”乘以 7。
- 我们将“i”对 19(磨坊宽度)进行模运算(19 个“i”值是 [0, 7, 14, 2, 9, 16, 4, 11, 18, 6, 13, 1, 8, 15, 3, 10, 17, 5, 12])。
- 我们将“k”设置为磨坊索引“i”。
- 我们将索引“ia”设置为“i”加 1,模 19。“模 19”在这里意味着,如果“i”的值是 18,我们不将“ia”设置为 19,而是设置为 0。
- 我们将索引“ib”设置为“i”加 2,模 19。“模 19”在这里意味着,如果 i + 2 是 19 或更高,我们从“i”中减去 19:如果“i”是 17,那么“ib”将是 0(而不是 19);如果“i”是 18,那么“ib”将是 1(而不是 20)。
- 我们将磨坊元素“ib”复制到“mb”中。
- 我们对“mb”进行按位取反操作(我们将每个 0 位变成 1,将每个 1 位变成 0)。
- 我们将磨坊元素“ia”复制到“ma”中。
- 我们对“ma”和“mb”进行按位或运算,并将结果放入“mc”中。
- 我们将“mc”与磨坊元素“i”进行异或运算,并将结果放入临时数组“t”中,其索引为“a”。
- 我们将临时数组“t”中索引为“a”的元素向右旋转“r”位。
这可能看起来像以下伪代码
r = 0 for a = 0 to 18 { r = r + a r = r % wordsize # 32 for 32-bit RadioGatún, 64 for 64-bit RadioGatún i = a * 7 i = i % 19 # Mill width k = mill[i] k = k ^ (mill[(i + 1) % 19] | (~mill[(i + 2) % 19])) t[a] = rotate_right(k, r) }
操作符与上面相同;此外,~ 是按位取反
rotate_right 是循环向右旋转。例如,对于 32 位 RadioGatún,rotate_right 可能看起来像 C 中的以下代码
uint32_t rotate_right(uint32_t k, int r) { return k>>r|k<<((32-r)%32); }
现在,我们已经对磨坊中的元素执行了一些操作并将它们放入了 19 个词的临时数组“t”中,我们现在可以根据“t”中保存的值来更新实际的磨坊。
对于“a”从 0 到 18(包括 18)的每个值
- “i1”将是“a”加 1,模 19。
- “i2”将是“a”加 4,模 19。
- 将磨坊元素“a”设置为“t”数组中元素“a”的值。
- 将磨坊元素“a”与“t”数组中元素“i1”的值进行异或运算。
- 将磨坊元素“a”与“t”数组中元素“i2”的值进行异或运算。
伪代码
for a = 0 to 18 { mill[a] = t[a] ^ t[(a + 1) % 19] ^ t[(a + 4) % 19] }
我们现在旋转皮带。在 RadioGatún 规范中,这被描述为将皮带的每一行都向右移动一个位置。所有词都向右移动一个位置,就像传送带一样,除了最右边的词会移动到最左边。皮带包含三行,每行 13 个元素;我们分别对三行进行此旋转操作。
为了使代码更简单、更小,我们可以将皮带视为包含 40 个(而不是 39 个)元素的数组,然后将所有元素向右移动一个位置。完成此操作后,我们将三个值向下移动皮带,使皮带变成循环的。
对于“i”从 38 到 0(包括 0;请注意,我们从 38 开始,然后向下到 0)
- 将“i”加 1 并赋予它值“i1”(因此 38 变成 39,37 变成 38,依此类推)
- 将索引为“i1”的皮带元素赋值为索引为“i”的皮带元素的值。
我们从 38 向下到 0 而不是从 0 向上到 39 的原因是,如果我们递增而不是递减“i”,磨坊中的所有元素将具有相同的值(磨坊元素 0),从而破坏磨坊。
将磨坊中的所有元素向上移动一个位置后,我们现在将三个元素向下移动。
对于“i”从 0 到 2(包括 2)
- “i0”是“i”乘以 13(皮带宽度)。
- “i1”是“i0”加 13。
- 取皮带元素“i0”并将皮带元素“i1”的值赋值给它。
第二个循环使皮带成为三个 13 个元素的循环皮带。
以上两个步骤可以用以下伪代码来完成
for a = 38 to 0 step -1 { # "step -1" makes it clear we're moving down from 38 instead of up from 0 belt[a + 1] = belt[a] } for a = 0 to 2 { belt[(a + 1) * 13] = belt[a * 13] }
请注意,RadioGatún 的速度优化实现(例如 Thomas Pornin 在 sphlib 中编写的实现)不旋转皮带;相反,它们运行 13 个不同的 beltmill 函数版本,通过改变相互作用的元素而不是移动 39 个元素来实现。
iota 步只是将磨坊中的第一个词(元素 0)与数字 1 进行异或运算。
伪代码
mill[0] = mill[0] ^ 1
请注意,在 Keccak 中,iota 步要复杂得多。
为了完成 beltmill 函数,我们将磨坊中的三个词与皮带中的三个词进行异或运算,并将结果存储在磨坊词中。
- 磨坊元素 13(磨坊中的第 14 个元素)与皮带元素 0(磨坊最下面的行的第一个词)进行异或运算,并将结果存储在磨坊元素 13 中。
- 磨坊元素 14(磨坊中的第 15 个元素)与皮带元素 13(磨坊中间行的第一个词)进行异或运算,并将结果存储在磨坊元素 14 中。
- 磨坊元素 15(磨坊中的第 16 个元素)与皮带元素 26(磨坊最上面的行的第一个词)进行异或运算,并将结果存储在磨坊元素 15 中。
伪代码
mill[13] = mill[13] ^ belt[0] mill[14] = mill[14] ^ belt[13] mill[15] = mill[15] ^ belt[26]
请注意,RadioGatún 的代码大小优化版本会将此操作放在一个 3 步循环中,因为我们可以将皮带旋转的第二部分和皮带到磨坊的操作放在一个小的循环中,但是速度优化版本会展开循环(将循环中执行的操作转换为循环外执行的多项操作,以节省处理循环所需的 CPU 周期)。
输入映射步骤是我们获取任意八位字节长度的二进制字符串,并使用它来建立 RadioGatún 的状态。
我们首先查看 RadioGatún[32](32 位 RadioGatún),在这种情况下,哈希输入的长度恰好为 32 位。
首先,我们初始化 RadioGatún:我们将皮带和磨坊中的所有词的值设置为 0。完成此操作后,我们将执行以下操作(所有这些步骤实际上都是异或运算,但由于我们是对值 0 进行异或运算,因此它等同于将它们设置为所需的值)。
- 我们将皮带的第一个元素(皮带元素 0)设置为 32 位哈希输入。
- 我们将磨坊的第 17 个字节(磨坊元素 16)设置为相同的 32 位哈希输入。
- 我们将皮带的第 14 个字节(皮带元素 13,或者也可以说是皮带第二行的第一个词)设置为二进制 1。
- 我们将磨坊的第 18 个字节(磨坊元素 17)也设置为值 1。
瞧!我们现在将 32 位哈希输入(或者也可以说是“种子”)放入 RadioGatún 中。
伪代码
function initRadioGatun32(int32bit a) { for b = 0 to 18 { mill[b] = 0 } for b = 1 to 39 { belt[b] = 0 } mill[16] = a belt[0] = a mill[17] = 1 belt[13] = 1 }
将此 32 位值放入 RadioGatún 状态(皮带和磨坊)后,我们现在可以执行“空白轮”步骤。
输入字符串结束后,RadioGatún 运行 18 轮空白轮:18 次 Beltmill() 函数迭代。
伪代码
for a = 1 to 18 { Beltmill() # As already described above }
执行完空白轮后,跳转到后面的“生成输出”部分。
RadioGatún 不仅支持 32 位输入,它支持从 0 开始的任何字节数的输入。接下来我们将介绍如何处理其他长度的输入。
RadioGatún 假设在小端序处理器上运行(或者更准确地说,用于生成参考测试向量的参考实现是在小端序机器上运行的)。考虑到这一点,8 位字节被转换为 32 位或 64 位字(取决于我们使用的是 32 位还是 64 位变体),第一个字节被设置为最低有效位。
换句话说,如果 RadioGatún 给定字符串“124”,它由字节(以十六进制表示)0x31、0x32 和 0x34 组成,这将被 RadioGatún[32](32 位版本)转换为 0x01343231,并被 RadioGatún 的 64 位版本转换为 0x0000000001343231。
这里,给定 RadioGatún 的第一个字节被设置为 RadioGatún 使用的字的最低(最低有效)八个字节。
知道自己在小端序机器上运行的 RadioGatún 的速度优化版本可以直接从内存中读取字,以便它们影响状态。
您可能还会注意到,字节 0x01 被放置在上面字符串的末尾;这是一个填充字节,用于让 RadioGatún 知道字符串在哪里结束。这个填充字节保护 RadioGatún 免受一些安全攻击。因此,如果我们的字符串是三个字节的字符串“124”,或者 0x31 0x32 0x34,它将变成 0x31 0x32 0x34 0x01,然后被转换为小端序数字,比如 0x01343231。如果给 RadioGatún 一个值为 0x01 的单字节字符串,这将被转换为 0x01 0x01,然后进行小端序转换。
RadioGatún 可以读取任何任意二进制字符串,包括包含 0x00(NULL)、0x01 等的字符串,只要字符串的长度是 8 的倍数(0 位长、8 位长、16 位长、24 位长等);RadioGatún 不支持其他输入长度的原因是,它的填充行为没有完全定义,并且没有官方的测试向量不是八位长的倍数。
一旦输入字符串被转换为小端序字,我们就取三个字,并将它们与 RadioGatún 状态进行异或运算,如下所示
- 我们将这三个字命名为“w1”、“w2”和“w3”。例如,如果 RadioGatún[32] 给定字符串“12345678901”作为输入,“w1”将具有值 0x34333231,“w2”将是 0x38373635,“w3”将是 0x01313039。如果字符串更短,例如“1”(0x31),我们将用 0 填充:”w1“将是 0x00000131,”w2“和”w3“将简单地是 0x00000000;如果使用 RadioGatún[64] 并且字符串仅仅是 ASCII 字符“1”,”w1“将是 0x0000000000000131,”w2“和”w3“将是 0x0000000000000000。
- 我们将皮带的第一个字节(皮带元素 0)与“w1”进行异或运算,并将结果放回 belt[0]
- 我们将磨坊的第 17 个字节(磨坊元素 16)与“w1”进行异或运算,并将结果放回 mill[16]
- 我们将皮带的第 14 个字节(皮带元素 13,或者你可以说,皮带的第二行的第一个字节)与“w2”进行异或运算,并将结果放回 belt[13]
- 我们将磨坊的第 18 个字节(磨坊元素 17)与“w2”进行异或运算,并将结果放回 mill[17]
- 我们将皮带的第 27 个字节(皮带元素 26,或者你可以说,皮带的顶部的第一个字节)与“w3”进行异或运算,并将结果放回 belt[26]
- 我们将磨坊的第 19 个字节(磨坊元素 18)与“w3”进行异或运算,并将结果放回 mill[18]
- 现在我们对 RadioGatún 的状态运行 Beltmill 函数
如果字符串,包括最后的 0x01 填充字节,被使用完,我们就转到下一部分。否则,我们向前移动字符串 12 或 24 个字节(取决于我们是在计算 RadioGatún[32] 还是 RadioGatún[64]),并重复上述步骤。
在这个伪代码示例中,我们逐字节读取输入,以使代码与端序无关(它在大小端序机器上运行方式相同)。RadioGatunWordSize 指示我们正在实现的 RadioGatún 变体的字大小,可以是 32 或 64;Beltmill() 是前面描述的皮带磨坊函数
endOfString = False array inputWords[3] # Initialize the input words for i = 0 to 2 { inputWords[i] = 0 } i = 0 shift = 0 while(endOfString is False) { a = getByteFromString() if(isEndOfString()) { a = 1 endOfString = True } a = shiftLeft(a, shift) inputWords[i] = inputWords[i] | a # Here, '|' is bitwise "or" shift = shift + 8 if(shift >= RadioGatunWordSize or endOfString is True) { belt[i + 16] = belt[i + 16] ^ inputWords[i] mill[i * 13] = mill[i * 13] ^ inputWords[i] i = i + 1 shift = 0 if(i >= 3 and endOfString is not True) { Beltmill() i = 0 } } }
getByteFromString() 是一个从输入字符串中获取单个八位字节(8 位字节)的函数。isEndOfString() 在我们到达输入字符串的末尾时,如果对 getByteFromString() 的最后一次调用失败,则返回 true。
完成此操作后,执行上述 18 轮空白轮,然后转到后面的“生成输出”部分,生成 RadioGatún 的哈希输出。
由于 RadioGatún 被设计为一个安全的哈希函数,因此在计算上不可行地找到两个不同的输入,它们建立相同的内部状态。此外,不应该能够将 RadioGatún 的输出与实际的随机位区分开 - 它看起来像是噪声。即使攻击者知道他们正在查看 RadioGatún 生成的位,也不应该能够预测下一个伪随机位,除非知道输入。
运行完空白轮后,我们现在可以输出 RadioGatun() 磨坊中的字,以生成任意数量的密码安全随机数。
过程很简单
- 我们查看磨坊中的第二个元素(磨坊元素编号 1),以获取前 32 位或 64 位随机输出,具体取决于我们选择实现 RadioGatún[32] 还是 RadioGatún[64]
- 我们查看磨坊中的第三个元素(磨坊元素编号 2),以获取另外 32/64 位输出
- 我们运行 Beltmill 操作,如前面所述
- 我们重复上述三个步骤,以获得任意数量的输出位
这是一些显示此过程的伪代码
phase = 1 # The word we get from the mill; initial value is one function getWordFromRadioGatun() { out = mill[phase] phase = phase + 1 if(phase > 2) { phase = 1 Beltmill() } return out }
getWordFromRadioGatun() 如果使用 32 位版本的 RadioGatun,则提供 32 位,如果使用 64 位版本,则提供 64 位。
请注意,为了与参考测试向量兼容,需要对数字进行端序交换(或者等效地,磨坊需要被视为小端序系统上的字节数组)。
在本示例中,我们使用 RadioGatún[32] 处理字符串“1234”。根据参考规范
RadioGatun[32]("1234") = 9EBDD24F469993796C4AAC6A821735A65A3CDEF8A359944CE71F34E7A08E1182
考虑到这一点,运行并完成输入映射后,皮带和磨坊如下所示
Mill Belt Row 1 Belt Row 2 Belt Row 3 0 0x00000000 0x34333231 0x00000001 0x00000000 1 0x00000000 0x00000000 0x00000000 0x00000000 2 0x00000000 0x00000000 0x00000000 0x00000000 3 0x00000000 0x00000000 0x00000000 0x00000000 4 0x00000000 0x00000000 0x00000000 0x00000000 5 0x00000000 0x00000000 0x00000000 0x00000000 6 0x00000000 0x00000000 0x00000000 0x00000000 7 0x00000000 0x00000000 0x00000000 0x00000000 8 0x00000000 0x00000000 0x00000000 0x00000000 9 0x00000000 0x00000000 0x00000000 0x00000000 10 0x00000000 0x00000000 0x00000000 0x00000000 11 0x00000000 0x00000000 0x00000000 0x00000000 12 0x00000000 0x00000000 0x00000000 0x00000000 13 0x00000000 14 0x00000000 15 0x00000000 16 0x34333231 17 0x00000001 18 0x00000000
在此图表中,左侧的数字是所讨论元素的数组索引。我们不将皮带视为一个包含 39(或 40)个字的单个数组,而是将皮带视为包含 13 个字的三行。如果使用将皮带视为单个数组的代码,皮带行 1 包含皮带中的元素 0-12,行 2 包含元素 13-25,行 3 包含元素 26-38。
执行完皮带旋转后,皮带和磨坊看起来像这样(观察皮带中元素是如何从第 0 行移动到第 1 行的)
Mill Belt Row 1 Belt Row 2 Belt Row 3 0 0x00000000 0x00000000 0x00000000 0x00000000 1 0x00000000 0x34333231 0x00000001 0x00000000 2 0x00000000 0x00000000 0x00000000 0x00000000 3 0x00000000 0x00000000 0x00000000 0x00000000 4 0x00000000 0x00000000 0x00000000 0x00000000 5 0x00000000 0x00000000 0x00000000 0x00000000 6 0x00000000 0x00000000 0x00000000 0x00000000 7 0x00000000 0x00000000 0x00000000 0x00000000 8 0x00000000 0x00000000 0x00000000 0x00000000 9 0x00000000 0x00000000 0x00000000 0x00000000 10 0x00000000 0x00000000 0x00000000 0x00000000 11 0x00000000 0x00000000 0x00000000 0x00000000 12 0x00000000 0x00000000 0x00000000 0x00000000 13 0x00000000 14 0x00000000 15 0x00000000 16 0x34333231 17 0x00000001 18 0x00000000
主磨坊操作以如下方式设置临时数组(“t”)中的 19 个元素
0 0xffffffff 1 0xffffffff 2 0xd97999b9 3 0xffffffff 4 0xffffffff 5 0x9b9d9799 6 0xffffffff 7 0xffffffff 8 0xffffffff 9 0xffffffff 10 0xffffffff 11 0xffffffff 12 0xffffffff 13 0xffffffff 14 0xffffffff 15 0xffffffff 16 0xfeffffff 17 0xffffffff 18 0xffffffff
使用这些值更新磨坊后,磨坊和皮带将看起来像这样
Mill Belt Row 1 Belt Row 2 Belt Row 3 0 0xffffffff 0x00000000 0x00000000 0x00000000 1 0xbd1bf1df 0x34333231 0x00000001 0x00000000 2 0xd97999b9 0x00000000 0x00000000 0x00000000 3 0xffffffff 0x00000000 0x00000000 0x00000000 4 0x9b9d9799 0x00000000 0x00000000 0x00000000 5 0x9b9d9799 0x00000000 0x00000000 0x00000000 6 0xffffffff 0x00000000 0x00000000 0x00000000 7 0xffffffff 0x00000000 0x00000000 0x00000000 8 0xffffffff 0x00000000 0x00000000 0x00000000 9 0xffffffff 0x00000000 0x00000000 0x00000000 10 0xffffffff 0x00000000 0x00000000 0x00000000 11 0xffffffff 0x00000000 0x00000000 0x00000000 12 0xfeffffff 0x00000000 0x00000000 0x00000000 13 0xffffffff 14 0xffffffff 15 0xfeffffff 16 0xfeffffff 17 0xd97999b9 18 0xffffffff
iota 步骤只影响磨坊的第一个元素(使其在这里变为 0xfffffffe)
Mill Belt Row 1 Belt Row 2 Belt Row 3 0 0xfffffffe 0x00000000 0x00000000 0x00000000 1 0xbd1bf1df 0x34333231 0x00000001 0x00000000 2 0xd97999b9 0x00000000 0x00000000 0x00000000 3 0xffffffff 0x00000000 0x00000000 0x00000000 4 0x9b9d9799 0x00000000 0x00000000 0x00000000 5 0x9b9d9799 0x00000000 0x00000000 0x00000000 6 0xffffffff 0x00000000 0x00000000 0x00000000 7 0xffffffff 0x00000000 0x00000000 0x00000000 8 0xffffffff 0x00000000 0x00000000 0x00000000 9 0xffffffff 0x00000000 0x00000000 0x00000000 10 0xffffffff 0x00000000 0x00000000 0x00000000 11 0xffffffff 0x00000000 0x00000000 0x00000000 12 0xfeffffff 0x00000000 0x00000000 0x00000000 13 0xffffffff 14 0xffffffff 15 0xfeffffff 16 0xfeffffff 17 0xd97999b9 18 0xffffffff
以上是运行一次 Beltmill 操作后状态的样子。以下是再次对其运行 Beltmill() 后它看起来的样子
Mill Belt Row 1 Belt Row 2 Belt Row 3 0 0x40600820 0x00000000 0x00000000 0x00000000 1 0xcc8c4f0c 0xbd1bf1df 0x00000000 0x00000000 2 0x189a1999 0x34333231 0xd97999b8 0x00000000 3 0x089a1999 0x00000000 0x00000000 0xffffffff 4 0xdc8c4f0c 0x9b9d9799 0x00000000 0x00000000 5 0xcc8c4f0c 0x00000000 0x9b9d9799 0x00000000 6 0x10000000 0x00000000 0x00000000 0xffffffff 7 0x99189a19 0xffffffff 0x00000000 0x00000000 8 0x10000000 0x00000000 0xffffffff 0x00000000 9 0x00000000 0x00000000 0x00000000 0xffffffff 10 0x99189a19 0xffffffff 0x00000000 0x00000000 11 0x99189a19 0x00000000 0xffffffff 0x00000000 12 0x46268666 0x00000000 0x00000000 0xfeffffff 13 0x31343332 14 0x00002000 15 0x06468e47 16 0x7712b554 17 0x31341332 18 0x58fa31b8
再运行 16 次(共 18 次 Beltmill() 调用)后,它看起来像这样
Mill Belt Row 1 Belt Row 2 Belt Row 3 0 0xd9958271 0x4fa9a4eb 0x3a5bda1c 0x599e1cba 1 0x4fd2bd9e 0xf40378f6 0xa5fff169 0xce6dad5a 2 0x79939946 0x7d0e377e 0x72637aad 0x83531eac 3 0xd9acd8da 0xbe8a2a76 0xc9958a5e 0x6a9d5b52 4 0x4a9b1478 0x44a12573 0xc4d346a6 0x0691e902 5 0xdefb4010 0xcfd07457 0x36adb7a1 0x112926ab 6 0x839db45d 0x08bc2beb 0x4cf52045 0x37060596 7 0x9bf58054 0x33bb3e40 0x978cf3fc 0x6452478c 8 0x734beab8 0x7873cf2d 0xbfaa6388 0x8840c4ae 9 0xa4c4d0dd 0x526856e9 0x1e33dbc2 0x80f95c1b 10 0x3fa1dc95 0x9829c411 0x4cd43100 0xfb9eb71a 11 0x0da8e828 0x1891da52 0x4f264567 0xccd49043 12 0x25fd61e7 0x031a49c7 0x5866b7ab 0x7f443d0c 13 0xd247ea91 14 0xb1e8b921 15 0x23154075 16 0x5ad6fbc4 17 0xbbc617dc 18 0x6eb76abb
此时,我们使用磨机索引 1(0x4fd2bd9e)生成前 32 位输出
9ebdd24f
观察到已经发生了字节序交换:第一个字节现在是第四个字节,第二个字节现在是第三个字节,第三个字节现在是第二个字节,最后一个字节现在是第一个字节。
散列的接下来的 32 位来自磨机索引 2(0x79939946)
46999379
现在我们已经提取了磨机上两个元素的值以生成一些随机数,我们再次运行 Beltmill() 操作。 我们将更详细地研究这一点。
首先,我们运行**皮带到磨机前馈**,导致
Mill Belt Row 1 Belt Row 2 Belt Row 3 0 0xd9958271 0x007b1975 0x3a5bda1c 0x599e1cba 1 0x4fd2bd9e 0xf40378f6 0xdc6c682f 0xce6dad5a 2 0x79939946 0x7d0e377e 0x72637aad 0x5affc676 3 0xd9acd8da 0xf4113e0e 0xc9958a5e 0x6a9d5b52 4 0x4a9b1478 0x44a12573 0x1a2806b6 0x0691e902 5 0xdefb4010 0xcfd07457 0x36adb7a1 0x92b492f6 6 0x839db45d 0x9349abbf 0x4cf52045 0x37060596 7 0x9bf58054 0x33bb3e40 0xe4c71944 0x6452478c 8 0x734beab8 0x7873cf2d 0xbfaa6388 0x2c841473 9 0xa4c4d0dd 0x6dc98a7c 0x1e33dbc2 0x80f95c1b 10 0x3fa1dc95 0x9829c411 0x417cd928 0xfb9eb71a 11 0x0da8e828 0x1891da52 0x4f264567 0xe929f1a4 12 0x25fd61e7 0x031a49c7 0x5866b7ab 0x7f443d0c 13 0xd247ea91 14 0xb1e8b921 15 0x23154075 16 0x5ad6fbc4 17 0xbbc617dc 18 0x6eb76abb
接下来我们**旋转皮带**
Mill Belt Row 1 Belt Row 2 Belt Row 3 0 0xd9958271 0x031a49c7 0x5866b7ab 0x7f443d0c 1 0x4fd2bd9e 0x007b1975 0x3a5bda1c 0x599e1cba 2 0x79939946 0xf40378f6 0xdc6c682f 0xce6dad5a 3 0xd9acd8da 0x7d0e377e 0x72637aad 0x5affc676 4 0x4a9b1478 0xf4113e0e 0xc9958a5e 0x6a9d5b52 5 0xdefb4010 0x44a12573 0x1a2806b6 0x0691e902 6 0x839db45d 0xcfd07457 0x36adb7a1 0x92b492f6 7 0x9bf58054 0x9349abbf 0x4cf52045 0x37060596 8 0x734beab8 0x33bb3e40 0xe4c71944 0x6452478c 9 0xa4c4d0dd 0x7873cf2d 0xbfaa6388 0x2c841473 10 0x3fa1dc95 0x6dc98a7c 0x1e33dbc2 0x80f95c1b 11 0x0da8e828 0x9829c411 0x417cd928 0xfb9eb71a 12 0x25fd61e7 0x1891da52 0x4f264567 0xe929f1a4 13 0xd247ea91 14 0xb1e8b921 15 0x23154075 16 0x5ad6fbc4 17 0xbbc617dc 18 0x6eb76abb
以下是我们在执行**主要磨机操作**后临时数组“t”的样子
0 0x166b7dce 1 0x704737f7 2 0xc2dabfab 3 0x6611fd8a 4 0xc296ccc3 5 0xd831c230 6 0x02fe55a3 7 0x0559dc72 8 0xa970aa8c 9 0x0850e341 10 0x5aaa745f 11 0x4c0040be 12 0x651e5e54 13 0xbd57724f 14 0x92d919b3 15 0x0b22ade0 16 0x63d53968 17 0xb25ff79c 18 0xe71f7551
现在我们使用这些值来**更新磨机**
Mill Belt Row 1 Belt Row 2 Belt Row 3 0 0xa4ba86fa 0x031a49c7 0x5866b7ab 0x7f443d0c 1 0x6aac4a6c 0x007b1975 0x3a5bda1c 0x599e1cba 2 0xa6351782 0xf40378f6 0xdc6c682f 0xce6dad5a 3 0xa1deed3b 0x7d0e377e 0x72637aad 0x5affc676 4 0xb3d7a47f 0xf4113e0e 0xc9958a5e 0x6a9d5b52 5 0xd29f74d2 0x44a12573 0x1a2806b6 0x0691e902 6 0x5d0dfd8e 0xcfd07457 0x36adb7a1 0x92b492f6 7 0xe0293640 0x9349abbf 0x4cf52045 0x37060596 8 0xc43e1799 0x33bb3e40 0xe4c71944 0x6452478c 9 0xefade551 0x7873cf2d 0xbfaa6388 0x2c841473 10 0x84732d52 0x6dc98a7c 0x1e33dbc2 0x80f95c1b 11 0x223cb30a 0x9829c411 0x417cd928 0xfb9eb71a 12 0xbb9c1573 0x1891da52 0x4f264567 0xe929f1a4 13 0x9dd19c60 14 0x7ee4c102 15 0x7e9ce946 16 0xa1cdf903 17 0x979a3d66 18 0x9765f515
**iota**步骤仅影响磨机的字 0
Mill Belt Row 1 Belt Row 2 Belt Row 3 0 0xa4ba86fb 0x031a49c7 0x5866b7ab 0x7f443d0c 1 0x6aac4a6c 0x007b1975 0x3a5bda1c 0x599e1cba 2 0xa6351782 0xf40378f6 0xdc6c682f 0xce6dad5a 3 0xa1deed3b 0x7d0e377e 0x72637aad 0x5affc676 4 0xb3d7a47f 0xf4113e0e 0xc9958a5e 0x6a9d5b52 5 0xd29f74d2 0x44a12573 0x1a2806b6 0x0691e902 6 0x5d0dfd8e 0xcfd07457 0x36adb7a1 0x92b492f6 7 0xe0293640 0x9349abbf 0x4cf52045 0x37060596 8 0xc43e1799 0x33bb3e40 0xe4c71944 0x6452478c 9 0xefade551 0x7873cf2d 0xbfaa6388 0x2c841473 10 0x84732d52 0x6dc98a7c 0x1e33dbc2 0x80f95c1b 11 0x223cb30a 0x9829c411 0x417cd928 0xfb9eb71a 12 0xbb9c1573 0x1891da52 0x4f264567 0xe929f1a4 13 0x9dd19c60 14 0x7ee4c102 15 0x7e9ce946 16 0xa1cdf903 17 0x979a3d66 18 0x9765f515
**皮带到磨机**操作影响磨机中的元素 13 到 15
Mill Belt Row 1 Belt Row 2 Belt Row 3 0 0xa4ba86fb 0x031a49c7 0x5866b7ab 0x7f443d0c 1 0x6aac4a6c 0x007b1975 0x3a5bda1c 0x599e1cba 2 0xa6351782 0xf40378f6 0xdc6c682f 0xce6dad5a 3 0xa1deed3b 0x7d0e377e 0x72637aad 0x5affc676 4 0xb3d7a47f 0xf4113e0e 0xc9958a5e 0x6a9d5b52 5 0xd29f74d2 0x44a12573 0x1a2806b6 0x0691e902 6 0x5d0dfd8e 0xcfd07457 0x36adb7a1 0x92b492f6 7 0xe0293640 0x9349abbf 0x4cf52045 0x37060596 8 0xc43e1799 0x33bb3e40 0xe4c71944 0x6452478c 9 0xefade551 0x7873cf2d 0xbfaa6388 0x2c841473 10 0x84732d52 0x6dc98a7c 0x1e33dbc2 0x80f95c1b 11 0x223cb30a 0x9829c411 0x417cd928 0xfb9eb71a 12 0xbb9c1573 0x1891da52 0x4f264567 0xe929f1a4 13 0x9ecbd5a7 14 0x268276a9 15 0x01d8d44a 16 0xa1cdf903 17 0x979a3d66 18 0x9765f515
此时,我们从磨机中取字 1 和 2 来生成 64 位散列输出
6c4aac6a821735a6
然后我们再次运行 Beltmill(),导致
Mill Belt Row 1 Belt Row 2 Belt Row 3 0 0x6cf36fda 0x1891da52 0x4f264567 0xe929f1a4 1 0xf8de3c5a 0x69b603ab 0x5866b7ab 0x7f443d0c 2 0x4c9459a3 0x007b1975 0x9c6ecd9e 0x599e1cba 3 0x54ab7f1f 0xf40378f6 0xdc6c682f 0x6fb34061 4 0x467fcf23 0xced99301 0x72637aad 0x5affc676 5 0xd40be986 0xf4113e0e 0x1b0afe8c 0x6a9d5b52 6 0x07c891d4 0x44a12573 0x1a2806b6 0x5b9c148c 7 0xdf077459 0x2ff94217 0x36adb7a1 0x92b492f6 8 0x7cfc3d41 0x9349abbf 0x88cb37dc 0x37060596 9 0xc0b4f9dd 0x33bb3e40 0xe4c71944 0x8bffa2dd 10 0x5e7d770b 0xfc00e27f 0xbfaa6388 0x2c841473 11 0x286065c7 0x6dc98a7c 0x3c0f68c8 0x80f95c1b 12 0xf47debb2 0x9829c411 0x417cd928 0x4002a269 13 0x9c7f8208 14 0x7173015d 15 0x49e3be00 16 0x4927ddf9 17 0x5fe72eb5 18 0x2af7cf5f
为我们提供这些 64 位散列输出
5a3cdef8a359944c
为了生成所需的 256 位散列的最后 64 位,我们最后一次运行 Beltmill()
Mill Belt Row 1 Belt Row 2 Belt Row 3 0 0x7ef726d9 0x9829c411 0x417cd928 0x4002a269 1 0xe7341fe7 0xe04fe608 0x4f264567 0xe929f1a4 2 0x82118ea0 0x69b603ab 0x14f2ee08 0x7f443d0c 3 0xb29a8d55 0x007b1975 0x9c6ecd9e 0x0d3563a5 4 0x7b4ebd5a 0xb27cb7d5 0xdc6c682f 0x6fb34061 5 0xfec38e38 0xced99301 0xa668932b 0x5affc676 6 0xf95a2809 0xf4113e0e 0x1b0afe8c 0x6d55ca86 7 0xb84b5869 0x9ba6512a 0x1a2806b6 0x5b9c148c 8 0x2ffcf15a 0x2ff94217 0x4a518ae0 0x92b492f6 9 0x235557d9 0x9349abbf 0x88cb37dc 0xf7b2fc4b 10 0x7658fde8 0x6dc6494b 0xe4c71944 0x8bffa2dd 11 0xc8320830 0xfc00e27f 0x97ca064f 0x2c841473 12 0xc39a12ae 0x6dc98a7c 0x3c0f68c8 0x7484b7a9 13 0x4801294c 14 0x4f6ee74f 15 0x82e8af69 16 0x63210515 17 0x2b657fd0 18 0xc6c57d5f
并以如下方式完成我们的散列
e71f34e7a08e1182
如果需要更多伪随机数,我们可以根据需要运行 Beltmill() 来生成更多数字。