-
代码结构
代码分为3个文件,Merkle_Tree.h(附录1)、Merkle_Tree.cpp(附录2)、Demo.cpp(附录3)。将类的构造及类函数的声明放在了Merkle_Tree.h文件中;Merkle_Tree.cpp中是对Merkle_Tree.h里定义类函数的具体实现;Demo.cpp是最终的测试文件,即主函数。 -
文件读取函数:
void GetAllFiles(~),用于获取path路径下所有文件的相对路径及文件名。 -
类:
(1) class Word,作为hash算法的基本数据处理类型(字),自带比较、复制、初始化、与加、求和及部分SHA1算法的操作。
(2) class HashValue,用于存储及计算hash值,采用SHA1算法。类中重要函数说明如下:
a. bool compare(~),与hash值hv比较
b. void set_h(~),重载函数,对长为length的Word数组进行SHA1运算,生成对应Hash值;或将两个Hash值按字相加后生成新的Hash值。
(3) class DataPackageRecord,用于存储一个数据包对应的文件名、数据包Hash值的默克尔树节点、数据包序号。
(4) class DataPackage,用于读取文件,按字节读取,目前暂时只做了txt文件的读取验证。类中重要函数说明如下:
a. bool readFromFile(~),以字节为基本单元读取文件。
b. void pretreatment(),进行SHA1算法的填充处理,填充方法见2.2节。
c. bool byteToWord(),将字节数据流变成Word格式数据流,方便后续处理。
d. Word* getDataWord(),获取Word向量指针,即获取数据
(5) class MerkleTreePoint,Merkle树节点类,存储节点对应哈希值,父节点指针,和两个子节点指针。
(6) class MerkleTree,Merkle树类,存储构造好的Merkle树。重要函数说明如下:
void setStructure(int n),将树初始化为n个叶子节点(数据包)的Merkle二叉树,由底层向上构造,每个父节点指向0~2个子节点,每个子节点指向一个父节点。
bool buildMerkleTree(string floder),读取floder文件夹下的文件计算各节点Hash值。单子节点的Hash值为子节点的Hash值的SHA1运算结果。双子节点的hash值为两个子节点的Hash值按字节求和后的值的SHA1运算结果
compareRoot(~),与另一棵Merkle树进行根节点比较
getError(~),与另一棵Merkle树由根节点向下进行节点比较,若存在不同,则返回存在不同hash值的底层节点的序号及文件名。
int getZeroKnowledgeProofHashSequence(~),Merkle树根据要验证的数据包序号,由底层向上依次返回需要参与零知识验证的hash值。4.测试文件
文件夹“Alice”含有按序号00到19共20各大小为115KB的测试文件
其中每个文件内容除第一个数字对应文件夹序号外,其它全部一致。文件夹“Bob”含有按序号00到19共20各大小为115KB的测试文件与Alex文件夹内容相同。
参考文献
[1]Nakamoto S. Bitcoin: A peer-to-peer electronic cash system, Available: http://bitcoin.org/bitcoin.pdf, 2008.
[2]周李京. 区块链隐私关键技术研究[D]. 北京: 北京邮电大学, 2019: 1-8
[3]Dwork C and Naor M. Pricing via processing or combatting junk mail[C]// In: Annual International Cryptology Conference, pp. 139-147. Berlin: Springer, 1992.
[4]Ding W. Block chain based instrument data management system[J]. China Instrumentation, 2015(10): 15-17.
[5]吴玲燕. 区块链技术在研究生教育教学管理中的应用与挑战[J]. 天津科技, 2019, 46(03): 6-10.
[6]付金华. 高效能区块链关键技术及应用研究[D]. 河南: 战略支援部队信息工程大学, 2020: 3-8, 45-47
[7]吴梦宇, 朱国胜, 吴善超. 基于Merkle树的区块链数据修改方法研究[J]. 信息通信, 2020(10): 10-12+16.
[8]Chelladurai U and Pandian S. Correction to: HARE: A new Hash-based Authenticated Reliable and Efficient Modified Merkle Tree Data Structure to Ensure Integrity of Data in the Healthcare Systems[J]. Journal of Ambient Intelligence and Humanized Computing, 2021, : 1-1.
[9]Secure Hash Standard[S]. Available: http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf
[10]庄上人家工作室, SHA1算法原理[DB/OL], CSDN, 2012.
```cpp
Merkle_Tree.h
#pragma once
#ifndef _MERKLE_TREE_H_
#define _MERKLE_TREE_H_
#include<iostream>
#include<vector>
#include<io.h>
#include<windows.h>
#include<fstream>
#include<string>
#include<iostream>
using namespace std;
namespace MERKLETREE {
//子类声明
class Word ; //字
class HashValue ; //哈希值
class DataPackageRecord ; //数据包记录
class DataPackage ; //数据包
class MerkleTreePoint ; //默克尔树节点
class MerkleTree ; //默克尔树
class Word {
public:
int word;
Word() { word = 0; }
bool compare(Word& w);
void s(int n);//循环左移n位
void ft(int t, Word& b, Word& c, Word& d);
void f(Word& a, Word& b, Word& c, Word& d);
void copy(Word& w){word = w.word;}
void setnum(int w) { word = w; }
void add(Word& b) { word +=b.word; }
void add(Word& b, Word& c) { word = b.word+c.word; }
};
class HashValue {
public:
Word h[5] ; //32*5;
Word kt[4]; //常量字
int length; //hash字长
HashValue() { init(); length = 5; }
HashValue(int n) {
init();
if (n == 0) {
for (int i = 0; i < length; i++)
{
h[i].setnum(0x00);
}
}
}
HashValue(const HashValue& hv) {
length = hv.length;
for (int i = 0; i < 5; i++)
{
h[i] = hv.h[i];
}
for (int i = 0; i < 4; i++)
{
kt[i] = hv.kt[i];
}
}
bool compare(HashValue& hv);
//sha-1
void init(); //初始化缓存、常量字
void set_h(Word* data, int length);
void set_h(HashValue& hv1, HashValue& hv2);
void output();
};
class DataPackageRecord {
public:
MerkleTreePoint* mtp;//该包对应的节点
string filename;//文件名//包名
int serialNumber;//序号
};
class DataPackage {
friend HashValue;
friend MerkleTree;
private:
string filename;
char* dataByte;
char* dataByteFill;//填充
Word* dataWord;
int lengthByte;
int lengthByteFill;
int lengthWord;
public:
DataPackage() {
dataByte = nullptr;
dataWord = nullptr;
dataByteFill = nullptr;
lengthByte = 0;
lengthWord = 0;
lengthByteFill = 0;
}
~DataPackage() {
if (dataByte != nullptr)
delete[] dataByte;
if (dataWord != nullptr)
delete dataWord;
if (dataByteFill != nullptr)
delete dataByteFill;
}
void init();
bool readFromFile(string fname); //文件读取为BYTE向量
void pretreatment(); //hash-1的预处理,BYTE类型
bool byteToWord();//byte转为word类型
Word* getDataWord() { return dataWord; }
int getLengthWord() { return lengthWord; }
};
class MerkleTreePoint {
friend MerkleTree;
DataPackageRecord* dpr;
public:
MerkleTreePoint() {
dpr = nullptr;
last = nullptr;
next[0] = nullptr;
next[1] = nullptr;
}
private:
HashValue hv;
MerkleTreePoint* last;//父节点
MerkleTreePoint* next[2];//子节点
};
class MerkleTree {
//严禁随意删除节点
private:
int deep;//深度
int num;//数据包个数
DataPackageRecord* datarecord;//数据包记录列表
MerkleTreePoint* root;//根节点
public:
MerkleTree() {//从文件夹中建立默克尔树
deep = 0;
num = 0;
datarecord = nullptr;
root = nullptr;
}
~MerkleTree() {
init();
}
void mtpDelete(MerkleTreePoint* mtp); //删除节点某节点及其后续节点
void init(); //初始化树&&清理树
void setStructure(int n); //依据n设置树结构
bool buildMerkleTree(string floder); //依据文件生成树
void treeHash(MerkleTreePoint* mtp);
bool compareRoot(MerkleTree& mt); //比较两个树根hash
int getError(MerkleTree& mt, vector<string>&errorfilenames, vector<int>&error); //返回error数量,返回error数组
void catchError(MerkleTreePoint* mtp1, MerkleTreePoint* mtp2, vector<int>& error);
int getZeroKnowledgeProofHashSequence(int serialNumber, vector<HashValue>&hashvalue); //返回验证序列个数
};
}
#endif _MERKLE_TREE_H_
附录2
Merkle_Tree.cpp
#include "Merkle_Tree.h"
using namespace MERKLETREE;
//文件读取函数声明
void GetAllFiles(string path, vector<string>& filenames, vector<string>& filepaths);
//Word类
bool
Word::compare(Word& w) {
if (word != w.word)
return false;
return true;
}
void
Word::s(int n) {
unsigned int temp = word;
word = (temp << n) | (temp >> (32 - n));
}
void
Word::ft(int t, Word& b, Word& c, Word& d) {
if (t / 20 == 0) {
word = ((b.word & c.word) | ((~b.word) & d.word));
}
else if (t / 20 == 1) {
word = (b.word ^ c.word ^ d.word);
}
else if (t / 20 == 2) {
word = ((b.word & c.word) | (b.word & d.word) | (c.word & d.word));
}
else if (t / 20 == 3) {
word = (b.word ^ c.word ^ d.word);
}
else {
word = 0;
}
}
void
Word::f(Word& a, Word& b, Word& c, Word& d) {
word = (a.word ^ b.word ^ c.word ^ d.word);
s(1);
}
//HashValue类
bool
HashValue::compare(HashValue& hv) {
if (length != hv.length)
return false;
for (int i = 0; i < length; i++)
{
if (!h[i].compare(hv.h[i]))
return false;
}
return true;
}
void
HashValue::init() {
length = 5;
h[0].word = 0x67452301;
h[1].word = 0xEFCDAB89;
h[2].word = 0x98BADCFE;
h[3].word = 0x10325476;
h[4].word = 0xC3D2E1F0;
kt[0].word = 0x5A827999;// (0 <= t <= 19)
kt[1].word = 0x6ED9EBA1;// (20 <= t <= 39)
kt[2].word = 0x8F1BBCDC;//(40 <= t <= 59)
kt[3].word = 0xCA62C1D6;// (60 <= t <= 79)
}
void
HashValue::set_h(Word* data, int length) {
init();
Word A, B, C, D, E;
Word cache80[80];
Word temp;
int len = length / 16;
for (int i = 0; i < len; i++)
{
//output();
//1
for (int t = 0; t < 16; t++)
cache80[t].copy(data[i * 16 + t]);
//2
for (int t = 16; t < 80; t++)
cache80[t].f(cache80[t - 3], cache80[t - 8], cache80[t - 14], cache80[t - 16]);
//3
A.copy(h[0]);
B.copy(h[1]);
C.copy(h[2]);
D.copy(h[3]);
E.copy(h[4]);
//4
for (int t = 0; t < 80; t++)
{
temp.copy(A);
temp.s(5);
Word temp2;
temp2.ft(t, B, C, D);
temp.add(temp2);
temp.add(E);
temp.add(cache80[t]);
temp.add(kt[t / 20]);
}
E.copy(D);
D.copy(C);
C.copy(B);
C.s(30);
B.copy(A);
A.copy(temp);
//5
h[0].add(A);
h[1].add(B);
h[2].add(C);
h[3].add(D);
h[4].add(E);
}
}
void
HashValue::set_h(HashValue& hv1, HashValue& hv2) {
Word w[16];
for (int i = 0; i < 5; i++)
{
w[i].add(hv1.h[i], hv2.h[i]);
}
w[5].setnum(0x80000000);
for (int i = 6; i < 15; i++)
{
w[i].setnum(0x00);
}
w[15].setnum(0x160);
set_h(w, 16);
}
void
HashValue::output() {
for (int i = 0; i < length; i++)
{
cout << h[i].word << " ";
}
cout << endl;
}
//DataPackage类
void
DataPackage::init() {
if (dataByte != nullptr)
delete[] dataByte;
lengthByte = 0;
if (dataByteFill != nullptr)
delete[] dataByteFill;
lengthByteFill = 0;
if (dataWord != nullptr)
delete dataWord;
lengthWord = 0;
}
bool
DataPackage::readFromFile(string fname) {
init();
ifstream file(fname, ios::in);
char temp = 0;//byte
int lengthtemp = 0;
if (!file)
return false;
while (!file.eof())
{
file.get(temp);
lengthtemp++;
}
file.close();
file.open(fname, ios::in);
dataByte = new char[lengthtemp];
while (!file.eof())
{
file.get(dataByte[lengthByte]);
lengthByte++;
}
if (lengthByte != lengthtemp)
{
file.close();
return false;//防止读取途中文件长度变化(被篡改)
}
file.close();
return true;
}
void
DataPackage::pretreatment() {
int temp = lengthByte % 64;
if (temp < 56)
lengthByteFill = 64 - temp;
else
lengthByteFill = 128 - temp;
dataByteFill = new char[lengthByteFill];
dataByteFill[0] = 0x80;
for (int i = 1; i < lengthByteFill - 4; i++)//int类型32bit,所以最多32位
dataByteFill[i] = 0x00;
for (int i = 0; i < 4; i++)
{
dataByteFill[lengthByteFill - 4 + i] = (char)((lengthByte * 8 << (i * 8)) >> 24);
}
}
bool
DataPackage::byteToWord() {
int len = lengthByte + lengthByteFill;
if (len % 4 != 0)
return false;
lengthWord = len / 4;
if (lengthWord % 16 != 0)
return false;
dataWord = new Word[lengthWord];
int i = 0;
for (i = 0; i < lengthByte; i++)
{
unsigned int temp = (unsigned int)dataByte[i];
temp = (temp << 24) >> (((i) % 4) * 8);
dataWord[(i) / 4].word += temp;
}
for (int j = 0; j < lengthByteFill; j++)
{
unsigned int temp = (unsigned int)dataByteFill[j];
temp = (temp << 24) >> (((i + j) % 4) * 8);
dataWord[(i + j) / 4].word += temp;
}
}
//MerkleTree类
void
MerkleTree::mtpDelete(MerkleTreePoint* mtp)//删除节点某节点及其后续节点
{
if (mtp->next[0] != nullptr) {
mtpDelete(mtp->next[0]);
}
if (mtp->next[1] != nullptr) {
mtpDelete(mtp->next[1]);
}
delete mtp;
}
void
MerkleTree::init()//初始化树&&清理树
{
if (root != nullptr)
mtpDelete(root);
root = nullptr;
if (datarecord != nullptr)
delete[] datarecord;
datarecord = nullptr;
deep = 0;
num = 0;
}
void
MerkleTree::setStructure(int n)//依据n设置树结构
{
init();
num = n;
deep = 0;
while (n > 1) {
deep++;
if (n % 2 == 0)
n = n / 2;
else
n = n / 2 + 1;
}
deep++;
MerkleTreePoint*** mtpList = new MerkleTreePoint * *[deep];//每层的节点列表
int* mtpListLength = new int[deep];//每层节点数
//空间申请
for (int i = deep, n = num; i > 0; i--)
{
mtpListLength[i - 1] = n;
mtpList[i - 1] = new MerkleTreePoint *[n];
if (n % 2 == 0)
n = n / 2;
else
n = n / 2 + 1;
}
for (int i = 0; i < deep; i++)
{
for (int j = 0; j < mtpListLength[i]; j++)
{
mtpList[i][j] = new MerkleTreePoint;
}
}
//设置结构
for (int i = 0; i < deep - 1; i++)
{
for (int j = 0; j < mtpListLength[i]; j++)
{
mtpList[i][j]->next[0] = mtpList[i + 1][j * 2];
mtpList[i + 1][j * 2]->last = mtpList[i][j];
if (j * 2 + 1 < mtpListLength[i + 1])
{
mtpList[i][j]->next[1] = mtpList[i + 1][j * 2 + 1];
mtpList[i + 1][j * 2 + 1]->last = mtpList[i][j];
}
}
}
datarecord = new DataPackageRecord[num];
for (int i = 0; i < num; i++)
{
datarecord[i].mtp = mtpList[deep - 1][i];
datarecord[i].serialNumber = i;
}
//控制权移交;
root = mtpList[0][0];
//空间清理
for (int i = 0; i < deep; i++)
{
delete[] mtpList[i];
mtpList[i] = nullptr;
}
delete[] mtpList;
}
bool
MerkleTree::buildMerkleTree(string floder)//依据文件生成树
{
vector<string>filenames;
vector<string>filepaths;
GetAllFiles(floder, filenames, filepaths);
setStructure(filenames.size());
DataPackage Toolman;//文件读取头
//处理叶子节点
for (int i = 0; i < num; i++)
{
if (!Toolman.readFromFile(filepaths[i]))
return false;
datarecord[i].filename = filenames[i];
Toolman.pretreatment();
if (!Toolman.byteToWord())
return false;
datarecord[i].mtp->hv.set_h(Toolman.getDataWord(), Toolman.getLengthWord());
datarecord[i].mtp->dpr = &datarecord[i];
//cout << filepaths[i] << endl;
//datarecord[i].mtp->hv.output();
}
//处理枝干节点
treeHash(root);
}
void
MerkleTree::treeHash(MerkleTreePoint* mtp) {
if (mtp->next[0] != nullptr)
treeHash(mtp->next[0]);
if (mtp->next[1] != nullptr)
treeHash(mtp->next[1]);
if ((mtp->next[0] != nullptr) && (mtp->next[1] != nullptr))//非叶子节点进行hash计算
mtp->hv.set_h(mtp->next[0]->hv, mtp->next[1]->hv);
else if ((mtp->next[0] == nullptr) && (mtp->next[1] == nullptr))
return;
else//单分支hash节点
{
HashValue hvtemp(0);
if (mtp->next[0] != nullptr)
{
mtp->hv.set_h(mtp->next[0]->hv, hvtemp);
}
else if (mtp->next[1] != nullptr)
{
mtp->hv.set_h(mtp->next[1]->hv, hvtemp);
}
}
}
bool
MerkleTree::compareRoot(MerkleTree& mt)//比较两个树根hash
{
return root->hv.compare(mt.root->hv);
}
int
MerkleTree::getError(MerkleTree& mt, vector<string>&errorfilenames, vector<int>&error)//返回error数量,返回error数组
{
if (compareRoot(mt))
return 0;
catchError(root, mt.root, error);
for (int i = 0; i < error.size(); i++)
{
errorfilenames.push_back(datarecord[error[i]].filename);
}
return error.size();
}
void
MerkleTree::catchError(MerkleTreePoint* mtp1, MerkleTreePoint* mtp2, vector<int>& error)
{
if ((mtp1->next[0] == nullptr) && (mtp2->next[0] == nullptr) && (mtp1->next[1] == nullptr) && (mtp2->next[1] == nullptr))
if (!mtp1->hv.compare(mtp2->hv))
error.push_back(mtp1->dpr->serialNumber);
if ((mtp1->next[0] != nullptr) && (mtp2->next[0] != nullptr))
if (!mtp1->next[0]->hv.compare(mtp2->next[0]->hv))
catchError(mtp1->next[0], mtp2->next[0], error);
if ((mtp1->next[1] != nullptr) && (mtp2->next[1] != nullptr))
if (!mtp1->next[1]->hv.compare(mtp2->next[1]->hv))
catchError(mtp1->next[1], mtp2->next[1], error);
}
int
MerkleTree::getZeroKnowledgeProofHashSequence(int serialNumber, vector<HashValue>&hashvalue)//返回验证序列个数
{
MerkleTreePoint* mtptemp = datarecord[serialNumber].mtp;
MerkleTreePoint* mtptemp2;
//HashValue* hvtemp;
while (mtptemp->last != nullptr) {
mtptemp2 = mtptemp->last;
if ((mtptemp2->next[0] != nullptr) && (mtptemp2->next[1] != nullptr))
{
if (mtptemp->hv.compare(mtptemp2->next[0]->hv))
{
//hvtemp = new HashValue();
hashvalue.push_back(mtptemp2->next[1]->hv);
}
else
{
//hvtemp = new HashValue();
hashvalue.push_back(mtptemp2->next[0]->hv);
}
}
else
{
HashValue hvtemp(0);
hashvalue.push_back(hvtemp);
}
mtptemp = mtptemp2;
mtptemp2 = nullptr;
}
//hvtemp = new HashValue();
hashvalue.push_back(root->hv);
return hashvalue.size();
}
//文件读取--获取path路径下的所有文件
void
GetAllFiles(string path, vector<string>& filenames, vector<string>& filepaths)
{
intptr_t hfile = 0;
struct _finddata_t fileinfo;
string p;
if ((hfile = _findfirst(p.assign(path).append("/*").c_str(), &fileinfo)) == NO_ERROR);
{
do
{
if ((fileinfo.attrib & _A_SUBDIR))
{
if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != NO_ERROR)
GetAllFiles(p.assign(path).append("/").append(fileinfo.name), filenames, filepaths);
}
else
{
filepaths.push_back(p.assign(path).append("/").append(fileinfo.name));
filenames.push_back(fileinfo.name);
}
} while (_findnext(hfile, &fileinfo) == NO_ERROR);
_findclose(hfile);
}
}
Demo.cpp
//代码中一些函数的参考网址:
/*
获取路径下的所有文件:
https://www.cnblogs.com/fnlingnzb-learner/p/6424563.html
*/
#include"Merkle_Tree.h"
using namespace std;
using namespace MERKLETREE;
int main() {
//准备,两个文件夹,各含20个文件
//生成发送方默克尔树,接收方默克尔树
MerkleTree Alice;
MerkleTree Bob;
Alice.buildMerkleTree("Alice");
Bob.buildMerkleTree("Bob");
cout << "数据比较实验:" << endl;
cout << "数据比较结果(1为相同,0为相异):" << Bob.compareRoot(Alice) << endl;
cout << endl;
cout << "定位错误实验:" << endl;
vector<int>error;
vector<string>errorfiles;
cout << "错误个数" << Alice.getError(Bob, errorfiles, error) << endl;
for (int i = 0; i < error.size(); i++)
{
cout << "错误文件序号:" << error[i] << "错误文件名:" << errorfiles[i] << endl;
}
cout << "零知识证明实验:" << endl;
vector<HashValue>hashvalue;
int num_datapakage = 6;
int n = Alice.getZeroKnowledgeProofHashSequence(num_datapakage, hashvalue);
HashValue temp;
DataPackage Carl;
bool ishavedatapackage = false;
string datapakage_filename = "";
if (0 < num_datapakage && num_datapakage < 10)
datapakage_filename += "Bob/0";
else if (num_datapakage > 10 && num_datapakage < 100)
datapakage_filename += "Bob/";
else
cout << "序号错误!" << endl;
datapakage_filename += to_string(num_datapakage) + ".txt";
Carl.readFromFile(datapakage_filename);
Carl.pretreatment();
Carl.byteToWord();
temp.set_h(Carl.getDataWord(), Carl.getLengthWord());
cout << "执行hash" << n - 1 << "次。" << endl;
for (int i = 0; i < n - 1; i++)
{
temp.set_h(temp, hashvalue[i]);
}
ishavedatapackage = temp.compare(hashvalue[n - 1]);
cout << "Alice 是否含有Carl文件(" << datapakage_filename << ")的内容(1为含有,0为不含有):" << ishavedatapackage << endl;
//零知识证明
//接收方发送一个数据包序号
//发送方根据序号发送root值序列、由底层向上,
//接收方依据本数据包生成哈希值,依次向上生成哈希值,
return 0;
}