SHA-256 的 16位表芯片(chip)

该芯片是基于一个16位查找表实现的,电路需要最小 行,适于整合进一些大的电路中。

我们的目标是使约束的度数最大为9。这让我们能够在同一行内处理进位的约束,以及对一些 的小片约束。

Compression 轮次

总共有64个 compression 轮次。每轮使用8个32位数 作为输入,然后执行如下操作:

需要处理进位 的情况。

The SHA-256 compression function

定义 为一个16位的输入-输出映射表。我们不需要另外定义映射表来进行范围约束,可以通过复用 实现该功能。

模加操作

我们注意到模 的加法,和先在有限域上做加法,然后通过掩码取出低32位的操作是等价的。例如,有两个操作数

我们将操作数分解为16位一个的 chunks :

然后使用有限域的加法重新定义约束:

更一般化的,输出可以被分解为任意的比特位组合,而不仅仅是16位一组的 chunks 。注意上述等式已经正确处理了从 的进位。

约束要求每个 chunk 都是在正确范围内的,否则之后的赋值可能会导致有限域上的溢出。

  • 操作数和结果的 chunk 可以通过 约束,具体方法是在表格的某个子集中的 "dense" 列中查找其 chunk 是否存在。我们通过这种方式还能获得输出结果的 "spread" 形式。特别是对于右下角的 其输出将变为 ,左下角的 其输出将变为 。我们在后面对 的优化中将用到它。

  • 必须被约束为操作数数量所允许的进位值的一个精确范围。我们通过 small range constraint 来进行约束。

Maj 函数

可以通过 次查找完成: chunks

  • 正如之前提到的,在第一轮之后我们已经有了了 的 spread 形式 。类似的, 相当于上一轮次中的 ,因此在稳态中我们也已有了它们的 spread 形式 。实际上,我们也可以假设我们在第一轮就有了它们的 spread 形式,要么从 fixed IV 或者从上一个块对 的使用中获取。
  • 在域中添加 spread 形式:
    • 我们可以以 32位计算机字的形式或者以片(piece) 的形式添加,两者是等价的
  • 对于 ,分别计算偶数位 和奇数位
  • 约束为 ,其中 函数的输出output。

注意:“偶数位”指的是 的偶数次方的权重,同理,“奇数位”我们指 的奇数次方的权重

Ch 函数

TODO: can probably be optimized to or lookups using an additional table.

可以通过 次查找完成: chunks

  • 如之前所提到的,在第一轮之后我们已经有了 的 spread 形式 。类似的,我们有 和上一轮中的 相等,因此在稳态中我们已经有了 的 spread 形式。实际上,我们也可以假设我们在第一轮中已经有了它们的 spread 形式,要么从 fixed IV 或者从上一个块对 的使用中获取。
  • 计算 ,其中
    • 我们可以以 32位计算机字的形式或者以片(piece)的形式添加,两者是等价的
    • 用于计算 的 spread ,尽管取反操作和 一般不满足交换律。可以这样计算是因为 中的每个 spread 位都减去了 ,因此没有借位。
  • 计算 使得 ,对 也有类似的计算。
  • 即为 函数的输出。

Σ_0 函数

可以通过 次查找完成。

我们首先将 分成长度为 的片 ,以小数端为例。同时我们也取出这些片的 spread 形式。这可以在 PLONK 电路中的两行内完成,因为10位的片和11位的片可以使用 的查找获得,而9位的片可以被进一步分为3个3位的子片。后者和剩下的2位的片可以通过多项式约束来限制它们的取值范围,每行两个片。这些小的片的 spread 形式可以由插值构成。

注意到将 chunk 划分为片可以和 的规约一起做,即后者不需要额外的查找了。在最后一轮,我们将 在前馈加法(需要最多处理7次进位)做完后进行规约。

等价于 :

然后,使用 个额外的 查找,我们可以得到结果 ,其二进制偶数位是上面片的一个线性组合。

即,我们计算出压缩后的偶数位 和压缩后的奇数位 ,并约束

