类库大魔王
类库大魔王 多年C++、Go项目经验,长期从事跨平台(Windows/macOS/iOS/Android)应用架构设计与开发。

做个hash工具


  一直想自己写一个计算hash的工具,而最无耻的是,我对各种密码学意义上的hash算法根本不了解,只知道有这么个东东。不过现在流行的hash算法网上有一些现成的代码,C和C++都有,比如著名的Crypto++、LibTomCrypt等等。
  本来跟hash算法是完全没有交集的,平台根本用不到,最多也就是直接LoadLibrary系统自带的CryptDll.dll,里面有计算MD5的函数,这是在好些年前就知道了的,也一直丢在一边。现在因为要做一下文件共享的东东,其中避免不了的是唯一标识一个文件,而一个良好的消息摘要算法就是一种可行的方法。鉴于网上早就出现了一些针对MD5碰撞的实际例子,所以我就有点不甘心只使用MD5算法,于是把LibTomCrypt的代码抠出来封装成C++的样子。这样的消息摘要算法计算过程一般分为初始化,更新数据,得到结果3步,所以可以把每个算法都封装成一个单独的类,每个类都向外提供这3个方法,这样只要根据需要可以以相同的计算逻辑得到最后的结果。
  封装好了各个算法后,再提供一个类,封装更高层次的功能,比如计算一个文件的hash值。文件需要打开,然后每次读出一部分数据来计算,最后才得到一个结果。这样的逻辑不需要为每个算法类都写一遍,写到单独的类里即可。至于该类与各算法实现类的关系,比较OO的做法是,用继承,所有的算法类都继承自该类,就自然而然地有该方法了,但似乎还需要增加3个虚函数,这样父类的方法才能调用到子类的方法,这样就多了点运行时间和空间上的开销,尽管不严重;另一个比较GP的方法是,把该类写成一个模板类,而各算法实现类做为模板参数,这样就可以省掉因虚函数引入而带来的开销了,当然带来的成本是用户使用这些类时,需要同时知道这个类的存在以及各个算法实现类的存在,而前面讲的偏OO的方法是只需要知道算法实现类就行了。
  上面解决了一个向外提供接口的问题。接着又有一个新的问题来困扰了我两天。因为上面已经有一个类是对于每个算法实现类都知道其存在的,但我又发现其中有几个算法实现类中的更新数据方法逻辑是完全一样的,要把数据根据当前算法的实际需要拆分成多块进行压缩运算,所以只是其中调用的压缩算法不一样,数据长度不一样,这样的情况也是代码复用的一个场合。但是开始的时候,我不知道应该把这段看起来可以公用的代码放到什么地方。同样,比较OO的做法是写一个基类,让这些算法类继承过来,同样需要一个虚函数,因为会有一个压缩运算过程,不同的算法是不一样的,虽然函数名可以一样,所以也多了一点开销;另一个当时很不想用的办法是,像LibTomCrypt中一样,把这个过程定义成一个宏,在这些算法实现类里都写一句这个宏调用,就没有虚函数带来的开销了,但我觉得这是C的做法,而不是C++,这个场合这种做法很丑陋;后来偶然在QQ群里看到别人谈及模板应用的一种方法,得到灵感,就像WTL那样的静态多态就可以,仍然需要一个基类,但基类是个模板类,而模板参数就是自己这个算法实现类,这样通过基类中的方法仍然可以调用到子类中的方法,但又没有虚函数的开销。基本解决这个问题!
  后来又遇到一个问题,不过没有解决。HAVAL算法,可以有3、4、5轮计算,不同轮数的计算中间的步骤也会有点小差别,每种计算最后的长度可以是128位、160位、192位、224位和256位。照理,前3轮或前4轮的计算通过宏定义等方法可以在某种层次上封装可相同的逻辑,这样3种算法就应该可以共用一部分代码,可是实际上,用宏定义的方法不行,第2次修改过的宏定义不能影响到上面的应用了宏调用的地方,所以实际上代码共享不了,只好重复写了,以后想想办法能不能改进。
  Panama hash算法的资料好少,Crypto++的实现又比较复杂,看不懂哦!

感觉本文不错,不妨小额鼓励我一下!
如果你有Visa、MasterCard之类的国际银行卡,也可以考虑以下选项:
如果你看不到评论框,说明Disqus被墙了。