Jelajahi Sumber

remove hash.js

lloydzhou 1 tahun lalu
induk
melakukan
1610675c8f
4 mengubah file dengan 251 tambahan dan 29 penghapusan
  1. 246 0
      app/utils/hmac.ts
  2. 4 14
      app/utils/tencent.ts
  3. 0 1
      package.json
  4. 1 14
      yarn.lock

+ 246 - 0
app/utils/hmac.ts

@@ -0,0 +1,246 @@
+// From https://gist.github.com/guillermodlpa/f6d955f838e9b10d1ef95b8e259b2c58
+// From https://gist.github.com/stevendesu/2d52f7b5e1f1184af3b667c0b5e054b8
+
+// To ensure cross-browser support even without a proper SubtleCrypto
+// impelmentation (or without access to the impelmentation, as is the case with
+// Chrome loaded over HTTP instead of HTTPS), this library can create SHA-256
+// HMAC signatures using nothing but raw JavaScript
+
+/* eslint-disable no-magic-numbers, id-length, no-param-reassign, new-cap */
+
+// By giving internal functions names that we can mangle, future calls to
+// them are reduced to a single byte (minor space savings in minified file)
+const uint8Array = Uint8Array;
+const uint32Array = Uint32Array;
+const pow = Math.pow;
+
+// Will be initialized below
+// Using a Uint32Array instead of a simple array makes the minified code
+// a bit bigger (we lose our `unshift()` hack), but comes with huge
+// performance gains
+const DEFAULT_STATE = new uint32Array(8);
+const ROUND_CONSTANTS: number[] = [];
+
+// Reusable object for expanded message
+// Using a Uint32Array instead of a simple array makes the minified code
+// 7 bytes larger, but comes with huge performance gains
+const M = new uint32Array(64);
+
+// After minification the code to compute the default state and round
+// constants is smaller than the output. More importantly, this serves as a
+// good educational aide for anyone wondering where the magic numbers come
+// from. No magic numbers FTW!
+function getFractionalBits(n: number) {
+  return ((n - (n | 0)) * pow(2, 32)) | 0;
+}
+
+let n = 2;
+let nPrime = 0;
+while (nPrime < 64) {
+  // isPrime() was in-lined from its original function form to save
+  // a few bytes
+  let isPrime = true;
+  // Math.sqrt() was replaced with pow(n, 1/2) to save a few bytes
+  // var sqrtN = pow(n, 1 / 2);
+  // So technically to determine if a number is prime you only need to
+  // check numbers up to the square root. However this function only runs
+  // once and we're only computing the first 64 primes (up to 311), so on
+  // any modern CPU this whole function runs in a couple milliseconds.
+  // By going to n / 2 instead of sqrt(n) we net 8 byte savings and no
+  // scaling performance cost
+  for (let factor = 2; factor <= n / 2; factor++) {
+    if (n % factor === 0) {
+      isPrime = false;
+    }
+  }
+  if (isPrime) {
+    if (nPrime < 8) {
+      DEFAULT_STATE[nPrime] = getFractionalBits(pow(n, 1 / 2));
+    }
+    ROUND_CONSTANTS[nPrime] = getFractionalBits(pow(n, 1 / 3));
+
+    nPrime++;
+  }
+
+  n++;
+}
+
+// For cross-platform support we need to ensure that all 32-bit words are
+// in the same endianness. A UTF-8 TextEncoder will return BigEndian data,
+// so upon reading or writing to our ArrayBuffer we'll only swap the bytes
+// if our system is LittleEndian (which is about 99% of CPUs)
+const LittleEndian = !!new uint8Array(new uint32Array([1]).buffer)[0];
+
+function convertEndian(word: number) {
+  if (LittleEndian) {
+    return (
+      // byte 1 -> byte 4
+      (word >>> 24) |
+      // byte 2 -> byte 3
+      (((word >>> 16) & 0xff) << 8) |
+      // byte 3 -> byte 2
+      ((word & 0xff00) << 8) |
+      // byte 4 -> byte 1
+      (word << 24)
+    );
+  } else {
+    return word;
+  }
+}
+
+function rightRotate(word: number, bits: number) {
+  return (word >>> bits) | (word << (32 - bits));
+}
+
+function sha256(data: Uint8Array) {
+  // Copy default state
+  const STATE = DEFAULT_STATE.slice();
+
+  // Caching this reduces occurrences of ".length" in minified JavaScript
+  // 3 more byte savings! :D
+  const legth = data.length;
+
+  // Pad data
+  const bitLength = legth * 8;
+  const newBitLength = 512 - ((bitLength + 64) % 512) - 1 + bitLength + 65;
+
+  // "bytes" and "words" are stored BigEndian
+  const bytes = new uint8Array(newBitLength / 8);
+  const words = new uint32Array(bytes.buffer);
+
+  bytes.set(data, 0);
+  // Append a 1
+  bytes[legth] = 0b10000000;
+  // Store length in BigEndian
+  words[words.length - 1] = convertEndian(bitLength);
+
+  // Loop iterator (avoid two instances of "var") -- saves 2 bytes
+  let round;
+
+  // Process blocks (512 bits / 64 bytes / 16 words at a time)
+  for (let block = 0; block < newBitLength / 32; block += 16) {
+    const workingState = STATE.slice();
+
+    // Rounds
+    for (round = 0; round < 64; round++) {
+      let MRound;
+      // Expand message
+      if (round < 16) {
+        // Convert to platform Endianness for later math
+        MRound = convertEndian(words[block + round]);
+      } else {
+        const gamma0x = M[round - 15];
+        const gamma1x = M[round - 2];
+        MRound =
+          M[round - 7] +
+          M[round - 16] +
+          (rightRotate(gamma0x, 7) ^
+            rightRotate(gamma0x, 18) ^
+            (gamma0x >>> 3)) +
+          (rightRotate(gamma1x, 17) ^
+            rightRotate(gamma1x, 19) ^
+            (gamma1x >>> 10));
+      }
+
+      // M array matches platform endianness
+      M[round] = MRound |= 0;
+
+      // Computation
+      const t1 =
+        (rightRotate(workingState[4], 6) ^
+          rightRotate(workingState[4], 11) ^
+          rightRotate(workingState[4], 25)) +
+        ((workingState[4] & workingState[5]) ^
+          (~workingState[4] & workingState[6])) +
+        workingState[7] +
+        MRound +
+        ROUND_CONSTANTS[round];
+      const t2 =
+        (rightRotate(workingState[0], 2) ^
+          rightRotate(workingState[0], 13) ^
+          rightRotate(workingState[0], 22)) +
+        ((workingState[0] & workingState[1]) ^
+          (workingState[2] & (workingState[0] ^ workingState[1])));
+      for (let i = 7; i > 0; i--) {
+        workingState[i] = workingState[i - 1];
+      }
+      workingState[0] = (t1 + t2) | 0;
+      workingState[4] = (workingState[4] + t1) | 0;
+    }
+
+    // Update state
+    for (round = 0; round < 8; round++) {
+      STATE[round] = (STATE[round] + workingState[round]) | 0;
+    }
+  }
+
+  // Finally the state needs to be converted to BigEndian for output
+  // And we want to return a Uint8Array, not a Uint32Array
+  return new uint8Array(
+    new uint32Array(
+      STATE.map(function (val) {
+        return convertEndian(val);
+      }),
+    ).buffer,
+  );
+}
+
+function hmac(key: Uint8Array, data: ArrayLike<number>) {
+  if (key.length > 64) key = sha256(key);
+
+  if (key.length < 64) {
+    const tmp = new Uint8Array(64);
+    tmp.set(key, 0);
+    key = tmp;
+  }
+
+  // Generate inner and outer keys
+  const innerKey = new Uint8Array(64);
+  const outerKey = new Uint8Array(64);
+  for (let i = 0; i < 64; i++) {
+    innerKey[i] = 0x36 ^ key[i];
+    outerKey[i] = 0x5c ^ key[i];
+  }
+
+  // Append the innerKey
+  const msg = new Uint8Array(data.length + 64);
+  msg.set(innerKey, 0);
+  msg.set(data, 64);
+
+  // Has the previous message and append the outerKey
+  const result = new Uint8Array(64 + 32);
+  result.set(outerKey, 0);
+  result.set(sha256(msg), 64);
+
+  // Hash the previous message
+  return sha256(result);
+}
+
+// Convert a string to a Uint8Array, SHA-256 it, and convert back to string
+const encoder = new TextEncoder();
+
+export function sign(
+  inputKey: string | Uint8Array,
+  inputData: string | Uint8Array,
+) {
+  const key =
+    typeof inputKey === "string" ? encoder.encode(inputKey) : inputKey;
+  const data =
+    typeof inputData === "string" ? encoder.encode(inputData) : inputData;
+  return hmac(key, data);
+}
+
+export function hex(bin: Uint8Array) {
+  return bin.reduce((acc, val) => {
+    const hexVal = "00" + val.toString(16);
+    return acc + hexVal.substring(hexVal.length - 2);
+  }, "");
+}
+
+export function hash(str: string) {
+  return hex(sha256(encoder.encode(str)));
+}
+
+export function hashWithSecret(str: string, secret: string) {
+  return hex(sign(secret, str)).toString();
+}

