x13 vs md5

x13 vs md5

阅读:   评论:   作者: Rybby  日期:   来源: rybby.com
最近在设计巴巴变时想对用户设计的节点模块添加锁定功能,比如你的网站可以让用户发表文章或评论,而你想让用户用巴巴变进行页面的设计,如果用户可以为所欲为地对网页DOM进行添加与删除节点的话,那用户可能会对所有的节点都删除掉。于是网站管理员可能希望对用户提供一个固定的模块,这个模块只对用户开放一定的权限,如是否可以修改属性、样式、文本与添加和删除子节点。一些重要的节点我们是不希望用户删除掉的,比如标题、文章内容BOX等等,这时就可以对某个节点加锁进行控制。锁定的节点用户是不能再编辑的,但可以在里面添加一个开放的节点让用户随意创作,如添加文本、画图、用路径设计图形等等。既然用到锁那就牵扯到密码的问题了,因为客户端的脚本语言没有md5函数,而我又不想将密码存放在服务端,再者,这个密码只是对用户进行一些权限的控制而已,并不是什么非常重要的东西,另外,这些加密的节点模块会频繁地传输在网络上供用户享用,所以我需要一个轻量级的加密程序,比如密码的长度用16位(指输出长度)就够了,于是我开始着手设计自己的密码生成器了。

由于密码是存放在网页DOM的节点里的,如果用户把源码刨出来看到密文,能不能破译出明文呢?这个几乎是不可能的,因为我对明文生成一个伪随机数,然后生成密文时再去掉一部份,即使对这个密文进行反译也找不回丢弃的部份。现在唯一担心的是密码冲突,即同一个密文可能有多个明文,就算是md5也不能做到唯一性,更何况它是用16进制生成密文的。如果密码是16进制的话,16位数的密码就是16的16次方,最多也就:16^16=1844,67440737,09551616;一个逗号代表一个亿,大约是一千多亿亿组取值;如果用32位就是16^32=3402823,66920938,46346337,46074317,70000000。而锐某设计的加密程序是用64进制输出密码的,还可以选择用10万进制输出密码,所谓的10万进制就是用unicode目前所能表示的所有字符表示密码,这些字符有的是2字节,有的是3字节或更多,所以输出32位的密码一般都长达90几字节,作为平时用的密码显然太长了,用32位或16位的64进制就够了。64^16=79228,16251426,43375935,43950336;64^32=62,77101735,38668076,38357894,23207700,00000000,00000000,00000000;如果密码的随机性很高的话,那唯一性也会相对提高,即使用“天河一号”这样的每秒能进行2千多万亿次计算能力的超级计算机,要对所有的密码都扫描的一遍以找出有冲突的密码也需要很多时间!!!

要如何实现密码的真随机呢?这个锐某没想到好方法,下面说一下我将明文编译成密文的原理,算是抛砖引玉吧。密码的唯一性我还没验证,可能一个密文有多个相同的明文,如:abc、ijkl、fio等都指向同一个密文。加密流程:

1)将明文转为unicode码连接成数字字串
2)将数字字串顺序打乱作为种子字串
3)将种子字串扩张到一定长度得到伪随机字串
4)将种子字串插入到伪随机字串
5)将伪随机字串以每5位数字为单位转成2进制字串
6)将2进制字串的unicode字符的最后6位2进制字串转成一位64进制字符
7)结束并输出

先来看第一步,将明文转为unicode码连接起来。比如将“abcd”各个字母的unicode码连接起来得到一串数字。

第二步,将数字字串顺序打乱。这一步的作用是将两串接近相同的字串差异化,使它们看起来相差很远。如“abcd”与“abcde”前面部份都一模一样,将它们前后对换连接一次就完全不同了:abcd > adbccbda;abcde > aebdccdbea。

