333
schangxiang@126.com
2025-09-19 18966e02fb573c7e2bb0c6426ed792b38b910940
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
'use strict';
 
const debug = require('debug')('egg-cookies:keygrip');
const crypto = require('crypto');
const assert = require('assert');
const constantTimeCompare = require('scmp');
 
const replacer = {
  '/': '_',
  '+': '-',
  '=': '',
};
 
// patch from https://github.com/crypto-utils/keygrip
 
class Keygrip {
  constructor(keys) {
    assert(Array.isArray(keys) && keys.length, 'keys must be provided and should be an array');
 
    this.keys = keys;
    this.hash = 'sha256';
    this.cipher = 'aes-256-cbc';
  }
 
  // encrypt a message
  encrypt(data, key) {
    key = key || this.keys[0];
    const cipher = crypto.createCipher(this.cipher, key);
    return crypt(cipher, data);
  }
 
  // decrypt a single message
  // returns false on bad decrypts
  decrypt(data, key) {
    if (!key) {
      // decrypt every key
      const keys = this.keys;
      for (let i = 0; i < keys.length; i++) {
        const value = this.decrypt(data, keys[i]);
        if (value !== false) return { value, index: i };
      }
      return false;
    }
 
    try {
      const cipher = crypto.createDecipher(this.cipher, key);
      return crypt(cipher, data);
    } catch (err) {
      debug('crypt error', err.stack);
      return false;
    }
  }
 
  sign(data, key) {
    // default to the first key
    key = key || this.keys[0];
 
    return crypto
      .createHmac(this.hash, key)
      .update(data)
      .digest('base64')
      .replace(/\/|\+|=/g, x => replacer[x]);
  }
 
  verify(data, digest) {
    const keys = this.keys;
    for (let i = 0; i < keys.length; i++) {
      if (constantTimeCompare(new Buffer(digest), new Buffer(this.sign(data, keys[i])))) {
        debug('data %s match key %s', data, keys[i]);
        return i;
      }
    }
    return -1;
  }
}
 
function crypt(cipher, data) {
  const text = cipher.update(data, 'utf8');
  const pad = cipher.final();
  return Buffer.concat([ text, pad ]);
}
 
module.exports = Keygrip;