+ 4 - 14
app/utils/tencent.ts

@@ -1,19 +1,9 @@
-import hash from "hash.js";
+import { sign, hash as getHash, hex } from "./hmac";
 
 // 使用 SHA-256 和 secret 进行 HMAC 加密
-function sha256(message: any, secret = "", encoding?: string) {
-  return hash
-    .hmac(hash.sha256 as any, secret)
-    .update(message)
-    .digest(encoding as any);
-}
-
-// 使用 SHA-256 进行哈希
-function getHash(message: any, encoding = "hex") {
-  return hash
-    .sha256()
-    .update(message)
-    .digest(encoding as any);
+function sha256(message: any, secret: any, encoding?: string) {
+  const result = sign(secret, message);
+  return encoding == "hex" ? hex(result).toString() : result;
 }
 
 function getDate(timestamp: number) {

+ 0 - 1
package.json

@@ -26,7 +26,6 @@
     "@vercel/speed-insights": "^1.0.2",
     "emoji-picker-react": "^4.9.2",
     "fuse.js": "^7.0.0",
-    "hash.js": "^1.1.7",
     "heic2any": "^0.0.4",
     "html-to-image": "^1.11.11",
     "lodash-es": "^4.17.21",

+ 1 - 14
yarn.lock

@@ -3799,14 +3799,6 @@ has@^1.0.3:
   dependencies:
     function-bind "^1.1.1"
 
-hash.js@^1.1.7:
-  version "1.1.7"
-  resolved "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42"
-  integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==
-  dependencies:
-    inherits "^2.0.3"
-    minimalistic-assert "^1.0.1"
-
 hast-util-from-dom@^4.0.0:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/hast-util-from-dom/-/hast-util-from-dom-4.2.0.tgz#25836ddecc3cc0849d32749c2a7aec03e94b59a7"
@@ -3970,7 +3962,7 @@ inflight@^1.0.4:
     once "^1.3.0"
     wrappy "1"
 
-inherits@2, inherits@^2.0.3:
+inherits@2:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
   integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -4962,11 +4954,6 @@ mimic-fn@^4.0.0:
   resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc"
   integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==
 
-minimalistic-assert@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
-  integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
-
 minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"