tinyhmacmd5 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +78 -4
  2. package/main.d.ts +12 -4
  3. package/main.js +16 -15
  4. package/package.json +14 -4
package/README.md CHANGED
@@ -1,15 +1,27 @@
1
+ English | [中文](README-zh.md)
2
+
1
3
  # Tiny HMAC MD5
2
4
 
3
- A tiny, reliable JavaScript HMAC-MD5 implementation.
5
+ A tiny, fully compliant HMAC-MD5 implementation for JavaScript:
6
+ [`browser.min.js`](browser.min.js) is only **1057 bytes**.
7
+
8
+ - **Input type**: `string` (UTF‑8), `Uint8Array` or `Uint8ClampedArray`
9
+ - **Output type**: hex `string` or raw `Uint8Array`
10
+ - **Supports inputs ≥ 512 MiB**: Full 64‑bit length padding per RFC 1321
11
+ - **TypeScript‑ready**: [`main.d.ts`](main.d.ts)
12
+ - **0 dependencies**
4
13
 
5
- The minified browser bundle ([`browser.min.js`](browser.min.js)) is only 1057 bytes.
14
+ **Live demo**: https://bddjr.github.io/tinyhmacmd5/
6
15
 
7
- Preview: https://bddjr.github.io/tinyhmacmd5/
16
+ > [!WARNING]
17
+ > MD5 is cryptographically broken and unsafe for security-sensitive applications.
18
+ > Do not rely on it for password hashing, digital signatures, or certificate verification.
8
19
 
9
20
  ## Setup
10
21
 
11
22
  ### npm
12
23
 
24
+
13
25
  ```
14
26
  npm i tinyhmacmd5
15
27
  ```
@@ -18,6 +30,10 @@ npm i tinyhmacmd5
18
30
  import md5 from "tinyhmacmd5";
19
31
  ```
20
32
 
33
+ ### Other Package Managers
34
+
35
+ You can also use other package managers (e.g. `pnpm` or `yarn`) in place of `npm`.
36
+
21
37
  ### jsDelivr
22
38
 
23
39
  See https://www.jsdelivr.com/package/npm/tinyhmacmd5
@@ -26,10 +42,22 @@ See https://www.jsdelivr.com/package/npm/tinyhmacmd5
26
42
  <script src="https://cdn.jsdelivr.net/npm/tinyhmacmd5"></script>
27
43
  ```
28
44
 
45
+ ### Inline
46
+
47
+ You can embed [`browser.min.js`](browser.min.js) directly into your script.
48
+
49
+ It will define the `md5` function using `var`.
50
+
29
51
  ---
30
52
 
31
53
  ## Example
32
54
 
55
+ > [!NOTE]
56
+ > This library does not validate input types.
57
+ > Please ensure the input type matches the definitions in [`main.d.ts`](main.d.ts).
58
+ > TypeScript will catch mismatches at compile time (unless you use `any`).
59
+ > Invalid types may produce an incorrect MD5 hash.
60
+
33
61
  HMAC-MD5:
34
62
 
35
63
  ```js
@@ -50,7 +78,7 @@ md5(Uint8Array.of(1, 2, 3), Uint8Array.of(4, 5, 6))
50
78
  md5(Uint8Array.of(1, 2, 3), Uint8Array.of(4, 5, 6), true)
51
79
  ```
52
80
 
53
- MD5:
81
+ MD5: (when `key` is `null` or `undefined`)
54
82
 
