2021SC@SDUSC
2021-11-21
第八周完成事项
工作内容
由于哈希学习代码分析工作量较大,这周的工作依旧是将分析上周的剩下的内容。
代码扫尾
先把上次剩下的代码分析完成再来开始新的代码
#define G(r,i,a,b,c,d) \
do { \
a = a + b + m[blake2b_sigma[r][2*i+0]]; \
d = rotr64(d ^ a, 32); \
c = c + d; \
b = rotr64(b ^ c, 24); \
a = a + b + m[blake2b_sigma[r][2*i+1]]; \
d = rotr64(d ^ a, 16); \
c = c + d; \
b = rotr64(b ^ c, 63); \
} while(0)
#define ROUND(r) \
do { \
G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \
G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \
G(r,2,v[ 2],v[ 6],v[10],v[14]); \
G(r,3,v[ 3],v[ 7],v[11],v[15]); \
G(r,4,v[ 0],v[ 5],v[10],v[15]); \
G(r,5,v[ 1],v[ 6],v[11],v[12]); \
G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \
G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \
} while(0)
static void blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] )
{
uint64_t m[16];
uint64_t v[16];
size_t i;
for( i = 0; i < 16; ++i ) {
m[i] = load64( block + i * sizeof( m[i] ) );
}
for( i = 0; i < 8; ++i ) {
v[i] = S->h[i];
}
v[ 8] = blake2b_IV[0];
v[ 9] = blake2b_IV[1];
v[10] = blake2b_IV[2];
v[11] = blake2b_IV[3];
v[12] = blake2b_IV[4] ^ S->t[0];
v[13] = blake2b_IV[5] ^ S->t[1];
v[14] = blake2b_IV[6] ^ S->f[0];
v[15] = blake2b_IV[7] ^ S->f[1];
ROUND( 0 );
ROUND( 1 );
ROUND( 2 );
ROUND( 3 );
ROUND( 4 );
ROUND( 5 );
ROUND( 6 );
ROUND( 7 );
ROUND( 8 );
ROUND( 9 );
ROUND( 10 );
ROUND( 11 );
for( i = 0; i < 8; ++i ) {
S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
}
}
上次剩下了blake2b算法更新过程中最重要的压缩过程,这次先对这个进行分析。
uint64_t m[16];
uint64_t v[16];
size_t i;
for( i = 0; i < 16; ++i ) {
m[i] = load64( block + i * sizeof( m[i] ) );
}
for( i = 0; i < 8; ++i ) {
v[i] = S->h[i];
}
v[ 8] = blake2b_IV[0];
v[ 9] = blake2b_IV[1];
v[10] = blake2b_IV[2];
v[11] = blake2b_IV[3];
v[12] = blake2b_IV[4] ^ S->t[0];
v[13] = blake2b_IV[5] ^ S->t[1];
v[14] = blake2b_IV[6] ^ S->f[0];
v[15] = blake2b_IV[7] ^ S->f[1];
首先根据传进来的参数S(blake2b算法状态的结构体)和block进行m与v的初始化,这里我们基本上可以解决上次的疑惑,这里的m应该是没有作用的。这里的blake2b_IV是定义的随机十六进制串。
static const uint64_t blake2b_IV[8] =
{
0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL,
0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL,
0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL,
0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL
};
接下来进行11轮的运算,最后用结果v去更新S中的h。
for( i = 0; i < 8; ++i ) {
S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
}
这里进行的运算是通过宏定义define定义的运算。
#define G(r,i,a,b,c,d) \
do { \
a = a + b + m[blake2b_sigma[r][2*i+0]]; \
d = rotr64(d ^ a, 32); \
c = c + d; \
b = rotr64(b ^ c, 24); \
a = a + b + m[blake2b_sigma[r][2*i+1]]; \
d = rotr64(d ^ a, 16); \
c = c + d; \
b = rotr64(b ^ c, 63); \
} while(0)
#define ROUND(r) \
do { \
G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \
G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \
G(r,2,v[ 2],v[ 6],v[10],v[14]); \
G(r,3,v[ 3],v[ 7],v[11],v[15]); \
G(r,4,v[ 0],v[ 5],v[10],v[15]); \
G(r,5,v[ 1],v[ 6],v[11],v[12]); \
G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \
G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \
} while(0)
这里感觉是有一些奇怪的地方,像是ROUND和G中的循环实际上只会执行一遍,却用了循环。但是里面的逻辑确实比较神奇,通过v数组的前8个元素和后8个元素的运算,达到更新修改前8个元素的目的。
上述便是blake2b算法更新过程中的压缩过程。不断执行此过程,将原文变成密文。
最后的函数是blake2b算法过程中的blake2b_final
方法,此过程将之前更新好的数据保存在out指针指向的地址空间中。
int blake2b_final( blake2b_state *S, void *out, size_t outlen )
{
uint8_t buffer[BLAKE2B_OUTBYTES] = {0};
size_t i;
if( out == NULL || outlen < S->outlen )
return -1;
if( blake2b_is_lastblock( S ) )
return -1;
blake2b_increment_counter( S, S->buflen );
blake2b_set_lastblock( S );
memset( S->buf + S->buflen, 0, BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */
blake2b_compress( S, S->buf );
for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */
store64( buffer + sizeof( S->h[i] ) * i, S->h[i] );
memcpy( out, buffer, S->outlen );
secure_zero_memory(buffer, sizeof(buffer));
return 0;
}
首先对于缓冲区位置进行初始化为0,同时判断out以及outlen是否符合规范,然后判度是否为lastblock,初次看这个还是有点懵圈的,但是多看几遍就明白了。通过对后面代码blake2b_set_lastblock( S );
,就可以知道对于一个blake2b算法的执行过程final只能执行一次,所以执行完成之后就设置为lastblock。
blake2b_increment_counter( S, S->buflen );
blake2b_set_lastblock( S );
memset( S->buf + S->buflen, 0, BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */
blake2b_compress( S, S->buf );
for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */
store64( buffer + sizeof( S->h[i] ) * i, S->h[i] );
memcpy( out, buffer, S->outlen );
secure_zero_memory(buffer, sizeof(buffer));
接下来依次执行增加计数器的值、设置算法终止、内存管理设置、压缩、保存结果以及最后的安全恢复。
以上便是blake2b算法的全过程,用了大概2篇多的篇幅进行介绍。
内容串流
目前对于我来说,分析了生成哈希码的blake2b算法、优化运算速度的RLWE带错误的环运算以及进行整数运算的numth。对于之前的我来说虽然每个部分的来源、作用以及实现都有了一定的了解,但是它们真正如何运用到实际的全同态加密算法中还是存在着些许疑惑。
这里我请教了孔凡玉老师,老师对我说可以从宏观上去进行划分。首先加密解密过程从宏观上可以分为多项式运算和整数运算,对于每种运算又可以分为不同计算,像是加减乘除的基本运算以及求余求模的扩展运算。所以之前分析的整数运算是整个全同态加密算法的一半的核心。
看似环运算是一个比较抽象的概念,如果回顾一下大一下学习学过的离散数学就可以发现,其实环运算不过是支持两种运算宏观加法运算和宏观乘法运算而已,同时对于宏观的加运算是交换群。所以对于换运算来说也是贯穿在多项式运算和整数运算之间的,如果用现在moba类游戏的职业来说的话,整数运算和多项式运算像是射手或中路,是队伍中的核心,而带错误的环运算更像是辅助,帮助整数运算和多项式运算加快推进速度。
对于用了差不多接近3篇的blake2b哈希算法来说,更像是一名合格的裁判,用于保障全同态加密算法的正常执行。
现在基本上将目前的知识整理到了一起,通过与孔凡玉老师的深入了解,后面的内容应该会着重于多项式的运算。
总结
这次是对上周没有完成工作的完善以及对于目前分析过代码内容的串流。
最后,感谢孔老师的指导,感谢戴老师和其他审核老师的阅读!