其中 就是 函数的输出。

Σ_1 函数

可以通过 次查找完成。

我们首先将 分成长度为 的片 ,以小数端为例。同时我们也取出这些片的 spread 形式。这可以在 PLONK 电路中的两行内完成,因为7位的片和14位的片可以使用 的查找获得,5位的片可以被进一步分为3位3位的子片,6位的片可以被分解为2个3位的片。这4个小的片可以通过多项式约束来限制它们的取值范围,每行两个片。这些小片的 spread 形式可以由插值构成。

注意到将 chunk 划分为片可以和 的规约一起做,即后者不需要额外的查找了。在最后一轮,我们将 在前馈加法(需要最多处理7次进位)做完后进行规约。

等价于 .

然后,使用 个额外的 查找,我们可以得到结果 ,其二进制偶数位是上面片的一个线性组合。

即,我们计算出压缩后的偶数位 和压缩后的奇数位 ,并约束

其中 就是 函数的输出。

块分解

对于消息中每一个512位的块 来说,其 位的计算机字可以通过以下方式构造:

  • 个字可以通过将 分解为32位的块来获得
  • 剩下的 个字通过如下公式构造: for .

Note: 的下标从 开始

Note: 是右移位运算,不是循环,和 区分

σ_0 函数

等价于

和之前类似,也用小数端表示,但 的长度分别为 。将 分解为两个2位的子片。

σ_1 函数

is equivalent to .

TODO: this diagram doesn't match the expression on the right. This is just for consistency with the other diagrams.

和之前一样用小数端表示, 的长度分别为 。将 分解为 位的子片。

消息调度

我们将 应用于 ,将 应用于 。为避免重复的应用 ,我们可以将 的分解进行合并。合并长度为 的片会得到长度分别为 的片。

如果我们可以将合并后的分解在3行中完成(单独分解 需要4行),我们就能节省下35行。

These might even be doable in rows; not sure. —Daira

我们可以将 的规约合并至它们在后续计算中的分解时,类似于我们对 做过的操作。

我们仍然需要对 进行规约,因为它们没有被分解。(技术上,我们可以暂时不对它们做规约,在后面计算 的时候会做规约,但是这会导致我们需要处理多达10次进位,而不是现在的6次,因此还是值得的)。

优化后的调度结果为:

  • 行用来约束 比特位
    • 技术上这不是必须的,这里进行约束是保持鲁棒性,因为输入部分的约束是免费的
  • 行用来拆分 为4个 比特位的 pieces
  • 行用来拆分 比特位的 pieces (和 的规约进行合并)
  • 行用来拆分 比特位的 pieces (和规约进行合并)
  • 行用来提取 计算结果
  • 行用来提取 计算结果
  • 行用来规约
  • rows.

总开销

对每一轮次:

  • 是无开销的
  • 每轮行数

这里第三步总计 行,再加上:

  • 行(进行消息调度之后)
  • 行给第4步中进行的8次模 的规约

总共是 行。

表格

我们只需要一个有 列的 表。我们需要一个标签列,用以将 and 表格的 比特位子集挑选出来。

spread 表格

标签表格 (16位)spread (32位)
0000000000000000000000000000000000000000000000000
0000000000000000100000000000000000000000000000001
0000000000000001000000000000000000000000000000100
0000000000000001100000000000000000000000000000101
...0......
0000000000111111100000000000000000001010101010101
1000000001000000000000000000000000100000000000000
...1......
1000000111111111100000000000001010101010101010101
...2......
2000001111111111100000000010101010101010101010101
...3......
3000111111111111100000001010101010101010101010101
...4......
4001111111111111100000101010101010101010101010101
...5......
5111111111111111101010101010101010101010101010101

例如,如果要做一次 个比特位的 查表,我们先对标签进行多项式约束使其必须为 。在最常用的 位查表中,我们不需要约束标签列。注意我们可以在没有用到的行中(即超过 的行数)填充任意重复值,例如都填充为全零行。

Ch 门