55
83
  ```js
56
84
  // string to hex
@@ -72,6 +100,52 @@ md5(Uint8Array.of(1, 2, 3), null, true)
72
100
 
73
101
  ---
74
102
 
103
+ ## Runtime Environment
104
+
105
+ Environments that support [Nullish coalescing assignment (`??=`)](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_assignment) :
106
+
107
+ - Chrome ≥ 85 (2020-08-25)
108
+ - Edge ≥ 85 (2020-08-27)
109
+ - Firefox ≥ 79 (2020-07-28)
110
+ - Safari ≥ 14 (2020-09-16)
111
+
112
+ ---
113
+
114
+ ## Why I Made This Project
115
+
116
+ > [!NOTE]
117
+ > AI-translated from Chinese.
118
+ > [查看原文](README-zh.md#为什么做这个项目)
119
+
120
+ I initially used HMAC-MD5 just to call a certain website's API. That site signs the request body with `crypto-js`'s HMAC-MD5 to make reverse engineering harder.
121
+
122
+ But I only needed HMAC-MD5. Depending on `crypto-js` felt way too bloated. The Web Crypto API doesn't support HMAC-MD5, so I had no choice but to drag in a dependency.
123
+
124
+ Then I found `blueimp-md5`. Its `md5.min.js` is only 3750 bytes, even smaller than `js-md5`.
125
+
126
+ But I felt `blueimp-md5` was still far too bloated. It has some completely unnecessary design choices—it repeatedly parses strings and creates new ones, adding a lot of unnecessary overhead.
127
+
128
+ So I decided to adapt it, use a more modern implementation, and shrink the size even further. That's how `tinyhmacmd5` was born.
129
+
130
+ During the adaptation, I discovered that `blueimp-md5` did not correctly handle the 64-bit length field required by MD5 when the input bit-length exceeded 32 bits. It wrote only the low 32 bits and ignored the high 32 bits. I fixed this bug.
131
+
132
+ I also found that using `Array` to process inputs over 512 MiB could throw a `RangeError`, so I replaced it with `Int32Array`.
133
+
134
+ I demonstrated in practice that HMAC-MD5 can be implemented in an extremely small footprint (1057 bytes) while also improving reliability.
135
+
136
+ Maybe not many people care about saving just a few KB, but `tinyhmacmd5` exists precisely to "explore the unknown".
137
+
138
+ INVINCIBLE EXPERIMENT!
139
+
140
+ ---
141
+
142
+ ## License
143
+
144
+ The MIT License
145
+ See: [`LICENSE`](LICENSE)
146
+
147
+ ---
148
+
75
149
  ## Clone
76
150
 
77
151
  ```
package/main.d.ts CHANGED
@@ -4,13 +4,21 @@
4
4
  * By default, returns the hash as a lowercase hexadecimal string.
5
5
  * If `raw` is true, returns a Uint8Array.
6
6
  *
7
- * @param {string | Uint8Array} data The input data to hash. Strings are UTF‑8 encoded.
8
- * @param {string | Uint8Array | null} [key] Optional HMAC key. When given, HMAC‑MD5 is calculated instead of plain MD5.
7
+ * @param {string | Uint8Array | Uint8ClampedArray} data The input data to hash. Strings are UTF‑8 encoded.
8
+ * @param {string | Uint8Array | Uint8ClampedArray | null} [key] Optional HMAC key. When given, HMAC‑MD5 is calculated instead of plain MD5.
9
9
  * @param {boolean} [raw] If true, the hash is returned as raw bytes (Uint8Array); otherwise, as a hex string.
10
10
  * @returns {string | Uint8Array<ArrayBuffer>} The MD5 (or HMAC‑MD5) digest, either as a hex string or a Uint8Array.
11
11
  */
12
12
  declare var md5: {
13
- (data: string | Uint8Array, key?: string | Uint8Array | null, raw?: false): string;
14
- (data: string | Uint8Array, key: string | Uint8Array | null | undefined, raw: true): Uint8Array<ArrayBuffer>;
13
+ (
14
+ data: string | Uint8Array | Uint8ClampedArray,
15
+ key?: string | Uint8Array | Uint8ClampedArray | null,
16
+ raw?: false
17
+ ): string;
18
+ (
19
+ data: string | Uint8Array | Uint8ClampedArray,
20
+ key: string | Uint8Array | Uint8ClampedArray | null | undefined,
21
+ raw: true
22
+ ): Uint8Array<ArrayBuffer>;
15
23
  };
16
24
  export default md5;
package/main.js CHANGED
@@ -64,10 +64,10 @@ let binlMD5 = (
64
64
  (l = j >> 4 << 2)
65
65
  ? l > 4
66
66
  ? l > 8
67
- ? t1 ^ (t0 | ~t2) // Round 4: II
68
- : t0 ^ t1 ^ t2 // Round 3: HH
69
- : t0 & t2 | t1 & ~t2 // Round 2: GG
70
- : t0 & t1 | ~t0 & t2 // Round 1: FF
67
+ ? t1 ^ (t0 | ~t2) // Round 4: I
68
+ : t0 ^ t1 ^ t2 // Round 3: H
69
+ : t0 & t2 | t1 & ~t2 // Round 2: G
70
+ : t0 & t1 | ~t0 & t2 // Round 1: F
71
71
  ) +
