参考的文章:https://byt3bit.github.io/primesym/mimc/
上述文章的作者是MiMC: Efficient Encryption and Cryptographic Hashing with Minimal Multiplicative Complexity的作者之一。
mimc是Minimal Multiplicative Complexity的缩写。
mimc.circom
mimc.circom有两个templates: MiMC7(nrounds)和MultiMiMC7(nInputs, nRounds)。一般调用的是MultiMiMC7模版。
下面三张图是MiMC的主要结构
在MiMC(nrounds) template中会将上述的函数的3次方改成了7次方并且直接固定。所有的运算都在或者上。我们具体看一下MiMc代码
template MiMC7(nrounds) {
signal input x_in;
signal input k;
signal output out;
var c[91] = [
0, //最开始ci并没有参与运算,因此为0
20888961410941983456478427210666206549300505294776164667214940546594746570981,
15265126113435022738560151911929040668591755459209400716467504685752745317193,
...//这里省略余下的数组元素。
];
var t;
signal t2[nrounds];
signal t4[nrounds];
signal t6[nrounds];
signal t7[nrounds-1];
for (var i=0; i<nrounds; i++) {
t = (i==0) ? k+x_in : k + t7[i-1] + c[i];
//当i==0,则图中最开始的部分
t2[i] <== t*t;
t4[i] <== t2[i]*t2[i];
t6[i] <== t4[i]*t2[i];
if (i<nrounds-1) {
t7[i] <== t6[i]*t;
} else {
out <== t6[i]*t + k; //对应图中最后一部分需要 +k
}
}
}
接下来具体看一下MultiMiMC7(nInputs,nRounds)的代码。
template MultiMiMC7(nInputs, nRounds) {
//显然输入的0<=nRounds<=91,因为调用的MiMC7中的c数组大小最多91
signal input in[nInputs]; //n个输入
signal input k; //给定一个初始的值k
signal output out;
signal r[nInputs +1];
component mims[nInputs];
r[0] <== k;
for (var i=0; i<nInputs; i++) {
mims[i] = MiMC7(nRounds); //主要调用上述的MiMC7 模版
mims[i].x_in <== in[i];
mims[i].k <== r[i]; //每次输入到MiMC7中的k值是 r[i]
r[i+1] <== r[i] + in[i] + mims[i].out;
//下一次输入到MiMC7中的k值由本次的k值,输入值以及MiMC7本次输出值相加而成
}
out <== r[nInputs];
}
mimcsponge.circom
mimcsponge.circom电路文件中有两个templates: MiMCFeistel(nrounds)和MiMCSponge(nInputs, nRounds, nOutputs)。
MiMCFeistel(nrounds)。这个模版主要实现的是一个等式
上述等式体现出来的结构就是Feistel轮转结构,将其中的F 替换成即可。现在我们看一下代码
template MiMCFeistel(nrounds) {
signal input xL_in;
signal input xR_in;
signal input k;
signal output xL_out;
signal output xR_out;
var c_partial[218] = [
7120861356467848435263064379192047478074060781135320967663101236819528304084,
//省略剩下217个数组元素
];
var t;
signal t2[nrounds];
signal t4[nrounds];
signal xL[nrounds-1];
signal xR[nrounds-1];
var c;
for (var i=0; i<nrounds; i++) {
if ((i == 0) || (i == nrounds - 1)) {
c = 0; // 也就是开始和结束时不需要加c
} else {
c = c_partial[i - 1];
}
t = (i==0) ? k+xL_in : k + xL[i-1] + c;
t2[i] <== t*t;
t4[i] <== t2[i]*t2[i];
if (i<nrounds-1) {
var aux = (i==0) ? xR_in : xR[i-1] ;
xL[i] <== aux + t4[i]*t; //本轮的左边xL[i] 会等于上一轮的右边xR[i-1] 加上函数(k + xL[i-1] + c)^5
xR[i] <== (i==0) ? xL_in : xL[i-1];//本轮的右边xR[i] 会等于上一轮的左边
} else {
xR_out <== xR[i-1] + t4[i]*t; //最后一轮不需要
xL_out <== xL[i-1];
}
}
}
MiMCSponge(nInputs, nRounds, nOutputs)
主要的结构为Sponge结构。如图所示:
template MiMCSponge(nInputs, nRounds, nOutputs) {
signal input ins[nInputs]; //相当于图中的P0,P1,...P_{nInputs-1}
signal input k;
signal output outs[nOutputs]; //相当于图中的Z0,Z1,...,Z_{nOutputs-1}
var i;
// S = R||C
component S[nInputs + nOutputs - 1];
// 从0到nInputs-1 是输入; 相当于
// nInputs 到 nInputs + nOutputs - 1是输出
for (i = 0; i < nInputs; i++) {
S[i] = MiMCFeistel(nRounds);
S[i].k <== k; //初始值
if (i == 0) {
S[i].xL_in <== ins[0];
S[i].xR_in <== 0;
} else {
S[i].xL_in <== S[i-1].xL_out + ins[i]; //本轮左边等于函数f输出的左边 + P_i
S[i].xR_in <== S[i-1].xR_out; //本轮右边等于 函数f输出的右边
}
}
outs[0] <== S[nInputs - 1].xL_out;
for (i = 0; i < nOutputs - 1; i++) {
S[nInputs + i] = MiMCFeistel(nRounds);
S[nInputs + i].k <== k;
S[nInputs + i].xL_in <== S[nInputs + i - 1].xL_out;
S[nInputs + i].xR_in <== S[nInputs + i - 1].xR_out;
outs[i + 1] <== S[nInputs + i].xL_out;
}
}