第三步,将数字字串扩张到一定长度。这一步的作用是取伪随机数,当然也可以用一串固定的随机字串直接代替,但所有的密码都使用同一串随机字串可能会使最终的密码同质化。所以应该用种子字串来生成随机字串,如何扩张字串的长度呢?将字串的每个数字字符都乘以一个系数就行了。乘以什么数字好呢?我们最终的目的是使字串随机化,尽可能将所有的数字都出现在字串里。从0123456789里选一个,0和1首先要排除;乘以5的结果只有0和5,也要排除;乘以2的结果只有02468,这个也不行;4、6、8最终的结果也比较多的出现偶数,也不行;剩下的只有3、7、9了。根据锐某的统计,7比3更能平均化所出现的数字,而9又比7更具随机化,但以这3个数字作为乘积都不会出现0,所以不得不再加一个系数。我又统计了一些数字,乘以13再加4非常接近随机数,其它的数字我没试过。x13+4的结果,1出现的概率最大,到达20%左右,其它的数字出现概率都在7%-8%。还有1个是x13+7也非常趋近随机数。13是个不错的数字,因此,锐某将加密的函数名取名为“x13”,原先我起的名字是c16,就是cipher 16,16位密码的意思。

第四步,将种子字串插入到伪随机字串。将种子字串的每个字符与伪随机数的每个字符作为数值相加,组成一串新的字串。到这里字串已经是不可逆的了,你怎么知道一串数字是由哪些字相加而来的呢?

第五步,将伪随机字串以每5位数字为单位转成2进制字串。5位数字可以代表00000-99999范围的unicode字符,共有10万个字符(你还可以用6位、7位、8位等扩大字符的范围,unicode定义的码位有100多万,目前用到的字符只有10多万)。如果密码最终以unicode字符输出的话,输出一位数的密码就有10万种可能,如果输出32位就是10万的32次方,很天文哪~~~

第六步,将2进制字串的unicode字符的最后6位2进制字串转成一位64进制字符,这个接触过base64的网友都知道怎么操作了。

第七步,结束,输出。

引玉不抛砖是不行滴,下面晒一下源码吧!

rm.x13 = function(A, B, C) {//cipher x13, 密文生成器, 不同的明文可能有相同的密文
  // A:string, 明文
  // B:length, 输出的密文长度,默认为32位
  // C:type, 输出的密码类型(进制数:10, 16, 64, 99(00000-99999))
  var a, b, c, d, e, y, z;
  A = A || 'x13';
  B = B || 32;
  C = C || 64;
  if(B < 16) B = 16;
  e = B*5;// 每位密码用5位数的unicode码(00000-99999, 简称10万进制)的字符表示
  a = [];
  // 将明文转为整数,然后扩张为密码所需的字符量
  for(z = 0; z < A.length; z++) a.push(A.charCodeAt(z).toString(10));
  b = a.join('');
  c = b.length;
  d = c - 1;
  a = [];
  for(z = 0; z < c; z++,d--) a.push(b[z]+b[d]);// 打乱顺序
  A = a.join('');
  
  for(z = 0; z < e; z++) {// 用密码作为种子生成伪随机数
    b = a.join('');
    c = b.length;
    if(c > e) break;
    a = [];
    // 将每次的结果扩张,为了使结果趋近随机数以出现所有数字而乘以13+4
    for(y = 0; y < c; y++) a.push(b[y]*13+4);
  }
  
  a = [];
  d = A.length - 1;
  for(z = 0,y = 0; z < b.length; z++,y++) {// 将密码插入到伪随机数里
    if(y > d) y = 0;
    a.push(A[y]*1+b[z]*1);
  }
  
  b = a.join('');
  b = b.substr(b.length-e);
  c = b.length;
  a = [];
  if(C == 10) {
    for(z = 0; z < c; z+=5) a.push(b[z]);
  } else if(C == 16) {
    for(z = 0; z < c; z+=5) {
      // 将每5位整数转为16进制字串
      d = parseInt(b.substr(z, 5)).toString(16);
      d = d.substr(0, 1);
      a.push(d);
    }
  } else if(C == 64) {
    for(z = 0; z < c; z+=5) {
      // 将每5位整数转为2进制字串
      d = parseInt(b.substr(z, 5)).toString(2);
      f = d.length;
      switch(f) {
        case 0 : d = '000000'; break;
        case 1 : d += '00000'; break;
        case 2 : d += '0000'; break;
        case 3 : d += '000'; break;
        case 4 : d += '00'; break;
        case 5 : d += '0'; break;
        default : d = d.substr(f-6);// 取2进制的最后6位
      }
      a.push(d);
    }
    b = a.join('');
    a = [];
    // 返回64进制所对应的字符
    for(z = 0; z < b.length; z+=6) a.push(rm.b64[(b.substr(z, 6))]);
  } else {
    // 将每5位整数转为unicode(10万进制)所对应的字符
    for(z = 0; z < c; z+=5) a.push(String.fromCharCode(parseInt(b.substr(z, 5))));
  }
  
  return a.join('');
};