72
72
  (
73
73
  0 | x[i + (j * (0x7351 >> l) + (0x0510 >> l) & 15)]
@@ -106,7 +106,8 @@ let inputToBinl = (
106
106
  ? input = new TextEncoder().encode(input)
107
107
  : input
108
108
  ).length,
109
- // Using Array would fail to handle large files, so Int32Array must be used.
109
+ // Using `Array` to process inputs over 512 MiB could throw a `RangeError`,
110
+ // so I replaced it with `Int32Array`.
110
111
  output = new Int32Array(toBinlLen(j * 4 + byteLen)),
111
112
  i = 0,
112
113
  ) => {
@@ -125,15 +126,15 @@ let inputToBinl = (
125
126
 
126
127
  /**
127
128
  * @overload
128
- * @param {string | Uint8Array} data
129
- * @param {string | Uint8Array | null} [key]
129
+ * @param {string | Uint8Array | Uint8ClampedArray} data
130
+ * @param {string | Uint8Array | Uint8ClampedArray | null} [key]
130
131
  * @param {false} [raw]
131
132
  * @returns {string}
132
133
  */
133
134
  /**
134
135
  * @overload
135
- * @param {string | Uint8Array} data
136
- * @param {string | Uint8Array | null | undefined} key
136
+ * @param {string | Uint8Array | Uint8ClampedArray} data
137
+ * @param {string | Uint8Array | Uint8ClampedArray | null | undefined} key
137
138
  * @param {true} raw
138
139
  * @returns {Uint8Array<ArrayBuffer>}
139
140
  */
@@ -143,8 +144,8 @@ let inputToBinl = (
143
144
  * By default, returns the hash as a lowercase hexadecimal string.
144
145
  * If `raw` is true, returns a Uint8Array.
145
146
  *
146
- * @param {string | Uint8Array} data The input data to hash. Strings are UTF‑8 encoded.
147
- * @param {string | Uint8Array | null} [key] Optional HMAC key. When given, HMAC‑MD5 is calculated instead of plain MD5.
147
+ * @param {string | Uint8Array | Uint8ClampedArray} data The input data to hash. Strings are UTF‑8 encoded.
148
+ * @param {string | Uint8Array | Uint8ClampedArray | null} [key] Optional HMAC key. When given, HMAC‑MD5 is calculated instead of plain MD5.
148
149
  * @param {boolean} [raw] If true, the hash is returned as raw bytes (Uint8Array); otherwise, as a hex string.
149
150
  * @returns {string | Uint8Array<ArrayBuffer>} The MD5 (or HMAC‑MD5) digest, either as a hex string or a Uint8Array.
150
151
  */
@@ -157,17 +158,17 @@ var md5 = (data, key, raw) => {
157
158
  if (hasKey) {
158
159
  // HMAC
159
160
  let [bkey, keyByteLen] = inputToBinl(key, 0)
160
- let pad5cArr = []
161
+ let opad = []
161
162
  if (keyByteLen > 64) {
162
163
  bkey = binlMD5(bkey, keyByteLen)
163
164
  }
164
165
  for (; i;) {
165
- bdata[--i] = 0x36363636 ^ bkey[i]
166
- pad5cArr[i] = 0x5c5c5c5c ^ bkey[i]
166
+ bdata[--i] = 0x36363636 ^ bkey[i] // ipad
167
+ opad[i] = 0x5c5c5c5c ^ bkey[i]
167
168
  }
168
169
  i = 16
169
170
  bdata = binlMD5(bdata, 64 + dataByteLen)
170
- bdata.unshift(...pad5cArr)
171
+ bdata.unshift(...opad)
171
172
  dataByteLen = 80
172
173
  }
173
174
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tinyhmacmd5",
3
- "version": "0.1.0",
4
- "description": "A tiny, reliable JavaScript HMAC-MD5 implementation. The minified browser bundle is only 1057 bytes.",
3
+ "version": "0.1.2",
4
+ "description": "A tiny, fully compliant HMAC-MD5 implementation for JavaScript: browser.min.js is only 1057 bytes.",
5
5
  "author": "bddjr",
6
6
  "license": "MIT",
7
7
  "type": "module",
@@ -16,14 +16,21 @@
16
16
  "homepage": "https://bddjr.github.io/tinyhmacmd5/",
17
17
  "keywords": [
18
18
  "md5",
19
+ "hash",
19
20
  "hmac",
20
21
  "hmacmd5",
21
22
  "hmac-md5",
22
23
  "tiny",
23
24
  "hex",
24
25
  "crypto",
26
+ "encryption",
27
+ "cryptography",
25
28
  "blueimp",
26
- "blueimp-md5"
29
+ "blueimp-md5",
30
+ "js-md5",
31
+ "crypto-js",
32
+ "javascript",
33
+ "typescript"
27
34
  ],
28
35
  "repository": {
29
36
  "type": "git",
@@ -37,6 +44,9 @@
37
44
  },
38
45
  "scripts": {
39
46
  "build": "tsc && node scripts/build.mjs && es-check es2021 --module --checkFeatures main.js browser.min.js",
40
- "test": "pnpm build && node scripts/test.mjs"
47
+ "build:unchanged": "node scripts/build-unchanged.mjs",
48
+ "test": "pnpm build && pnpm test:only",
49
+ "test:only": "node scripts/test.mjs",
50
+ "test:unchanged": "pnpm build:unchanged && pnpm test:only"
41
51
  }
42
52
  }