最近学习WP8.1应用开发,想把C语言的SM3国密算法移植到手机app中。由于把C语言的代码转换成C#代码工作量较大,因此只能用winodws运行时组件来实现。
SM3国密算法是一种HASH算法,具体详情请自行百度。
结果测试发现手机上SM3算法计算出来的结果和电脑上运行的结果不同!经过我一点点调试发现是位移操作惹的祸,代码中有如下宏定义:
class="brush:cpp;gutter:true;">#define SHL(x,n) (((x) & 0xFFFFFFFF) << n) #define ROTL(x,n) (SHL((x),n) | ((x) >> (32 - n)))
第一行的宏定义当n大于32时,手机运算结果与电脑不同;第二行的宏定义当32-n小于0时,手机运算结果与电脑不同。
为了更好地测试,我决定建一个干净的测试环境。首先新建一个C#语言的空白的windows phone应用项目,在xaml中放一个textblock和2个按钮
<Grid> <StackPanel> <TextBlock x:Name="txbResult"/> <Button x:Name="btnCSharpLeft" Content="C#位移" Click="btnCSharpLeft_Click"></Button> <Button x:Name="btnCLeft" Content="C左位移" Click="btnCLeft_Click"></Button> </StackPanel> </Grid>
再实现按钮点击事件
private void btnCSharpLeft_Click(object sender, RoutedEventArgs e) { txbResult.Text += "C#位移\n"; for (int i = 0; i < 96; i += 8) { string str = "0x12345678<<" + i.ToString() + "=" + (0x12345678 << i).ToString("X"); txbResult.Text += str.PadRight(40) + "0x12345678>>" + i.ToString() + "=" + (0x12345678 >> i).ToString("X") + "\n"; } } private void btnCLeft_Click(object sender, RoutedEventArgs e) { txbResult.Text += "C位移\n"; Class1 c = new Class1(); for (int i = 0; i < 96; i += 8) { string str = "0x12345678<<" + i.ToString() + "=" + c.Func2(0x12345678, i).ToString("X"); txbResult.Text += str.PadRight(40) + "0x12345678>>" + i.ToString() + "=" + c.Func1(0x12345678, i).ToString("X") + "\n"; } }
接着新建一个C++语言的windows运行时组件,对外提供两个函数,用以实现左位移和右位移
unsigned int Class1::Func1(unsigned int n, int b) { return n >> b; } unsigned int Class1::Func2(unsigned int n, int b) { return n << b; }
然后连上手机进行测试,我的手机是nokia920,公司测试部的测试机,先拿来用用,吼吼!
可以看到C#的位移在超过32时是对32取模的,也就是说位移33位相当于位移1位,而手机上运行结果测试超过32位都是0!
为了确定是否C++语言的位移都是这么处理的,我再新建一个WIN32的控制台项目来测试
#include "stdio.h" int main() { printf("C左移\n"); for (int i = 0; i < 96; i += 8) { printf("0x12345678<<%d = %x\n",i,0x12345678 << i); } printf("C右移\n"); for (int i = 0; i < 96; i += 8) { printf("0x12345678>>%d = %x\n", i, 0x12345678 >> i); } return 0; }
运行结果如下
可以看到和C#的结果是一样的。说明只有C++的windows运行时组件中才会有不同的结果。
同理我再测试位移负数位,比如 0x12345678>>-2,在C#和C++的WIN32程序中位移-2位相当于位移30位,而在C++的windows运行时组件中则结果是0。
结论:为了兼容性,位移时要对32取模,比如1>>n要写成1>>(n%32),这样在不同的平台中也能得到相同的结果。