rm.b64是2进制到64进制的模板,这里就不提供了,rm是RuiMa(锐码)的缩写,这是锐某自己写的小型库。用上面的加密函数生成的密文:

明文 '' 的密文:apH13qwdBVgxUT2ZYH1TT1YQWXjAhxeK //空字符
明文 0 的密文:LJz5lho3_njd738v7thEnvmdyltlJLvk
明文 1 的密文:6_Y_3hBxbs_bdvZpx_LDONTj_Y7cDB5I
明文 2 的密文:E_2VV_4cJ1TEBpAfTG0o27WC05Lb4SuV
明文 3 的密文:jevH3P9Tb88fXKH0fWkzOjtv3KYRxPkk
明文 4 的密文:DzkkywlTsZM3LWu2BfXqjT2wF9_ZX9jb
明文 5 的密文:OppXhyDQyVvuHRF3SvPC95KInRk0loz6
明文 6 的密文:Cf1xUDVdh51jZWIVS7773V_Na7BYx-h-
明文 7 的密文:vICApPYCbhZE0fUeTPfIeLTICApZ15R6
明文 8 的密文:RItjKmbBPwpW3p6BPwN5rs72mp9chIRA
明文 9 的密文:CbDK-mPx1Db8sFjSgTz8EdleEQ_9jztI
明文 a 的密文:KlDXAL1w3QLwToDINn2EhxvXFDoLPsBE
明文 b 的密文:_ca_McdzKtwrr1hjX1QhLLBAv1hjX1Qh
明文 c 的密文:kBoxsvh1RrDc1ZN4URdNVhV7iDwnmixw
明文 d 的密文:LANLUB6Gla9mvbQhEf0ila9tiEl96x5N
明文 e 的密文:VXEseE60Kx15BtPQZ5ks8Q7QRHCS56Mv
明文 f 的密文:3aURVTGRV7vl97cw5N0_x5TdjvxFf3aU
明文 g 的密文:92ePCAwWrarUbMFCuJ-OB_RlSoTSqHTA
明文 h 的密文:b1dus_uRP1xlpsTxY9Wzao7LqFr90krR
明文 i 的密文:AkJriWUTwFcPAgOgP0Dw3-QoBrSVNn6L
明文 j 的密文:x-8bq7w_EFfJw7FslKOg3slP5YdnHUH2
明文 k 的密文:66a5-24xECUlAdSlGFsZbb1IPA5BrBd-
明文 l 的密文:DTfjidKYxFF-jEJbEzb5APjZ7wdF_8XQ
明文 m 的密文:rsXuJAZd2XJdFgk6P9G5vgF5DwIPQHGC
明文 n 的密文:6CEZ5I6qmyahyrYsumOR8pZCEIc2dCR8
明文 o 的密文:EkYP5y0jcPHleC_EGWka9WcMg8RASMuG
明文 p 的密文:AM6fRQRWUwiZ1BWshE_mIMUkxGfcgkUj
明文 q 的密文:GUiP8_-nu9X_wgfW2H96T_kR0Rk60fwO
明文 r 的密文:S4CROFNjkk9E3iaSzp1lpOIfy8UIoP_m
明文 s 的密文:ArnOuYzkKajBeBBAr1Y5OEkRKzQ5CLjO
明文 t 的密文:2TJZu4ZyslspZB6PKgpC9Q8VBr-dekKn
明文 u 的密文:-Sf1ynOZijapFvgxBJOlEak2y82Z5f64
明文 v 的密文:bN0DPJC1nOaMpu9RndbEfzYKydvk5bzp
明文 w 的密文:r2jndS5pwHnhZyPHxgjlwAhPGVzUHBbG
明文 x 的密文:3wAJlaTAVFb54jn-7cx2HQNbbhVSf0is
明文 y 的密文:SSEa1vojnAGXJ7_wo7k4R7d2shSQ4XvW
明文 z 的密文:5OSuopMyb4qvGVSPQUkcAUDV_529BjBp
明文 A 的密文:wQnhav9WzFUtchAKR27T4FzzdDN6K9xk
明文 B 的密文:Mf43VJt17QVKs_t17QV3jhhKe4XkyIf4
明文 C 的密文:FJx-ohBSf84w-BwTyghMoHdfyFLjTz1T
明文 D 的密文:n1__ZxRHARLRg53pH5dhHcrhdQUDuAWq
明文 E 的密文:pDH1DZ3MhJKXMa1MvpXMaNttwSbBF63t
明文 F 的密文:8txmvhHTvDwkjv5VvU2ElyI-VcylliUG
明文 G 的密文:zXMLzc6_w1esA2lO62T-LHZI9SiRamM0
明文 H 的密文:7JXVU_QJ2ayLR7pfgDHycd4F1duuFjA4
明文 I 的密文:YcytZ_Ftx9TfhJrh7ho02WMwzqJ-s_98
明文 J 的密文:Wzzv3mjv3m7KyLzK7lZiRTtnx4Vk3anW
明文 K 的密文:9GgAr1so-ATz6NZgqGTz6NZgqG7wCbDK
明文 L 的密文:y3Xe3u0FVL313ad_bA1jnGr_d1pghNVl
明文 M 的密文:DmiIP_j9PKdVdipDmiIP_j9PKdVdipDm
明文 N 的密文:Yf3xvhqkVRd4frrObrTWbn8ULtNOj3ln
明文 O 的密文:KX9xDjpTjXbfLppdHbfnMoLoakH_qJ90
明文 P 的密文:yst7BdLLUb-DnlkBfYli117uj7qOk9TS
明文 Q 的密文:ctnG77lh9O7KXX1h7l9FOEG_hb3BlnLL
明文 R 的密文:ANbKzeGz0NbKzeGz03_hVg6WgdtDBjlY
明文 S 的密文:1VZQHSn-MdihbkBBFUBb_f1VbXA1Dm-d
明文 T 的密文:OP_z58lw1JNFg5jLJR_JkejPpVvPlx_u
明文 U 的密文:GlJmkXd6E9_7xvx_dFyH5QB-HV9e3IaL
明文 V 的密文:hdzAHAv3Fi3tzpRZdJFaZo4tpy1sZlpG
明文 W 的密文:b9RhsdnXolrrQlb507Cx2Sd0BpnrTz1N
明文 X 的密文:_JjvXJZI169l_49wz3_JjvXJZI169l_4
明文 Y 的密文:xjx1TF--N50vLnnTCjZi1xnrTfTVT9jj
明文 Z 的密文:Ld7Zi7v1gxFdJVTimM7up9Z41STfDWju
明文 - 的密文:Jf0pg34WRd8fw3z_pm777sJTv1zBw7-4
明文 _ 的密文:3rHNve9_1W_o1V09_0HpDvfXvDMjvWlg


