authcode.js 2.79 KB
'use strict';
const md5 = require('md5');

const microtime = function() {
  let unixtimeMs = new Date().getTime();
  let sec = parseInt(unixtimeMs / 1000, 10);

  return (unixtimeMs - (sec * 1000)) / 1000 + ' ' + sec;
};

const getTimestamp = function() {
  let unixtimeMs = new Date().getTime();

  return parseInt(unixtimeMs / 1000, 10);
};

module.exports = function(str, key, expiry, operation) {
  operation = operation ? operation : 'decode';
  key = key ? key : '';
  expiry = expiry ? expiry : 0;
  let tmpstr, tmp;

  let ckeyLength = 4;

  key = md5(key);

  // 密匙a会参与加解密
  let keya = md5(key.substr(0, 16));

  // 密匙b会用来做数据完整性验证
  let keyb = md5(key.substr(16, 16));

  // 密匙c用于变化生成的密文
  let keyc = operation === 'decode' ?
    str.substr(0, ckeyLength) : md5(microtime()).substr(-ckeyLength);

  // 参与运算的密匙
  let cryptkey = keya + md5(keya + keyc);

  let strbuf;

  if (operation === 'decode') {
    str = str.substr(ckeyLength);
    strbuf = new Buffer(str, 'base64');

    // string = b.toString();
  } else {
    expiry = expiry ? expiry + getTimestamp() : 0;
    tmpstr = expiry.toString();
    if (tmpstr.length >= 10) {
      str = tmpstr.substr(0, 10) + md5(str + keyb).substr(0, 16) + str;
    } else {
      let count = 10 - tmpstr.length;

      for (let i = 0; i < count; i++) {
        tmpstr = '0' + tmpstr;
      }
      str = tmpstr + md5(str + keyb).substr(0, 16) + str;
    }
    strbuf = new Buffer(str);
  }


  let box = new Array(256);
  let rndkey = [];

  for (let i = 0; i < 256; i++) {
    box[i] = i;

    // 产生密匙簿
    rndkey[i] = cryptkey.charCodeAt(i % cryptkey.length);
  }

  // 用固定的算法,打乱密匙簿,增加随机性,好像很复杂,实际上对并不会增加密文的强度
  for (let j = 0, i = 0; i < 256; i++) {
    j = (j + box[i] + rndkey[i]) % 256;
    tmp = box[i];
    box[i] = box[j];
    box[j] = tmp;
  }


  // 核心加解密部分
  let s = '';

  for (let a = 0, j = 0, i = 0; i < strbuf.length; i++) {
    a = (a + 1) % 256;
    j = (j + box[a]) % 256;
    tmp = box[a];
    box[a] = box[j];
    box[j] = tmp;

    // 从密匙簿得出密匙进行异或,再转成字符
    // s += String.fromCharCode(string[i] ^ (box[(box[a] + box[j]) % 256]));
    /* jshint -W016*/
    strbuf[i] = strbuf[i] ^ (box[(box[a] + box[j]) % 256]);
  }

  if (operation === 'decode') {
    s = strbuf.toString();
    if ((s.substr(0, 10) === '0'.repeat(10) ||
                s.substr(0, 10) - getTimestamp() > 0) &&
            s.substr(10, 16) === md5(s.substr(26) + keyb).substr(0, 16)) {
      s = s.substr(26);
    } else {
      s = '';
    }
  } else {
    s = strbuf.toString('base64');
    let regex = new RegExp('=', 'g');

    s = s.replace(regex, '');
    s = keyc + s;
  }

  return s;
};