CipheredBase32.php 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. <?php
  2. namespace loyalsoft;
  3. /**
  4. * 激活码数据结构
  5. */
  6. class ActiveCode {
  7. /**
  8. * 平台编号
  9. * @var int
  10. */
  11. public $plat;
  12. /**
  13. * 礼包编号
  14. * @var int
  15. */
  16. public $package;
  17. /**
  18. * 编号
  19. * @var int
  20. */
  21. public $number;
  22. }
  23. /**
  24. * 在定制的Base32(去掉了 0 和 o, 1 和 l. 使用剩余的 24个小写字母和8个数字)基础上使用
  25. * 密码本算法打乱字母规律, 并对number进行了散列, 增加猜解难度.
  26. * @author gwang 2016.2
  27. */
  28. class CipheredBase32 {
  29. const codeLength = 10; # 兑换码的长度固定为10
  30. /**
  31. * 密码本
  32. * @return string
  33. */
  34. public static function charMap() {
  35. return GameConstants::GetCipherString(); # 密码本
  36. }
  37. /**
  38. * 解码
  39. * @param string $codeString
  40. * @return \ActiveCode
  41. * @throws \Exception
  42. */
  43. public static function Decode($codeString) {
  44. if (self::codeLength != strlen($codeString)) { # 校验兑换码字符串长度
  45. return "activecode length wrong!";
  46. } else {
  47. $bytesof5bits = array(); # 将字符串转为索引数组:=[0..32]{10}
  48. $cipher = self::charMap();
  49. for ($i = 0; $i < self::codeLength; $i++) {
  50. $c = $codeString[$i]; # 取字符
  51. $b = strpos($cipher, $c); # 按照密码本还原真实字符的索引
  52. if ($b < 32 && $b > -1) { # 索引范围校验
  53. $bytesof5bits[] = $b;
  54. } else {
  55. // CLogUtil::console("b ($b)is not a valid index ");
  56. }
  57. }
  58. $by8bits = self::fetch5BitByte($bytesof5bits); # 将索引数组转为字节数组:=[0..256]{5}
  59. $activeCode = new ActiveCode(); # 创建对象
  60. $activeCode->plat = $by8bits[1]; # 第一个字节: 平台
  61. $activeCode->package = $by8bits[3]; # 第二个字节: 礼包编号
  62. $i = 0; # 临时变量
  63. $i += ($by8bits[0] ^ $by8bits[3] ^ $by8bits[1]); # 剩余字节为兑换码的编号, 低8位
  64. $i += ( $by8bits[4] ^ $by8bits[3] ^ $by8bits[1] ) << 8; # 高8位
  65. $i += ($by8bits[2] ^ $by8bits[3] ^ $by8bits[1] ) << 16; # 第17-24位
  66. $i += ($by8bits[5] ^ $by8bits[3] ^ $by8bits[1] ) << 24; # 第25-32位
  67. $i = self::uint32val($i); # 提取编号(32位环境,大int值会转为float进行运算)
  68. $i = 4294967295 - $i; # 补码才是真正的编号
  69. $i /= 85889; # 编号再除以一个特殊值才还原为最终编号
  70. if (intval($i) - $i == 0) { # 确认是合法整数
  71. $activeCode->number = intval($i);
  72. }
  73. return $activeCode;
  74. }
  75. }
  76. /**
  77. * php本身没有unint类型, 网上找的,具体代码看不懂--gwang
  78. * @param type $var
  79. * @return type
  80. */
  81. static function uint32val($var) {
  82. if (is_string($var)) { # 字符串
  83. if (PHP_INT_MAX > 2147483647) { # 大于32位
  84. $var = intval($var);
  85. } else { # 利用float来避免数值较大时损失高位信息
  86. $var = floatval($var);
  87. }
  88. }
  89. if (!is_int($var)) {
  90. $var = intval($var); # 再次强转int
  91. }
  92. if ((0 > $var) || ($var > 4294967295)) {
  93. $var &= 4294967295; # uint32最大值
  94. if (0 > $var) {
  95. $var = sprintf('%u', $var); # 转为uint(string)
  96. }
  97. }
  98. return $var;
  99. }
  100. /**
  101. * 转换 10个 5bit Bytes 到 6个 8bit Bytes
  102. * @param array $data 长度不小于10的字节数组
  103. * @return array
  104. */
  105. static function fetch5BitByte($data) {
  106. $ret5 = array();
  107. $ret5[0] = ( $data[0] + intval(( $data[1] & 0x7) << 5)); # 5 + 3 = 8
  108. $ret5[1] = ( intval(($data[1] & 0x18 ) >> 3) # 2 + 5 + 1 = 8
  109. + intval($data[2] << 2) + intval(($data[3] & 0x1) << 7));
  110. $ret5[2] = ( intval(($data[3] & 0x1E) >> 1) # 4 + 4 = 8
  111. + intval(($data[4] & 0xf) << 4));
  112. $ret5[3] = ( intval(($data[4] & 0x10) >> 4) # 1 + 5 + 2 = 8
  113. + intval($data[5] << 1) + intval(($data[6] & 0x3) << 6));
  114. $ret5[4] = ( intval(($data[6] & 0x1C) >> 2) + intval($data[7] << 3)); # 3 + 5 = 8
  115. $ret5[5] = ( intval(($data[8]) + intval($data[9] << 5))); # 5 + 3 = 8
  116. return $ret5;
  117. }
  118. }