一开始锐某就说了,用这个生成的密码不是真唯一,我写了个碰撞程序扫描了一下4位数的32位密码(耗时将近3个小时),在16777216组密文里与上面64组密文有冲突的密文是:3,33指向同一组密文,7,77相同,I,II相同。如果谁的计算机够快的话可以测试一定范围的密文,看看有多少存在冲突。


碰撞程序,Node.js
exports.x13Afoul = function() {// x13 cipher afoul
  var a, b, c, d, e, f, k, o, p, q, r, s, t, u, v, w, x, y, z, cp, co;
  s = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_';
  cp = [];
  co = {};
  console.time('time');
  for(z = 0; z < s.length; z++) {
    b = exports.x13(s[z]);
    co[b] = [];
    cp.push(b);
  }
  
  a = [];
  for(z = 0; z < s.length; z++) {
  b = exports.x13(s[z]);
  for(x = 0; x < cp.length; x++) if(b == cp[x]) co[b].push(s[z]);
  
  for(y = 0; y < s.length; y++) {
  b = exports.x13(s[z]+s[y]);
  for(x = 0; x < cp.length; x++) if(b == cp[x]) co[b].push(s[z]+s[y]);
  
  for(w = 0; w < s.length; w++) {
  b = exports.x13(s[z]+s[y]+s[w]);
  for(x = 0; x < cp.length; x++) if(b == cp[x]) co[b].push(s[z]+s[y]+s[w]);
  
  for(v = 0; v < s.length; v++) {
  b = exports.x13(s[z]+s[y]+s[w]+s[v]);
  for(x = 0; x < cp.length; x++) if(b == cp[x]) co[b].push(s[z]+s[y]+s[w]+s[v]);
  
  /*for(u = 0; u < s.length; u++) {
  b = exports.x13(s[z]+s[y]+s[w]+s[v]+s[u]);
  for(x = 0; x < cp.length; x++) if(b == cp[x]) co[b].push(s[z]+s[y]+s[w]+s[v]+s[u]);
  
  for(t = 0; t < s.length; t++) {
  b = exports.x13(s[z]+s[y]+s[w]+s[v]+s[u]+s[t]);
  for(x = 0; x < cp.length; x++) if(b == cp[x]) co[b].push(s[z]+s[y]+s[w]+s[v]+s[u]+s[t]);
  
  for(s = 0; s < s.length; s++) {
  b = exports.x13(s[z]+s[y]+s[w]+s[v]+s[u]+s[t]+s[s]);
  for(x = 0; x < cp.length; x++) if(b == cp[x]) co[b].push(s[z]+s[y]+s[w]+s[v]+s[u]+s[t]+s[s]);
  
  for(r = 0; r < s.length; r++) {
  b = exports.x13(s[z]+s[y]+s[w]+s[v]+s[u]+s[t]+s[s]+s[r]);
  for(x = 0; x < cp.length; x++) if(b == cp[x]) co[b].push(s[z]+s[y]+s[w]+s[v]+s[u]+s[t]+s[s]+s[r]);
  
  }
  
  }
  }
  }*/
  }
  }
  }
  }
  
  a = [];
  for(z in co) a.push('密文 '+z+' 的明文列表:'+co[z].join(', '));
  s = a.join('\n');
  f = fs.openSync(rDir+'/x13.ttd', 'w+');
  fs.writeSync(f, s, 0, 'utf8');
  fs.closeSync(f);
  console.timeEnd('time');
};


