基于Merkle Tree 的挑战应答环节
上一节,说到MerkleTree的建立,是基于文件分块,对分块进行哈希,用对应的哈希值作为MerkleTree的叶子节点。计算父亲节点=两个孩子节点哈希值的连接,再哈希所得。为了方便表示,个人选择哈希值已十六进制表示,考虑效率选用哈希函数unsigned int SDBMHash(string str);
在云存储数据去重研究领域里, MerkleTree的建立往往是为了让服务器检查客户端的文件所有权,即客户端有没有完整的文件内容。在服务器-客户端的挑战应答环节中,服务器会随机生成一定数量的随机数,作为发问的叶子节点序号。客户端收到随机的叶子节点序号则要返回相应的叶子节点以及该节点到根节点路径上的兄弟节点。服务器收到后计算对应的树根节点,在于服务器中存放的树根节点对比,由此来决定客户端是否存放有完整的文件。
图示说明:
如果挑战的叶子节点序列号为10,则应该回应{10,9,3,2}这四个节点的值。
服务器收到后,可以沿着叶子到根的路径,计算相应的根节点的值,在于存放的根节点值对比,相等则通过,不相等则挑战失败。
一下重点说明在挑战应答过程中需要注意的两个细节:
为了统一,选择所有的哈希值都以16进制无符号整型表示。
1.客户端-building Merkle Tree
由两个孩子节点计算其父亲节点时,要先把孩子节点的无符号整型值转化为字符串,再字符串前后拼接,对拼接后的字符串哈希,得到父亲节点的值(无符号整型)。
使用转换函数
char *itoa( int value, char *string,int radix);
原型说明:
value:欲转换的数据。
string:目标字符串的地址。
radix:转换后的进制数,可以是10进制、16进制等。
2.客户端-response_node
为了方便服务器端读取节点数据,将找到的应答节点的无符号整型值(十六进制)转化为字符串形式,依次写入目标文件。好处:每个节点所表示的字符串的长度一致都是8位串长。
3.服务器端-check_response
依次读取传回文件中的节点值(节点字符串定长为8Bytes),再按照制定的随机叶子节点的序号的奇偶性来决定读到的两个节点连接的前后次序(如果两节点连接顺序出错,即使传回的节点值是正确的,计算根节点的值也是错误的。)
图示:
图中:挑战的叶子节点序号为4,则应答节点有{4,5,3,0}就是图中画斜线方格的节点。
服务器通过叶子节点序号4,先读出4,5号节点;
由于叶子节点序号为偶数,所以4号放在前面,连接后读出的5节点;
计算得父亲节点序号2=4/2=5/2;
由于父亲节点为偶数,所以父亲节点在前,连接后面读出的节点值;
计算得父父亲节点序号2/2=1;
由于父父亲节点序号为奇数,所以后面读出的那个节点值放在前,父父亲的节点值放后面。
最后连接,计算得根节点,完成!
注意:这里树的建立是用上一节中讲到的Path[]数组,可以有不同的建树结构,但大体思路都是相似的,请做参考。
源代码:
merkle_tree.h
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct Path{
int * node;
}Path;
unsigned int SDBMHash(char *str);
int merkle_root(Path path[],int num);
int merkle_overFile(char * filename,Path path[],unsigned int * root);
void response_over_merkletree(Path path[],int depth,int leaf,unsigned int response[] );
void print_hex_int(unsigned int temp);
void show_wholeTree(Path path[],int depth);
int check_root(char * filename,int leaf_no,int depth);
merkle_tree.c
#include "merkle_tree.h"
unsigned int SDBMHash(char *str)
{
unsigned int hash = 0;
while (*str)
{
// equivalent to: hash = 65599*hash + (*str++);
hash = (*str++) + (hash << 6) + (hash << 16) - hash;
}
return (hash & 0x7FFFFFFF);
}
void print_hex_int(unsigned int temp){
int i=0;
unsigned char *p=(unsigned char *)&temp;
for(i=0;i<4;i++)
printf("%02x",*(p++));
printf(" ");
}
void print_hex_string(unsigned char buffer[],int len){
int i=0;
// unsigned char *p=(unsigned char *)&temp;
for(i=0;i<len;i++)
printf("%02x",buffer[i]);
printf(" ");
}
int merkle_root(Path path[],int num){
int node_num=num,depth=0,i,j;
unsigned char temp1[10],temp2[10],buf[20];
unsigned int k1,k2;
//char *p;
while(node_num/2!=0){
node_num/=2;
path[++depth].node=(int *)malloc(node_num*sizeof(int));
i=0;
while(i<node_num){
k1=path[depth-1].node[i*2];
k2=path[depth-1].node[i*2+1];
itoa(k1,temp1,16);
itoa(k2,temp2,16);
strcpy(buf,temp1);
strcat(buf,temp2);
path[depth].node[i]=SDBMHash(buf);
printf("temp1=%s,temp2=%s,buf=%s,father=%08x\n",temp1,temp2,buf,path[depth].node[i]);
i++;
}
printf("\n");
}
printf("root=%x,%d\n",path[depth].node[0],path[depth].node[0]);
// printf("root(hex_int)=");print_hex_int(path[depth].node[0]);
return depth;
}
/*在文件的基础上建立MKT*/
int merkle_overFile(char * filename,Path path[],unsigned int * root){
FILE *fp;
int n,len,i,depth;
// filename[20];
unsigned char buffer[64];
if((fp=fopen(filename,"rb"))==NULL){
printf("the file %s cannot be opened!\n",filename);
}
fseek(fp,0,SEEK_END);
if((len=ftell(fp))==-1L){
printf("Sorry! Can not calculate files which larger than 2GB!\n");
fclose(fp);
}
rewind(fp);
printf("len=%d\n",len);
n=len/64;
/*文件按64bytes大小分块,叶子节点初始化*/
path[0].node=(int *)malloc(n*sizeof(int));
i=0;
while(i<n){
fread(buffer,1,64,fp);
path[0].node[i]=SDBMHash(buffer);
i++;
}
fclose(fp);
depth=merkle_root(path,n);
*root=path[depth].node[0];
return depth;
}
void response_over_merkletree(Path path[],int depth,int leaf,unsigned int response[]){
int i,j,tar;
tar=leaf;
response[0]=path[0].node[tar];
for(i=1,j=0;i<=depth;i++,j++){
if(tar%2==1){
response[i]=path[j].node[tar-1];
}
else{
response[i]=path[j].node[tar+1];
}
tar=tar/2;
}
}
void show_wholeTree(Path path[],int depth){
int i,n=1,j;
for(i=depth;i>=0;i--){
for(j=0;j<n;j++){
// print_hex_int(path[i].node[j]);
printf("%x ",path[i].node[j]);
}
printf("\n");
n=n*2;
}
}
int check_root(char * filename,int leaf_no,int depth){
char resp_file[40];
unsigned int temp=0;
unsigned char buf[10],temp1[10],temp2[10],cal[20],*p;
int i,len,n=leaf_no,j;
FILE *fp;
strcpy(resp_file,filename);
strcat(resp_file,"_respone.txt");
if((fp=fopen(resp_file,"rb"))==NULL){printf("the file %s cannot be opened!\n",resp_file);return 0;}
fread(temp1,8,1,fp);temp1[8]='\0';
// strcpy(temp1,buf[0]);
for(i=1;i<=depth;i++){
fread(temp2,8,1,fp);temp2[8]='\0';
if(n%2==1){
strcpy(cal,temp2);
strcat(cal,temp1);
}
else {
strcpy(cal,temp1);
strcat(cal,temp2);
}
// print_hex_string(buf,8);printf("\n");
temp=SDBMHash(cal);
// printf(" temp: ");
// print_hex_int(temp);
memset(temp1,0,sizeof(temp1));
itoa(temp,temp1,16);
n=n/2;
}
printf("the root:%s\n",temp1);
return 1;
}
/*
//测试输入文件file_1k.txt
int main(){
char filename[20];
int depth;
Path path[30];
unsigned int * root;
printf("please input the file:");
scanf("%s",filename);
depth=merkle_overFile(filename,path,root);
printf("depth=%d\n",depth);
return 0;
}
*/
挑战应答成功!截图:
以上是个人想法,感兴趣的朋友可以一起探讨更高效的算法。请私信或者留言。