从之前操作来的输入:

  • 是 32位字 位 spread 形式,假设服从之前的约束
    • 实际中,我们在分解 位子片后能得到 的 spread 形式
  • 定义为

E ∧ F

s_ch
0{0,1,2,3,4,5}
1{0,1,2,3,4,5}
0{0,1,2,3,4,5}
0{0,1,2,3,4,5}

¬E ∧ G

s_ch_neg
0{0,1,2,3,4,5}
1{0,1,2,3,4,5}
0{0,1,2,3,4,5}
0{0,1,2,3,4,5}

约束:

  • s_ch (choice):
  • s_ch_neg (negation): 检查 s_ch 是否为负数
  • 中查找
  • 之间的置换

输出:

Maj 门

从之前操作来的输入:

  • 32位字的 位 spread 形式 ,假设服从之前的约束
    • 实际中,我们在分解 位子片后能得到 的 spread 形式
s_maj
0{0,1,2,3,4,5}
1{0,1,2,3,4,5}
0{0,1,2,3,4,5}
0{0,1,2,3,4,5}

约束:

  • s_maj (majority):
  • 中查找
  • 之间的置换

输出:

Σ_0 门

是一个 32位字,分解为 位 chunks,从小数端开始。我们将这些 chunks 分别称为 ,并且进一步将 分解为3位的 chunks 。我们将小 chunks 的 spread 形式写成 witness :

s_upp_sigma_0
0{0,1,2,3,4,5}
1{0,1,2,3,4,5}
0{0,1,2,3,4,5}
0{0,1,2,3,4,5}

约束:

  • s_upp_sigma_0 ( 约束):

  • 中查找
  • 中的 2比特位范围检查及2比特位 spread 检查
  • 上的 3比特位范围检查和3比特位 spread 检查