最后探讨一下真唯一的密码生成方法,如果对每一串明文加上同一串字串,如此确实能实现真唯一,比如对a、ab、abc分别加上0123456789,达到足够长的密码为止,这样的密码肯定是唯一的,但是存在被人破译出明文的风险,而且密码的范围也被固定了,因为后面一段都是相同的。靠插值来实现随机化,在某个范围内应该会有冲突,但如果范围够大要找到冲突也是相当耗时的。只要使结果趋近真随机就能接近真唯一,像上面的x13得到的结果显然跟真随机差很远,因为它的结果都是由10个13的倍数连接起来的,虽说通过x13可以使结果出现所有的数字,但它们的顺序都差不多趋近相同。我们现在要做的就是如何用种子字串打乱这些顺序,或者通过某种公式将种子字串插进伪随机数字字符串里。对于3与33、7与77、I与II的密文相同令我感到很好奇,看了一下它们的unicode,恍然大悟!如下:

3=51 > 5115
33=5151 > 51155115
7=55 > 5555
77=5555 > 55555555
I=73 > 7337
II=7373 > 73377337

由于它们生成的伪随机数是一模一样的,所以它们相加的结果也一模一样。要解决这个问题也很简单,因为种子字串的长度不一样,当到达种子字串的终点时就倒过来从伪随机字串的后面取值,然后再到达终点又反过来从前面取,如此交替进行便能得到完全不同的结果。

修改后的程序已完全解决上面的问题

  a = [];
  c = 0;
  d = A.length - 1;
  x = b.length - 1;
  for(z = 0,y = 0; z < b.length; z++,y++,x--) {// 将密码插入到伪随机数里
    if(y > d) {
      y = 0;
      if(c == 0) c = 1;// 每当到达种子字串的终点就取反操作
      else c = 0;
    }
    if(c == 0) f = b[z]*1;
    else f = b[x]*1;
    a.push(A[y]*1+f);
  }
  

如果你有更好的方法或观点,请踊跃发表自己的论点,也许我们能推导出某种公式来生成真正的随机数或真正的唯一数。

你可能感兴趣的:(MD5,加密,x13)