(见 辅助门

输出:

Σ_1 门

是一个 32位字,分解为 位 chunks,从小数端开始。我们将这些 chunks 分别称为 ,并且进一步将 分解为两个3位的 chunks ,并更进一步将两个3比特位的chunk 分解为 (2,3)比特位的 chunks 。我们将小 chunks 的 spread 形式写成 witness :

s_upp_sigma_1
0{0,1,2,3,4,5}
1{0,1,2,3,4,5}
0{0,1,2,3,4,5}
0{0,1,2,3,4,5}

约束:

  • s_upp_sigma_1 ( constraint):

  • 中查找
  • 中的 2比特位范围检查及2比特位 spread 检查
  • 上的 3比特位范围检查和3比特位 spread 检查

(见辅助门

输出:

σ_0 门

v1

门的v1占一个字,可以分解为 位的 chunks (已经在消息调度中被约束)。我们将这些 chunks 称为 被进一步分解为两个2比特位的 chunks 我们将小 chunks 的 spread 形式写成 witness 。我们从消息调度中已经获得了

等价于

s_low_sigma_0
0{0,1,2,3,4,5}
1{0,1,2,3,4,5}
0{0,1,2,3,4,5}
0{0,1,2,3,4,5}

约束:

  • s_low_sigma_0 ( v1 约束):

  • 检查 b 是否被正确的分解为4比特位的一些片
  • 中的 2比特位范围检查及2比特位 spread 检查
  • 上的 3比特位范围检查和3比特位 spread 检查

v2

门的v2占一个字,可以分解为 位的 chunks (已经在消息调度中被约束)。我们将这些 chunks 称为 我们从消息调度中已经获得了 。1比特位的 在 spread 操作中不做更改,可以直接使用。另外 被进一步分解为两个2比特位的 chunks 我们将小 chunks 的 spread 形式写成 witness 。

等价于

s_low_sigma_0_v2
0{0,1,2,3,4,5}
1{0,1,2,3,4,5}
0{0,1,2,3,4,5}
0{0,1,2,3,4,5}

约束:

  • s_low_sigma_0_v2 ( v2 约束):

  • 检查 b 是否被正确的分解为4比特位的片
  • 中的 2比特位范围检查及2比特位 spread 检查
  • 上的 3比特位范围检查和3比特位 spread 检查

σ_1 门

v1

门的v1占一个字,可以分解为 位的 chunks (已经在消息调度中被约束)。我们将这些 chunks 称为 被进一步分解为 比特位的 chunks 我们将小 chunks 的 spread 形式写成 witness 。我们从消息调度中已经获得了

等价于 .

s_low_sigma_1
0{0,1,2,3,4,5}
1{0,1,2,3,4,5}
0{0,1,2,3,4,5}
0{0,1,2,3,4,5}

约束:

  • s_low_sigma_1 ( v1 约束):

  • 检查 b 是否被正确的分解为7比特位的片。

  • 中的 2比特位范围检查及2比特位 spread 检查

  • 上的 3比特位范围检查和3比特位 spread 检查

v2

门的v2占一个字,可以分解为 位的 chunks (已经在消息调度中被约束)。我们将这些 chunks 称为 我们从消息调度中已经获得了 。1比特位的 在 spread 操作中不做更改,可以直接使用。另外 被进一步分解为两个2比特位的 chunks 我们将小 chunks 的 spread 形式写成 witness 。

等价于

s_low_sigma_1_v2
0{0,1,2,3,4,5}
1{0,1,2,3,4,5}
0{0,1,2,3,4,5}
0{0,1,2,3,4,5}

约束:

  • s_low_sigma_1_v2 ( v2 constraint):

  • 检查 b 是否被正确的分解为4比特位的子片。
  • 中的 2比特位范围检查及2比特位 spread 检查
  • 上的 3比特位范围检查和3比特位 spread 检查

辅助门

小范围约束

. 约束该表达式的值为0,相当于约束了

2比特位范围检查

sr2
1a

2比特位 spread

ss2
1aa'

插值多项式:

  • ()
  • ()
  • ()
  • ()

3比特位范围检查

sr3
1a

3比特位 spread

ss3
1aa'

插值多项式:

  • ()
  • ()
  • ()
  • ()
  • ()
  • ()
  • ()
  • ()

reduce_6 门

6元素的 加法

输入:

检查:

假设输入被约束为16个比特位

  • 加法门 (sa):
  • 进位门 (sc):
sasc
10
11

假设输入被约束为16个比特位

  • 加法门 (sa):
  • 进位门 (sc):
sasc
00
11
00
10

reduce_7 门

7元素的 加法

输入:

检查:

假设输入被约束为16个比特位

  • 加法门 (sa):
  • 进位门 (sc):
sasc
10
11

消息调度区域

对于消息 中的每一个 block ,其64个32位字以如下方式构造:

  • 通过将 以每32个比特进行划分,得到前16个
  • 剩下的48个字使用如下的方式构造:

for .

swsd0sd1sd2sd3ss0ss0_v2ss1ss1_v2
010000000{0,1,2,3,4,5}
100000000{0,1,2,3,4,5}
011000000{0,1,2,3,4}
100000000{0,1,2}
000000000{0,1,2,3,4,5}
000001000{0,1,2,3,4,5}
000000000{0,1,2,3,4,5}
000000000{0,1,2,3,4,5}
.....................................................
000000000{0,1,2,3}
0101000000
1000000000
000000000{0,1,2,3,4,5}
000000100{0,1,2,3,4,5}
000000000{0,1,2,3,4,5}
000000000{0,1,2,3,4,5}
000000000{0,1,2,3,4,5}
000000001{0,1,2,3,4,5}
000000000{0,1,2,3,4,5}
000000000{0,1,2,3,4,5}
.....................................................
010010000{0,1,2,3}
000000000{0,1}
000000000{0,1,2,3,4,5}
000000001{0,1,2,3,4,5}
000000000{0,1,2,3,4,5}
000000000{0,1,2,3,4,5}
.....................................................
010000000{0,1,2,3,4,5}
000000000{0,1,2,3,4,5}
010000000{0,1,2,3,4,5}
000000000{0,1,2,3,4,5}

约束:

  • sw: 使用 构造字
  • sd0: 对应的分解门
  • sd1: 的分解门 (分解为 位的片)
  • sd2: 的分解门 (分解为 位的片)
  • sd3: 的分解门 (分解为 位的片)

Compression 区域

+----------------------------------------------------------+
|                                                          |
|          分解 E,										   |
|          Σ_1(E)                                          |
|                                                          |
|                  +---------------------------------------+
|                  |                                       |
|                  |        reduce_5() 以获得  H'		   |
|                  |                                       |
+----------------------------------------------------------+
|          分解 F, 分解 G								   |
|                                                          |
|                        Ch(E,F,G)                         |
|                                                          |
+----------------------------------------------------------+
|                                                          |
|          分解 A,										   |
|          Σ_0(A)                                          |
|                                                          |
|                                                          |
|                  +---------------------------------------+
|                  |                                       |
|                  |        使用 H' 和 reduce_7(),		   |
|				   |		以获得 A_new				       |
|                  |                                       |
+------------------+---------------------------------------+
|          分解 B, 分解 C								   |
|                                                          |
|          Maj(A,B,C)                                      |
|                                                          |
|                  +---------------------------------------+
|                  |        使用 H' 和 reduce_6(),		   | 
|				   |		以获得 E_new					   |
|                  |									   |
+------------------+---------------------------------------+

初始轮:

s_digestsd_abcdsd_efghss0ss1s_majs_ch_negs_chs_a_news_e_news_h_prime
00100000000{0,1,2}
00000000000{0,1}
00000000000{0,1,2,3,4,5}
00001000000{0,1,2,3,4,5}
00000000000{0,1,2,3,4,5}
00000000000{0,1,2,3,4,5}
00100000000{0,1,2}
00000000000{0,1}
00100000000{0,1,2}
00000000000{0,1}
00000000000{0,1,2,3,4,5}
00000001001{0,1,2,3,4,5}
00000000000{0,1,2,3,4,5}
00000000000{0,1,2,3,4,5}
00000000010{0,1,2,3,4,5}
00000010000{0,1,2,3,4,5}
00000000000{0,1,2,3,4,5}
00000000000{0,1,2,3,4,5}
01000000000{0,1,2}
00000000000{0,1}
00000000000{0,1,2,3,4,5}
00010000000{0,1,2,3,4,5}
00000000000{0,1,2,3,4,5}
00000000000{0,1,2,3,4,5}
01000000000{0,1,2}
00000000000{0,1}
01000000000{0,1,2}
00000000000{0,1}
00000000000{0,1,2,3,4,5}
00000100100{0,1,2,3,4,5}
00000000000{0,1,2,3,4,5}
00000000000{0,1,2,3,4,5}

稳态

s_digestsd_abcdsd_efghss0ss1s_majs_ch_negs_chs_a_news_e_news_h_prime
00100000000{0,1,2}
00000000000{0,1}
00000000000{0,1,2,3,4,5}
00001000000{0,1,2,3,4,5}
00000000000{0,1,2,3,4,5}
00000000000{0,1,2,3,4,5}
00000000000{0,1,2,3,4,5}
00000001001{0,1,2,3,4,5}
00000000000{0,1,2,3,4,5}
00000000000{0,1,2,3,4,5}
00000000010{0,1,2,3,4,5}
00000010000{0,1,2,3,4,5}
00000000000{0,1,2,3,4,5}
00000000000{0,1,2,3,4,5}
01000000000{0,1,2}
00000000000{0,1}
00000000000{0,1,2,3,4,5}
00010000000{0,1,2,3,4,5}
00000000000{0,1,2,3,4,5}
00000000000{0,1,2,3,4,5}
00000000000{0,1,2,3,4,5}
00000100100{0,1,2,3,4,5}
00000000000{0,1,2,3,4,5}
00000000000{0,1,2,3,4,5}

最后的摘要输出:

s_digestsd_abcdsd_efghss0ss1s_majs_ch_negs_chs_a_news_e_news_h_prime
10000000000000
00000000000000
10000000000000
00000000000000